in6_cksum.c revision 160051
162587Sitojun/* $FreeBSD: head/sys/netinet6/in6_cksum.c 160051 2006-06-30 18:25:07Z yar $ */ 278064Sume/* $KAME: in6_cksum.c,v 1.10 2000/12/03 00:53:59 itojun Exp $ */ 362587Sitojun 4139826Simp/*- 553541Sshin * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project. 653541Sshin * All rights reserved. 753541Sshin * 853541Sshin * Redistribution and use in source and binary forms, with or without 953541Sshin * modification, are permitted provided that the following conditions 1053541Sshin * are met: 1153541Sshin * 1. Redistributions of source code must retain the above copyright 1253541Sshin * notice, this list of conditions and the following disclaimer. 1353541Sshin * 2. Redistributions in binary form must reproduce the above copyright 1453541Sshin * notice, this list of conditions and the following disclaimer in the 1553541Sshin * documentation and/or other materials provided with the distribution. 1653541Sshin * 3. Neither the name of the project nor the names of its contributors 1753541Sshin * may be used to endorse or promote products derived from this software 1853541Sshin * without specific prior written permission. 1953541Sshin * 2053541Sshin * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND 2153541Sshin * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 2253541Sshin * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 2353541Sshin * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE 2453541Sshin * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 2553541Sshin * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 2653541Sshin * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 2753541Sshin * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 2853541Sshin * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 2953541Sshin * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 3053541Sshin * SUCH DAMAGE. 3153541Sshin */ 3253541Sshin 33139826Simp/*- 3453541Sshin * Copyright (c) 1988, 1992, 1993 3553541Sshin * The Regents of the University of California. All rights reserved. 3653541Sshin * 3753541Sshin * Redistribution and use in source and binary forms, with or without 3853541Sshin * modification, are permitted provided that the following conditions 3953541Sshin * are met: 4053541Sshin * 1. Redistributions of source code must retain the above copyright 4153541Sshin * notice, this list of conditions and the following disclaimer. 4253541Sshin * 2. Redistributions in binary form must reproduce the above copyright 4353541Sshin * notice, this list of conditions and the following disclaimer in the 4453541Sshin * documentation and/or other materials provided with the distribution. 4553541Sshin * 4. Neither the name of the University nor the names of its contributors 4653541Sshin * may be used to endorse or promote products derived from this software 4753541Sshin * without specific prior written permission. 4853541Sshin * 4953541Sshin * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 5053541Sshin * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 5153541Sshin * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 5253541Sshin * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 5353541Sshin * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 5453541Sshin * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 5553541Sshin * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 5653541Sshin * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 5753541Sshin * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 5853541Sshin * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 5953541Sshin * SUCH DAMAGE. 6053541Sshin * 6153541Sshin * @(#)in_cksum.c 8.1 (Berkeley) 6/10/93 6253541Sshin */ 6353541Sshin 6453541Sshin#include <sys/param.h> 6553541Sshin#include <sys/mbuf.h> 6653541Sshin#include <sys/systm.h> 6753541Sshin#include <netinet/in.h> 6862587Sitojun#include <netinet/ip6.h> 69148385Sume#include <netinet6/scope6_var.h> 7053541Sshin 7153541Sshin#include <net/net_osdep.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) 8162587Sitojun#define REDUCE {l_util.l = sum; sum = l_util.s[0] + l_util.s[1]; ADDCARRY(sum);} 8253541Sshin 8353541Sshin/* 8453541Sshin * m MUST contain a continuous IP6 header. 85108533Sschweikh * off is an offset where TCP/UDP/ICMP6 header starts. 8653541Sshin * len is a total length of a transport segment. 8753541Sshin * (e.g. TCP header + TCP payload) 8853541Sshin */ 8953541Sshin 9053541Sshinint 91154667Srwatsonin6_cksum(struct mbuf *m, u_int8_t nxt, u_int32_t off, u_int32_t len) 9253541Sshin{ 9378064Sume u_int16_t *w; 9478064Sume int sum = 0; 9578064Sume int mlen = 0; 9653541Sshin int byte_swapped = 0; 97114205Ssuz struct ip6_hdr *ip6; 98148385Sume struct in6_addr in6; 9953541Sshin union { 10065637Sitojun u_int16_t phs[4]; 10165637Sitojun struct { 10265637Sitojun u_int32_t ph_len; 10365637Sitojun u_int8_t ph_zero[3]; 10465637Sitojun u_int8_t ph_nxt; 105103842Salfred } ph __packed; 10665637Sitojun } uph; 10765637Sitojun union { 10853541Sshin u_int8_t c[2]; 10953541Sshin u_int16_t s; 11053541Sshin } s_util; 11153541Sshin union { 11253541Sshin u_int16_t s[2]; 11353541Sshin u_int32_t l; 11453541Sshin } l_util; 11553541Sshin 11653541Sshin /* sanity check */ 11753541Sshin if (m->m_pkthdr.len < off + len) { 118114205Ssuz panic("in6_cksum: mbuf len (%d) < off+len (%d+%d)", 11953541Sshin m->m_pkthdr.len, off, len); 12053541Sshin } 12153541Sshin 12265637Sitojun bzero(&uph, sizeof(uph)); 12365637Sitojun 12453541Sshin /* 12553541Sshin * First create IP6 pseudo header and calculate a summary. 12653541Sshin */ 12753541Sshin ip6 = mtod(m, struct ip6_hdr *); 12853541Sshin uph.ph.ph_len = htonl(len); 12953541Sshin uph.ph.ph_nxt = nxt; 13053541Sshin 131148385Sume /* 132148385Sume * IPv6 source address. 133148385Sume * XXX: we'd like to avoid copying the address, but we can't due to 134148385Sume * the possibly embedded scope zone ID. 135148385Sume */ 136148385Sume in6 = ip6->ip6_src; 137148385Sume in6_clearscope(&in6); 138148385Sume w = (u_int16_t *)&in6; 139148385Sume sum += w[0]; sum += w[1]; sum += w[2]; sum += w[3]; 140148385Sume sum += w[4]; sum += w[5]; sum += w[6]; sum += w[7]; 141148385Sume 14253541Sshin /* IPv6 destination address */ 143148385Sume in6 = ip6->ip6_dst; 144148385Sume in6_clearscope(&in6); 145148385Sume w = (u_int16_t *)&in6; 146148385Sume sum += w[0]; sum += w[1]; sum += w[2]; sum += w[3]; 147148385Sume sum += w[4]; sum += w[5]; sum += w[6]; sum += w[7]; 148148385Sume 14953541Sshin /* Payload length and upper layer identifier */ 15053541Sshin sum += uph.phs[0]; sum += uph.phs[1]; 15153541Sshin sum += uph.phs[2]; sum += uph.phs[3]; 15253541Sshin 15353541Sshin /* 15453541Sshin * Secondly calculate a summary of the first mbuf excluding offset. 15553541Sshin */ 156160051Syar while (off > 0) { 15753541Sshin if (m->m_len <= off) 15853541Sshin off -= m->m_len; 15953541Sshin else 16053541Sshin break; 16153541Sshin m = m->m_next; 16253541Sshin } 16353541Sshin w = (u_int16_t *)(mtod(m, u_char *) + off); 16453541Sshin mlen = m->m_len - off; 16553541Sshin if (len < mlen) 16653541Sshin mlen = len; 16753541Sshin len -= mlen; 16853541Sshin /* 16953541Sshin * Force to even boundary. 17053541Sshin */ 17153541Sshin if ((1 & (long) w) && (mlen > 0)) { 17253541Sshin REDUCE; 17353541Sshin sum <<= 8; 17453541Sshin s_util.c[0] = *(u_char *)w; 17553541Sshin w = (u_int16_t *)((char *)w + 1); 17653541Sshin mlen--; 17753541Sshin byte_swapped = 1; 17853541Sshin } 17953541Sshin /* 18053541Sshin * Unroll the loop to make overhead from 18153541Sshin * branches &c small. 18253541Sshin */ 18353541Sshin while ((mlen -= 32) >= 0) { 18453541Sshin sum += w[0]; sum += w[1]; sum += w[2]; sum += w[3]; 18553541Sshin sum += w[4]; sum += w[5]; sum += w[6]; sum += w[7]; 18653541Sshin sum += w[8]; sum += w[9]; sum += w[10]; sum += w[11]; 18753541Sshin sum += w[12]; sum += w[13]; sum += w[14]; sum += w[15]; 18853541Sshin w += 16; 18953541Sshin } 19053541Sshin mlen += 32; 19153541Sshin while ((mlen -= 8) >= 0) { 19253541Sshin sum += w[0]; sum += w[1]; sum += w[2]; sum += w[3]; 19353541Sshin w += 4; 19453541Sshin } 19553541Sshin mlen += 8; 19653541Sshin if (mlen == 0 && byte_swapped == 0) 19753541Sshin goto next; 19853541Sshin REDUCE; 19953541Sshin while ((mlen -= 2) >= 0) { 20053541Sshin sum += *w++; 20153541Sshin } 20253541Sshin if (byte_swapped) { 20353541Sshin REDUCE; 20453541Sshin sum <<= 8; 20553541Sshin byte_swapped = 0; 20653541Sshin if (mlen == -1) { 20753541Sshin s_util.c[1] = *(char *)w; 20853541Sshin sum += s_util.s; 20953541Sshin mlen = 0; 21053541Sshin } else 21153541Sshin mlen = -1; 21253541Sshin } else if (mlen == -1) 21353541Sshin s_util.c[0] = *(char *)w; 21453541Sshin next: 21553541Sshin m = m->m_next; 21653541Sshin 21753541Sshin /* 21853541Sshin * Lastly calculate a summary of the rest of mbufs. 21953541Sshin */ 220114205Ssuz 22153541Sshin for (;m && len; m = m->m_next) { 22253541Sshin if (m->m_len == 0) 22353541Sshin continue; 22453541Sshin w = mtod(m, u_int16_t *); 22553541Sshin if (mlen == -1) { 22653541Sshin /* 22753541Sshin * The first byte of this mbuf is the continuation 22853541Sshin * of a word spanning between this mbuf and the 22953541Sshin * last mbuf. 23053541Sshin * 23153541Sshin * s_util.c[0] is already saved when scanning previous 23253541Sshin * mbuf. 23353541Sshin */ 23453541Sshin s_util.c[1] = *(char *)w; 23553541Sshin sum += s_util.s; 23653541Sshin w = (u_int16_t *)((char *)w + 1); 23753541Sshin mlen = m->m_len - 1; 23853541Sshin len--; 23953541Sshin } else 24053541Sshin mlen = m->m_len; 24153541Sshin if (len < mlen) 24253541Sshin mlen = len; 24353541Sshin len -= mlen; 24453541Sshin /* 24553541Sshin * Force to even boundary. 24653541Sshin */ 24753541Sshin if ((1 & (long) w) && (mlen > 0)) { 24853541Sshin REDUCE; 24953541Sshin sum <<= 8; 25053541Sshin s_util.c[0] = *(u_char *)w; 25153541Sshin w = (u_int16_t *)((char *)w + 1); 25253541Sshin mlen--; 25353541Sshin byte_swapped = 1; 25453541Sshin } 25553541Sshin /* 25653541Sshin * Unroll the loop to make overhead from 25753541Sshin * branches &c small. 25853541Sshin */ 25953541Sshin while ((mlen -= 32) >= 0) { 26053541Sshin sum += w[0]; sum += w[1]; sum += w[2]; sum += w[3]; 26153541Sshin sum += w[4]; sum += w[5]; sum += w[6]; sum += w[7]; 26253541Sshin sum += w[8]; sum += w[9]; sum += w[10]; sum += w[11]; 26353541Sshin sum += w[12]; sum += w[13]; sum += w[14]; sum += w[15]; 26453541Sshin w += 16; 26553541Sshin } 26653541Sshin mlen += 32; 26753541Sshin while ((mlen -= 8) >= 0) { 26853541Sshin sum += w[0]; sum += w[1]; sum += w[2]; sum += w[3]; 26953541Sshin w += 4; 27053541Sshin } 27153541Sshin mlen += 8; 27253541Sshin if (mlen == 0 && byte_swapped == 0) 27353541Sshin continue; 27453541Sshin REDUCE; 27553541Sshin while ((mlen -= 2) >= 0) { 27653541Sshin sum += *w++; 27753541Sshin } 27853541Sshin if (byte_swapped) { 27953541Sshin REDUCE; 28053541Sshin sum <<= 8; 28153541Sshin byte_swapped = 0; 28253541Sshin if (mlen == -1) { 28353541Sshin s_util.c[1] = *(char *)w; 28453541Sshin sum += s_util.s; 28553541Sshin mlen = 0; 28653541Sshin } else 28753541Sshin mlen = -1; 28853541Sshin } else if (mlen == -1) 28953541Sshin s_util.c[0] = *(char *)w; 29053541Sshin } 29153541Sshin if (len) 292114205Ssuz panic("in6_cksum: out of data"); 29353541Sshin if (mlen == -1) { 29453541Sshin /* The last mbuf has odd # of bytes. Follow the 29553541Sshin standard (the odd byte may be shifted left by 8 bits 29653541Sshin or not as determined by endian-ness of the machine) */ 29753541Sshin s_util.c[1] = 0; 29853541Sshin sum += s_util.s; 29953541Sshin } 30053541Sshin REDUCE; 30153541Sshin return (~sum & 0xffff); 30253541Sshin} 303