1686cdd19SJun-ichiro itojun Hagino /* $FreeBSD$ */ 233841545SHajimu UMEMOTO /* $KAME: in6_cksum.c,v 1.10 2000/12/03 00:53:59 itojun Exp $ */ 3686cdd19SJun-ichiro itojun Hagino 4caf43b02SWarner Losh /*- 582cd038dSYoshinobu Inoue * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project. 682cd038dSYoshinobu Inoue * All rights reserved. 782cd038dSYoshinobu Inoue * 882cd038dSYoshinobu Inoue * Redistribution and use in source and binary forms, with or without 982cd038dSYoshinobu Inoue * modification, are permitted provided that the following conditions 1082cd038dSYoshinobu Inoue * are met: 1182cd038dSYoshinobu Inoue * 1. Redistributions of source code must retain the above copyright 1282cd038dSYoshinobu Inoue * notice, this list of conditions and the following disclaimer. 1382cd038dSYoshinobu Inoue * 2. Redistributions in binary form must reproduce the above copyright 1482cd038dSYoshinobu Inoue * notice, this list of conditions and the following disclaimer in the 1582cd038dSYoshinobu Inoue * documentation and/or other materials provided with the distribution. 1682cd038dSYoshinobu Inoue * 3. Neither the name of the project nor the names of its contributors 1782cd038dSYoshinobu Inoue * may be used to endorse or promote products derived from this software 1882cd038dSYoshinobu Inoue * without specific prior written permission. 1982cd038dSYoshinobu Inoue * 2082cd038dSYoshinobu Inoue * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND 2182cd038dSYoshinobu Inoue * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 2282cd038dSYoshinobu Inoue * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 2382cd038dSYoshinobu Inoue * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE 2482cd038dSYoshinobu Inoue * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 2582cd038dSYoshinobu Inoue * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 2682cd038dSYoshinobu Inoue * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 2782cd038dSYoshinobu Inoue * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 2882cd038dSYoshinobu Inoue * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 2982cd038dSYoshinobu Inoue * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 3082cd038dSYoshinobu Inoue * SUCH DAMAGE. 3182cd038dSYoshinobu Inoue */ 3282cd038dSYoshinobu Inoue 33caf43b02SWarner Losh /*- 3482cd038dSYoshinobu Inoue * Copyright (c) 1988, 1992, 1993 3582cd038dSYoshinobu Inoue * The Regents of the University of California. All rights reserved. 3682cd038dSYoshinobu Inoue * 3782cd038dSYoshinobu Inoue * Redistribution and use in source and binary forms, with or without 3882cd038dSYoshinobu Inoue * modification, are permitted provided that the following conditions 3982cd038dSYoshinobu Inoue * are met: 4082cd038dSYoshinobu Inoue * 1. Redistributions of source code must retain the above copyright 4182cd038dSYoshinobu Inoue * notice, this list of conditions and the following disclaimer. 4282cd038dSYoshinobu Inoue * 2. Redistributions in binary form must reproduce the above copyright 4382cd038dSYoshinobu Inoue * notice, this list of conditions and the following disclaimer in the 4482cd038dSYoshinobu Inoue * documentation and/or other materials provided with the distribution. 4582cd038dSYoshinobu Inoue * 4. Neither the name of the University nor the names of its contributors 4682cd038dSYoshinobu Inoue * may be used to endorse or promote products derived from this software 4782cd038dSYoshinobu Inoue * without specific prior written permission. 4882cd038dSYoshinobu Inoue * 4982cd038dSYoshinobu Inoue * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 5082cd038dSYoshinobu Inoue * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 5182cd038dSYoshinobu Inoue * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 5282cd038dSYoshinobu Inoue * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 5382cd038dSYoshinobu Inoue * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 5482cd038dSYoshinobu Inoue * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 5582cd038dSYoshinobu Inoue * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 5682cd038dSYoshinobu Inoue * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 5782cd038dSYoshinobu Inoue * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 5882cd038dSYoshinobu Inoue * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 5982cd038dSYoshinobu Inoue * SUCH DAMAGE. 6082cd038dSYoshinobu Inoue * 6182cd038dSYoshinobu Inoue * @(#)in_cksum.c 8.1 (Berkeley) 6/10/93 6282cd038dSYoshinobu Inoue */ 6382cd038dSYoshinobu Inoue 6482cd038dSYoshinobu Inoue #include <sys/param.h> 6582cd038dSYoshinobu Inoue #include <sys/mbuf.h> 6682cd038dSYoshinobu Inoue #include <sys/systm.h> 6782cd038dSYoshinobu Inoue #include <netinet/in.h> 68686cdd19SJun-ichiro itojun Hagino #include <netinet/ip6.h> 69a1f7e5f8SHajimu UMEMOTO #include <netinet6/scope6_var.h> 7082cd038dSYoshinobu Inoue 7182cd038dSYoshinobu Inoue /* 7282cd038dSYoshinobu Inoue * Checksum routine for Internet Protocol family headers (Portable Version). 7382cd038dSYoshinobu Inoue * 7482cd038dSYoshinobu Inoue * This routine is very heavily used in the network 7582cd038dSYoshinobu Inoue * code and should be modified for each CPU to be as fast as possible. 7682cd038dSYoshinobu Inoue */ 7782cd038dSYoshinobu Inoue 7882cd038dSYoshinobu Inoue #define ADDCARRY(x) (x > 65535 ? x -= 65535 : x) 7982cd038dSYoshinobu Inoue #define REDUCE {l_util.l = sum; sum = l_util.s[0] + l_util.s[1]; ADDCARRY(sum);} 8082cd038dSYoshinobu Inoue 8182cd038dSYoshinobu Inoue /* 8282cd038dSYoshinobu Inoue * m MUST contain a continuous IP6 header. 839d5abbddSJens Schweikhardt * off is an offset where TCP/UDP/ICMP6 header starts. 8482cd038dSYoshinobu Inoue * len is a total length of a transport segment. 8582cd038dSYoshinobu Inoue * (e.g. TCP header + TCP payload) 8682cd038dSYoshinobu Inoue */ 8782cd038dSYoshinobu Inoue 8882cd038dSYoshinobu Inoue int 899f8a02f1SRobert Watson in6_cksum(struct mbuf *m, u_int8_t nxt, u_int32_t off, u_int32_t len) 9082cd038dSYoshinobu Inoue { 9133841545SHajimu UMEMOTO u_int16_t *w; 9233841545SHajimu UMEMOTO int sum = 0; 9333841545SHajimu UMEMOTO int mlen = 0; 9482cd038dSYoshinobu Inoue int byte_swapped = 0; 9582cd038dSYoshinobu Inoue struct ip6_hdr *ip6; 96a1f7e5f8SHajimu UMEMOTO struct in6_addr in6; 9729a3d1b0SJun-ichiro itojun Hagino union { 9829a3d1b0SJun-ichiro itojun Hagino u_int16_t phs[4]; 9929a3d1b0SJun-ichiro itojun Hagino struct { 10029a3d1b0SJun-ichiro itojun Hagino u_int32_t ph_len; 10129a3d1b0SJun-ichiro itojun Hagino u_int8_t ph_zero[3]; 10229a3d1b0SJun-ichiro itojun Hagino u_int8_t ph_nxt; 1031c0ee39eSWarner Losh } __packed ph; 10429a3d1b0SJun-ichiro itojun Hagino } uph; 10582cd038dSYoshinobu Inoue union { 10682cd038dSYoshinobu Inoue u_int8_t c[2]; 10782cd038dSYoshinobu Inoue u_int16_t s; 10882cd038dSYoshinobu Inoue } s_util; 10982cd038dSYoshinobu Inoue union { 11082cd038dSYoshinobu Inoue u_int16_t s[2]; 11182cd038dSYoshinobu Inoue u_int32_t l; 11282cd038dSYoshinobu Inoue } l_util; 11382cd038dSYoshinobu Inoue 11482cd038dSYoshinobu Inoue /* sanity check */ 11582cd038dSYoshinobu Inoue if (m->m_pkthdr.len < off + len) { 11677d43daeSSUZUKI Shinsuke panic("in6_cksum: mbuf len (%d) < off+len (%d+%d)", 11782cd038dSYoshinobu Inoue m->m_pkthdr.len, off, len); 11882cd038dSYoshinobu Inoue } 11982cd038dSYoshinobu Inoue 12029a3d1b0SJun-ichiro itojun Hagino bzero(&uph, sizeof(uph)); 12129a3d1b0SJun-ichiro itojun Hagino 12282cd038dSYoshinobu Inoue /* 12382cd038dSYoshinobu Inoue * First create IP6 pseudo header and calculate a summary. 12482cd038dSYoshinobu Inoue */ 12582cd038dSYoshinobu Inoue ip6 = mtod(m, struct ip6_hdr *); 12682cd038dSYoshinobu Inoue uph.ph.ph_len = htonl(len); 12782cd038dSYoshinobu Inoue uph.ph.ph_nxt = nxt; 12882cd038dSYoshinobu Inoue 129a1f7e5f8SHajimu UMEMOTO /* 130a1f7e5f8SHajimu UMEMOTO * IPv6 source address. 131a1f7e5f8SHajimu UMEMOTO * XXX: we'd like to avoid copying the address, but we can't due to 132a1f7e5f8SHajimu UMEMOTO * the possibly embedded scope zone ID. 133a1f7e5f8SHajimu UMEMOTO */ 134a1f7e5f8SHajimu UMEMOTO in6 = ip6->ip6_src; 135a1f7e5f8SHajimu UMEMOTO in6_clearscope(&in6); 136a1f7e5f8SHajimu UMEMOTO w = (u_int16_t *)&in6; 137a1f7e5f8SHajimu UMEMOTO sum += w[0]; sum += w[1]; sum += w[2]; sum += w[3]; 138a1f7e5f8SHajimu UMEMOTO sum += w[4]; sum += w[5]; sum += w[6]; sum += w[7]; 139a1f7e5f8SHajimu UMEMOTO 14082cd038dSYoshinobu Inoue /* IPv6 destination address */ 141a1f7e5f8SHajimu UMEMOTO in6 = ip6->ip6_dst; 142a1f7e5f8SHajimu UMEMOTO in6_clearscope(&in6); 143a1f7e5f8SHajimu UMEMOTO w = (u_int16_t *)&in6; 144a1f7e5f8SHajimu UMEMOTO sum += w[0]; sum += w[1]; sum += w[2]; sum += w[3]; 145a1f7e5f8SHajimu UMEMOTO sum += w[4]; sum += w[5]; sum += w[6]; sum += w[7]; 146a1f7e5f8SHajimu UMEMOTO 14782cd038dSYoshinobu Inoue /* Payload length and upper layer identifier */ 14882cd038dSYoshinobu Inoue sum += uph.phs[0]; sum += uph.phs[1]; 14982cd038dSYoshinobu Inoue sum += uph.phs[2]; sum += uph.phs[3]; 15082cd038dSYoshinobu Inoue 15182cd038dSYoshinobu Inoue /* 15282cd038dSYoshinobu Inoue * Secondly calculate a summary of the first mbuf excluding offset. 15382cd038dSYoshinobu Inoue */ 1544e6098c6SYaroslav Tykhiy while (off > 0) { 15582cd038dSYoshinobu Inoue if (m->m_len <= off) 15682cd038dSYoshinobu Inoue off -= m->m_len; 15782cd038dSYoshinobu Inoue else 15882cd038dSYoshinobu Inoue break; 15982cd038dSYoshinobu Inoue m = m->m_next; 16082cd038dSYoshinobu Inoue } 16182cd038dSYoshinobu Inoue w = (u_int16_t *)(mtod(m, u_char *) + off); 16282cd038dSYoshinobu Inoue mlen = m->m_len - off; 16382cd038dSYoshinobu Inoue if (len < mlen) 16482cd038dSYoshinobu Inoue mlen = len; 16582cd038dSYoshinobu Inoue len -= mlen; 16682cd038dSYoshinobu Inoue /* 16782cd038dSYoshinobu Inoue * Force to even boundary. 16882cd038dSYoshinobu Inoue */ 16982cd038dSYoshinobu Inoue if ((1 & (long) w) && (mlen > 0)) { 17082cd038dSYoshinobu Inoue REDUCE; 17182cd038dSYoshinobu Inoue sum <<= 8; 17282cd038dSYoshinobu Inoue s_util.c[0] = *(u_char *)w; 17382cd038dSYoshinobu Inoue w = (u_int16_t *)((char *)w + 1); 17482cd038dSYoshinobu Inoue mlen--; 17582cd038dSYoshinobu Inoue byte_swapped = 1; 17682cd038dSYoshinobu Inoue } 17782cd038dSYoshinobu Inoue /* 17882cd038dSYoshinobu Inoue * Unroll the loop to make overhead from 17982cd038dSYoshinobu Inoue * branches &c small. 18082cd038dSYoshinobu Inoue */ 18182cd038dSYoshinobu Inoue while ((mlen -= 32) >= 0) { 18282cd038dSYoshinobu Inoue sum += w[0]; sum += w[1]; sum += w[2]; sum += w[3]; 18382cd038dSYoshinobu Inoue sum += w[4]; sum += w[5]; sum += w[6]; sum += w[7]; 18482cd038dSYoshinobu Inoue sum += w[8]; sum += w[9]; sum += w[10]; sum += w[11]; 18582cd038dSYoshinobu Inoue sum += w[12]; sum += w[13]; sum += w[14]; sum += w[15]; 18682cd038dSYoshinobu Inoue w += 16; 18782cd038dSYoshinobu Inoue } 18882cd038dSYoshinobu Inoue mlen += 32; 18982cd038dSYoshinobu Inoue while ((mlen -= 8) >= 0) { 19082cd038dSYoshinobu Inoue sum += w[0]; sum += w[1]; sum += w[2]; sum += w[3]; 19182cd038dSYoshinobu Inoue w += 4; 19282cd038dSYoshinobu Inoue } 19382cd038dSYoshinobu Inoue mlen += 8; 19482cd038dSYoshinobu Inoue if (mlen == 0 && byte_swapped == 0) 19582cd038dSYoshinobu Inoue goto next; 19682cd038dSYoshinobu Inoue REDUCE; 19782cd038dSYoshinobu Inoue while ((mlen -= 2) >= 0) { 19882cd038dSYoshinobu Inoue sum += *w++; 19982cd038dSYoshinobu Inoue } 20082cd038dSYoshinobu Inoue if (byte_swapped) { 20182cd038dSYoshinobu Inoue REDUCE; 20282cd038dSYoshinobu Inoue sum <<= 8; 20382cd038dSYoshinobu Inoue byte_swapped = 0; 20482cd038dSYoshinobu Inoue if (mlen == -1) { 20582cd038dSYoshinobu Inoue s_util.c[1] = *(char *)w; 20682cd038dSYoshinobu Inoue sum += s_util.s; 20782cd038dSYoshinobu Inoue mlen = 0; 20882cd038dSYoshinobu Inoue } else 20982cd038dSYoshinobu Inoue mlen = -1; 21082cd038dSYoshinobu Inoue } else if (mlen == -1) 21182cd038dSYoshinobu Inoue s_util.c[0] = *(char *)w; 21282cd038dSYoshinobu Inoue next: 21382cd038dSYoshinobu Inoue m = m->m_next; 21482cd038dSYoshinobu Inoue 21582cd038dSYoshinobu Inoue /* 21682cd038dSYoshinobu Inoue * Lastly calculate a summary of the rest of mbufs. 21782cd038dSYoshinobu Inoue */ 21882cd038dSYoshinobu Inoue 21982cd038dSYoshinobu Inoue for (;m && len; m = m->m_next) { 22082cd038dSYoshinobu Inoue if (m->m_len == 0) 22182cd038dSYoshinobu Inoue continue; 22282cd038dSYoshinobu Inoue w = mtod(m, u_int16_t *); 22382cd038dSYoshinobu Inoue if (mlen == -1) { 22482cd038dSYoshinobu Inoue /* 22582cd038dSYoshinobu Inoue * The first byte of this mbuf is the continuation 22682cd038dSYoshinobu Inoue * of a word spanning between this mbuf and the 22782cd038dSYoshinobu Inoue * last mbuf. 22882cd038dSYoshinobu Inoue * 22982cd038dSYoshinobu Inoue * s_util.c[0] is already saved when scanning previous 23082cd038dSYoshinobu Inoue * mbuf. 23182cd038dSYoshinobu Inoue */ 23282cd038dSYoshinobu Inoue s_util.c[1] = *(char *)w; 23382cd038dSYoshinobu Inoue sum += s_util.s; 23482cd038dSYoshinobu Inoue w = (u_int16_t *)((char *)w + 1); 23582cd038dSYoshinobu Inoue mlen = m->m_len - 1; 23682cd038dSYoshinobu Inoue len--; 23782cd038dSYoshinobu Inoue } else 23882cd038dSYoshinobu Inoue mlen = m->m_len; 23982cd038dSYoshinobu Inoue if (len < mlen) 24082cd038dSYoshinobu Inoue mlen = len; 24182cd038dSYoshinobu Inoue len -= mlen; 24282cd038dSYoshinobu Inoue /* 24382cd038dSYoshinobu Inoue * Force to even boundary. 24482cd038dSYoshinobu Inoue */ 24582cd038dSYoshinobu Inoue if ((1 & (long) w) && (mlen > 0)) { 24682cd038dSYoshinobu Inoue REDUCE; 24782cd038dSYoshinobu Inoue sum <<= 8; 24882cd038dSYoshinobu Inoue s_util.c[0] = *(u_char *)w; 24982cd038dSYoshinobu Inoue w = (u_int16_t *)((char *)w + 1); 25082cd038dSYoshinobu Inoue mlen--; 25182cd038dSYoshinobu Inoue byte_swapped = 1; 25282cd038dSYoshinobu Inoue } 25382cd038dSYoshinobu Inoue /* 25482cd038dSYoshinobu Inoue * Unroll the loop to make overhead from 25582cd038dSYoshinobu Inoue * branches &c small. 25682cd038dSYoshinobu Inoue */ 25782cd038dSYoshinobu Inoue while ((mlen -= 32) >= 0) { 25882cd038dSYoshinobu Inoue sum += w[0]; sum += w[1]; sum += w[2]; sum += w[3]; 25982cd038dSYoshinobu Inoue sum += w[4]; sum += w[5]; sum += w[6]; sum += w[7]; 26082cd038dSYoshinobu Inoue sum += w[8]; sum += w[9]; sum += w[10]; sum += w[11]; 26182cd038dSYoshinobu Inoue sum += w[12]; sum += w[13]; sum += w[14]; sum += w[15]; 26282cd038dSYoshinobu Inoue w += 16; 26382cd038dSYoshinobu Inoue } 26482cd038dSYoshinobu Inoue mlen += 32; 26582cd038dSYoshinobu Inoue while ((mlen -= 8) >= 0) { 26682cd038dSYoshinobu Inoue sum += w[0]; sum += w[1]; sum += w[2]; sum += w[3]; 26782cd038dSYoshinobu Inoue w += 4; 26882cd038dSYoshinobu Inoue } 26982cd038dSYoshinobu Inoue mlen += 8; 27082cd038dSYoshinobu Inoue if (mlen == 0 && byte_swapped == 0) 27182cd038dSYoshinobu Inoue continue; 27282cd038dSYoshinobu Inoue REDUCE; 27382cd038dSYoshinobu Inoue while ((mlen -= 2) >= 0) { 27482cd038dSYoshinobu Inoue sum += *w++; 27582cd038dSYoshinobu Inoue } 27682cd038dSYoshinobu Inoue if (byte_swapped) { 27782cd038dSYoshinobu Inoue REDUCE; 27882cd038dSYoshinobu Inoue sum <<= 8; 27982cd038dSYoshinobu Inoue byte_swapped = 0; 28082cd038dSYoshinobu Inoue if (mlen == -1) { 28182cd038dSYoshinobu Inoue s_util.c[1] = *(char *)w; 28282cd038dSYoshinobu Inoue sum += s_util.s; 28382cd038dSYoshinobu Inoue mlen = 0; 28482cd038dSYoshinobu Inoue } else 28582cd038dSYoshinobu Inoue mlen = -1; 28682cd038dSYoshinobu Inoue } else if (mlen == -1) 28782cd038dSYoshinobu Inoue s_util.c[0] = *(char *)w; 28882cd038dSYoshinobu Inoue } 28982cd038dSYoshinobu Inoue if (len) 29077d43daeSSUZUKI Shinsuke panic("in6_cksum: out of data"); 29182cd038dSYoshinobu Inoue if (mlen == -1) { 29282cd038dSYoshinobu Inoue /* The last mbuf has odd # of bytes. Follow the 29382cd038dSYoshinobu Inoue standard (the odd byte may be shifted left by 8 bits 29482cd038dSYoshinobu Inoue or not as determined by endian-ness of the machine) */ 29582cd038dSYoshinobu Inoue s_util.c[1] = 0; 29682cd038dSYoshinobu Inoue sum += s_util.s; 29782cd038dSYoshinobu Inoue } 29882cd038dSYoshinobu Inoue REDUCE; 29982cd038dSYoshinobu Inoue return (~sum & 0xffff); 30082cd038dSYoshinobu Inoue } 301