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