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