Home | History | Annotate | Download | only in ip
      1 /*
      2  * CDDL HEADER START
      3  *
      4  * The contents of this file are subject to the terms of the
      5  * Common Development and Distribution License, Version 1.0 only
      6  * (the "License").  You may not use this file except in compliance
      7  * with the License.
      8  *
      9  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
     10  * or http://www.opensolaris.org/os/licensing.
     11  * See the License for the specific language governing permissions
     12  * and limitations under the License.
     13  *
     14  * When distributing Covered Code, include this CDDL HEADER in each
     15  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
     16  * If applicable, add the following below this CDDL HEADER, with the
     17  * fields enclosed by brackets "[]" replaced with your own identifying
     18  * information: Portions Copyright [yyyy] [name of copyright owner]
     19  *
     20  * CDDL HEADER END
     21  */
     22 /*
     23  * Copyright 2004 Sun Microsystems, Inc.  All rights reserved.
     24  * Use is subject to license terms.
     25  */
     26 /* Copyright (c) 1990 Mentat Inc. */
     27 
     28 #pragma ident	"%Z%%M%	%I%	%E% SMI"
     29 
     30 #include <sys/types.h>
     31 #include <sys/inttypes.h>
     32 #include <sys/systm.h>
     33 #include <sys/stream.h>
     34 #include <sys/strsun.h>
     35 #include <sys/debug.h>
     36 #include <sys/ddi.h>
     37 #include <sys/vtrace.h>
     38 #include <inet/sctp_crc32.h>
     39 
     40 #include <sys/multidata.h>
     41 #include <sys/multidata_impl.h>
     42 
     43 extern unsigned int 	ip_ocsum(ushort_t *address, int halfword_count,
     44     unsigned int sum);
     45 
     46 /*
     47  * Checksum routine for Internet Protocol family headers.
     48  * This routine is very heavily used in the network
     49  * code and should be modified for each CPU to be as fast as possible.
     50  */
     51 
     52 #define	mp_len(mp) ((mp)->b_wptr - (mp)->b_rptr)
     53 
     54 /*
     55  * Even/Odd checks. Usually it is performed on pointers but may be
     56  * used on integers as well. uintptr_t is long enough to hold both
     57  * integer and pointer.
     58  */
     59 #define	is_odd(p) (((uintptr_t)(p) & 0x1) != 0)
     60 #define	is_even(p) (!is_odd(p))
     61 
     62 
     63 #ifdef ZC_TEST
     64 /*
     65  * Disable the TCP s/w cksum.
     66  * XXX - This is just a hack for testing purpose. Don't use it for
     67  * anything else!
     68  */
     69 int noswcksum = 0;
     70 #endif
     71 /*
     72  * Note: this does not ones-complement the result since it is used
     73  * when computing partial checksums.
     74  * For nonSTRUIO_IP mblks, assumes mp->b_rptr+offset is 16 bit aligned.
     75  * For STRUIO_IP mblks, assumes mp->b_datap->db_struiobase is 16 bit aligned.
     76  *
     77  * Note: for STRUIO_IP special mblks some data may have been previously
     78  *	 checksumed, this routine will handle additional data prefixed within
     79  *	 an mblk or b_cont (chained) mblk(s). This routine will also handle
     80  *	 suffixed b_cont mblk(s) and data suffixed within an mblk.
     81  */
     82 unsigned int
     83 ip_cksum(mblk_t *mp, int offset, uint_t sum)
     84 {
     85 	ushort_t *w;
     86 	ssize_t	mlen;
     87 	int pmlen;
     88 	mblk_t *pmp;
     89 	dblk_t *dp = mp->b_datap;
     90 	ushort_t psum = 0;
     91 
     92 #ifdef ZC_TEST
     93 	if (noswcksum)
     94 		return (0xffff);
     95 #endif
     96 	ASSERT(dp);
     97 
     98 	TRACE_2(TR_FAC_IP, TR_IP_CKSUM_START,
     99 	    "ip_cksum_start:%p (%X)", mp, sum);
    100 
    101 	if (mp->b_cont == NULL) {
    102 		/*
    103 		 * May be fast-path, only one mblk.
    104 		 */
    105 		w = (ushort_t *)(mp->b_rptr + offset);
    106 		if (dp->db_struioflag & STRUIO_IP) {
    107 			/*
    108 			 * Checksum any data not already done by
    109 			 * the caller and add in any partial checksum.
    110 			 */
    111 			if ((offset > dp->db_cksumstart) ||
    112 			    mp->b_wptr != (uchar_t *)(mp->b_rptr +
    113 			    dp->db_cksumend)) {
    114 				/*
    115 				 * Mblk data pointers aren't inclusive
    116 				 * of uio data, so disregard checksum.
    117 				 *
    118 				 * not using all of data in dblk make sure
    119 				 * not use to use the precalculated checksum
    120 				 * in this case.
    121 				 */
    122 				dp->db_struioflag &= ~STRUIO_IP;
    123 				goto norm;
    124 			}
    125 			ASSERT(mp->b_wptr == (mp->b_rptr + dp->db_cksumend));
    126 			psum = *(ushort_t *)dp->db_struioun.data;
    127 			if ((mlen = dp->db_cksumstart - offset) < 0)
    128 				mlen = 0;
    129 			if (is_odd(mlen))
    130 				goto slow;
    131 			if (mlen && dp->db_cksumstart != dp->db_cksumstuff &&
    132 			    dp->db_cksumend != dp->db_cksumstuff) {
    133 				/*
    134 				 * There is prefix data to do and some uio
    135 				 * data has already been checksumed and there
    136 				 * is more uio data to do, so do the prefix
    137 				 * data first, then do the remainder of the
    138 				 * uio data.
    139 				 */
    140 				sum = ip_ocsum(w, mlen >> 1, sum);
    141 				w = (ushort_t *)(mp->b_rptr +
    142 				    dp->db_cksumstuff);
    143 				if (is_odd(w)) {
    144 					pmp = mp;
    145 					goto slow1;
    146 				}
    147 				mlen = dp->db_cksumend - dp->db_cksumstuff;
    148 			} else if (dp->db_cksumend != dp->db_cksumstuff) {
    149 				/*
    150 				 * There may be uio data to do, if there is
    151 				 * prefix data to do then add in all of the
    152 				 * uio data (if any) to do, else just do any
    153 				 * uio data.
    154 				 */
    155 				if (mlen)
    156 					mlen += dp->db_cksumend
    157 						- dp->db_cksumstuff;
    158 				else {
    159 					w = (ushort_t *)(mp->b_rptr +
    160 					    dp->db_cksumstuff);
    161 					if (is_odd(w))
    162 						goto slow;
    163 					mlen = dp->db_cksumend
    164 						- dp->db_cksumstuff;
    165 				}
    166 			} else if (mlen == 0)
    167 				return (psum);
    168 
    169 			if (is_odd(mlen))
    170 				goto slow;
    171 			sum += psum;
    172 		} else {
    173 			/*
    174 			 * Checksum all data not already done by the caller.
    175 			 */
    176 		norm:
    177 			mlen = mp->b_wptr - (uchar_t *)w;
    178 			if (is_odd(mlen))
    179 				goto slow;
    180 		}
    181 		ASSERT(is_even(w));
    182 		ASSERT(is_even(mlen));
    183 		return (ip_ocsum(w, mlen >> 1, sum));
    184 	}
    185 	if (dp->db_struioflag & STRUIO_IP)
    186 		psum = *(ushort_t *)dp->db_struioun.data;
    187 slow:
    188 	pmp = 0;
    189 slow1:
    190 	mlen = 0;
    191 	pmlen = 0;
    192 	for (; ; ) {
    193 		/*
    194 		 * Each trip around loop adds in word(s) from one mbuf segment
    195 		 * (except for when pmp == mp, then its two partial trips).
    196 		 */
    197 		w = (ushort_t *)(mp->b_rptr + offset);
    198 		if (pmp) {
    199 			/*
    200 			 * This is the second trip around for this mblk.
    201 			 */
    202 			pmp = 0;
    203 			mlen = 0;
    204 			goto douio;
    205 		} else if (dp->db_struioflag & STRUIO_IP) {
    206 			/*
    207 			 * Checksum any data not already done by the
    208 			 * caller and add in any partial checksum.
    209 			 */
    210 			if ((offset > dp->db_cksumstart) ||
    211 			    mp->b_wptr != (uchar_t *)(mp->b_rptr +
    212 			    dp->db_cksumend)) {
    213 				/*
    214 				 * Mblk data pointers aren't inclusive
    215 				 * of uio data, so disregard checksum.
    216 				 *
    217 				 * not using all of data in dblk make sure
    218 				 * not use to use the precalculated checksum
    219 				 * in this case.
    220 				 */
    221 				dp->db_struioflag &= ~STRUIO_IP;
    222 				goto snorm;
    223 			}
    224 			ASSERT(mp->b_wptr == (mp->b_rptr + dp->db_cksumend));
    225 			if ((mlen = dp->db_cksumstart - offset) < 0)
    226 				mlen = 0;
    227 			if (mlen && dp->db_cksumstart != dp->db_cksumstuff) {
    228 				/*
    229 				 * There is prefix data too do and some
    230 				 * uio data has already been checksumed,
    231 				 * so do the prefix data only this trip.
    232 				 */
    233 				pmp = mp;
    234 			} else {
    235 				/*
    236 				 * Add in any partial cksum (if any) and
    237 				 * do the remainder of the uio data.
    238 				 */
    239 				int odd;
    240 			douio:
    241 				odd = is_odd(dp->db_cksumstuff -
    242 						dp->db_cksumstart);
    243 				if (pmlen == -1) {
    244 					/*
    245 					 * Previous mlen was odd, so swap
    246 					 * the partial checksum bytes.
    247 					 */
    248 					sum += ((psum << 8) & 0xffff)
    249 					    | (psum >> 8);
    250 					if (odd)
    251 						pmlen = 0;
    252 				} else {
    253 					sum += psum;
    254 					if (odd)
    255 						pmlen = -1;
    256 				}
    257 				if (dp->db_cksumend != dp->db_cksumstuff) {
    258 					/*
    259 					 * If prefix data to do and then all
    260 					 * the uio data nees to be checksumed,
    261 					 * else just do any uio data.
    262 					 */
    263 					if (mlen)
    264 						mlen += dp->db_cksumend
    265 							- dp->db_cksumstuff;
    266 					else {
    267 						w = (ushort_t *)(mp->b_rptr +
    268 						    dp->db_cksumstuff);
    269 						mlen = dp->db_cksumend -
    270 						    dp->db_cksumstuff;
    271 					}
    272 				}
    273 			}
    274 		} else {
    275 			/*
    276 			 * Checksum all of the mblk data.
    277 			 */
    278 		snorm:
    279 			mlen = mp->b_wptr - (uchar_t *)w;
    280 		}
    281 
    282 		TRACE_2(TR_FAC_IP, TR_IP_CKSUM_START,
    283 		    "ip_cksum_start:%p (%X)", mp, sum)
    284 
    285 		mp = mp->b_cont;
    286 		if (mlen > 0 && pmlen == -1) {
    287 			/*
    288 			 * There is a byte left from the last
    289 			 * segment; add it into the checksum.
    290 			 * Don't have to worry about a carry-
    291 			 * out here because we make sure that
    292 			 * high part of (32 bit) sum is small
    293 			 * below.
    294 			 */
    295 #ifdef _LITTLE_ENDIAN
    296 			sum += *(uchar_t *)w << 8;
    297 #else
    298 			sum += *(uchar_t *)w;
    299 #endif
    300 			w = (ushort_t *)((char *)w + 1);
    301 			mlen--;
    302 			pmlen = 0;
    303 		}
    304 		if (mlen > 0) {
    305 			if (is_even(w)) {
    306 				sum = ip_ocsum(w, mlen>>1, sum);
    307 				w += mlen>>1;
    308 				/*
    309 				 * If we had an odd number of bytes,
    310 				 * then the last byte goes in the high
    311 				 * part of the sum, and we take the
    312 				 * first byte to the low part of the sum
    313 				 * the next time around the loop.
    314 				 */
    315 				if (is_odd(mlen)) {
    316 #ifdef _LITTLE_ENDIAN
    317 					sum += *(uchar_t *)w;
    318 #else
    319 					sum += *(uchar_t *)w << 8;
    320 #endif
    321 					pmlen = -1;
    322 				}
    323 			} else {
    324 				ushort_t swsum;
    325 #ifdef _LITTLE_ENDIAN
    326 				sum += *(uchar_t *)w;
    327 #else
    328 				sum += *(uchar_t *)w << 8;
    329 #endif
    330 				mlen--;
    331 				w = (ushort_t *)(1 + (uintptr_t)w);
    332 
    333 				/* Do a separate checksum and copy operation */
    334 				swsum = ip_ocsum(w, mlen>>1, 0);
    335 				sum += ((swsum << 8) & 0xffff) | (swsum >> 8);
    336 				w += mlen>>1;
    337 				/*
    338 				 * If we had an even number of bytes,
    339 				 * then the last byte goes in the low
    340 				 * part of the sum.  Otherwise we had an
    341 				 * odd number of bytes and we take the first
    342 				 * byte to the low part of the sum the
    343 				 * next time around the loop.
    344 				 */
    345 				if (is_odd(mlen)) {
    346 #ifdef _LITTLE_ENDIAN
    347 					sum += *(uchar_t *)w << 8;
    348 #else
    349 					sum += *(uchar_t *)w;
    350 #endif
    351 				}
    352 				else
    353 					pmlen = -1;
    354 			}
    355 		}
    356 		/*
    357 		 * Locate the next block with some data.
    358 		 * If there is a word split across a boundary we
    359 		 * will wrap to the top with mlen == -1 and
    360 		 * then add it in shifted appropriately.
    361 		 */
    362 		offset = 0;
    363 		if (! pmp) {
    364 			for (; ; ) {
    365 				if (mp == 0) {
    366 					goto done;
    367 				}
    368 				if (mp_len(mp))
    369 					break;
    370 				mp = mp->b_cont;
    371 			}
    372 			dp = mp->b_datap;
    373 			if (dp->db_struioflag & STRUIO_IP)
    374 				psum = *(ushort_t *)dp->db_struioun.data;
    375 		} else
    376 			mp = pmp;
    377 	}
    378 done:
    379 	/*
    380 	 * Add together high and low parts of sum
    381 	 * and carry to get cksum.
    382 	 * Have to be careful to not drop the last
    383 	 * carry here.
    384 	 */
    385 	sum = (sum & 0xFFFF) + (sum >> 16);
    386 	sum = (sum & 0xFFFF) + (sum >> 16);
    387 	TRACE_3(TR_FAC_IP, TR_IP_CKSUM_END,
    388 		"ip_cksum_end:(%S) type %d (%X)", "ip_cksum", 1, sum);
    389 	return (sum);
    390 }
    391 
    392 uint32_t
    393 sctp_cksum(mblk_t *mp, int offset)
    394 {
    395 	uint32_t crc32;
    396 	uchar_t *p = NULL;
    397 
    398 	crc32 = 0xFFFFFFFF;
    399 	p = mp->b_rptr + offset;
    400 	crc32 = sctp_crc32(crc32, p, mp->b_wptr - p);
    401 	for (mp = mp->b_cont; mp != NULL; mp = mp->b_cont) {
    402 		crc32 = sctp_crc32(crc32, mp->b_rptr, MBLKL(mp));
    403 	}
    404 
    405 	/* Complement the result */
    406 	crc32 = ~crc32;
    407 
    408 	return (crc32);
    409 }
    410 
    411 /*
    412  * Routine to compute Internet checksum (16-bit 1's complement) of a given
    413  * Multidata packet descriptor.  As in the non-Multidata routine, this doesn't
    414  * 1's complement the result, such that it may be used to compute partial
    415  * checksums.  Since it works on buffer spans rather than mblks, this routine
    416  * does not handle existing partial checksum value as in the STRUIO_IP special
    417  * mblk case (supporting this is rather trivial, but is perhaps of no use at
    418  * the moment unless synchronous streams and delayed checksum calculation are
    419  * revived.)
    420  *
    421  * Note also here that the given Multidata packet descriptor must refer to
    422  * a header buffer, i.e. it must have a header fragment.  In addition, the
    423  * offset must lie within the boundary of the header fragment.  For the
    424  * outbound tcp (MDT) case, this will not be an issue because the stack
    425  * ensures that such conditions are met, and that there is no need whatsoever
    426  * to compute partial checksums on an arbitrary offset that is not part of
    427  * the header fragment.  We may need to revisit this routine to handle all
    428  * cases of the inbound (MDR) case, especially when we need to perform partial
    429  * checksum calculation due to padded bytes (non-zeroes) in the frame.
    430  */
    431 uint_t
    432 ip_md_cksum(pdesc_t *pd, int offset, uint_t sum)
    433 {
    434 	pdescinfo_t	*pdi = &pd->pd_pdi;
    435 	uchar_t		*reg_start, *reg_end;
    436 	ssize_t		mlen, i;
    437 	ushort_t	*w;
    438 	boolean_t	byteleft = B_FALSE;
    439 
    440 	ASSERT((pdi->flags & PDESC_HAS_REF) != 0);
    441 	ASSERT(pdi->hdr_rptr != NULL && pdi->hdr_wptr != NULL);
    442 	ASSERT(offset <= PDESC_HDRL(pdi));
    443 
    444 	for (i = 0; i < pdi->pld_cnt + 1; i++) {
    445 		if (i == 0) {
    446 			reg_start = pdi->hdr_rptr;
    447 			reg_end = pdi->hdr_wptr;
    448 		} else {
    449 			reg_start = pdi->pld_ary[i - 1].pld_rptr;
    450 			reg_end = pdi->pld_ary[i - 1].pld_wptr;
    451 			offset = 0;
    452 		}
    453 
    454 		w = (ushort_t *)(reg_start + offset);
    455 		mlen = reg_end - (uchar_t *)w;
    456 
    457 		if (mlen > 0 && byteleft) {
    458 			/*
    459 			 * There is a byte left from the last
    460 			 * segment; add it into the checksum.
    461 			 * Don't have to worry about a carry-
    462 			 * out here because we make sure that
    463 			 * high part of (32 bit) sum is small
    464 			 * below.
    465 			 */
    466 #ifdef _LITTLE_ENDIAN
    467 			sum += *(uchar_t *)w << 8;
    468 #else
    469 			sum += *(uchar_t *)w;
    470 #endif
    471 			w = (ushort_t *)((char *)w + 1);
    472 			mlen--;
    473 			byteleft = B_FALSE;
    474 		}
    475 
    476 		if (mlen == 0)
    477 			continue;
    478 
    479 		if (is_even(w)) {
    480 			sum = ip_ocsum(w, mlen >> 1, sum);
    481 			w += mlen >> 1;
    482 			/*
    483 			 * If we had an odd number of bytes,
    484 			 * then the last byte goes in the high
    485 			 * part of the sum, and we take the
    486 			 * first byte to the low part of the sum
    487 			 * the next time around the loop.
    488 			 */
    489 			if (is_odd(mlen)) {
    490 #ifdef _LITTLE_ENDIAN
    491 				sum += *(uchar_t *)w;
    492 #else
    493 				sum += *(uchar_t *)w << 8;
    494 #endif
    495 				byteleft = B_TRUE;
    496 			}
    497 		} else {
    498 			ushort_t swsum;
    499 #ifdef _LITTLE_ENDIAN
    500 			sum += *(uchar_t *)w;
    501 #else
    502 			sum += *(uchar_t *)w << 8;
    503 #endif
    504 			mlen--;
    505 			w = (ushort_t *)(1 + (uintptr_t)w);
    506 
    507 			/* Do a separate checksum and copy operation */
    508 			swsum = ip_ocsum(w, mlen >> 1, 0);
    509 			sum += ((swsum << 8) & 0xffff) | (swsum >> 8);
    510 			w += mlen >> 1;
    511 			/*
    512 			 * If we had an even number of bytes,
    513 			 * then the last byte goes in the low
    514 			 * part of the sum.  Otherwise we had an
    515 			 * odd number of bytes and we take the first
    516 			 * byte to the low part of the sum the
    517 			 * next time around the loop.
    518 			 */
    519 			if (is_odd(mlen)) {
    520 #ifdef _LITTLE_ENDIAN
    521 				sum += *(uchar_t *)w << 8;
    522 #else
    523 				sum += *(uchar_t *)w;
    524 #endif
    525 			} else {
    526 				byteleft = B_TRUE;
    527 			}
    528 		}
    529 	}
    530 
    531 	/*
    532 	 * Add together high and low parts of sum and carry to get cksum.
    533 	 * Have to be careful to not drop the last carry here.
    534 	 */
    535 	sum = (sum & 0xffff) + (sum >> 16);
    536 	sum = (sum & 0xffff) + (sum >> 16);
    537 
    538 	return (sum);
    539 }
    540