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 80 union l_util { 81 uint16_t s[2]; 82 uint32_t l; 83 }; 84 85 union s_util { 86 uint8_t c[2]; 87 uint16_t s; 88 }; 89 90 static 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 136 int 137 in6_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 147 static int 148 in6_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; 227 out: 228 *lenp = len & 1; 229 return (sum); 230 } 231 232 struct in6_cksum_partial_arg { 233 int sum; 234 int rlen; 235 uint8_t resid; 236 }; 237 238 static int 239 in6_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 */ 255 int 256 in6_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 330 int 331 in6_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