1*fe1ad185Smiod /* $OpenBSD: in_cksum.c,v 1.10 2022/02/01 15:30:10 miod Exp $ */
23a630e3fSniklas /* $NetBSD: in_cksum.c,v 1.4 1996/11/13 21:13:06 cgd Exp $ */
3417eba8cSderaadt
4417eba8cSderaadt /*
5417eba8cSderaadt * Copyright (c) 1988, 1992, 1993
6417eba8cSderaadt * The Regents of the University of California. All rights reserved.
7417eba8cSderaadt * Copyright (c) 1996
8417eba8cSderaadt * Matt Thomas <matt@3am-software.com>
9417eba8cSderaadt *
10417eba8cSderaadt * Redistribution and use in source and binary forms, with or without
11417eba8cSderaadt * modification, are permitted provided that the following conditions
12417eba8cSderaadt * are met:
13417eba8cSderaadt * 1. Redistributions of source code must retain the above copyright
14417eba8cSderaadt * notice, this list of conditions and the following disclaimer.
15417eba8cSderaadt * 2. Redistributions in binary form must reproduce the above copyright
16417eba8cSderaadt * notice, this list of conditions and the following disclaimer in the
17417eba8cSderaadt * documentation and/or other materials provided with the distribution.
1829295d1cSmillert * 3. Neither the name of the University nor the names of its contributors
19417eba8cSderaadt * may be used to endorse or promote products derived from this software
20417eba8cSderaadt * without specific prior written permission.
21417eba8cSderaadt *
22417eba8cSderaadt * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
23417eba8cSderaadt * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
24417eba8cSderaadt * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
25417eba8cSderaadt * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
26417eba8cSderaadt * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
27417eba8cSderaadt * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
28417eba8cSderaadt * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
29417eba8cSderaadt * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
30417eba8cSderaadt * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
31417eba8cSderaadt * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
32417eba8cSderaadt * SUCH DAMAGE.
33417eba8cSderaadt *
34417eba8cSderaadt * @(#)in_cksum.c 8.1 (Berkeley) 6/10/93
35417eba8cSderaadt */
36417eba8cSderaadt
37417eba8cSderaadt #include <sys/param.h>
38417eba8cSderaadt #include <sys/mbuf.h>
39417eba8cSderaadt #include <sys/systm.h>
40b1fc721fSbrad #include <sys/socketvar.h>
41417eba8cSderaadt #include <netinet/in.h>
42b1fc721fSbrad #include <netinet/ip.h>
43b1fc721fSbrad #include <netinet/ip_var.h>
4450ce9ee0Sniklas
45417eba8cSderaadt /*
46417eba8cSderaadt * Checksum routine for Internet Protocol family headers
47417eba8cSderaadt * (Portable Alpha version).
48417eba8cSderaadt *
49417eba8cSderaadt * This routine is very heavily used in the network
50417eba8cSderaadt * code and should be modified for each CPU to be as fast as possible.
51417eba8cSderaadt */
52417eba8cSderaadt
53417eba8cSderaadt #define ADDCARRY(x) (x > 65535 ? x -= 65535 : x)
54417eba8cSderaadt #define REDUCE32 \
55417eba8cSderaadt { \
56417eba8cSderaadt q_util.q = sum; \
57417eba8cSderaadt sum = q_util.s[0] + q_util.s[1] + q_util.s[2] + q_util.s[3]; \
58417eba8cSderaadt }
59417eba8cSderaadt #define REDUCE16 \
60417eba8cSderaadt { \
61417eba8cSderaadt q_util.q = sum; \
62417eba8cSderaadt l_util.l = q_util.s[0] + q_util.s[1] + q_util.s[2] + q_util.s[3]; \
63417eba8cSderaadt sum = l_util.s[0] + l_util.s[1]; \
64417eba8cSderaadt ADDCARRY(sum); \
65417eba8cSderaadt }
66417eba8cSderaadt
67417eba8cSderaadt static const u_int32_t in_masks[] = {
68417eba8cSderaadt /*0 bytes*/ /*1 byte*/ /*2 bytes*/ /*3 bytes*/
69417eba8cSderaadt 0x00000000, 0x000000FF, 0x0000FFFF, 0x00FFFFFF, /* offset 0 */
70417eba8cSderaadt 0x00000000, 0x0000FF00, 0x00FFFF00, 0xFFFFFF00, /* offset 1 */
71417eba8cSderaadt 0x00000000, 0x00FF0000, 0xFFFF0000, 0xFFFF0000, /* offset 2 */
72417eba8cSderaadt 0x00000000, 0xFF000000, 0xFF000000, 0xFF000000, /* offset 3 */
73417eba8cSderaadt };
74417eba8cSderaadt
75417eba8cSderaadt union l_util {
76417eba8cSderaadt u_int16_t s[2];
77417eba8cSderaadt u_int32_t l;
78417eba8cSderaadt };
79417eba8cSderaadt union q_util {
80417eba8cSderaadt u_int16_t s[4];
81417eba8cSderaadt u_int32_t l[2];
82417eba8cSderaadt u_int64_t q;
83417eba8cSderaadt };
84417eba8cSderaadt
85b1fc721fSbrad static u_int64_t
in_cksumdata(caddr_t buf,int len)86b1fc721fSbrad in_cksumdata(caddr_t buf, int len)
87417eba8cSderaadt {
88417eba8cSderaadt const u_int32_t *lw = (u_int32_t *) buf;
89417eba8cSderaadt u_int64_t sum = 0;
90417eba8cSderaadt u_int64_t prefilled;
91417eba8cSderaadt int offset;
92417eba8cSderaadt union q_util q_util;
93417eba8cSderaadt
94417eba8cSderaadt if ((3 & (long) lw) == 0 && len == 20) {
95417eba8cSderaadt sum = (u_int64_t) lw[0] + lw[1] + lw[2] + lw[3] + lw[4];
96417eba8cSderaadt REDUCE32;
97417eba8cSderaadt return sum;
98417eba8cSderaadt }
99417eba8cSderaadt
100417eba8cSderaadt if ((offset = 3 & (long) lw) != 0) {
101417eba8cSderaadt const u_int32_t *masks = in_masks + (offset << 2);
102417eba8cSderaadt lw = (u_int32_t *) (((long) lw) - offset);
103417eba8cSderaadt sum = *lw++ & masks[len >= 3 ? 3 : len];
104417eba8cSderaadt len -= 4 - offset;
105417eba8cSderaadt if (len <= 0) {
106417eba8cSderaadt REDUCE32;
107417eba8cSderaadt return sum;
108417eba8cSderaadt }
109417eba8cSderaadt }
110417eba8cSderaadt #if 0
111417eba8cSderaadt /*
112417eba8cSderaadt * Force to cache line boundary.
113417eba8cSderaadt */
114417eba8cSderaadt offset = 32 - (0x1f & (long) lw);
115417eba8cSderaadt if (offset < 32 && len > offset) {
116417eba8cSderaadt len -= offset;
117417eba8cSderaadt if (4 & offset) {
118417eba8cSderaadt sum += (u_int64_t) lw[0];
119417eba8cSderaadt lw += 1;
120417eba8cSderaadt }
121417eba8cSderaadt if (8 & offset) {
122417eba8cSderaadt sum += (u_int64_t) lw[0] + lw[1];
123417eba8cSderaadt lw += 2;
124417eba8cSderaadt }
125417eba8cSderaadt if (16 & offset) {
126417eba8cSderaadt sum += (u_int64_t) lw[0] + lw[1] + lw[2] + lw[3];
127417eba8cSderaadt lw += 4;
128417eba8cSderaadt }
129417eba8cSderaadt }
130417eba8cSderaadt #endif
131417eba8cSderaadt /*
132417eba8cSderaadt * access prefilling to start load of next cache line.
133417eba8cSderaadt * then add current cache line
134417eba8cSderaadt * save result of prefilling for loop iteration.
135417eba8cSderaadt */
136417eba8cSderaadt prefilled = lw[0];
137417eba8cSderaadt while ((len -= 32) >= 4) {
138417eba8cSderaadt u_int64_t prefilling = lw[8];
139417eba8cSderaadt sum += prefilled + lw[1] + lw[2] + lw[3]
140417eba8cSderaadt + lw[4] + lw[5] + lw[6] + lw[7];
141417eba8cSderaadt lw += 8;
142417eba8cSderaadt prefilled = prefilling;
143417eba8cSderaadt }
144417eba8cSderaadt if (len >= 0) {
145417eba8cSderaadt sum += prefilled + lw[1] + lw[2] + lw[3]
146417eba8cSderaadt + lw[4] + lw[5] + lw[6] + lw[7];
147417eba8cSderaadt lw += 8;
148417eba8cSderaadt } else {
149417eba8cSderaadt len += 32;
150417eba8cSderaadt }
151417eba8cSderaadt while ((len -= 16) >= 0) {
152417eba8cSderaadt sum += (u_int64_t) lw[0] + lw[1] + lw[2] + lw[3];
153417eba8cSderaadt lw += 4;
154417eba8cSderaadt }
155417eba8cSderaadt len += 16;
156417eba8cSderaadt while ((len -= 4) >= 0) {
157417eba8cSderaadt sum += (u_int64_t) *lw++;
158417eba8cSderaadt }
159417eba8cSderaadt len += 4;
160417eba8cSderaadt if (len > 0)
161417eba8cSderaadt sum += (u_int64_t) (in_masks[len] & *lw);
162417eba8cSderaadt REDUCE32;
163417eba8cSderaadt return sum;
164417eba8cSderaadt }
165417eba8cSderaadt
166417eba8cSderaadt int
in_cksum(struct mbuf * m,int len)167b1fc721fSbrad in_cksum(struct mbuf *m, int len)
168417eba8cSderaadt {
169b1fc721fSbrad u_int64_t sum = 0;
170b1fc721fSbrad int mlen = 0;
171b1fc721fSbrad int clen = 0;
172b1fc721fSbrad caddr_t addr;
173417eba8cSderaadt union l_util l_util;
1743a630e3fSniklas union q_util q_util;
175417eba8cSderaadt
176417eba8cSderaadt for (; m && len; m = m->m_next) {
177417eba8cSderaadt if (m->m_len == 0)
178417eba8cSderaadt continue;
179417eba8cSderaadt mlen = m->m_len;
180417eba8cSderaadt if (len < mlen)
181417eba8cSderaadt mlen = len;
182417eba8cSderaadt addr = mtod(m, caddr_t);
183417eba8cSderaadt if ((clen ^ (long) addr) & 1)
184417eba8cSderaadt sum += in_cksumdata(addr, mlen) << 8;
185417eba8cSderaadt else
186417eba8cSderaadt sum += in_cksumdata(addr, mlen);
187417eba8cSderaadt
188417eba8cSderaadt clen += mlen;
189417eba8cSderaadt len -= mlen;
190417eba8cSderaadt }
191417eba8cSderaadt REDUCE16;
192417eba8cSderaadt return (~sum & 0xffff);
193417eba8cSderaadt }
194b1fc721fSbrad
195b1fc721fSbrad int
in4_cksum(struct mbuf * m,u_int8_t nxt,int off,int len)196b1fc721fSbrad in4_cksum(struct mbuf *m, u_int8_t nxt, int off, int len)
197b1fc721fSbrad {
198b1fc721fSbrad u_int64_t sum = 0;
199b1fc721fSbrad int mlen = 0;
200b1fc721fSbrad int clen = 0;
201b1fc721fSbrad caddr_t addr;
202b1fc721fSbrad union q_util q_util;
203b1fc721fSbrad union l_util l_util;
204b1fc721fSbrad struct ipovly ipov;
205b1fc721fSbrad
206b1fc721fSbrad if (nxt != 0) {
207070da95bSmiod #ifdef DEBUG
208b1fc721fSbrad /* pseudo header */
209b1fc721fSbrad if (off < sizeof(struct ipovly))
210b1fc721fSbrad panic("in4_cksum: offset too short");
211b1fc721fSbrad if (m->m_len < sizeof(struct ip))
212b1fc721fSbrad panic("in4_cksum: bad mbuf chain");
213070da95bSmiod #endif
214b1fc721fSbrad
215*fe1ad185Smiod ipov.ih_x1[8] = 0;
216b1fc721fSbrad ipov.ih_pr = nxt;
217*fe1ad185Smiod ipov.ih_len = htons(len);
218b1fc721fSbrad ipov.ih_src = mtod(m, struct ip *)->ip_src;
219b1fc721fSbrad ipov.ih_dst = mtod(m, struct ip *)->ip_dst;
220b1fc721fSbrad
221*fe1ad185Smiod /* first 8 bytes are zeroes */
222*fe1ad185Smiod sum += in_cksumdata((caddr_t) &ipov + 8, sizeof(ipov) - 8);
223b1fc721fSbrad }
224b1fc721fSbrad
225b1fc721fSbrad /* skip over unnecessary part */
226b1fc721fSbrad while (m != NULL && off > 0) {
227b1fc721fSbrad if (m->m_len > off)
228b1fc721fSbrad break;
229b1fc721fSbrad off -= m->m_len;
230b1fc721fSbrad m = m->m_next;
231b1fc721fSbrad }
232b1fc721fSbrad
233b1fc721fSbrad for (; m && len; m = m->m_next, off = 0) {
234b1fc721fSbrad if (m->m_len == 0)
235b1fc721fSbrad continue;
236b1fc721fSbrad mlen = m->m_len - off;
237b1fc721fSbrad if (len < mlen)
238b1fc721fSbrad mlen = len;
239b1fc721fSbrad addr = mtod(m, caddr_t) + off;
240b1fc721fSbrad if ((clen ^ (long) addr) & 1)
241b1fc721fSbrad sum += in_cksumdata(addr, mlen) << 8;
242b1fc721fSbrad else
243b1fc721fSbrad sum += in_cksumdata(addr, mlen);
244b1fc721fSbrad
245b1fc721fSbrad clen += mlen;
246b1fc721fSbrad len -= mlen;
247b1fc721fSbrad }
248b1fc721fSbrad REDUCE16;
249b1fc721fSbrad return (~sum & 0xffff);
250b1fc721fSbrad }
251