18275SEric Cheng /*
28275SEric Cheng * CDDL HEADER START
38275SEric Cheng *
48275SEric Cheng * The contents of this file are subject to the terms of the
58275SEric Cheng * Common Development and Distribution License (the "License").
68275SEric Cheng * You may not use this file except in compliance with the License.
78275SEric Cheng *
88275SEric Cheng * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
98275SEric Cheng * or http://www.opensolaris.org/os/licensing.
108275SEric Cheng * See the License for the specific language governing permissions
118275SEric Cheng * and limitations under the License.
128275SEric Cheng *
138275SEric Cheng * When distributing Covered Code, include this CDDL HEADER in each
148275SEric Cheng * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
158275SEric Cheng * If applicable, add the following below this CDDL HEADER, with the
168275SEric Cheng * fields enclosed by brackets "[]" replaced with your own identifying
178275SEric Cheng * information: Portions Copyright [yyyy] [name of copyright owner]
188275SEric Cheng *
198275SEric Cheng * CDDL HEADER END
208275SEric Cheng */
218275SEric Cheng /*
22*11042SErik.Nordmark@Sun.COM * Copyright 2009 Sun Microsystems, Inc. All rights reserved.
238275SEric Cheng * Use is subject to license terms.
248275SEric Cheng */
258275SEric Cheng /* Copyright (c) 1990 Mentat Inc. */
268275SEric Cheng
278275SEric Cheng #include <sys/types.h>
288275SEric Cheng #include <sys/inttypes.h>
298275SEric Cheng #include <sys/systm.h>
308275SEric Cheng #include <sys/stream.h>
318275SEric Cheng #include <sys/strsun.h>
328275SEric Cheng #include <sys/debug.h>
338275SEric Cheng #include <sys/ddi.h>
348275SEric Cheng #include <sys/vtrace.h>
358275SEric Cheng #include <inet/sctp_crc32.h>
368275SEric Cheng #include <inet/ip.h>
378275SEric Cheng
388275SEric Cheng #include <sys/multidata.h>
398275SEric Cheng #include <sys/multidata_impl.h>
408275SEric Cheng
418275SEric Cheng extern unsigned int ip_ocsum(ushort_t *address, int halfword_count,
428275SEric Cheng unsigned int sum);
438275SEric Cheng
448275SEric Cheng /*
458275SEric Cheng * Checksum routine for Internet Protocol family headers.
468275SEric Cheng * This routine is very heavily used in the network
478275SEric Cheng * code and should be modified for each CPU to be as fast as possible.
488275SEric Cheng */
498275SEric Cheng
508275SEric Cheng #define mp_len(mp) ((mp)->b_wptr - (mp)->b_rptr)
518275SEric Cheng
528275SEric Cheng /*
538275SEric Cheng * Even/Odd checks. Usually it is performed on pointers but may be
548275SEric Cheng * used on integers as well. uintptr_t is long enough to hold both
558275SEric Cheng * integer and pointer.
568275SEric Cheng */
578275SEric Cheng #define is_odd(p) (((uintptr_t)(p) & 0x1) != 0)
588275SEric Cheng #define is_even(p) (!is_odd(p))
598275SEric Cheng
608275SEric Cheng
618275SEric Cheng #ifdef ZC_TEST
628275SEric Cheng /*
638275SEric Cheng * Disable the TCP s/w cksum.
648275SEric Cheng * XXX - This is just a hack for testing purpose. Don't use it for
658275SEric Cheng * anything else!
668275SEric Cheng */
678275SEric Cheng int noswcksum = 0;
688275SEric Cheng #endif
698275SEric Cheng /*
708275SEric Cheng * Note: this does not ones-complement the result since it is used
718275SEric Cheng * when computing partial checksums.
728275SEric Cheng * For nonSTRUIO_IP mblks, assumes mp->b_rptr+offset is 16 bit aligned.
738275SEric Cheng * For STRUIO_IP mblks, assumes mp->b_datap->db_struiobase is 16 bit aligned.
748275SEric Cheng *
758275SEric Cheng * Note: for STRUIO_IP special mblks some data may have been previously
768275SEric Cheng * checksumed, this routine will handle additional data prefixed within
778275SEric Cheng * an mblk or b_cont (chained) mblk(s). This routine will also handle
788275SEric Cheng * suffixed b_cont mblk(s) and data suffixed within an mblk.
798275SEric Cheng */
808275SEric Cheng unsigned int
ip_cksum(mblk_t * mp,int offset,uint_t sum)818275SEric Cheng ip_cksum(mblk_t *mp, int offset, uint_t sum)
828275SEric Cheng {
838275SEric Cheng ushort_t *w;
848275SEric Cheng ssize_t mlen;
858275SEric Cheng int pmlen;
868275SEric Cheng mblk_t *pmp;
878275SEric Cheng dblk_t *dp = mp->b_datap;
888275SEric Cheng ushort_t psum = 0;
898275SEric Cheng
908275SEric Cheng #ifdef ZC_TEST
918275SEric Cheng if (noswcksum)
928275SEric Cheng return (0xffff);
938275SEric Cheng #endif
948275SEric Cheng ASSERT(dp);
958275SEric Cheng
968275SEric Cheng if (mp->b_cont == NULL) {
978275SEric Cheng /*
988275SEric Cheng * May be fast-path, only one mblk.
998275SEric Cheng */
1008275SEric Cheng w = (ushort_t *)(mp->b_rptr + offset);
1018275SEric Cheng if (dp->db_struioflag & STRUIO_IP) {
1028275SEric Cheng /*
1038275SEric Cheng * Checksum any data not already done by
1048275SEric Cheng * the caller and add in any partial checksum.
1058275SEric Cheng */
1068275SEric Cheng if ((offset > dp->db_cksumstart) ||
1078275SEric Cheng mp->b_wptr != (uchar_t *)(mp->b_rptr +
1088275SEric Cheng dp->db_cksumend)) {
1098275SEric Cheng /*
1108275SEric Cheng * Mblk data pointers aren't inclusive
1118275SEric Cheng * of uio data, so disregard checksum.
1128275SEric Cheng *
1138275SEric Cheng * not using all of data in dblk make sure
1148275SEric Cheng * not use to use the precalculated checksum
1158275SEric Cheng * in this case.
1168275SEric Cheng */
1178275SEric Cheng dp->db_struioflag &= ~STRUIO_IP;
1188275SEric Cheng goto norm;
1198275SEric Cheng }
1208275SEric Cheng ASSERT(mp->b_wptr == (mp->b_rptr + dp->db_cksumend));
1218275SEric Cheng psum = *(ushort_t *)dp->db_struioun.data;
1228275SEric Cheng if ((mlen = dp->db_cksumstart - offset) < 0)
1238275SEric Cheng mlen = 0;
1248275SEric Cheng if (is_odd(mlen))
1258275SEric Cheng goto slow;
1268275SEric Cheng if (mlen && dp->db_cksumstart != dp->db_cksumstuff &&
1278275SEric Cheng dp->db_cksumend != dp->db_cksumstuff) {
1288275SEric Cheng /*
1298275SEric Cheng * There is prefix data to do and some uio
1308275SEric Cheng * data has already been checksumed and there
1318275SEric Cheng * is more uio data to do, so do the prefix
1328275SEric Cheng * data first, then do the remainder of the
1338275SEric Cheng * uio data.
1348275SEric Cheng */
1358275SEric Cheng sum = ip_ocsum(w, mlen >> 1, sum);
1368275SEric Cheng w = (ushort_t *)(mp->b_rptr +
1378275SEric Cheng dp->db_cksumstuff);
1388275SEric Cheng if (is_odd(w)) {
1398275SEric Cheng pmp = mp;
1408275SEric Cheng goto slow1;
1418275SEric Cheng }
1428275SEric Cheng mlen = dp->db_cksumend - dp->db_cksumstuff;
1438275SEric Cheng } else if (dp->db_cksumend != dp->db_cksumstuff) {
1448275SEric Cheng /*
1458275SEric Cheng * There may be uio data to do, if there is
1468275SEric Cheng * prefix data to do then add in all of the
1478275SEric Cheng * uio data (if any) to do, else just do any
1488275SEric Cheng * uio data.
1498275SEric Cheng */
1508275SEric Cheng if (mlen)
1518275SEric Cheng mlen += dp->db_cksumend
1528275SEric Cheng - dp->db_cksumstuff;
1538275SEric Cheng else {
1548275SEric Cheng w = (ushort_t *)(mp->b_rptr +
1558275SEric Cheng dp->db_cksumstuff);
1568275SEric Cheng if (is_odd(w))
1578275SEric Cheng goto slow;
1588275SEric Cheng mlen = dp->db_cksumend
1598275SEric Cheng - dp->db_cksumstuff;
1608275SEric Cheng }
1618275SEric Cheng } else if (mlen == 0)
1628275SEric Cheng return (psum);
1638275SEric Cheng
1648275SEric Cheng if (is_odd(mlen))
1658275SEric Cheng goto slow;
1668275SEric Cheng sum += psum;
1678275SEric Cheng } else {
1688275SEric Cheng /*
1698275SEric Cheng * Checksum all data not already done by the caller.
1708275SEric Cheng */
1718275SEric Cheng norm:
1728275SEric Cheng mlen = mp->b_wptr - (uchar_t *)w;
1738275SEric Cheng if (is_odd(mlen))
1748275SEric Cheng goto slow;
1758275SEric Cheng }
1768275SEric Cheng ASSERT(is_even(w));
1778275SEric Cheng ASSERT(is_even(mlen));
1788275SEric Cheng return (ip_ocsum(w, mlen >> 1, sum));
1798275SEric Cheng }
1808275SEric Cheng if (dp->db_struioflag & STRUIO_IP)
1818275SEric Cheng psum = *(ushort_t *)dp->db_struioun.data;
1828275SEric Cheng slow:
1838275SEric Cheng pmp = 0;
1848275SEric Cheng slow1:
1858275SEric Cheng mlen = 0;
1868275SEric Cheng pmlen = 0;
1878275SEric Cheng for (; ; ) {
1888275SEric Cheng /*
1898275SEric Cheng * Each trip around loop adds in word(s) from one mbuf segment
1908275SEric Cheng * (except for when pmp == mp, then its two partial trips).
1918275SEric Cheng */
1928275SEric Cheng w = (ushort_t *)(mp->b_rptr + offset);
1938275SEric Cheng if (pmp) {
1948275SEric Cheng /*
1958275SEric Cheng * This is the second trip around for this mblk.
1968275SEric Cheng */
1978275SEric Cheng pmp = 0;
1988275SEric Cheng mlen = 0;
1998275SEric Cheng goto douio;
2008275SEric Cheng } else if (dp->db_struioflag & STRUIO_IP) {
2018275SEric Cheng /*
2028275SEric Cheng * Checksum any data not already done by the
2038275SEric Cheng * caller and add in any partial checksum.
2048275SEric Cheng */
2058275SEric Cheng if ((offset > dp->db_cksumstart) ||
2068275SEric Cheng mp->b_wptr != (uchar_t *)(mp->b_rptr +
2078275SEric Cheng dp->db_cksumend)) {
2088275SEric Cheng /*
2098275SEric Cheng * Mblk data pointers aren't inclusive
2108275SEric Cheng * of uio data, so disregard checksum.
2118275SEric Cheng *
2128275SEric Cheng * not using all of data in dblk make sure
2138275SEric Cheng * not use to use the precalculated checksum
2148275SEric Cheng * in this case.
2158275SEric Cheng */
2168275SEric Cheng dp->db_struioflag &= ~STRUIO_IP;
2178275SEric Cheng goto snorm;
2188275SEric Cheng }
2198275SEric Cheng ASSERT(mp->b_wptr == (mp->b_rptr + dp->db_cksumend));
2208275SEric Cheng if ((mlen = dp->db_cksumstart - offset) < 0)
2218275SEric Cheng mlen = 0;
2228275SEric Cheng if (mlen && dp->db_cksumstart != dp->db_cksumstuff) {
2238275SEric Cheng /*
2248275SEric Cheng * There is prefix data too do and some
2258275SEric Cheng * uio data has already been checksumed,
2268275SEric Cheng * so do the prefix data only this trip.
2278275SEric Cheng */
2288275SEric Cheng pmp = mp;
2298275SEric Cheng } else {
2308275SEric Cheng /*
2318275SEric Cheng * Add in any partial cksum (if any) and
2328275SEric Cheng * do the remainder of the uio data.
2338275SEric Cheng */
2348275SEric Cheng int odd;
2358275SEric Cheng douio:
2368275SEric Cheng odd = is_odd(dp->db_cksumstuff -
2378275SEric Cheng dp->db_cksumstart);
2388275SEric Cheng if (pmlen == -1) {
2398275SEric Cheng /*
2408275SEric Cheng * Previous mlen was odd, so swap
2418275SEric Cheng * the partial checksum bytes.
2428275SEric Cheng */
2438275SEric Cheng sum += ((psum << 8) & 0xffff)
2448275SEric Cheng | (psum >> 8);
2458275SEric Cheng if (odd)
2468275SEric Cheng pmlen = 0;
2478275SEric Cheng } else {
2488275SEric Cheng sum += psum;
2498275SEric Cheng if (odd)
2508275SEric Cheng pmlen = -1;
2518275SEric Cheng }
2528275SEric Cheng if (dp->db_cksumend != dp->db_cksumstuff) {
2538275SEric Cheng /*
2548275SEric Cheng * If prefix data to do and then all
2558275SEric Cheng * the uio data nees to be checksumed,
2568275SEric Cheng * else just do any uio data.
2578275SEric Cheng */
2588275SEric Cheng if (mlen)
2598275SEric Cheng mlen += dp->db_cksumend
2608275SEric Cheng - dp->db_cksumstuff;
2618275SEric Cheng else {
2628275SEric Cheng w = (ushort_t *)(mp->b_rptr +
2638275SEric Cheng dp->db_cksumstuff);
2648275SEric Cheng mlen = dp->db_cksumend -
2658275SEric Cheng dp->db_cksumstuff;
2668275SEric Cheng }
2678275SEric Cheng }
2688275SEric Cheng }
2698275SEric Cheng } else {
2708275SEric Cheng /*
2718275SEric Cheng * Checksum all of the mblk data.
2728275SEric Cheng */
2738275SEric Cheng snorm:
2748275SEric Cheng mlen = mp->b_wptr - (uchar_t *)w;
2758275SEric Cheng }
2768275SEric Cheng
2778275SEric Cheng mp = mp->b_cont;
2788275SEric Cheng if (mlen > 0 && pmlen == -1) {
2798275SEric Cheng /*
2808275SEric Cheng * There is a byte left from the last
2818275SEric Cheng * segment; add it into the checksum.
2828275SEric Cheng * Don't have to worry about a carry-
2838275SEric Cheng * out here because we make sure that
2848275SEric Cheng * high part of (32 bit) sum is small
2858275SEric Cheng * below.
2868275SEric Cheng */
2878275SEric Cheng #ifdef _LITTLE_ENDIAN
2888275SEric Cheng sum += *(uchar_t *)w << 8;
2898275SEric Cheng #else
2908275SEric Cheng sum += *(uchar_t *)w;
2918275SEric Cheng #endif
2928275SEric Cheng w = (ushort_t *)((char *)w + 1);
2938275SEric Cheng mlen--;
2948275SEric Cheng pmlen = 0;
2958275SEric Cheng }
2968275SEric Cheng if (mlen > 0) {
2978275SEric Cheng if (is_even(w)) {
2988275SEric Cheng sum = ip_ocsum(w, mlen>>1, sum);
2998275SEric Cheng w += mlen>>1;
3008275SEric Cheng /*
3018275SEric Cheng * If we had an odd number of bytes,
3028275SEric Cheng * then the last byte goes in the high
3038275SEric Cheng * part of the sum, and we take the
3048275SEric Cheng * first byte to the low part of the sum
3058275SEric Cheng * the next time around the loop.
3068275SEric Cheng */
3078275SEric Cheng if (is_odd(mlen)) {
3088275SEric Cheng #ifdef _LITTLE_ENDIAN
3098275SEric Cheng sum += *(uchar_t *)w;
3108275SEric Cheng #else
3118275SEric Cheng sum += *(uchar_t *)w << 8;
3128275SEric Cheng #endif
3138275SEric Cheng pmlen = -1;
3148275SEric Cheng }
3158275SEric Cheng } else {
3168275SEric Cheng ushort_t swsum;
3178275SEric Cheng #ifdef _LITTLE_ENDIAN
3188275SEric Cheng sum += *(uchar_t *)w;
3198275SEric Cheng #else
3208275SEric Cheng sum += *(uchar_t *)w << 8;
3218275SEric Cheng #endif
3228275SEric Cheng mlen--;
3238275SEric Cheng w = (ushort_t *)(1 + (uintptr_t)w);
3248275SEric Cheng
3258275SEric Cheng /* Do a separate checksum and copy operation */
3268275SEric Cheng swsum = ip_ocsum(w, mlen>>1, 0);
3278275SEric Cheng sum += ((swsum << 8) & 0xffff) | (swsum >> 8);
3288275SEric Cheng w += mlen>>1;
3298275SEric Cheng /*
3308275SEric Cheng * If we had an even number of bytes,
3318275SEric Cheng * then the last byte goes in the low
3328275SEric Cheng * part of the sum. Otherwise we had an
3338275SEric Cheng * odd number of bytes and we take the first
3348275SEric Cheng * byte to the low part of the sum the
3358275SEric Cheng * next time around the loop.
3368275SEric Cheng */
3378275SEric Cheng if (is_odd(mlen)) {
3388275SEric Cheng #ifdef _LITTLE_ENDIAN
3398275SEric Cheng sum += *(uchar_t *)w << 8;
3408275SEric Cheng #else
3418275SEric Cheng sum += *(uchar_t *)w;
3428275SEric Cheng #endif
3438275SEric Cheng }
3448275SEric Cheng else
3458275SEric Cheng pmlen = -1;
3468275SEric Cheng }
3478275SEric Cheng }
3488275SEric Cheng /*
3498275SEric Cheng * Locate the next block with some data.
3508275SEric Cheng * If there is a word split across a boundary we
3518275SEric Cheng * will wrap to the top with mlen == -1 and
3528275SEric Cheng * then add it in shifted appropriately.
3538275SEric Cheng */
3548275SEric Cheng offset = 0;
3558275SEric Cheng if (! pmp) {
3568275SEric Cheng for (; ; ) {
3578275SEric Cheng if (mp == 0) {
3588275SEric Cheng goto done;
3598275SEric Cheng }
3608275SEric Cheng if (mp_len(mp))
3618275SEric Cheng break;
3628275SEric Cheng mp = mp->b_cont;
3638275SEric Cheng }
3648275SEric Cheng dp = mp->b_datap;
3658275SEric Cheng if (dp->db_struioflag & STRUIO_IP)
3668275SEric Cheng psum = *(ushort_t *)dp->db_struioun.data;
3678275SEric Cheng } else
3688275SEric Cheng mp = pmp;
3698275SEric Cheng }
3708275SEric Cheng done:
3718275SEric Cheng /*
3728275SEric Cheng * Add together high and low parts of sum
3738275SEric Cheng * and carry to get cksum.
3748275SEric Cheng * Have to be careful to not drop the last
3758275SEric Cheng * carry here.
3768275SEric Cheng */
3778275SEric Cheng sum = (sum & 0xFFFF) + (sum >> 16);
3788275SEric Cheng sum = (sum & 0xFFFF) + (sum >> 16);
3798275SEric Cheng TRACE_3(TR_FAC_IP, TR_IP_CKSUM_END,
3808275SEric Cheng "ip_cksum_end:(%S) type %d (%X)", "ip_cksum", 1, sum);
3818275SEric Cheng return (sum);
3828275SEric Cheng }
3838275SEric Cheng
3848275SEric Cheng uint32_t
sctp_cksum(mblk_t * mp,int offset)3858275SEric Cheng sctp_cksum(mblk_t *mp, int offset)
3868275SEric Cheng {
3878275SEric Cheng uint32_t crc32;
3888275SEric Cheng uchar_t *p = NULL;
3898275SEric Cheng
3908275SEric Cheng crc32 = 0xFFFFFFFF;
3918275SEric Cheng p = mp->b_rptr + offset;
3928275SEric Cheng crc32 = sctp_crc32(crc32, p, mp->b_wptr - p);
3938275SEric Cheng for (mp = mp->b_cont; mp != NULL; mp = mp->b_cont) {
3948275SEric Cheng crc32 = sctp_crc32(crc32, mp->b_rptr, MBLKL(mp));
3958275SEric Cheng }
3968275SEric Cheng
3978275SEric Cheng /* Complement the result */
3988275SEric Cheng crc32 = ~crc32;
3998275SEric Cheng
4008275SEric Cheng return (crc32);
4018275SEric Cheng }
4028275SEric Cheng
4038275SEric Cheng /*
4048275SEric Cheng * Routine to compute Internet checksum (16-bit 1's complement) of a given
4058275SEric Cheng * Multidata packet descriptor. As in the non-Multidata routine, this doesn't
4068275SEric Cheng * 1's complement the result, such that it may be used to compute partial
4078275SEric Cheng * checksums. Since it works on buffer spans rather than mblks, this routine
4088275SEric Cheng * does not handle existing partial checksum value as in the STRUIO_IP special
4098275SEric Cheng * mblk case (supporting this is rather trivial, but is perhaps of no use at
4108275SEric Cheng * the moment unless synchronous streams and delayed checksum calculation are
4118275SEric Cheng * revived.)
4128275SEric Cheng *
4138275SEric Cheng * Note also here that the given Multidata packet descriptor must refer to
4148275SEric Cheng * a header buffer, i.e. it must have a header fragment. In addition, the
4158275SEric Cheng * offset must lie within the boundary of the header fragment. For the
4168275SEric Cheng * outbound tcp (MDT) case, this will not be an issue because the stack
4178275SEric Cheng * ensures that such conditions are met, and that there is no need whatsoever
4188275SEric Cheng * to compute partial checksums on an arbitrary offset that is not part of
4198275SEric Cheng * the header fragment. We may need to revisit this routine to handle all
4208275SEric Cheng * cases of the inbound (MDR) case, especially when we need to perform partial
4218275SEric Cheng * checksum calculation due to padded bytes (non-zeroes) in the frame.
4228275SEric Cheng */
4238275SEric Cheng uint_t
ip_md_cksum(pdesc_t * pd,int offset,uint_t sum)4248275SEric Cheng ip_md_cksum(pdesc_t *pd, int offset, uint_t sum)
4258275SEric Cheng {
4268275SEric Cheng pdescinfo_t *pdi = &pd->pd_pdi;
4278275SEric Cheng uchar_t *reg_start, *reg_end;
4288275SEric Cheng ssize_t mlen, i;
4298275SEric Cheng ushort_t *w;
4308275SEric Cheng boolean_t byteleft = B_FALSE;
4318275SEric Cheng
4328275SEric Cheng ASSERT((pdi->flags & PDESC_HAS_REF) != 0);
4338275SEric Cheng ASSERT(pdi->hdr_rptr != NULL && pdi->hdr_wptr != NULL);
4348275SEric Cheng ASSERT(offset <= PDESC_HDRL(pdi));
4358275SEric Cheng
4368275SEric Cheng for (i = 0; i < pdi->pld_cnt + 1; i++) {
4378275SEric Cheng if (i == 0) {
4388275SEric Cheng reg_start = pdi->hdr_rptr;
4398275SEric Cheng reg_end = pdi->hdr_wptr;
4408275SEric Cheng } else {
4418275SEric Cheng reg_start = pdi->pld_ary[i - 1].pld_rptr;
4428275SEric Cheng reg_end = pdi->pld_ary[i - 1].pld_wptr;
4438275SEric Cheng offset = 0;
4448275SEric Cheng }
4458275SEric Cheng
4468275SEric Cheng w = (ushort_t *)(reg_start + offset);
4478275SEric Cheng mlen = reg_end - (uchar_t *)w;
4488275SEric Cheng
4498275SEric Cheng if (mlen > 0 && byteleft) {
4508275SEric Cheng /*
4518275SEric Cheng * There is a byte left from the last
4528275SEric Cheng * segment; add it into the checksum.
4538275SEric Cheng * Don't have to worry about a carry-
4548275SEric Cheng * out here because we make sure that
4558275SEric Cheng * high part of (32 bit) sum is small
4568275SEric Cheng * below.
4578275SEric Cheng */
4588275SEric Cheng #ifdef _LITTLE_ENDIAN
4598275SEric Cheng sum += *(uchar_t *)w << 8;
4608275SEric Cheng #else
4618275SEric Cheng sum += *(uchar_t *)w;
4628275SEric Cheng #endif
4638275SEric Cheng w = (ushort_t *)((char *)w + 1);
4648275SEric Cheng mlen--;
4658275SEric Cheng byteleft = B_FALSE;
4668275SEric Cheng }
4678275SEric Cheng
4688275SEric Cheng if (mlen == 0)
4698275SEric Cheng continue;
4708275SEric Cheng
4718275SEric Cheng if (is_even(w)) {
4728275SEric Cheng sum = ip_ocsum(w, mlen >> 1, sum);
4738275SEric Cheng w += mlen >> 1;
4748275SEric Cheng /*
4758275SEric Cheng * If we had an odd number of bytes,
4768275SEric Cheng * then the last byte goes in the high
4778275SEric Cheng * part of the sum, and we take the
4788275SEric Cheng * first byte to the low part of the sum
4798275SEric Cheng * the next time around the loop.
4808275SEric Cheng */
4818275SEric Cheng if (is_odd(mlen)) {
4828275SEric Cheng #ifdef _LITTLE_ENDIAN
4838275SEric Cheng sum += *(uchar_t *)w;
4848275SEric Cheng #else
4858275SEric Cheng sum += *(uchar_t *)w << 8;
4868275SEric Cheng #endif
4878275SEric Cheng byteleft = B_TRUE;
4888275SEric Cheng }
4898275SEric Cheng } else {
4908275SEric Cheng ushort_t swsum;
4918275SEric Cheng #ifdef _LITTLE_ENDIAN
4928275SEric Cheng sum += *(uchar_t *)w;
4938275SEric Cheng #else
4948275SEric Cheng sum += *(uchar_t *)w << 8;
4958275SEric Cheng #endif
4968275SEric Cheng mlen--;
4978275SEric Cheng w = (ushort_t *)(1 + (uintptr_t)w);
4988275SEric Cheng
4998275SEric Cheng /* Do a separate checksum and copy operation */
5008275SEric Cheng swsum = ip_ocsum(w, mlen >> 1, 0);
5018275SEric Cheng sum += ((swsum << 8) & 0xffff) | (swsum >> 8);
5028275SEric Cheng w += mlen >> 1;
5038275SEric Cheng /*
5048275SEric Cheng * If we had an even number of bytes,
5058275SEric Cheng * then the last byte goes in the low
5068275SEric Cheng * part of the sum. Otherwise we had an
5078275SEric Cheng * odd number of bytes and we take the first
5088275SEric Cheng * byte to the low part of the sum the
5098275SEric Cheng * next time around the loop.
5108275SEric Cheng */
5118275SEric Cheng if (is_odd(mlen)) {
5128275SEric Cheng #ifdef _LITTLE_ENDIAN
5138275SEric Cheng sum += *(uchar_t *)w << 8;
5148275SEric Cheng #else
5158275SEric Cheng sum += *(uchar_t *)w;
5168275SEric Cheng #endif
5178275SEric Cheng } else {
5188275SEric Cheng byteleft = B_TRUE;
5198275SEric Cheng }
5208275SEric Cheng }
5218275SEric Cheng }
5228275SEric Cheng
5238275SEric Cheng /*
5248275SEric Cheng * Add together high and low parts of sum and carry to get cksum.
5258275SEric Cheng * Have to be careful to not drop the last carry here.
5268275SEric Cheng */
5278275SEric Cheng sum = (sum & 0xffff) + (sum >> 16);
5288275SEric Cheng sum = (sum & 0xffff) + (sum >> 16);
5298275SEric Cheng
5308275SEric Cheng return (sum);
5318275SEric Cheng }
5328275SEric Cheng
5338275SEric Cheng /* Return the IP checksum for the IP header at "iph". */
5348275SEric Cheng uint16_t
ip_csum_hdr(ipha_t * ipha)5358275SEric Cheng ip_csum_hdr(ipha_t *ipha)
5368275SEric Cheng {
5378275SEric Cheng uint16_t *uph;
5388275SEric Cheng uint32_t sum;
5398275SEric Cheng int opt_len;
5408275SEric Cheng
5418275SEric Cheng opt_len = (ipha->ipha_version_and_hdr_length & 0xF) -
5428275SEric Cheng IP_SIMPLE_HDR_LENGTH_IN_WORDS;
5438275SEric Cheng uph = (uint16_t *)ipha;
5448275SEric Cheng sum = uph[0] + uph[1] + uph[2] + uph[3] + uph[4] +
5458275SEric Cheng uph[5] + uph[6] + uph[7] + uph[8] + uph[9];
5468275SEric Cheng if (opt_len > 0) {
5478275SEric Cheng do {
5488275SEric Cheng sum += uph[10];
5498275SEric Cheng sum += uph[11];
5508275SEric Cheng uph += 2;
5518275SEric Cheng } while (--opt_len);
5528275SEric Cheng }
5538275SEric Cheng sum = (sum & 0xFFFF) + (sum >> 16);
5548275SEric Cheng sum = ~(sum + (sum >> 16)) & 0xFFFF;
5558275SEric Cheng if (sum == 0xffff)
5568275SEric Cheng sum = 0;
5578275SEric Cheng return ((uint16_t)sum);
5588275SEric Cheng }
559