xref: /onnv-gate/usr/src/uts/common/os/ip_cksum.c (revision 11042:2d6e217af1b4)
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