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