1 /* 2 * CDDL HEADER START 3 * 4 * The contents of this file are subject to the terms of the 5 * Common Development and Distribution License, Version 1.0 only 6 * (the "License"). You may not use this file except in compliance 7 * with the License. 8 * 9 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 10 * or http://www.opensolaris.org/os/licensing. 11 * See the License for the specific language governing permissions 12 * and limitations under the License. 13 * 14 * When distributing Covered Code, include this CDDL HEADER in each 15 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 16 * If applicable, add the following below this CDDL HEADER, with the 17 * fields enclosed by brackets "[]" replaced with your own identifying 18 * information: Portions Copyright [yyyy] [name of copyright owner] 19 * 20 * CDDL HEADER END 21 */ 22 /* 23 * Copyright 2004 Sun Microsystems, Inc. All rights reserved. 24 * Use is subject to license terms. 25 */ 26 /* Copyright (c) 1990 Mentat Inc. */ 27 28 #pragma ident "%Z%%M% %I% %E% SMI" 29 30 #include <sys/types.h> 31 #include <sys/inttypes.h> 32 #include <sys/systm.h> 33 #include <sys/stream.h> 34 #include <sys/strsun.h> 35 #include <sys/debug.h> 36 #include <sys/ddi.h> 37 #include <sys/vtrace.h> 38 #include <inet/sctp_crc32.h> 39 40 #include <sys/multidata.h> 41 #include <sys/multidata_impl.h> 42 43 extern unsigned int ip_ocsum(ushort_t *address, int halfword_count, 44 unsigned int sum); 45 46 /* 47 * Checksum routine for Internet Protocol family headers. 48 * This routine is very heavily used in the network 49 * code and should be modified for each CPU to be as fast as possible. 50 */ 51 52 #define mp_len(mp) ((mp)->b_wptr - (mp)->b_rptr) 53 54 /* 55 * Even/Odd checks. Usually it is performed on pointers but may be 56 * used on integers as well. uintptr_t is long enough to hold both 57 * integer and pointer. 58 */ 59 #define is_odd(p) (((uintptr_t)(p) & 0x1) != 0) 60 #define is_even(p) (!is_odd(p)) 61 62 63 #ifdef ZC_TEST 64 /* 65 * Disable the TCP s/w cksum. 66 * XXX - This is just a hack for testing purpose. Don't use it for 67 * anything else! 68 */ 69 int noswcksum = 0; 70 #endif 71 /* 72 * Note: this does not ones-complement the result since it is used 73 * when computing partial checksums. 74 * For nonSTRUIO_IP mblks, assumes mp->b_rptr+offset is 16 bit aligned. 75 * For STRUIO_IP mblks, assumes mp->b_datap->db_struiobase is 16 bit aligned. 76 * 77 * Note: for STRUIO_IP special mblks some data may have been previously 78 * checksumed, this routine will handle additional data prefixed within 79 * an mblk or b_cont (chained) mblk(s). This routine will also handle 80 * suffixed b_cont mblk(s) and data suffixed within an mblk. 81 */ 82 unsigned int 83 ip_cksum(mblk_t *mp, int offset, uint_t sum) 84 { 85 ushort_t *w; 86 ssize_t mlen; 87 int pmlen; 88 mblk_t *pmp; 89 dblk_t *dp = mp->b_datap; 90 ushort_t psum = 0; 91 92 #ifdef ZC_TEST 93 if (noswcksum) 94 return (0xffff); 95 #endif 96 ASSERT(dp); 97 98 TRACE_2(TR_FAC_IP, TR_IP_CKSUM_START, 99 "ip_cksum_start:%p (%X)", mp, sum); 100 101 if (mp->b_cont == NULL) { 102 /* 103 * May be fast-path, only one mblk. 104 */ 105 w = (ushort_t *)(mp->b_rptr + offset); 106 if (dp->db_struioflag & STRUIO_IP) { 107 /* 108 * Checksum any data not already done by 109 * the caller and add in any partial checksum. 110 */ 111 if ((offset > dp->db_cksumstart) || 112 mp->b_wptr != (uchar_t *)(mp->b_rptr + 113 dp->db_cksumend)) { 114 /* 115 * Mblk data pointers aren't inclusive 116 * of uio data, so disregard checksum. 117 * 118 * not using all of data in dblk make sure 119 * not use to use the precalculated checksum 120 * in this case. 121 */ 122 dp->db_struioflag &= ~STRUIO_IP; 123 goto norm; 124 } 125 ASSERT(mp->b_wptr == (mp->b_rptr + dp->db_cksumend)); 126 psum = *(ushort_t *)dp->db_struioun.data; 127 if ((mlen = dp->db_cksumstart - offset) < 0) 128 mlen = 0; 129 if (is_odd(mlen)) 130 goto slow; 131 if (mlen && dp->db_cksumstart != dp->db_cksumstuff && 132 dp->db_cksumend != dp->db_cksumstuff) { 133 /* 134 * There is prefix data to do and some uio 135 * data has already been checksumed and there 136 * is more uio data to do, so do the prefix 137 * data first, then do the remainder of the 138 * uio data. 139 */ 140 sum = ip_ocsum(w, mlen >> 1, sum); 141 w = (ushort_t *)(mp->b_rptr + 142 dp->db_cksumstuff); 143 if (is_odd(w)) { 144 pmp = mp; 145 goto slow1; 146 } 147 mlen = dp->db_cksumend - dp->db_cksumstuff; 148 } else if (dp->db_cksumend != dp->db_cksumstuff) { 149 /* 150 * There may be uio data to do, if there is 151 * prefix data to do then add in all of the 152 * uio data (if any) to do, else just do any 153 * uio data. 154 */ 155 if (mlen) 156 mlen += dp->db_cksumend 157 - dp->db_cksumstuff; 158 else { 159 w = (ushort_t *)(mp->b_rptr + 160 dp->db_cksumstuff); 161 if (is_odd(w)) 162 goto slow; 163 mlen = dp->db_cksumend 164 - dp->db_cksumstuff; 165 } 166 } else if (mlen == 0) 167 return (psum); 168 169 if (is_odd(mlen)) 170 goto slow; 171 sum += psum; 172 } else { 173 /* 174 * Checksum all data not already done by the caller. 175 */ 176 norm: 177 mlen = mp->b_wptr - (uchar_t *)w; 178 if (is_odd(mlen)) 179 goto slow; 180 } 181 ASSERT(is_even(w)); 182 ASSERT(is_even(mlen)); 183 return (ip_ocsum(w, mlen >> 1, sum)); 184 } 185 if (dp->db_struioflag & STRUIO_IP) 186 psum = *(ushort_t *)dp->db_struioun.data; 187 slow: 188 pmp = 0; 189 slow1: 190 mlen = 0; 191 pmlen = 0; 192 for (; ; ) { 193 /* 194 * Each trip around loop adds in word(s) from one mbuf segment 195 * (except for when pmp == mp, then its two partial trips). 196 */ 197 w = (ushort_t *)(mp->b_rptr + offset); 198 if (pmp) { 199 /* 200 * This is the second trip around for this mblk. 201 */ 202 pmp = 0; 203 mlen = 0; 204 goto douio; 205 } else if (dp->db_struioflag & STRUIO_IP) { 206 /* 207 * Checksum any data not already done by the 208 * caller and add in any partial checksum. 209 */ 210 if ((offset > dp->db_cksumstart) || 211 mp->b_wptr != (uchar_t *)(mp->b_rptr + 212 dp->db_cksumend)) { 213 /* 214 * Mblk data pointers aren't inclusive 215 * of uio data, so disregard checksum. 216 * 217 * not using all of data in dblk make sure 218 * not use to use the precalculated checksum 219 * in this case. 220 */ 221 dp->db_struioflag &= ~STRUIO_IP; 222 goto snorm; 223 } 224 ASSERT(mp->b_wptr == (mp->b_rptr + dp->db_cksumend)); 225 if ((mlen = dp->db_cksumstart - offset) < 0) 226 mlen = 0; 227 if (mlen && dp->db_cksumstart != dp->db_cksumstuff) { 228 /* 229 * There is prefix data too do and some 230 * uio data has already been checksumed, 231 * so do the prefix data only this trip. 232 */ 233 pmp = mp; 234 } else { 235 /* 236 * Add in any partial cksum (if any) and 237 * do the remainder of the uio data. 238 */ 239 int odd; 240 douio: 241 odd = is_odd(dp->db_cksumstuff - 242 dp->db_cksumstart); 243 if (pmlen == -1) { 244 /* 245 * Previous mlen was odd, so swap 246 * the partial checksum bytes. 247 */ 248 sum += ((psum << 8) & 0xffff) 249 | (psum >> 8); 250 if (odd) 251 pmlen = 0; 252 } else { 253 sum += psum; 254 if (odd) 255 pmlen = -1; 256 } 257 if (dp->db_cksumend != dp->db_cksumstuff) { 258 /* 259 * If prefix data to do and then all 260 * the uio data nees to be checksumed, 261 * else just do any uio data. 262 */ 263 if (mlen) 264 mlen += dp->db_cksumend 265 - dp->db_cksumstuff; 266 else { 267 w = (ushort_t *)(mp->b_rptr + 268 dp->db_cksumstuff); 269 mlen = dp->db_cksumend - 270 dp->db_cksumstuff; 271 } 272 } 273 } 274 } else { 275 /* 276 * Checksum all of the mblk data. 277 */ 278 snorm: 279 mlen = mp->b_wptr - (uchar_t *)w; 280 } 281 282 TRACE_2(TR_FAC_IP, TR_IP_CKSUM_START, 283 "ip_cksum_start:%p (%X)", mp, sum) 284 285 mp = mp->b_cont; 286 if (mlen > 0 && pmlen == -1) { 287 /* 288 * There is a byte left from the last 289 * segment; add it into the checksum. 290 * Don't have to worry about a carry- 291 * out here because we make sure that 292 * high part of (32 bit) sum is small 293 * below. 294 */ 295 #ifdef _LITTLE_ENDIAN 296 sum += *(uchar_t *)w << 8; 297 #else 298 sum += *(uchar_t *)w; 299 #endif 300 w = (ushort_t *)((char *)w + 1); 301 mlen--; 302 pmlen = 0; 303 } 304 if (mlen > 0) { 305 if (is_even(w)) { 306 sum = ip_ocsum(w, mlen>>1, sum); 307 w += mlen>>1; 308 /* 309 * If we had an odd number of bytes, 310 * then the last byte goes in the high 311 * part of the sum, and we take the 312 * first byte to the low part of the sum 313 * the next time around the loop. 314 */ 315 if (is_odd(mlen)) { 316 #ifdef _LITTLE_ENDIAN 317 sum += *(uchar_t *)w; 318 #else 319 sum += *(uchar_t *)w << 8; 320 #endif 321 pmlen = -1; 322 } 323 } else { 324 ushort_t swsum; 325 #ifdef _LITTLE_ENDIAN 326 sum += *(uchar_t *)w; 327 #else 328 sum += *(uchar_t *)w << 8; 329 #endif 330 mlen--; 331 w = (ushort_t *)(1 + (uintptr_t)w); 332 333 /* Do a separate checksum and copy operation */ 334 swsum = ip_ocsum(w, mlen>>1, 0); 335 sum += ((swsum << 8) & 0xffff) | (swsum >> 8); 336 w += mlen>>1; 337 /* 338 * If we had an even number of bytes, 339 * then the last byte goes in the low 340 * part of the sum. Otherwise we had an 341 * odd number of bytes and we take the first 342 * byte to the low part of the sum the 343 * next time around the loop. 344 */ 345 if (is_odd(mlen)) { 346 #ifdef _LITTLE_ENDIAN 347 sum += *(uchar_t *)w << 8; 348 #else 349 sum += *(uchar_t *)w; 350 #endif 351 } 352 else 353 pmlen = -1; 354 } 355 } 356 /* 357 * Locate the next block with some data. 358 * If there is a word split across a boundary we 359 * will wrap to the top with mlen == -1 and 360 * then add it in shifted appropriately. 361 */ 362 offset = 0; 363 if (! pmp) { 364 for (; ; ) { 365 if (mp == 0) { 366 goto done; 367 } 368 if (mp_len(mp)) 369 break; 370 mp = mp->b_cont; 371 } 372 dp = mp->b_datap; 373 if (dp->db_struioflag & STRUIO_IP) 374 psum = *(ushort_t *)dp->db_struioun.data; 375 } else 376 mp = pmp; 377 } 378 done: 379 /* 380 * Add together high and low parts of sum 381 * and carry to get cksum. 382 * Have to be careful to not drop the last 383 * carry here. 384 */ 385 sum = (sum & 0xFFFF) + (sum >> 16); 386 sum = (sum & 0xFFFF) + (sum >> 16); 387 TRACE_3(TR_FAC_IP, TR_IP_CKSUM_END, 388 "ip_cksum_end:(%S) type %d (%X)", "ip_cksum", 1, sum); 389 return (sum); 390 } 391 392 uint32_t 393 sctp_cksum(mblk_t *mp, int offset) 394 { 395 uint32_t crc32; 396 uchar_t *p = NULL; 397 398 crc32 = 0xFFFFFFFF; 399 p = mp->b_rptr + offset; 400 crc32 = sctp_crc32(crc32, p, mp->b_wptr - p); 401 for (mp = mp->b_cont; mp != NULL; mp = mp->b_cont) { 402 crc32 = sctp_crc32(crc32, mp->b_rptr, MBLKL(mp)); 403 } 404 405 /* Complement the result */ 406 crc32 = ~crc32; 407 408 return (crc32); 409 } 410 411 /* 412 * Routine to compute Internet checksum (16-bit 1's complement) of a given 413 * Multidata packet descriptor. As in the non-Multidata routine, this doesn't 414 * 1's complement the result, such that it may be used to compute partial 415 * checksums. Since it works on buffer spans rather than mblks, this routine 416 * does not handle existing partial checksum value as in the STRUIO_IP special 417 * mblk case (supporting this is rather trivial, but is perhaps of no use at 418 * the moment unless synchronous streams and delayed checksum calculation are 419 * revived.) 420 * 421 * Note also here that the given Multidata packet descriptor must refer to 422 * a header buffer, i.e. it must have a header fragment. In addition, the 423 * offset must lie within the boundary of the header fragment. For the 424 * outbound tcp (MDT) case, this will not be an issue because the stack 425 * ensures that such conditions are met, and that there is no need whatsoever 426 * to compute partial checksums on an arbitrary offset that is not part of 427 * the header fragment. We may need to revisit this routine to handle all 428 * cases of the inbound (MDR) case, especially when we need to perform partial 429 * checksum calculation due to padded bytes (non-zeroes) in the frame. 430 */ 431 uint_t 432 ip_md_cksum(pdesc_t *pd, int offset, uint_t sum) 433 { 434 pdescinfo_t *pdi = &pd->pd_pdi; 435 uchar_t *reg_start, *reg_end; 436 ssize_t mlen, i; 437 ushort_t *w; 438 boolean_t byteleft = B_FALSE; 439 440 ASSERT((pdi->flags & PDESC_HAS_REF) != 0); 441 ASSERT(pdi->hdr_rptr != NULL && pdi->hdr_wptr != NULL); 442 ASSERT(offset <= PDESC_HDRL(pdi)); 443 444 for (i = 0; i < pdi->pld_cnt + 1; i++) { 445 if (i == 0) { 446 reg_start = pdi->hdr_rptr; 447 reg_end = pdi->hdr_wptr; 448 } else { 449 reg_start = pdi->pld_ary[i - 1].pld_rptr; 450 reg_end = pdi->pld_ary[i - 1].pld_wptr; 451 offset = 0; 452 } 453 454 w = (ushort_t *)(reg_start + offset); 455 mlen = reg_end - (uchar_t *)w; 456 457 if (mlen > 0 && byteleft) { 458 /* 459 * There is a byte left from the last 460 * segment; add it into the checksum. 461 * Don't have to worry about a carry- 462 * out here because we make sure that 463 * high part of (32 bit) sum is small 464 * below. 465 */ 466 #ifdef _LITTLE_ENDIAN 467 sum += *(uchar_t *)w << 8; 468 #else 469 sum += *(uchar_t *)w; 470 #endif 471 w = (ushort_t *)((char *)w + 1); 472 mlen--; 473 byteleft = B_FALSE; 474 } 475 476 if (mlen == 0) 477 continue; 478 479 if (is_even(w)) { 480 sum = ip_ocsum(w, mlen >> 1, sum); 481 w += mlen >> 1; 482 /* 483 * If we had an odd number of bytes, 484 * then the last byte goes in the high 485 * part of the sum, and we take the 486 * first byte to the low part of the sum 487 * the next time around the loop. 488 */ 489 if (is_odd(mlen)) { 490 #ifdef _LITTLE_ENDIAN 491 sum += *(uchar_t *)w; 492 #else 493 sum += *(uchar_t *)w << 8; 494 #endif 495 byteleft = B_TRUE; 496 } 497 } else { 498 ushort_t swsum; 499 #ifdef _LITTLE_ENDIAN 500 sum += *(uchar_t *)w; 501 #else 502 sum += *(uchar_t *)w << 8; 503 #endif 504 mlen--; 505 w = (ushort_t *)(1 + (uintptr_t)w); 506 507 /* Do a separate checksum and copy operation */ 508 swsum = ip_ocsum(w, mlen >> 1, 0); 509 sum += ((swsum << 8) & 0xffff) | (swsum >> 8); 510 w += mlen >> 1; 511 /* 512 * If we had an even number of bytes, 513 * then the last byte goes in the low 514 * part of the sum. Otherwise we had an 515 * odd number of bytes and we take the first 516 * byte to the low part of the sum the 517 * next time around the loop. 518 */ 519 if (is_odd(mlen)) { 520 #ifdef _LITTLE_ENDIAN 521 sum += *(uchar_t *)w << 8; 522 #else 523 sum += *(uchar_t *)w; 524 #endif 525 } else { 526 byteleft = B_TRUE; 527 } 528 } 529 } 530 531 /* 532 * Add together high and low parts of sum and carry to get cksum. 533 * Have to be careful to not drop the last carry here. 534 */ 535 sum = (sum & 0xffff) + (sum >> 16); 536 sum = (sum & 0xffff) + (sum >> 16); 537 538 return (sum); 539 } 540