4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License (the "License").
6 * You may not use this file except in compliance with the License.
8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 * or http://www.opensolaris.org/os/licensing.
10 * See the License for the specific language governing permissions
11 * and limitations under the License.
13 * When distributing Covered Code, include this CDDL HEADER in each
14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15 * If applicable, add the following below this CDDL HEADER, with the
16 * fields enclosed by brackets "[]" replaced with your own identifying
17 * information: Portions Copyright [yyyy] [name of copyright owner]
22 * Copyright 2009 Sun Microsystems, Inc. All rights reserved.
23 * Use is subject to license terms.
25 /* Copyright (c) 1990 Mentat Inc. */
27 #include <sys/types.h>
28 #include <sys/inttypes.h>
29 #include <sys/systm.h>
30 #include <sys/stream.h>
31 #include <sys/strsun.h>
32 #include <sys/debug.h>
34 #include <sys/vtrace.h>
35 #include <inet/sctp_crc32.h>
38 #include <sys/multidata.h>
39 #include <sys/multidata_impl.h>
41 extern unsigned int ip_ocsum(ushort_t
*address
, int halfword_count
,
45 * Checksum routine for Internet Protocol family headers.
46 * This routine is very heavily used in the network
47 * code and should be modified for each CPU to be as fast as possible.
50 #define mp_len(mp) ((mp)->b_wptr - (mp)->b_rptr)
53 * Even/Odd checks. Usually it is performed on pointers but may be
54 * used on integers as well. uintptr_t is long enough to hold both
55 * integer and pointer.
57 #define is_odd(p) (((uintptr_t)(p) & 0x1) != 0)
58 #define is_even(p) (!is_odd(p))
63 * Disable the TCP s/w cksum.
64 * XXX - This is just a hack for testing purpose. Don't use it for
70 * Note: this does not ones-complement the result since it is used
71 * when computing partial checksums.
72 * For nonSTRUIO_IP mblks, assumes mp->b_rptr+offset is 16 bit aligned.
73 * For STRUIO_IP mblks, assumes mp->b_datap->db_struiobase is 16 bit aligned.
75 * Note: for STRUIO_IP special mblks some data may have been previously
76 * checksumed, this routine will handle additional data prefixed within
77 * an mblk or b_cont (chained) mblk(s). This routine will also handle
78 * suffixed b_cont mblk(s) and data suffixed within an mblk.
81 ip_cksum(mblk_t
*mp
, int offset
, uint_t sum
)
87 dblk_t
*dp
= mp
->b_datap
;
96 if (mp
->b_cont
== NULL
) {
98 * May be fast-path, only one mblk.
100 w
= (ushort_t
*)(mp
->b_rptr
+ offset
);
101 if (dp
->db_struioflag
& STRUIO_IP
) {
103 * Checksum any data not already done by
104 * the caller and add in any partial checksum.
106 if ((offset
> dp
->db_cksumstart
) ||
107 mp
->b_wptr
!= (uchar_t
*)(mp
->b_rptr
+
110 * Mblk data pointers aren't inclusive
111 * of uio data, so disregard checksum.
113 * not using all of data in dblk make sure
114 * not use to use the precalculated checksum
117 dp
->db_struioflag
&= ~STRUIO_IP
;
120 ASSERT(mp
->b_wptr
== (mp
->b_rptr
+ dp
->db_cksumend
));
121 psum
= *(ushort_t
*)dp
->db_struioun
.data
;
122 if ((mlen
= dp
->db_cksumstart
- offset
) < 0)
126 if (mlen
&& dp
->db_cksumstart
!= dp
->db_cksumstuff
&&
127 dp
->db_cksumend
!= dp
->db_cksumstuff
) {
129 * There is prefix data to do and some uio
130 * data has already been checksumed and there
131 * is more uio data to do, so do the prefix
132 * data first, then do the remainder of the
135 sum
= ip_ocsum(w
, mlen
>> 1, sum
);
136 w
= (ushort_t
*)(mp
->b_rptr
+
142 mlen
= dp
->db_cksumend
- dp
->db_cksumstuff
;
143 } else if (dp
->db_cksumend
!= dp
->db_cksumstuff
) {
145 * There may be uio data to do, if there is
146 * prefix data to do then add in all of the
147 * uio data (if any) to do, else just do any
151 mlen
+= dp
->db_cksumend
154 w
= (ushort_t
*)(mp
->b_rptr
+
158 mlen
= dp
->db_cksumend
161 } else if (mlen
== 0)
169 * Checksum all data not already done by the caller.
172 mlen
= mp
->b_wptr
- (uchar_t
*)w
;
177 ASSERT(is_even(mlen
));
178 return (ip_ocsum(w
, mlen
>> 1, sum
));
180 if (dp
->db_struioflag
& STRUIO_IP
)
181 psum
= *(ushort_t
*)dp
->db_struioun
.data
;
189 * Each trip around loop adds in word(s) from one mbuf segment
190 * (except for when pmp == mp, then its two partial trips).
192 w
= (ushort_t
*)(mp
->b_rptr
+ offset
);
195 * This is the second trip around for this mblk.
200 } else if (dp
->db_struioflag
& STRUIO_IP
) {
202 * Checksum any data not already done by the
203 * caller and add in any partial checksum.
205 if ((offset
> dp
->db_cksumstart
) ||
206 mp
->b_wptr
!= (uchar_t
*)(mp
->b_rptr
+
209 * Mblk data pointers aren't inclusive
210 * of uio data, so disregard checksum.
212 * not using all of data in dblk make sure
213 * not use to use the precalculated checksum
216 dp
->db_struioflag
&= ~STRUIO_IP
;
219 ASSERT(mp
->b_wptr
== (mp
->b_rptr
+ dp
->db_cksumend
));
220 if ((mlen
= dp
->db_cksumstart
- offset
) < 0)
222 if (mlen
&& dp
->db_cksumstart
!= dp
->db_cksumstuff
) {
224 * There is prefix data too do and some
225 * uio data has already been checksumed,
226 * so do the prefix data only this trip.
231 * Add in any partial cksum (if any) and
232 * do the remainder of the uio data.
236 odd
= is_odd(dp
->db_cksumstuff
-
240 * Previous mlen was odd, so swap
241 * the partial checksum bytes.
243 sum
+= ((psum
<< 8) & 0xffff)
252 if (dp
->db_cksumend
!= dp
->db_cksumstuff
) {
254 * If prefix data to do and then all
255 * the uio data nees to be checksumed,
256 * else just do any uio data.
259 mlen
+= dp
->db_cksumend
262 w
= (ushort_t
*)(mp
->b_rptr
+
264 mlen
= dp
->db_cksumend
-
271 * Checksum all of the mblk data.
274 mlen
= mp
->b_wptr
- (uchar_t
*)w
;
278 if (mlen
> 0 && pmlen
== -1) {
280 * There is a byte left from the last
281 * segment; add it into the checksum.
282 * Don't have to worry about a carry-
283 * out here because we make sure that
284 * high part of (32 bit) sum is small
287 #ifdef _LITTLE_ENDIAN
288 sum
+= *(uchar_t
*)w
<< 8;
290 sum
+= *(uchar_t
*)w
;
292 w
= (ushort_t
*)((char *)w
+ 1);
298 sum
= ip_ocsum(w
, mlen
>>1, sum
);
301 * If we had an odd number of bytes,
302 * then the last byte goes in the high
303 * part of the sum, and we take the
304 * first byte to the low part of the sum
305 * the next time around the loop.
308 #ifdef _LITTLE_ENDIAN
309 sum
+= *(uchar_t
*)w
;
311 sum
+= *(uchar_t
*)w
<< 8;
317 #ifdef _LITTLE_ENDIAN
318 sum
+= *(uchar_t
*)w
;
320 sum
+= *(uchar_t
*)w
<< 8;
323 w
= (ushort_t
*)(1 + (uintptr_t)w
);
325 /* Do a separate checksum and copy operation */
326 swsum
= ip_ocsum(w
, mlen
>>1, 0);
327 sum
+= ((swsum
<< 8) & 0xffff) | (swsum
>> 8);
330 * If we had an even number of bytes,
331 * then the last byte goes in the low
332 * part of the sum. Otherwise we had an
333 * odd number of bytes and we take the first
334 * byte to the low part of the sum the
335 * next time around the loop.
338 #ifdef _LITTLE_ENDIAN
339 sum
+= *(uchar_t
*)w
<< 8;
341 sum
+= *(uchar_t
*)w
;
349 * Locate the next block with some data.
350 * If there is a word split across a boundary we
351 * will wrap to the top with mlen == -1 and
352 * then add it in shifted appropriately.
365 if (dp
->db_struioflag
& STRUIO_IP
)
366 psum
= *(ushort_t
*)dp
->db_struioun
.data
;
372 * Add together high and low parts of sum
373 * and carry to get cksum.
374 * Have to be careful to not drop the last
377 sum
= (sum
& 0xFFFF) + (sum
>> 16);
378 sum
= (sum
& 0xFFFF) + (sum
>> 16);
379 TRACE_3(TR_FAC_IP
, TR_IP_CKSUM_END
,
380 "ip_cksum_end:(%S) type %d (%X)", "ip_cksum", 1, sum
);
385 sctp_cksum(mblk_t
*mp
, int offset
)
391 p
= mp
->b_rptr
+ offset
;
392 crc32
= sctp_crc32(crc32
, p
, mp
->b_wptr
- p
);
393 for (mp
= mp
->b_cont
; mp
!= NULL
; mp
= mp
->b_cont
) {
394 crc32
= sctp_crc32(crc32
, mp
->b_rptr
, MBLKL(mp
));
397 /* Complement the result */
404 * Routine to compute Internet checksum (16-bit 1's complement) of a given
405 * Multidata packet descriptor. As in the non-Multidata routine, this doesn't
406 * 1's complement the result, such that it may be used to compute partial
407 * checksums. Since it works on buffer spans rather than mblks, this routine
408 * does not handle existing partial checksum value as in the STRUIO_IP special
409 * mblk case (supporting this is rather trivial, but is perhaps of no use at
410 * the moment unless synchronous streams and delayed checksum calculation are
413 * Note also here that the given Multidata packet descriptor must refer to
414 * a header buffer, i.e. it must have a header fragment. In addition, the
415 * offset must lie within the boundary of the header fragment. For the
416 * outbound tcp (MDT) case, this will not be an issue because the stack
417 * ensures that such conditions are met, and that there is no need whatsoever
418 * to compute partial checksums on an arbitrary offset that is not part of
419 * the header fragment. We may need to revisit this routine to handle all
420 * cases of the inbound (MDR) case, especially when we need to perform partial
421 * checksum calculation due to padded bytes (non-zeroes) in the frame.
424 ip_md_cksum(pdesc_t
*pd
, int offset
, uint_t sum
)
426 pdescinfo_t
*pdi
= &pd
->pd_pdi
;
427 uchar_t
*reg_start
, *reg_end
;
430 boolean_t byteleft
= B_FALSE
;
432 ASSERT((pdi
->flags
& PDESC_HAS_REF
) != 0);
433 ASSERT(pdi
->hdr_rptr
!= NULL
&& pdi
->hdr_wptr
!= NULL
);
434 ASSERT(offset
<= PDESC_HDRL(pdi
));
436 for (i
= 0; i
< pdi
->pld_cnt
+ 1; i
++) {
438 reg_start
= pdi
->hdr_rptr
;
439 reg_end
= pdi
->hdr_wptr
;
441 reg_start
= pdi
->pld_ary
[i
- 1].pld_rptr
;
442 reg_end
= pdi
->pld_ary
[i
- 1].pld_wptr
;
446 w
= (ushort_t
*)(reg_start
+ offset
);
447 mlen
= reg_end
- (uchar_t
*)w
;
449 if (mlen
> 0 && byteleft
) {
451 * There is a byte left from the last
452 * segment; add it into the checksum.
453 * Don't have to worry about a carry-
454 * out here because we make sure that
455 * high part of (32 bit) sum is small
458 #ifdef _LITTLE_ENDIAN
459 sum
+= *(uchar_t
*)w
<< 8;
461 sum
+= *(uchar_t
*)w
;
463 w
= (ushort_t
*)((char *)w
+ 1);
472 sum
= ip_ocsum(w
, mlen
>> 1, sum
);
475 * If we had an odd number of bytes,
476 * then the last byte goes in the high
477 * part of the sum, and we take the
478 * first byte to the low part of the sum
479 * the next time around the loop.
482 #ifdef _LITTLE_ENDIAN
483 sum
+= *(uchar_t
*)w
;
485 sum
+= *(uchar_t
*)w
<< 8;
491 #ifdef _LITTLE_ENDIAN
492 sum
+= *(uchar_t
*)w
;
494 sum
+= *(uchar_t
*)w
<< 8;
497 w
= (ushort_t
*)(1 + (uintptr_t)w
);
499 /* Do a separate checksum and copy operation */
500 swsum
= ip_ocsum(w
, mlen
>> 1, 0);
501 sum
+= ((swsum
<< 8) & 0xffff) | (swsum
>> 8);
504 * If we had an even number of bytes,
505 * then the last byte goes in the low
506 * part of the sum. Otherwise we had an
507 * odd number of bytes and we take the first
508 * byte to the low part of the sum the
509 * next time around the loop.
512 #ifdef _LITTLE_ENDIAN
513 sum
+= *(uchar_t
*)w
<< 8;
515 sum
+= *(uchar_t
*)w
;
524 * Add together high and low parts of sum and carry to get cksum.
525 * Have to be careful to not drop the last carry here.
527 sum
= (sum
& 0xffff) + (sum
>> 16);
528 sum
= (sum
& 0xffff) + (sum
>> 16);
533 /* Return the IP checksum for the IP header at "iph". */
535 ip_csum_hdr(ipha_t
*ipha
)
541 opt_len
= (ipha
->ipha_version_and_hdr_length
& 0xF) -
542 IP_SIMPLE_HDR_LENGTH_IN_WORDS
;
543 uph
= (uint16_t *)ipha
;
544 sum
= uph
[0] + uph
[1] + uph
[2] + uph
[3] + uph
[4] +
545 uph
[5] + uph
[6] + uph
[7] + uph
[8] + uph
[9];
553 sum
= (sum
& 0xFFFF) + (sum
>> 16);
554 sum
= ~(sum
+ (sum
>> 16)) & 0xFFFF;
557 return ((uint16_t)sum
);