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