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 * @(#)in_cksum.c 8.1 (Berkeley) 6/10/93
63 */
64
65 #include <sys/cdefs.h>
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
83 union l_util {
84 uint16_t s[2];
85 uint32_t l;
86 };
87
88 union s_util {
89 uint8_t c[2];
90 uint16_t s;
91 };
92
93 static int
_in6_cksum_pseudo(struct ip6_hdr * ip6,uint32_t len,uint8_t nxt,uint16_t csum)94 _in6_cksum_pseudo(struct ip6_hdr *ip6, uint32_t len, uint8_t nxt, uint16_t csum)
95 {
96 int sum;
97 uint16_t scope, *w;
98 union {
99 u_int16_t phs[4];
100 struct {
101 u_int32_t ph_len;
102 u_int8_t ph_zero[3];
103 u_int8_t ph_nxt;
104 } __packed ph;
105 } uph;
106
107 sum = csum;
108
109 /*
110 * First create IP6 pseudo header and calculate a summary.
111 */
112 uph.ph.ph_len = htonl(len);
113 uph.ph.ph_zero[0] = uph.ph.ph_zero[1] = uph.ph.ph_zero[2] = 0;
114 uph.ph.ph_nxt = nxt;
115
116 /* Payload length and upper layer identifier. */
117 sum += uph.phs[0]; sum += uph.phs[1];
118 sum += uph.phs[2]; sum += uph.phs[3];
119
120 /* IPv6 source address. */
121 scope = in6_getscope(&ip6->ip6_src);
122 w = (u_int16_t *)&ip6->ip6_src;
123 sum += w[0]; sum += w[1]; sum += w[2]; sum += w[3];
124 sum += w[4]; sum += w[5]; sum += w[6]; sum += w[7];
125 if (scope != 0)
126 sum -= scope;
127
128 /* IPv6 destination address. */
129 scope = in6_getscope(&ip6->ip6_dst);
130 w = (u_int16_t *)&ip6->ip6_dst;
131 sum += w[0]; sum += w[1]; sum += w[2]; sum += w[3];
132 sum += w[4]; sum += w[5]; sum += w[6]; sum += w[7];
133 if (scope != 0)
134 sum -= scope;
135
136 return (sum);
137 }
138
139 int
in6_cksum_pseudo(struct ip6_hdr * ip6,uint32_t len,uint8_t nxt,uint16_t csum)140 in6_cksum_pseudo(struct ip6_hdr *ip6, uint32_t len, uint8_t nxt, uint16_t csum)
141 {
142 union l_util l_util;
143 int sum;
144
145 sum = _in6_cksum_pseudo(ip6, len, nxt, csum);
146 REDUCE;
147 return (sum);
148 }
149
150 static int
in6_cksumdata(void * data,int * lenp,uint8_t * residp,int rlen)151 in6_cksumdata(void *data, int *lenp, uint8_t *residp, int rlen)
152 {
153 union l_util l_util;
154 union s_util s_util;
155 uint16_t *w;
156 int len, sum;
157 bool byte_swapped;
158
159 KASSERT(*lenp >= 0, ("%s: negative len %d", __func__, *lenp));
160 KASSERT(rlen == 0 || rlen == 1, ("%s: rlen %d", __func__, rlen));
161
162 len = *lenp;
163 sum = 0;
164
165 if (len == 0) {
166 len = rlen;
167 goto out;
168 }
169
170 byte_swapped = false;
171 w = data;
172
173 /*
174 * Do we have a residual byte left over from the previous buffer?
175 */
176 if (rlen == 1) {
177 s_util.c[0] = *residp;
178 s_util.c[1] = *(uint8_t *)w;
179 sum += s_util.s;
180 w = (uint16_t *)((uint8_t *)w + 1);
181 len--;
182 rlen = 0;
183 }
184
185 /*
186 * Force to even boundary.
187 */
188 if ((1 & (uintptr_t)w) && len > 0) {
189 REDUCE;
190 sum <<= 8;
191 s_util.c[0] = *(uint8_t *)w;
192 w = (uint16_t *)((uint8_t *)w + 1);
193 len--;
194 byte_swapped = true;
195 }
196
197 /*
198 * Unroll the loop to make overhead from branches &c small.
199 */
200 while ((len -= 32) >= 0) {
201 sum += w[0]; sum += w[1]; sum += w[2]; sum += w[3];
202 sum += w[4]; sum += w[5]; sum += w[6]; sum += w[7];
203 sum += w[8]; sum += w[9]; sum += w[10]; sum += w[11];
204 sum += w[12]; sum += w[13]; sum += w[14]; sum += w[15];
205 w += 16;
206 }
207 len += 32;
208 while ((len -= 8) >= 0) {
209 sum += w[0]; sum += w[1]; sum += w[2]; sum += w[3];
210 w += 4;
211 }
212 len += 8;
213 if (len == 0 && !byte_swapped)
214 goto out;
215 REDUCE;
216 while ((len -= 2) >= 0) {
217 sum += *w++;
218 }
219 if (byte_swapped) {
220 REDUCE;
221 sum <<= 8;
222 if (len == -1) {
223 s_util.c[1] = *(uint8_t *)w;
224 sum += s_util.s;
225 } else /* len == -2 */
226 *residp = s_util.c[0];
227 len++;
228 } else if (len == -1)
229 *residp = *(uint8_t *)w;
230 out:
231 *lenp = len & 1;
232 return (sum);
233 }
234
235 struct in6_cksum_partial_arg {
236 int sum;
237 int rlen;
238 uint8_t resid;
239 };
240
241 static int
in6_cksum_partial_one(void * _arg,void * data,u_int len)242 in6_cksum_partial_one(void *_arg, void *data, u_int len)
243 {
244 struct in6_cksum_partial_arg *arg = _arg;
245
246 arg->sum += in6_cksumdata(data, &len, &arg->resid, arg->rlen);
247 arg->rlen = len;
248 return (0);
249 }
250
251 /*
252 * m MUST contain a contiguous IP6 header.
253 * off is an offset where TCP/UDP/ICMP6 header starts.
254 * len is a total length of a transport segment.
255 * (e.g. TCP header + TCP payload)
256 * cov is the number of bytes to be taken into account for the checksum
257 */
258 int
in6_cksum_partial(struct mbuf * m,uint8_t nxt,uint32_t off,uint32_t len,uint32_t cov)259 in6_cksum_partial(struct mbuf *m, uint8_t nxt, uint32_t off, uint32_t len,
260 uint32_t cov)
261 {
262 struct in6_cksum_partial_arg arg;
263 union l_util l_util;
264 union s_util s_util;
265 struct ip6_hdr *ip6;
266 uint16_t *w, scope;
267 int sum;
268 union {
269 uint16_t phs[4];
270 struct {
271 uint32_t ph_len;
272 uint8_t ph_zero[3];
273 uint8_t ph_nxt;
274 } __packed ph;
275 } uph;
276
277 /* Sanity check. */
278 KASSERT(m->m_pkthdr.len >= off + len, ("%s: mbuf len (%d) < off(%d)+"
279 "len(%d)", __func__, m->m_pkthdr.len, off, len));
280 KASSERT(m->m_len >= sizeof(*ip6),
281 ("%s: mbuf len %d < sizeof(ip6)", __func__, m->m_len));
282
283 /*
284 * First create IP6 pseudo header and calculate a summary.
285 */
286 uph.ph.ph_len = htonl(len);
287 uph.ph.ph_zero[0] = uph.ph.ph_zero[1] = uph.ph.ph_zero[2] = 0;
288 uph.ph.ph_nxt = nxt;
289
290 /* Payload length and upper layer identifier. */
291 sum = uph.phs[0]; sum += uph.phs[1];
292 sum += uph.phs[2]; sum += uph.phs[3];
293
294 ip6 = mtod(m, struct ip6_hdr *);
295
296 /* IPv6 source address. */
297 scope = in6_getscope(&ip6->ip6_src);
298 w = (uint16_t *)&ip6->ip6_src;
299 sum += w[0]; sum += w[1]; sum += w[2]; sum += w[3];
300 sum += w[4]; sum += w[5]; sum += w[6]; sum += w[7];
301 if (scope != 0)
302 sum -= scope;
303
304 /* IPv6 destination address. */
305 scope = in6_getscope(&ip6->ip6_dst);
306 w = (uint16_t *)&ip6->ip6_dst;
307 sum += w[0]; sum += w[1]; sum += w[2]; sum += w[3];
308 sum += w[4]; sum += w[5]; sum += w[6]; sum += w[7];
309 if (scope != 0)
310 sum -= scope;
311
312 /*
313 * Loop over the rest of the mbuf chain and compute the rest of the
314 * checksum. m_apply() handles unmapped mbufs.
315 */
316 arg.sum = sum;
317 arg.rlen = 0;
318 (void)m_apply(m, off, cov, in6_cksum_partial_one, &arg);
319 sum = arg.sum;
320
321 /*
322 * Handle a residual byte.
323 */
324 if (arg.rlen == 1) {
325 s_util.c[0] = arg.resid;
326 s_util.c[1] = 0;
327 sum += s_util.s;
328 }
329 REDUCE;
330 return (~sum & 0xffff);
331 }
332
333 int
in6_cksum(struct mbuf * m,uint8_t nxt,uint32_t off,uint32_t len)334 in6_cksum(struct mbuf *m, uint8_t nxt, uint32_t off, uint32_t len)
335 {
336 return (in6_cksum_partial(m, nxt, off, len, len));
337 }
338