xref: /titanic_50/usr/src/uts/common/os/ip_cksum.c (revision bd670b35a010421b6e1a5536c34453a827007c81)
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
ip_cksum(mblk_t * mp,int offset,uint_t sum)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
sctp_cksum(mblk_t * mp,int offset)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
ip_md_cksum(pdesc_t * pd,int offset,uint_t sum)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
ip_csum_hdr(ipha_t * ipha)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