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