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