1/*-
2 * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project.
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 *    notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 *    notice, this list of conditions and the following disclaimer in the
12 *    documentation and/or other materials provided with the distribution.
13 * 3. Neither the name of the project nor the names of its contributors
14 *    may be used to endorse or promote products derived from this software
15 *    without specific prior written permission.
16 *
17 * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND
18 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20 * ARE DISCLAIMED.  IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE
21 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27 * SUCH DAMAGE.
28 *
29 *	$KAME: in6_cksum.c,v 1.10 2000/12/03 00:53:59 itojun Exp $
30 */
31
32/*-
33 * Copyright (c) 1988, 1992, 1993
34 *	The Regents of the University of California.  All rights reserved.
35 *
36 * Redistribution and use in source and binary forms, with or without
37 * modification, are permitted provided that the following conditions
38 * are met:
39 * 1. Redistributions of source code must retain the above copyright
40 *    notice, this list of conditions and the following disclaimer.
41 * 2. Redistributions in binary form must reproduce the above copyright
42 *    notice, this list of conditions and the following disclaimer in the
43 *    documentation and/or other materials provided with the distribution.
44 * 4. Neither the name of the University nor the names of its contributors
45 *    may be used to endorse or promote products derived from this software
46 *    without specific prior written permission.
47 *
48 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
49 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
50 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
51 * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
52 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
53 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
54 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
55 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
56 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
57 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
58 * SUCH DAMAGE.
59 *
60 *	@(#)in_cksum.c	8.1 (Berkeley) 6/10/93
61 */
62
63#include <sys/cdefs.h>
64__FBSDID("$FreeBSD$");
65
66#include <sys/param.h>
67#include <sys/mbuf.h>
68#include <sys/systm.h>
69#include <netinet/in.h>
70#include <netinet/ip6.h>
71#include <netinet6/scope6_var.h>
72
73/*
74 * Checksum routine for Internet Protocol family headers (Portable Version).
75 *
76 * This routine is very heavily used in the network
77 * code and should be modified for each CPU to be as fast as possible.
78 */
79
80#define ADDCARRY(x)  (x > 65535 ? x -= 65535 : x)
81#define REDUCE {l_util.l = sum; sum = l_util.s[0] + l_util.s[1]; (void)ADDCARRY(sum);}
82
83static int
84_in6_cksum_pseudo(struct ip6_hdr *ip6, uint32_t len, uint8_t nxt, uint16_t csum)
85{
86	int sum;
87	uint16_t scope, *w;
88	union {
89		u_int16_t phs[4];
90		struct {
91			u_int32_t	ph_len;
92			u_int8_t	ph_zero[3];
93			u_int8_t	ph_nxt;
94		} __packed ph;
95	} uph;
96
97	sum = csum;
98
99	/*
100	 * First create IP6 pseudo header and calculate a summary.
101	 */
102	uph.ph.ph_len = htonl(len);
103	uph.ph.ph_zero[0] = uph.ph.ph_zero[1] = uph.ph.ph_zero[2] = 0;
104	uph.ph.ph_nxt = nxt;
105
106	/* Payload length and upper layer identifier. */
107	sum += uph.phs[0];  sum += uph.phs[1];
108	sum += uph.phs[2];  sum += uph.phs[3];
109
110	/* IPv6 source address. */
111	scope = in6_getscope(&ip6->ip6_src);
112	w = (u_int16_t *)&ip6->ip6_src;
113	sum += w[0]; sum += w[1]; sum += w[2]; sum += w[3];
114	sum += w[4]; sum += w[5]; sum += w[6]; sum += w[7];
115	if (scope != 0)
116		sum -= scope;
117
118	/* IPv6 destination address. */
119	scope = in6_getscope(&ip6->ip6_dst);
120	w = (u_int16_t *)&ip6->ip6_dst;
121	sum += w[0]; sum += w[1]; sum += w[2]; sum += w[3];
122	sum += w[4]; sum += w[5]; sum += w[6]; sum += w[7];
123	if (scope != 0)
124		sum -= scope;
125
126	return (sum);
127}
128
129int
130in6_cksum_pseudo(struct ip6_hdr *ip6, uint32_t len, uint8_t nxt, uint16_t csum)
131{
132	int sum;
133	union {
134		u_int16_t s[2];
135		u_int32_t l;
136	} l_util;
137
138	sum = _in6_cksum_pseudo(ip6, len, nxt, csum);
139	REDUCE;
140	return (sum);
141}
142
143/*
144 * m MUST contain a contiguous IP6 header.
145 * off is an offset where TCP/UDP/ICMP6 header starts.
146 * len is a total length of a transport segment.
147 * (e.g. TCP header + TCP payload)
148 * cov is the number of bytes to be taken into account for the checksum
149 */
150int
151in6_cksum_partial(struct mbuf *m, u_int8_t nxt, u_int32_t off,
152    u_int32_t len, u_int32_t cov)
153{
154	struct ip6_hdr *ip6;
155	u_int16_t *w, scope;
156	int byte_swapped, mlen;
157	int sum;
158	union {
159		u_int16_t phs[4];
160		struct {
161			u_int32_t	ph_len;
162			u_int8_t	ph_zero[3];
163			u_int8_t	ph_nxt;
164		} __packed ph;
165	} uph;
166	union {
167		u_int8_t	c[2];
168		u_int16_t	s;
169	} s_util;
170	union {
171		u_int16_t s[2];
172		u_int32_t l;
173	} l_util;
174
175	/* Sanity check. */
176	KASSERT(m->m_pkthdr.len >= off + len, ("%s: mbuf len (%d) < off(%d)+"
177	    "len(%d)", __func__, m->m_pkthdr.len, off, len));
178
179	/*
180	 * First create IP6 pseudo header and calculate a summary.
181	 */
182	uph.ph.ph_len = htonl(len);
183	uph.ph.ph_zero[0] = uph.ph.ph_zero[1] = uph.ph.ph_zero[2] = 0;
184	uph.ph.ph_nxt = nxt;
185
186	/* Payload length and upper layer identifier. */
187	sum = uph.phs[0];  sum += uph.phs[1];
188	sum += uph.phs[2];  sum += uph.phs[3];
189
190	ip6 = mtod(m, struct ip6_hdr *);
191
192	/* IPv6 source address. */
193	scope = in6_getscope(&ip6->ip6_src);
194	w = (u_int16_t *)&ip6->ip6_src;
195	sum += w[0]; sum += w[1]; sum += w[2]; sum += w[3];
196	sum += w[4]; sum += w[5]; sum += w[6]; sum += w[7];
197	if (scope != 0)
198		sum -= scope;
199
200	/* IPv6 destination address. */
201	scope = in6_getscope(&ip6->ip6_dst);
202	w = (u_int16_t *)&ip6->ip6_dst;
203	sum += w[0]; sum += w[1]; sum += w[2]; sum += w[3];
204	sum += w[4]; sum += w[5]; sum += w[6]; sum += w[7];
205	if (scope != 0)
206		sum -= scope;
207
208	/*
209	 * Secondly calculate a summary of the first mbuf excluding offset.
210	 */
211	while (off > 0) {
212		if (m->m_len <= off)
213			off -= m->m_len;
214		else
215			break;
216		m = m->m_next;
217	}
218	w = (u_int16_t *)(mtod(m, u_char *) + off);
219	mlen = m->m_len - off;
220	if (cov < mlen)
221		mlen = cov;
222	cov -= mlen;
223	/*
224	 * Force to even boundary.
225	 */
226	if ((1 & (long)w) && (mlen > 0)) {
227		REDUCE;
228		sum <<= 8;
229		s_util.c[0] = *(u_char *)w;
230		w = (u_int16_t *)((char *)w + 1);
231		mlen--;
232		byte_swapped = 1;
233	} else
234		byte_swapped = 0;
235
236	/*
237	 * Unroll the loop to make overhead from
238	 * branches &c small.
239	 */
240	while ((mlen -= 32) >= 0) {
241		sum += w[0]; sum += w[1]; sum += w[2]; sum += w[3];
242		sum += w[4]; sum += w[5]; sum += w[6]; sum += w[7];
243		sum += w[8]; sum += w[9]; sum += w[10]; sum += w[11];
244		sum += w[12]; sum += w[13]; sum += w[14]; sum += w[15];
245		w += 16;
246	}
247	mlen += 32;
248	while ((mlen -= 8) >= 0) {
249		sum += w[0]; sum += w[1]; sum += w[2]; sum += w[3];
250		w += 4;
251	}
252	mlen += 8;
253	if (mlen == 0 && byte_swapped == 0)
254		goto next;
255	REDUCE;
256	while ((mlen -= 2) >= 0) {
257		sum += *w++;
258	}
259	if (byte_swapped) {
260		REDUCE;
261		sum <<= 8;
262		byte_swapped = 0;
263		if (mlen == -1) {
264			s_util.c[1] = *(char *)w;
265			sum += s_util.s;
266			mlen = 0;
267		} else
268			mlen = -1;
269	} else if (mlen == -1)
270		s_util.c[0] = *(char *)w;
271 next:
272	m = m->m_next;
273
274	/*
275	 * Lastly calculate a summary of the rest of mbufs.
276	 */
277
278	for (;m && cov; m = m->m_next) {
279		if (m->m_len == 0)
280			continue;
281		w = mtod(m, u_int16_t *);
282		if (mlen == -1) {
283			/*
284			 * The first byte of this mbuf is the continuation
285			 * of a word spanning between this mbuf and the
286			 * last mbuf.
287			 *
288			 * s_util.c[0] is already saved when scanning previous
289			 * mbuf.
290			 */
291			s_util.c[1] = *(char *)w;
292			sum += s_util.s;
293			w = (u_int16_t *)((char *)w + 1);
294			mlen = m->m_len - 1;
295			cov--;
296		} else
297			mlen = m->m_len;
298		if (cov < mlen)
299			mlen = cov;
300		cov -= mlen;
301		/*
302		 * Force to even boundary.
303		 */
304		if ((1 & (long) w) && (mlen > 0)) {
305			REDUCE;
306			sum <<= 8;
307			s_util.c[0] = *(u_char *)w;
308			w = (u_int16_t *)((char *)w + 1);
309			mlen--;
310			byte_swapped = 1;
311		}
312		/*
313		 * Unroll the loop to make overhead from
314		 * branches &c small.
315		 */
316		while ((mlen -= 32) >= 0) {
317			sum += w[0]; sum += w[1]; sum += w[2]; sum += w[3];
318			sum += w[4]; sum += w[5]; sum += w[6]; sum += w[7];
319			sum += w[8]; sum += w[9]; sum += w[10]; sum += w[11];
320			sum += w[12]; sum += w[13]; sum += w[14]; sum += w[15];
321			w += 16;
322		}
323		mlen += 32;
324		while ((mlen -= 8) >= 0) {
325			sum += w[0]; sum += w[1]; sum += w[2]; sum += w[3];
326			w += 4;
327		}
328		mlen += 8;
329		if (mlen == 0 && byte_swapped == 0)
330			continue;
331		REDUCE;
332		while ((mlen -= 2) >= 0) {
333			sum += *w++;
334		}
335		if (byte_swapped) {
336			REDUCE;
337			sum <<= 8;
338			byte_swapped = 0;
339			if (mlen == -1) {
340				s_util.c[1] = *(char *)w;
341				sum += s_util.s;
342				mlen = 0;
343			} else
344				mlen = -1;
345		} else if (mlen == -1)
346			s_util.c[0] = *(char *)w;
347	}
348	if (cov)
349		panic("in6_cksum: out of data");
350	if (mlen == -1) {
351		/* The last mbuf has odd # of bytes. Follow the
352		   standard (the odd byte may be shifted left by 8 bits
353		   or not as determined by endian-ness of the machine) */
354		s_util.c[1] = 0;
355		sum += s_util.s;
356	}
357	REDUCE;
358	return (~sum & 0xffff);
359}
360
361int
362in6_cksum(struct mbuf *m, u_int8_t nxt, u_int32_t off, u_int32_t len)
363{
364	return (in6_cksum_partial(m, nxt, off, len, len));
365}
366