xref: /illumos-gate/usr/src/uts/common/os/ip_cksum.c (revision 9b664393d4fdda96221e6ea9ea95790d3c15be70)
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 /*
22bd670b35SErik Nordmark  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
23da14cebeSEric Cheng  * Use is subject to license terms.
249495f63eSDan McDonald  * Copyright 2021 Joyent, Inc.
25*9b664393SGarrett D'Amore  * Copyright 2022 Garrett D'Amore
26da14cebeSEric Cheng  */
27da14cebeSEric Cheng /* Copyright (c) 1990 Mentat Inc. */
28da14cebeSEric Cheng 
29da14cebeSEric Cheng #include <sys/types.h>
30da14cebeSEric Cheng #include <sys/inttypes.h>
31da14cebeSEric Cheng #include <sys/systm.h>
32da14cebeSEric Cheng #include <sys/stream.h>
33da14cebeSEric Cheng #include <sys/strsun.h>
34da14cebeSEric Cheng #include <sys/debug.h>
35da14cebeSEric Cheng #include <sys/ddi.h>
36da14cebeSEric Cheng #include <sys/vtrace.h>
37da14cebeSEric Cheng #include <inet/sctp_crc32.h>
38da14cebeSEric Cheng #include <inet/ip.h>
39c61a1653SRyan Zezeski #include <inet/ip6.h>
40da14cebeSEric Cheng 
419495f63eSDan McDonald extern unsigned int ip_ocsum(ushort_t *, int, unsigned int);
42da14cebeSEric Cheng 
43da14cebeSEric Cheng /*
44da14cebeSEric Cheng  * Checksum routine for Internet Protocol family headers.
45da14cebeSEric Cheng  * This routine is very heavily used in the network
46da14cebeSEric Cheng  * code and should be modified for each CPU to be as fast as possible.
47da14cebeSEric Cheng  */
48da14cebeSEric Cheng 
49da14cebeSEric Cheng #define	mp_len(mp) ((mp)->b_wptr - (mp)->b_rptr)
50da14cebeSEric Cheng 
51da14cebeSEric Cheng /*
52da14cebeSEric Cheng  * Even/Odd checks. Usually it is performed on pointers but may be
53da14cebeSEric Cheng  * used on integers as well. uintptr_t is long enough to hold both
54da14cebeSEric Cheng  * integer and pointer.
55da14cebeSEric Cheng  */
56da14cebeSEric Cheng #define	is_odd(p) (((uintptr_t)(p) & 0x1) != 0)
57da14cebeSEric Cheng #define	is_even(p) (!is_odd(p))
58da14cebeSEric Cheng 
59da14cebeSEric Cheng 
60da14cebeSEric Cheng #ifdef ZC_TEST
61da14cebeSEric Cheng /*
62da14cebeSEric Cheng  * Disable the TCP s/w cksum.
63da14cebeSEric Cheng  * XXX - This is just a hack for testing purpose. Don't use it for
64da14cebeSEric Cheng  * anything else!
65da14cebeSEric Cheng  */
66da14cebeSEric Cheng int noswcksum = 0;
67da14cebeSEric Cheng #endif
68da14cebeSEric Cheng /*
69da14cebeSEric Cheng  * Note: this does not ones-complement the result since it is used
70da14cebeSEric Cheng  * when computing partial checksums.
71da14cebeSEric Cheng  * For nonSTRUIO_IP mblks, assumes mp->b_rptr+offset is 16 bit aligned.
72da14cebeSEric Cheng  * For STRUIO_IP mblks, assumes mp->b_datap->db_struiobase is 16 bit aligned.
73da14cebeSEric Cheng  *
74da14cebeSEric Cheng  * Note: for STRUIO_IP special mblks some data may have been previously
75da14cebeSEric Cheng  *	 checksumed, this routine will handle additional data prefixed within
76da14cebeSEric Cheng  *	 an mblk or b_cont (chained) mblk(s). This routine will also handle
77da14cebeSEric Cheng  *	 suffixed b_cont mblk(s) and data suffixed within an mblk.
78da14cebeSEric Cheng  */
79da14cebeSEric Cheng unsigned int
ip_cksum(mblk_t * mp,int offset,uint_t sum)80da14cebeSEric Cheng ip_cksum(mblk_t *mp, int offset, uint_t sum)
81da14cebeSEric Cheng {
82da14cebeSEric Cheng 	ushort_t *w;
83da14cebeSEric Cheng 	ssize_t	mlen;
84da14cebeSEric Cheng 	int pmlen;
85da14cebeSEric Cheng 	mblk_t *pmp;
86da14cebeSEric Cheng 	dblk_t *dp = mp->b_datap;
87da14cebeSEric Cheng 	ushort_t psum = 0;
88da14cebeSEric Cheng 
89da14cebeSEric Cheng #ifdef ZC_TEST
90da14cebeSEric Cheng 	if (noswcksum)
91da14cebeSEric Cheng 		return (0xffff);
92da14cebeSEric Cheng #endif
93da14cebeSEric Cheng 	ASSERT(dp);
94da14cebeSEric Cheng 
95da14cebeSEric Cheng 	if (mp->b_cont == NULL) {
96da14cebeSEric Cheng 		/*
97da14cebeSEric Cheng 		 * May be fast-path, only one mblk.
98da14cebeSEric Cheng 		 */
99da14cebeSEric Cheng 		w = (ushort_t *)(mp->b_rptr + offset);
100da14cebeSEric Cheng 		if (dp->db_struioflag & STRUIO_IP) {
101da14cebeSEric Cheng 			/*
102da14cebeSEric Cheng 			 * Checksum any data not already done by
103da14cebeSEric Cheng 			 * the caller and add in any partial checksum.
104da14cebeSEric Cheng 			 */
105da14cebeSEric Cheng 			if ((offset > dp->db_cksumstart) ||
106da14cebeSEric Cheng 			    mp->b_wptr != (uchar_t *)(mp->b_rptr +
107da14cebeSEric Cheng 			    dp->db_cksumend)) {
108da14cebeSEric Cheng 				/*
109da14cebeSEric Cheng 				 * Mblk data pointers aren't inclusive
110da14cebeSEric Cheng 				 * of uio data, so disregard checksum.
111da14cebeSEric Cheng 				 *
112da14cebeSEric Cheng 				 * not using all of data in dblk make sure
113da14cebeSEric Cheng 				 * not use to use the precalculated checksum
114da14cebeSEric Cheng 				 * in this case.
115da14cebeSEric Cheng 				 */
116da14cebeSEric Cheng 				dp->db_struioflag &= ~STRUIO_IP;
117da14cebeSEric Cheng 				goto norm;
118da14cebeSEric Cheng 			}
119da14cebeSEric Cheng 			ASSERT(mp->b_wptr == (mp->b_rptr + dp->db_cksumend));
120da14cebeSEric Cheng 			psum = *(ushort_t *)dp->db_struioun.data;
121da14cebeSEric Cheng 			if ((mlen = dp->db_cksumstart - offset) < 0)
122da14cebeSEric Cheng 				mlen = 0;
123da14cebeSEric Cheng 			if (is_odd(mlen))
124da14cebeSEric Cheng 				goto slow;
125da14cebeSEric Cheng 			if (mlen && dp->db_cksumstart != dp->db_cksumstuff &&
126da14cebeSEric Cheng 			    dp->db_cksumend != dp->db_cksumstuff) {
127da14cebeSEric Cheng 				/*
128da14cebeSEric Cheng 				 * There is prefix data to do and some uio
129da14cebeSEric Cheng 				 * data has already been checksumed and there
130da14cebeSEric Cheng 				 * is more uio data to do, so do the prefix
131da14cebeSEric Cheng 				 * data first, then do the remainder of the
132da14cebeSEric Cheng 				 * uio data.
133da14cebeSEric Cheng 				 */
134da14cebeSEric Cheng 				sum = ip_ocsum(w, mlen >> 1, sum);
135da14cebeSEric Cheng 				w = (ushort_t *)(mp->b_rptr +
136da14cebeSEric Cheng 				    dp->db_cksumstuff);
137da14cebeSEric Cheng 				if (is_odd(w)) {
138da14cebeSEric Cheng 					pmp = mp;
139da14cebeSEric Cheng 					goto slow1;
140da14cebeSEric Cheng 				}
141da14cebeSEric Cheng 				mlen = dp->db_cksumend - dp->db_cksumstuff;
142da14cebeSEric Cheng 			} else if (dp->db_cksumend != dp->db_cksumstuff) {
143da14cebeSEric Cheng 				/*
144da14cebeSEric Cheng 				 * There may be uio data to do, if there is
145da14cebeSEric Cheng 				 * prefix data to do then add in all of the
146da14cebeSEric Cheng 				 * uio data (if any) to do, else just do any
147da14cebeSEric Cheng 				 * uio data.
148da14cebeSEric Cheng 				 */
149da14cebeSEric Cheng 				if (mlen)
150da14cebeSEric Cheng 					mlen += dp->db_cksumend
151da14cebeSEric Cheng 					    - dp->db_cksumstuff;
152da14cebeSEric Cheng 				else {
153da14cebeSEric Cheng 					w = (ushort_t *)(mp->b_rptr +
154da14cebeSEric Cheng 					    dp->db_cksumstuff);
155da14cebeSEric Cheng 					if (is_odd(w))
156da14cebeSEric Cheng 						goto slow;
157da14cebeSEric Cheng 					mlen = dp->db_cksumend
158da14cebeSEric Cheng 					    - dp->db_cksumstuff;
159da14cebeSEric Cheng 				}
160da14cebeSEric Cheng 			} else if (mlen == 0)
161da14cebeSEric Cheng 				return (psum);
162da14cebeSEric Cheng 
163da14cebeSEric Cheng 			if (is_odd(mlen))
164da14cebeSEric Cheng 				goto slow;
165da14cebeSEric Cheng 			sum += psum;
166da14cebeSEric Cheng 		} else {
167da14cebeSEric Cheng 			/*
168da14cebeSEric Cheng 			 * Checksum all data not already done by the caller.
169da14cebeSEric Cheng 			 */
170da14cebeSEric Cheng 		norm:
171da14cebeSEric Cheng 			mlen = mp->b_wptr - (uchar_t *)w;
172da14cebeSEric Cheng 			if (is_odd(mlen))
173da14cebeSEric Cheng 				goto slow;
174da14cebeSEric Cheng 		}
175da14cebeSEric Cheng 		ASSERT(is_even(w));
176da14cebeSEric Cheng 		ASSERT(is_even(mlen));
177da14cebeSEric Cheng 		return (ip_ocsum(w, mlen >> 1, sum));
178da14cebeSEric Cheng 	}
179da14cebeSEric Cheng 	if (dp->db_struioflag & STRUIO_IP)
180da14cebeSEric Cheng 		psum = *(ushort_t *)dp->db_struioun.data;
181da14cebeSEric Cheng slow:
182da14cebeSEric Cheng 	pmp = 0;
183da14cebeSEric Cheng slow1:
184da14cebeSEric Cheng 	mlen = 0;
185da14cebeSEric Cheng 	pmlen = 0;
186da14cebeSEric Cheng 	for (; ; ) {
187da14cebeSEric Cheng 		/*
188da14cebeSEric Cheng 		 * Each trip around loop adds in word(s) from one mbuf segment
189da14cebeSEric Cheng 		 * (except for when pmp == mp, then its two partial trips).
190da14cebeSEric Cheng 		 */
191da14cebeSEric Cheng 		w = (ushort_t *)(mp->b_rptr + offset);
192da14cebeSEric Cheng 		if (pmp) {
193da14cebeSEric Cheng 			/*
194da14cebeSEric Cheng 			 * This is the second trip around for this mblk.
195da14cebeSEric Cheng 			 */
196da14cebeSEric Cheng 			pmp = 0;
197da14cebeSEric Cheng 			mlen = 0;
198da14cebeSEric Cheng 			goto douio;
199da14cebeSEric Cheng 		} else if (dp->db_struioflag & STRUIO_IP) {
200da14cebeSEric Cheng 			/*
201da14cebeSEric Cheng 			 * Checksum any data not already done by the
202da14cebeSEric Cheng 			 * caller and add in any partial checksum.
203da14cebeSEric Cheng 			 */
204da14cebeSEric Cheng 			if ((offset > dp->db_cksumstart) ||
205da14cebeSEric Cheng 			    mp->b_wptr != (uchar_t *)(mp->b_rptr +
206da14cebeSEric Cheng 			    dp->db_cksumend)) {
207da14cebeSEric Cheng 				/*
208da14cebeSEric Cheng 				 * Mblk data pointers aren't inclusive
209da14cebeSEric Cheng 				 * of uio data, so disregard checksum.
210da14cebeSEric Cheng 				 *
211da14cebeSEric Cheng 				 * not using all of data in dblk make sure
212da14cebeSEric Cheng 				 * not use to use the precalculated checksum
213da14cebeSEric Cheng 				 * in this case.
214da14cebeSEric Cheng 				 */
215da14cebeSEric Cheng 				dp->db_struioflag &= ~STRUIO_IP;
216da14cebeSEric Cheng 				goto snorm;
217da14cebeSEric Cheng 			}
218da14cebeSEric Cheng 			ASSERT(mp->b_wptr == (mp->b_rptr + dp->db_cksumend));
219da14cebeSEric Cheng 			if ((mlen = dp->db_cksumstart - offset) < 0)
220da14cebeSEric Cheng 				mlen = 0;
221da14cebeSEric Cheng 			if (mlen && dp->db_cksumstart != dp->db_cksumstuff) {
222da14cebeSEric Cheng 				/*
223da14cebeSEric Cheng 				 * There is prefix data too do and some
224da14cebeSEric Cheng 				 * uio data has already been checksumed,
225da14cebeSEric Cheng 				 * so do the prefix data only this trip.
226da14cebeSEric Cheng 				 */
227da14cebeSEric Cheng 				pmp = mp;
228da14cebeSEric Cheng 			} else {
229da14cebeSEric Cheng 				/*
230da14cebeSEric Cheng 				 * Add in any partial cksum (if any) and
231da14cebeSEric Cheng 				 * do the remainder of the uio data.
232da14cebeSEric Cheng 				 */
233da14cebeSEric Cheng 				int odd;
234da14cebeSEric Cheng 			douio:
235da14cebeSEric Cheng 				odd = is_odd(dp->db_cksumstuff -
236da14cebeSEric Cheng 				    dp->db_cksumstart);
237da14cebeSEric Cheng 				if (pmlen == -1) {
238da14cebeSEric Cheng 					/*
239da14cebeSEric Cheng 					 * Previous mlen was odd, so swap
240da14cebeSEric Cheng 					 * the partial checksum bytes.
241da14cebeSEric Cheng 					 */
242da14cebeSEric Cheng 					sum += ((psum << 8) & 0xffff)
243da14cebeSEric Cheng 					    | (psum >> 8);
244da14cebeSEric Cheng 					if (odd)
245da14cebeSEric Cheng 						pmlen = 0;
246da14cebeSEric Cheng 				} else {
247da14cebeSEric Cheng 					sum += psum;
248da14cebeSEric Cheng 					if (odd)
249da14cebeSEric Cheng 						pmlen = -1;
250da14cebeSEric Cheng 				}
251da14cebeSEric Cheng 				if (dp->db_cksumend != dp->db_cksumstuff) {
252da14cebeSEric Cheng 					/*
253da14cebeSEric Cheng 					 * If prefix data to do and then all
254da14cebeSEric Cheng 					 * the uio data nees to be checksumed,
255da14cebeSEric Cheng 					 * else just do any uio data.
256da14cebeSEric Cheng 					 */
257da14cebeSEric Cheng 					if (mlen)
258da14cebeSEric Cheng 						mlen += dp->db_cksumend
259da14cebeSEric Cheng 						    - dp->db_cksumstuff;
260da14cebeSEric Cheng 					else {
261da14cebeSEric Cheng 						w = (ushort_t *)(mp->b_rptr +
262da14cebeSEric Cheng 						    dp->db_cksumstuff);
263da14cebeSEric Cheng 						mlen = dp->db_cksumend -
264da14cebeSEric Cheng 						    dp->db_cksumstuff;
265da14cebeSEric Cheng 					}
266da14cebeSEric Cheng 				}
267da14cebeSEric Cheng 			}
268da14cebeSEric Cheng 		} else {
269da14cebeSEric Cheng 			/*
270da14cebeSEric Cheng 			 * Checksum all of the mblk data.
271da14cebeSEric Cheng 			 */
272da14cebeSEric Cheng 		snorm:
273da14cebeSEric Cheng 			mlen = mp->b_wptr - (uchar_t *)w;
274da14cebeSEric Cheng 		}
275da14cebeSEric Cheng 
276da14cebeSEric Cheng 		mp = mp->b_cont;
277da14cebeSEric Cheng 		if (mlen > 0 && pmlen == -1) {
278da14cebeSEric Cheng 			/*
279da14cebeSEric Cheng 			 * There is a byte left from the last
280da14cebeSEric Cheng 			 * segment; add it into the checksum.
281da14cebeSEric Cheng 			 * Don't have to worry about a carry-
282da14cebeSEric Cheng 			 * out here because we make sure that
283da14cebeSEric Cheng 			 * high part of (32 bit) sum is small
284da14cebeSEric Cheng 			 * below.
285da14cebeSEric Cheng 			 */
286da14cebeSEric Cheng #ifdef _LITTLE_ENDIAN
287da14cebeSEric Cheng 			sum += *(uchar_t *)w << 8;
288da14cebeSEric Cheng #else
289da14cebeSEric Cheng 			sum += *(uchar_t *)w;
290da14cebeSEric Cheng #endif
291da14cebeSEric Cheng 			w = (ushort_t *)((char *)w + 1);
292da14cebeSEric Cheng 			mlen--;
293da14cebeSEric Cheng 			pmlen = 0;
294da14cebeSEric Cheng 		}
295da14cebeSEric Cheng 		if (mlen > 0) {
296da14cebeSEric Cheng 			if (is_even(w)) {
297da14cebeSEric Cheng 				sum = ip_ocsum(w, mlen>>1, sum);
298da14cebeSEric Cheng 				w += mlen>>1;
299da14cebeSEric Cheng 				/*
300da14cebeSEric Cheng 				 * If we had an odd number of bytes,
301da14cebeSEric Cheng 				 * then the last byte goes in the high
302da14cebeSEric Cheng 				 * part of the sum, and we take the
303da14cebeSEric Cheng 				 * first byte to the low part of the sum
304da14cebeSEric Cheng 				 * the next time around the loop.
305da14cebeSEric Cheng 				 */
306da14cebeSEric Cheng 				if (is_odd(mlen)) {
307da14cebeSEric Cheng #ifdef _LITTLE_ENDIAN
308da14cebeSEric Cheng 					sum += *(uchar_t *)w;
309da14cebeSEric Cheng #else
310da14cebeSEric Cheng 					sum += *(uchar_t *)w << 8;
311da14cebeSEric Cheng #endif
312da14cebeSEric Cheng 					pmlen = -1;
313da14cebeSEric Cheng 				}
314da14cebeSEric Cheng 			} else {
315da14cebeSEric Cheng 				ushort_t swsum;
316da14cebeSEric Cheng #ifdef _LITTLE_ENDIAN
317da14cebeSEric Cheng 				sum += *(uchar_t *)w;
318da14cebeSEric Cheng #else
319da14cebeSEric Cheng 				sum += *(uchar_t *)w << 8;
320da14cebeSEric Cheng #endif
321da14cebeSEric Cheng 				mlen--;
322da14cebeSEric Cheng 				w = (ushort_t *)(1 + (uintptr_t)w);
323da14cebeSEric Cheng 
324da14cebeSEric Cheng 				/* Do a separate checksum and copy operation */
325da14cebeSEric Cheng 				swsum = ip_ocsum(w, mlen>>1, 0);
326da14cebeSEric Cheng 				sum += ((swsum << 8) & 0xffff) | (swsum >> 8);
327da14cebeSEric Cheng 				w += mlen>>1;
328da14cebeSEric Cheng 				/*
329da14cebeSEric Cheng 				 * If we had an even number of bytes,
330da14cebeSEric Cheng 				 * then the last byte goes in the low
331da14cebeSEric Cheng 				 * part of the sum.  Otherwise we had an
332da14cebeSEric Cheng 				 * odd number of bytes and we take the first
333da14cebeSEric Cheng 				 * byte to the low part of the sum the
334da14cebeSEric Cheng 				 * next time around the loop.
335da14cebeSEric Cheng 				 */
336da14cebeSEric Cheng 				if (is_odd(mlen)) {
337da14cebeSEric Cheng #ifdef _LITTLE_ENDIAN
338da14cebeSEric Cheng 					sum += *(uchar_t *)w << 8;
339da14cebeSEric Cheng #else
340da14cebeSEric Cheng 					sum += *(uchar_t *)w;
341da14cebeSEric Cheng #endif
342da14cebeSEric Cheng 				}
343da14cebeSEric Cheng 				else
344da14cebeSEric Cheng 					pmlen = -1;
345da14cebeSEric Cheng 			}
346da14cebeSEric Cheng 		}
347da14cebeSEric Cheng 		/*
348da14cebeSEric Cheng 		 * Locate the next block with some data.
349da14cebeSEric Cheng 		 * If there is a word split across a boundary we
350da14cebeSEric Cheng 		 * will wrap to the top with mlen == -1 and
351da14cebeSEric Cheng 		 * then add it in shifted appropriately.
352da14cebeSEric Cheng 		 */
353da14cebeSEric Cheng 		offset = 0;
354da14cebeSEric Cheng 		if (! pmp) {
355da14cebeSEric Cheng 			for (; ; ) {
356da14cebeSEric Cheng 				if (mp == 0) {
357da14cebeSEric Cheng 					goto done;
358da14cebeSEric Cheng 				}
359da14cebeSEric Cheng 				if (mp_len(mp))
360da14cebeSEric Cheng 					break;
361da14cebeSEric Cheng 				mp = mp->b_cont;
362da14cebeSEric Cheng 			}
363da14cebeSEric Cheng 			dp = mp->b_datap;
364da14cebeSEric Cheng 			if (dp->db_struioflag & STRUIO_IP)
365da14cebeSEric Cheng 				psum = *(ushort_t *)dp->db_struioun.data;
366da14cebeSEric Cheng 		} else
367da14cebeSEric Cheng 			mp = pmp;
368da14cebeSEric Cheng 	}
369da14cebeSEric Cheng done:
370da14cebeSEric Cheng 	/*
371da14cebeSEric Cheng 	 * Add together high and low parts of sum
372da14cebeSEric Cheng 	 * and carry to get cksum.
373da14cebeSEric Cheng 	 * Have to be careful to not drop the last
374da14cebeSEric Cheng 	 * carry here.
375da14cebeSEric Cheng 	 */
376da14cebeSEric Cheng 	sum = (sum & 0xFFFF) + (sum >> 16);
377da14cebeSEric Cheng 	sum = (sum & 0xFFFF) + (sum >> 16);
378da14cebeSEric Cheng 	TRACE_3(TR_FAC_IP, TR_IP_CKSUM_END,
379da14cebeSEric Cheng 	    "ip_cksum_end:(%S) type %d (%X)", "ip_cksum", 1, sum);
380da14cebeSEric Cheng 	return (sum);
381da14cebeSEric Cheng }
382da14cebeSEric Cheng 
383da14cebeSEric Cheng uint32_t
sctp_cksum(mblk_t * mp,int offset)384da14cebeSEric Cheng sctp_cksum(mblk_t *mp, int offset)
385da14cebeSEric Cheng {
386da14cebeSEric Cheng 	uint32_t crc32;
387da14cebeSEric Cheng 	uchar_t *p = NULL;
388da14cebeSEric Cheng 
389da14cebeSEric Cheng 	crc32 = 0xFFFFFFFF;
390da14cebeSEric Cheng 	p = mp->b_rptr + offset;
391da14cebeSEric Cheng 	crc32 = sctp_crc32(crc32, p, mp->b_wptr - p);
392da14cebeSEric Cheng 	for (mp = mp->b_cont; mp != NULL; mp = mp->b_cont) {
393da14cebeSEric Cheng 		crc32 = sctp_crc32(crc32, mp->b_rptr, MBLKL(mp));
394da14cebeSEric Cheng 	}
395da14cebeSEric Cheng 
396da14cebeSEric Cheng 	/* Complement the result */
397da14cebeSEric Cheng 	crc32 = ~crc32;
398da14cebeSEric Cheng 
399da14cebeSEric Cheng 	return (crc32);
400da14cebeSEric Cheng }
401da14cebeSEric Cheng 
402da14cebeSEric Cheng /* Return the IP checksum for the IP header at "iph". */
403da14cebeSEric Cheng uint16_t
ip_csum_hdr(ipha_t * ipha)404da14cebeSEric Cheng ip_csum_hdr(ipha_t *ipha)
405da14cebeSEric Cheng {
406da14cebeSEric Cheng 	uint16_t	*uph;
407da14cebeSEric Cheng 	uint32_t	sum;
408da14cebeSEric Cheng 	int		opt_len;
409da14cebeSEric Cheng 
410da14cebeSEric Cheng 	opt_len = (ipha->ipha_version_and_hdr_length & 0xF) -
411da14cebeSEric Cheng 	    IP_SIMPLE_HDR_LENGTH_IN_WORDS;
412da14cebeSEric Cheng 	uph = (uint16_t *)ipha;
413da14cebeSEric Cheng 	sum = uph[0] + uph[1] + uph[2] + uph[3] + uph[4] +
414da14cebeSEric Cheng 	    uph[5] + uph[6] + uph[7] + uph[8] + uph[9];
415da14cebeSEric Cheng 	if (opt_len > 0) {
416da14cebeSEric Cheng 		do {
417da14cebeSEric Cheng 			sum += uph[10];
418da14cebeSEric Cheng 			sum += uph[11];
419da14cebeSEric Cheng 			uph += 2;
420da14cebeSEric Cheng 		} while (--opt_len);
421da14cebeSEric Cheng 	}
422da14cebeSEric Cheng 	sum = (sum & 0xFFFF) + (sum >> 16);
423da14cebeSEric Cheng 	sum = ~(sum + (sum >> 16)) & 0xFFFF;
424da14cebeSEric Cheng 	if (sum == 0xffff)
425da14cebeSEric Cheng 		sum = 0;
426da14cebeSEric Cheng 	return ((uint16_t)sum);
427da14cebeSEric Cheng }
428c61a1653SRyan Zezeski 
429c61a1653SRyan Zezeski /*
430c61a1653SRyan Zezeski  * This function takes an mblk and IPv6 header as input and returns
431c61a1653SRyan Zezeski  * three pieces of information.
432c61a1653SRyan Zezeski  *
433c61a1653SRyan Zezeski  * 'hdr_length_ptr': The IPv6 header length including extension headers.
434c61a1653SRyan Zezeski  *
435c61a1653SRyan Zezeski  * 'nethdrpp': A pointer to the "next hedader" value, aka the
436c61a1653SRyan Zezeski  *             transport header. This argument may be set to NULL if
437c61a1653SRyan Zezeski  *             only the length is desired.
438c61a1653SRyan Zezeski  *
439c61a1653SRyan Zezeski  * return: Whether or not the header was malformed.
440c61a1653SRyan Zezeski  *
441c61a1653SRyan Zezeski  * This function assumes the IPv6 header along with all extensions are
442c61a1653SRyan Zezeski  * contained solely in this mblk: i.e., there is no b_cont walking.
443c61a1653SRyan Zezeski  */
444c61a1653SRyan Zezeski boolean_t
ip_hdr_length_nexthdr_v6(mblk_t * mp,ip6_t * ip6h,uint16_t * hdr_length_ptr,uint8_t ** nexthdrpp)445c61a1653SRyan Zezeski ip_hdr_length_nexthdr_v6(mblk_t *mp, ip6_t *ip6h, uint16_t *hdr_length_ptr,
446c61a1653SRyan Zezeski     uint8_t **nexthdrpp)
447c61a1653SRyan Zezeski {
448c61a1653SRyan Zezeski 	uint16_t length;
449c61a1653SRyan Zezeski 	uint_t	ehdrlen;
450c61a1653SRyan Zezeski 	uint8_t	*nexthdrp;
451c61a1653SRyan Zezeski 	uint8_t *whereptr;
452c61a1653SRyan Zezeski 	uint8_t *endptr;
453c61a1653SRyan Zezeski 	ip6_dest_t *desthdr;
454c61a1653SRyan Zezeski 	ip6_rthdr_t *rthdr;
455c61a1653SRyan Zezeski 	ip6_frag_t *fraghdr;
456c61a1653SRyan Zezeski 
4579495f63eSDan McDonald 	if (IPH_HDR_VERSION(ip6h) != IPV6_VERSION)
4589495f63eSDan McDonald 		return (B_FALSE);
459c61a1653SRyan Zezeski 	length = IPV6_HDR_LEN;
460c61a1653SRyan Zezeski 	whereptr = ((uint8_t *)&ip6h[1]); /* point to next hdr */
461c61a1653SRyan Zezeski 	endptr = mp->b_wptr;
462c61a1653SRyan Zezeski 
463c61a1653SRyan Zezeski 	nexthdrp = &ip6h->ip6_nxt;
464c61a1653SRyan Zezeski 	while (whereptr < endptr) {
465c61a1653SRyan Zezeski 		/* Is there enough left for len + nexthdr? */
466c61a1653SRyan Zezeski 		if (whereptr + MIN_EHDR_LEN > endptr)
467c61a1653SRyan Zezeski 			break;
468c61a1653SRyan Zezeski 
469c61a1653SRyan Zezeski 		switch (*nexthdrp) {
470c61a1653SRyan Zezeski 		case IPPROTO_HOPOPTS:
471c61a1653SRyan Zezeski 		case IPPROTO_DSTOPTS:
472c61a1653SRyan Zezeski 			/* Assumes the headers are identical for hbh and dst */
473c61a1653SRyan Zezeski 			desthdr = (ip6_dest_t *)whereptr;
474c61a1653SRyan Zezeski 			ehdrlen = 8 * (desthdr->ip6d_len + 1);
475c61a1653SRyan Zezeski 			if ((uchar_t *)desthdr +  ehdrlen > endptr)
476c61a1653SRyan Zezeski 				return (B_FALSE);
477c61a1653SRyan Zezeski 			nexthdrp = &desthdr->ip6d_nxt;
478c61a1653SRyan Zezeski 			break;
479c61a1653SRyan Zezeski 		case IPPROTO_ROUTING:
480c61a1653SRyan Zezeski 			rthdr = (ip6_rthdr_t *)whereptr;
481c61a1653SRyan Zezeski 			ehdrlen =  8 * (rthdr->ip6r_len + 1);
482c61a1653SRyan Zezeski 			if ((uchar_t *)rthdr +  ehdrlen > endptr)
483c61a1653SRyan Zezeski 				return (B_FALSE);
484c61a1653SRyan Zezeski 			nexthdrp = &rthdr->ip6r_nxt;
485c61a1653SRyan Zezeski 			break;
486c61a1653SRyan Zezeski 		case IPPROTO_FRAGMENT:
487c61a1653SRyan Zezeski 			fraghdr = (ip6_frag_t *)whereptr;
488c61a1653SRyan Zezeski 			ehdrlen = sizeof (ip6_frag_t);
489c61a1653SRyan Zezeski 			if ((uchar_t *)&fraghdr[1] > endptr)
490c61a1653SRyan Zezeski 				return (B_FALSE);
491c61a1653SRyan Zezeski 			nexthdrp = &fraghdr->ip6f_nxt;
492c61a1653SRyan Zezeski 			break;
493c61a1653SRyan Zezeski 		case IPPROTO_NONE:
494c61a1653SRyan Zezeski 			/* No next header means we're finished */
495c61a1653SRyan Zezeski 		default:
496c61a1653SRyan Zezeski 			*hdr_length_ptr = length;
497c61a1653SRyan Zezeski 
498c61a1653SRyan Zezeski 			if (nexthdrpp != NULL)
499c61a1653SRyan Zezeski 				*nexthdrpp = nexthdrp;
500c61a1653SRyan Zezeski 
501c61a1653SRyan Zezeski 			return (B_TRUE);
502c61a1653SRyan Zezeski 		}
503c61a1653SRyan Zezeski 		length += ehdrlen;
504c61a1653SRyan Zezeski 		whereptr += ehdrlen;
505c61a1653SRyan Zezeski 		*hdr_length_ptr = length;
506c61a1653SRyan Zezeski 
507c61a1653SRyan Zezeski 		if (nexthdrpp != NULL)
508c61a1653SRyan Zezeski 			*nexthdrpp = nexthdrp;
509c61a1653SRyan Zezeski 	}
510c61a1653SRyan Zezeski 	switch (*nexthdrp) {
511c61a1653SRyan Zezeski 	case IPPROTO_HOPOPTS:
512c61a1653SRyan Zezeski 	case IPPROTO_DSTOPTS:
513c61a1653SRyan Zezeski 	case IPPROTO_ROUTING:
514c61a1653SRyan Zezeski 	case IPPROTO_FRAGMENT:
515c61a1653SRyan Zezeski 		/*
516c61a1653SRyan Zezeski 		 * If any know extension headers are still to be processed,
517c61a1653SRyan Zezeski 		 * the packet's malformed (or at least all the IP header(s) are
518c61a1653SRyan Zezeski 		 * not in the same mblk - and that should never happen.
519c61a1653SRyan Zezeski 		 */
520c61a1653SRyan Zezeski 		return (B_FALSE);
521c61a1653SRyan Zezeski 
522c61a1653SRyan Zezeski 	default:
523c61a1653SRyan Zezeski 		/*
524c61a1653SRyan Zezeski 		 * If we get here, we know that all of the IP headers were in
525c61a1653SRyan Zezeski 		 * the same mblk, even if the ULP header is in the next mblk.
526c61a1653SRyan Zezeski 		 */
527c61a1653SRyan Zezeski 		*hdr_length_ptr = length;
528c61a1653SRyan Zezeski 
529c61a1653SRyan Zezeski 		if (nexthdrpp != NULL)
530c61a1653SRyan Zezeski 			*nexthdrpp = nexthdrp;
531c61a1653SRyan Zezeski 
532c61a1653SRyan Zezeski 		return (B_TRUE);
533c61a1653SRyan Zezeski 	}
534c61a1653SRyan Zezeski }
535