in6_cksum.c revision 215956
1239400Sandreast/*-
2239400Sandreast * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project.
3239400Sandreast * All rights reserved.
4239400Sandreast *
5239400Sandreast * Redistribution and use in source and binary forms, with or without
6239400Sandreast * modification, are permitted provided that the following conditions
7239400Sandreast * are met:
8239400Sandreast * 1. Redistributions of source code must retain the above copyright
9239400Sandreast *    notice, this list of conditions and the following disclaimer.
10239400Sandreast * 2. Redistributions in binary form must reproduce the above copyright
11239400Sandreast *    notice, this list of conditions and the following disclaimer in the
12239400Sandreast *    documentation and/or other materials provided with the distribution.
13239400Sandreast * 3. Neither the name of the project nor the names of its contributors
14239400Sandreast *    may be used to endorse or promote products derived from this software
15239400Sandreast *    without specific prior written permission.
16239400Sandreast *
17239400Sandreast * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND
18239400Sandreast * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19239400Sandreast * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20239400Sandreast * ARE DISCLAIMED.  IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE
21239400Sandreast * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22239400Sandreast * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23239400Sandreast * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24239400Sandreast * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25239400Sandreast * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26239400Sandreast * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27239400Sandreast * SUCH DAMAGE.
28239400Sandreast *
29239400Sandreast *	$KAME: in6_cksum.c,v 1.10 2000/12/03 00:53:59 itojun Exp $
30239400Sandreast */
31239400Sandreast
32239400Sandreast/*-
33239400Sandreast * Copyright (c) 1988, 1992, 1993
34239400Sandreast *	The Regents of the University of California.  All rights reserved.
35239400Sandreast *
36239400Sandreast * Redistribution and use in source and binary forms, with or without
37239400Sandreast * modification, are permitted provided that the following conditions
38239400Sandreast * are met:
39239400Sandreast * 1. Redistributions of source code must retain the above copyright
40239400Sandreast *    notice, this list of conditions and the following disclaimer.
41239400Sandreast * 2. Redistributions in binary form must reproduce the above copyright
42239400Sandreast *    notice, this list of conditions and the following disclaimer in the
43239400Sandreast *    documentation and/or other materials provided with the distribution.
44239400Sandreast * 4. Neither the name of the University nor the names of its contributors
45239400Sandreast *    may be used to endorse or promote products derived from this software
46239400Sandreast *    without specific prior written permission.
47239400Sandreast *
48239400Sandreast * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
49239400Sandreast * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
50239400Sandreast * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
51239400Sandreast * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
52239400Sandreast * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
53239400Sandreast * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
54239400Sandreast * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
55239400Sandreast * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
56239400Sandreast * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
57239400Sandreast * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
58239400Sandreast * SUCH DAMAGE.
59239400Sandreast *
60239400Sandreast *	@(#)in_cksum.c	8.1 (Berkeley) 6/10/93
61239400Sandreast */
62239400Sandreast
63239400Sandreast#include <sys/cdefs.h>
64239400Sandreast__FBSDID("$FreeBSD: head/sys/netinet6/in6_cksum.c 215956 2010-11-27 21:51:39Z brucec $");
65239400Sandreast
66239400Sandreast#include <sys/param.h>
67239400Sandreast#include <sys/mbuf.h>
68239400Sandreast#include <sys/systm.h>
69239400Sandreast#include <netinet/in.h>
70239400Sandreast#include <netinet/ip6.h>
71239400Sandreast#include <netinet6/scope6_var.h>
72239400Sandreast
73239400Sandreast/*
74239400Sandreast * Checksum routine for Internet Protocol family headers (Portable Version).
75239400Sandreast *
76239400Sandreast * This routine is very heavily used in the network
77239400Sandreast * code and should be modified for each CPU to be as fast as possible.
78239400Sandreast */
79239400Sandreast
80239400Sandreast#define ADDCARRY(x)  (x > 65535 ? x -= 65535 : x)
81239400Sandreast#define REDUCE {l_util.l = sum; sum = l_util.s[0] + l_util.s[1]; (void)ADDCARRY(sum);}
82239400Sandreast
83239400Sandreast/*
84239400Sandreast * m MUST contain a contiguous IP6 header.
85239400Sandreast * off is an offset where TCP/UDP/ICMP6 header starts.
86239400Sandreast * len is a total length of a transport segment.
87239400Sandreast * (e.g. TCP header + TCP payload)
88239400Sandreast */
89239400Sandreastint
90239400Sandreastin6_cksum(struct mbuf *m, u_int8_t nxt, u_int32_t off, u_int32_t len)
91239400Sandreast{
92239400Sandreast	u_int16_t *w;
93239400Sandreast	int sum = 0;
94239400Sandreast	int mlen = 0;
95239400Sandreast	int byte_swapped = 0;
96239400Sandreast	struct ip6_hdr *ip6;
97239400Sandreast	struct in6_addr in6;
98239400Sandreast	union {
99239400Sandreast		u_int16_t phs[4];
100239400Sandreast		struct {
101239400Sandreast			u_int32_t	ph_len;
102239400Sandreast			u_int8_t	ph_zero[3];
103239400Sandreast			u_int8_t	ph_nxt;
104239400Sandreast		} __packed ph;
105239400Sandreast	} uph;
106239400Sandreast	union {
107239400Sandreast		u_int8_t	c[2];
108239400Sandreast		u_int16_t	s;
109239400Sandreast	} s_util;
110239400Sandreast	union {
111239400Sandreast		u_int16_t s[2];
112239400Sandreast		u_int32_t l;
113239400Sandreast	} l_util;
114239400Sandreast
115239400Sandreast	/* sanity check */
116239400Sandreast	if (m->m_pkthdr.len < off + len) {
117239400Sandreast		panic("in6_cksum: mbuf len (%d) < off+len (%d+%d)",
118239400Sandreast			m->m_pkthdr.len, off, len);
119239400Sandreast	}
120239400Sandreast
121239400Sandreast	bzero(&uph, sizeof(uph));
122239400Sandreast
123239400Sandreast	/*
124239400Sandreast	 * First create IP6 pseudo header and calculate a summary.
125239400Sandreast	 */
126239400Sandreast	ip6 = mtod(m, struct ip6_hdr *);
127239400Sandreast	uph.ph.ph_len = htonl(len);
128239400Sandreast	uph.ph.ph_nxt = nxt;
129239400Sandreast
130239400Sandreast	/*
131239400Sandreast	 * IPv6 source address.
132239400Sandreast	 * XXX: we'd like to avoid copying the address, but we can't due to
133239400Sandreast	 * the possibly embedded scope zone ID.
134239400Sandreast	 */
135239400Sandreast	in6 = ip6->ip6_src;
136239400Sandreast	in6_clearscope(&in6);
137239400Sandreast	w = (u_int16_t *)&in6;
138239400Sandreast	sum += w[0]; sum += w[1]; sum += w[2]; sum += w[3];
139239400Sandreast	sum += w[4]; sum += w[5]; sum += w[6]; sum += w[7];
140239400Sandreast
141239400Sandreast	/* IPv6 destination address */
142239400Sandreast	in6 = ip6->ip6_dst;
143239400Sandreast	in6_clearscope(&in6);
144239400Sandreast	w = (u_int16_t *)&in6;
145239400Sandreast	sum += w[0]; sum += w[1]; sum += w[2]; sum += w[3];
146239400Sandreast	sum += w[4]; sum += w[5]; sum += w[6]; sum += w[7];
147239400Sandreast
148239400Sandreast	/* Payload length and upper layer identifier */
149239400Sandreast	sum += uph.phs[0];  sum += uph.phs[1];
150239400Sandreast	sum += uph.phs[2];  sum += uph.phs[3];
151239400Sandreast
152239400Sandreast	/*
153239400Sandreast	 * Secondly calculate a summary of the first mbuf excluding offset.
154239400Sandreast	 */
155239400Sandreast	while (off > 0) {
156239400Sandreast		if (m->m_len <= off)
157239400Sandreast			off -= m->m_len;
158239400Sandreast		else
159239400Sandreast			break;
160239400Sandreast		m = m->m_next;
161239400Sandreast	}
162239400Sandreast	w = (u_int16_t *)(mtod(m, u_char *) + off);
163239400Sandreast	mlen = m->m_len - off;
164239400Sandreast	if (len < mlen)
165239400Sandreast		mlen = len;
166239400Sandreast	len -= mlen;
167239400Sandreast	/*
168239400Sandreast	 * Force to even boundary.
169239400Sandreast	 */
170239400Sandreast	if ((1 & (long) w) && (mlen > 0)) {
171239400Sandreast		REDUCE;
172239400Sandreast		sum <<= 8;
173239400Sandreast		s_util.c[0] = *(u_char *)w;
174239400Sandreast		w = (u_int16_t *)((char *)w + 1);
175239400Sandreast		mlen--;
176239400Sandreast		byte_swapped = 1;
177239400Sandreast	}
178239400Sandreast	/*
179239400Sandreast	 * Unroll the loop to make overhead from
180239400Sandreast	 * branches &c small.
181239400Sandreast	 */
182239400Sandreast	while ((mlen -= 32) >= 0) {
183239400Sandreast		sum += w[0]; sum += w[1]; sum += w[2]; sum += w[3];
184239400Sandreast		sum += w[4]; sum += w[5]; sum += w[6]; sum += w[7];
185239400Sandreast		sum += w[8]; sum += w[9]; sum += w[10]; sum += w[11];
186239400Sandreast		sum += w[12]; sum += w[13]; sum += w[14]; sum += w[15];
187239400Sandreast		w += 16;
188239400Sandreast	}
189239400Sandreast	mlen += 32;
190239400Sandreast	while ((mlen -= 8) >= 0) {
191239400Sandreast		sum += w[0]; sum += w[1]; sum += w[2]; sum += w[3];
192239400Sandreast		w += 4;
193239400Sandreast	}
194239400Sandreast	mlen += 8;
195239400Sandreast	if (mlen == 0 && byte_swapped == 0)
196239400Sandreast		goto next;
197239400Sandreast	REDUCE;
198239400Sandreast	while ((mlen -= 2) >= 0) {
199239400Sandreast		sum += *w++;
200239400Sandreast	}
201239400Sandreast	if (byte_swapped) {
202239400Sandreast		REDUCE;
203239400Sandreast		sum <<= 8;
204239400Sandreast		byte_swapped = 0;
205239400Sandreast		if (mlen == -1) {
206239400Sandreast			s_util.c[1] = *(char *)w;
207239400Sandreast			sum += s_util.s;
208239400Sandreast			mlen = 0;
209239400Sandreast		} else
210239400Sandreast			mlen = -1;
211239400Sandreast	} else if (mlen == -1)
212239400Sandreast		s_util.c[0] = *(char *)w;
213239400Sandreast next:
214239400Sandreast	m = m->m_next;
215239400Sandreast
216239400Sandreast	/*
217239400Sandreast	 * Lastly calculate a summary of the rest of mbufs.
218239400Sandreast	 */
219239400Sandreast
220239400Sandreast	for (;m && len; m = m->m_next) {
221239400Sandreast		if (m->m_len == 0)
222239400Sandreast			continue;
223239400Sandreast		w = mtod(m, u_int16_t *);
224239400Sandreast		if (mlen == -1) {
225239400Sandreast			/*
226239400Sandreast			 * The first byte of this mbuf is the continuation
227239400Sandreast			 * of a word spanning between this mbuf and the
228239400Sandreast			 * last mbuf.
229239400Sandreast			 *
230239400Sandreast			 * s_util.c[0] is already saved when scanning previous
231239400Sandreast			 * mbuf.
232239400Sandreast			 */
233239400Sandreast			s_util.c[1] = *(char *)w;
234239400Sandreast			sum += s_util.s;
235239400Sandreast			w = (u_int16_t *)((char *)w + 1);
236239400Sandreast			mlen = m->m_len - 1;
237239400Sandreast			len--;
238239400Sandreast		} else
239239400Sandreast			mlen = m->m_len;
240239400Sandreast		if (len < mlen)
241239400Sandreast			mlen = len;
242239400Sandreast		len -= mlen;
243239400Sandreast		/*
244239400Sandreast		 * Force to even boundary.
245239400Sandreast		 */
246239400Sandreast		if ((1 & (long) w) && (mlen > 0)) {
247239400Sandreast			REDUCE;
248239400Sandreast			sum <<= 8;
249239400Sandreast			s_util.c[0] = *(u_char *)w;
250239400Sandreast			w = (u_int16_t *)((char *)w + 1);
251239400Sandreast			mlen--;
252239400Sandreast			byte_swapped = 1;
253239400Sandreast		}
254239400Sandreast		/*
255239400Sandreast		 * Unroll the loop to make overhead from
256239400Sandreast		 * branches &c small.
257239400Sandreast		 */
258239400Sandreast		while ((mlen -= 32) >= 0) {
259239400Sandreast			sum += w[0]; sum += w[1]; sum += w[2]; sum += w[3];
260239400Sandreast			sum += w[4]; sum += w[5]; sum += w[6]; sum += w[7];
261239400Sandreast			sum += w[8]; sum += w[9]; sum += w[10]; sum += w[11];
262239400Sandreast			sum += w[12]; sum += w[13]; sum += w[14]; sum += w[15];
263239400Sandreast			w += 16;
264239400Sandreast		}
265239400Sandreast		mlen += 32;
266239400Sandreast		while ((mlen -= 8) >= 0) {
267239400Sandreast			sum += w[0]; sum += w[1]; sum += w[2]; sum += w[3];
268239400Sandreast			w += 4;
269239400Sandreast		}
270239400Sandreast		mlen += 8;
271239400Sandreast		if (mlen == 0 && byte_swapped == 0)
272239400Sandreast			continue;
273239400Sandreast		REDUCE;
274239400Sandreast		while ((mlen -= 2) >= 0) {
275239400Sandreast			sum += *w++;
276239400Sandreast		}
277239400Sandreast		if (byte_swapped) {
278239400Sandreast			REDUCE;
279239400Sandreast			sum <<= 8;
280239400Sandreast			byte_swapped = 0;
281239400Sandreast			if (mlen == -1) {
282239400Sandreast				s_util.c[1] = *(char *)w;
283239400Sandreast				sum += s_util.s;
284239400Sandreast				mlen = 0;
285239400Sandreast			} else
286239400Sandreast				mlen = -1;
287239400Sandreast		} else if (mlen == -1)
288239400Sandreast			s_util.c[0] = *(char *)w;
289239400Sandreast	}
290239400Sandreast	if (len)
291239400Sandreast		panic("in6_cksum: out of data");
292239400Sandreast	if (mlen == -1) {
293239400Sandreast		/* The last mbuf has odd # of bytes. Follow the
294239400Sandreast		   standard (the odd byte may be shifted left by 8 bits
295239400Sandreast		   or not as determined by endian-ness of the machine) */
296239400Sandreast		s_util.c[1] = 0;
297239400Sandreast		sum += s_util.s;
298239400Sandreast	}
299239400Sandreast	REDUCE;
300239400Sandreast	return (~sum & 0xffff);
301239400Sandreast}
302239400Sandreast