Home | History | Annotate | Download | only in os
      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 (the "License").
      6  * You may not use this file except in compliance with the License.
      7  *
      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.
     12  *
     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]
     18  *
     19  * CDDL HEADER END
     20  */
     21 /*
     22  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
     23  * Use is subject to license terms.
     24  */
     25 /* Copyright (c) 1990 Mentat Inc. */
     26 
     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>
     33 #include <sys/ddi.h>
     34 #include <sys/vtrace.h>
     35 #include <inet/sctp_crc32.h>
     36 #include <inet/ip.h>
     37 
     38 #include <sys/multidata.h>
     39 #include <sys/multidata_impl.h>
     40 
     41 extern unsigned int 	ip_ocsum(ushort_t *address, int halfword_count,
     42     unsigned int sum);
     43 
     44 /*
     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.
     48  */
     49 
     50 #define	mp_len(mp) ((mp)->b_wptr - (mp)->b_rptr)
     51 
     52 /*
     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.
     56  */
     57 #define	is_odd(p) (((uintptr_t)(p) & 0x1) != 0)
     58 #define	is_even(p) (!is_odd(p))
     59 
     60 
     61 #ifdef ZC_TEST
     62 /*
     63  * Disable the TCP s/w cksum.
     64  * XXX - This is just a hack for testing purpose. Don't use it for
     65  * anything else!
     66  */
     67 int noswcksum = 0;
     68 #endif
     69 /*
     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.
     74  *
     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.
     79  */
     80 unsigned int
     81 ip_cksum(mblk_t *mp, int offset, uint_t sum)
     82 {
     83 	ushort_t *w;
     84 	ssize_t	mlen;
     85 	int pmlen;
     86 	mblk_t *pmp;
     87 	dblk_t *dp = mp->b_datap;
     88 	ushort_t psum = 0;
     89 
     90 #ifdef ZC_TEST
     91 	if (noswcksum)
     92 		return (0xffff);
     93 #endif
     94 	ASSERT(dp);
     95 
     96 	if (mp->b_cont == NULL) {
     97 		/*
     98 		 * May be fast-path, only one mblk.
     99 		 */
    100 		w = (ushort_t *)(mp->b_rptr + offset);
    101 		if (dp->db_struioflag & STRUIO_IP) {
    102 			/*
    103 			 * Checksum any data not already done by
    104 			 * the caller and add in any partial checksum.
    105 			 */
    106 			if ((offset > dp->db_cksumstart) ||
    107 			    mp->b_wptr != (uchar_t *)(mp->b_rptr +
    108 			    dp->db_cksumend)) {
    109 				/*
    110 				 * Mblk data pointers aren't inclusive
    111 				 * of uio data, so disregard checksum.
    112 				 *
    113 				 * not using all of data in dblk make sure
    114 				 * not use to use the precalculated checksum
    115 				 * in this case.
    116 				 */
    117 				dp->db_struioflag &= ~STRUIO_IP;
    118 				goto norm;
    119 			}
    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)
    123 				mlen = 0;
    124 			if (is_odd(mlen))
    125 				goto slow;
    126 			if (mlen && dp->db_cksumstart != dp->db_cksumstuff &&
    127 			    dp->db_cksumend != dp->db_cksumstuff) {
    128 				/*
    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
    133 				 * uio data.
    134 				 */
    135 				sum = ip_ocsum(w, mlen >> 1, sum);
    136 				w = (ushort_t *)(mp->b_rptr +
    137 				    dp->db_cksumstuff);
    138 				if (is_odd(w)) {
    139 					pmp = mp;
    140 					goto slow1;
    141 				}
    142 				mlen = dp->db_cksumend - dp->db_cksumstuff;
    143 			} else if (dp->db_cksumend != dp->db_cksumstuff) {
    144 				/*
    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
    148 				 * uio data.
    149 				 */
    150 				if (mlen)
    151 					mlen += dp->db_cksumend
    152 					    - dp->db_cksumstuff;
    153 				else {
    154 					w = (ushort_t *)(mp->b_rptr +
    155 					    dp->db_cksumstuff);
    156 					if (is_odd(w))
    157 						goto slow;
    158 					mlen = dp->db_cksumend
    159 					    - dp->db_cksumstuff;
    160 				}
    161 			} else if (mlen == 0)
    162 				return (psum);
    163 
    164 			if (is_odd(mlen))
    165 				goto slow;
    166 			sum += psum;
    167 		} else {
    168 			/*
    169 			 * Checksum all data not already done by the caller.
    170 			 */
    171 		norm:
    172 			mlen = mp->b_wptr - (uchar_t *)w;
    173 			if (is_odd(mlen))
    174 				goto slow;
    175 		}
    176 		ASSERT(is_even(w));
    177 		ASSERT(is_even(mlen));
    178 		return (ip_ocsum(w, mlen >> 1, sum));
    179 	}
    180 	if (dp->db_struioflag & STRUIO_IP)
    181 		psum = *(ushort_t *)dp->db_struioun.data;
    182 slow:
    183 	pmp = 0;
    184 slow1:
    185 	mlen = 0;
    186 	pmlen = 0;
    187 	for (; ; ) {
    188 		/*
    189 		 * Each trip around loop adds in word(s) from one mbuf segment
    190 		 * (except for when pmp == mp, then its two partial trips).
    191 		 */
    192 		w = (ushort_t *)(mp->b_rptr + offset);
    193 		if (pmp) {
    194 			/*
    195 			 * This is the second trip around for this mblk.
    196 			 */
    197 			pmp = 0;
    198 			mlen = 0;
    199 			goto douio;
    200 		} else if (dp->db_struioflag & STRUIO_IP) {
    201 			/*
    202 			 * Checksum any data not already done by the
    203 			 * caller and add in any partial checksum.
    204 			 */
    205 			if ((offset > dp->db_cksumstart) ||
    206 			    mp->b_wptr != (uchar_t *)(mp->b_rptr +
    207 			    dp->db_cksumend)) {
    208 				/*
    209 				 * Mblk data pointers aren't inclusive
    210 				 * of uio data, so disregard checksum.
    211 				 *
    212 				 * not using all of data in dblk make sure
    213 				 * not use to use the precalculated checksum
    214 				 * in this case.
    215 				 */
    216 				dp->db_struioflag &= ~STRUIO_IP;
    217 				goto snorm;
    218 			}
    219 			ASSERT(mp->b_wptr == (mp->b_rptr + dp->db_cksumend));
    220 			if ((mlen = dp->db_cksumstart - offset) < 0)
    221 				mlen = 0;
    222 			if (mlen && dp->db_cksumstart != dp->db_cksumstuff) {
    223 				/*
    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.
    227 				 */
    228 				pmp = mp;
    229 			} else {
    230 				/*
    231 				 * Add in any partial cksum (if any) and
    232 				 * do the remainder of the uio data.
    233 				 */
    234 				int odd;
    235 			douio:
    236 				odd = is_odd(dp->db_cksumstuff -
    237 				    dp->db_cksumstart);
    238 				if (pmlen == -1) {
    239 					/*
    240 					 * Previous mlen was odd, so swap
    241 					 * the partial checksum bytes.
    242 					 */
    243 					sum += ((psum << 8) & 0xffff)
    244 					    | (psum >> 8);
    245 					if (odd)
    246 						pmlen = 0;
    247 				} else {
    248 					sum += psum;
    249 					if (odd)
    250 						pmlen = -1;
    251 				}
    252 				if (dp->db_cksumend != dp->db_cksumstuff) {
    253 					/*
    254 					 * If prefix data to do and then all
    255 					 * the uio data nees to be checksumed,
    256 					 * else just do any uio data.
    257 					 */
    258 					if (mlen)
    259 						mlen += dp->db_cksumend
    260 						    - dp->db_cksumstuff;
    261 					else {
    262 						w = (ushort_t *)(mp->b_rptr +
    263 						    dp->db_cksumstuff);
    264 						mlen = dp->db_cksumend -
    265 						    dp->db_cksumstuff;
    266 					}
    267 				}
    268 			}
    269 		} else {
    270 			/*
    271 			 * Checksum all of the mblk data.
    272 			 */
    273 		snorm:
    274 			mlen = mp->b_wptr - (uchar_t *)w;
    275 		}
    276 
    277 		mp = mp->b_cont;
    278 		if (mlen > 0 && pmlen == -1) {
    279 			/*
    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
    285 			 * below.
    286 			 */
    287 #ifdef _LITTLE_ENDIAN
    288 			sum += *(uchar_t *)w << 8;
    289 #else
    290 			sum += *(uchar_t *)w;
    291 #endif
    292 			w = (ushort_t *)((char *)w + 1);
    293 			mlen--;
    294 			pmlen = 0;
    295 		}
    296 		if (mlen > 0) {
    297 			if (is_even(w)) {
    298 				sum = ip_ocsum(w, mlen>>1, sum);
    299 				w += mlen>>1;
    300 				/*
    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.
    306 				 */
    307 				if (is_odd(mlen)) {
    308 #ifdef _LITTLE_ENDIAN
    309 					sum += *(uchar_t *)w;
    310 #else
    311 					sum += *(uchar_t *)w << 8;
    312 #endif
    313 					pmlen = -1;
    314 				}
    315 			} else {
    316 				ushort_t swsum;
    317 #ifdef _LITTLE_ENDIAN
    318 				sum += *(uchar_t *)w;
    319 #else
    320 				sum += *(uchar_t *)w << 8;
    321 #endif
    322 				mlen--;
    323 				w = (ushort_t *)(1 + (uintptr_t)w);
    324 
    325 				/* Do a separate checksum and copy operation */
    326 				swsum = ip_ocsum(w, mlen>>1, 0);
    327 				sum += ((swsum << 8) & 0xffff) | (swsum >> 8);
    328 				w += mlen>>1;
    329 				/*
    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.
    336 				 */
    337 				if (is_odd(mlen)) {
    338 #ifdef _LITTLE_ENDIAN
    339 					sum += *(uchar_t *)w << 8;
    340 #else
    341 					sum += *(uchar_t *)w;
    342 #endif
    343 				}
    344 				else
    345 					pmlen = -1;
    346 			}
    347 		}
    348 		/*
    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.
    353 		 */
    354 		offset = 0;
    355 		if (! pmp) {
    356 			for (; ; ) {
    357 				if (mp == 0) {
    358 					goto done;
    359 				}
    360 				if (mp_len(mp))
    361 					break;
    362 				mp = mp->b_cont;
    363 			}
    364 			dp = mp->b_datap;
    365 			if (dp->db_struioflag & STRUIO_IP)
    366 				psum = *(ushort_t *)dp->db_struioun.data;
    367 		} else
    368 			mp = pmp;
    369 	}
    370 done:
    371 	/*
    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
    375 	 * carry here.
    376 	 */
    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);
    381 	return (sum);
    382 }
    383 
    384 uint32_t
    385 sctp_cksum(mblk_t *mp, int offset)
    386 {
    387 	uint32_t crc32;
    388 	uchar_t *p = NULL;
    389 
    390 	crc32 = 0xFFFFFFFF;
    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));
    395 	}
    396 
    397 	/* Complement the result */
    398 	crc32 = ~crc32;
    399 
    400 	return (crc32);
    401 }
    402 
    403 /*
    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
    411  * revived.)
    412  *
    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.
    422  */
    423 uint_t
    424 ip_md_cksum(pdesc_t *pd, int offset, uint_t sum)
    425 {
    426 	pdescinfo_t	*pdi = &pd->pd_pdi;
    427 	uchar_t		*reg_start, *reg_end;
    428 	ssize_t		mlen, i;
    429 	ushort_t	*w;
    430 	boolean_t	byteleft = B_FALSE;
    431 
    432 	ASSERT((pdi->flags & PDESC_HAS_REF) != 0);
    433 	ASSERT(pdi->hdr_rptr != NULL && pdi->hdr_wptr != NULL);
    434 	ASSERT(offset <= PDESC_HDRL(pdi));
    435 
    436 	for (i = 0; i < pdi->pld_cnt + 1; i++) {
    437 		if (i == 0) {
    438 			reg_start = pdi->hdr_rptr;
    439 			reg_end = pdi->hdr_wptr;
    440 		} else {
    441 			reg_start = pdi->pld_ary[i - 1].pld_rptr;
    442 			reg_end = pdi->pld_ary[i - 1].pld_wptr;
    443 			offset = 0;
    444 		}
    445 
    446 		w = (ushort_t *)(reg_start + offset);
    447 		mlen = reg_end - (uchar_t *)w;
    448 
    449 		if (mlen > 0 && byteleft) {
    450 			/*
    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
    456 			 * below.
    457 			 */
    458 #ifdef _LITTLE_ENDIAN
    459 			sum += *(uchar_t *)w << 8;
    460 #else
    461 			sum += *(uchar_t *)w;
    462 #endif
    463 			w = (ushort_t *)((char *)w + 1);
    464 			mlen--;
    465 			byteleft = B_FALSE;
    466 		}
    467 
    468 		if (mlen == 0)
    469 			continue;
    470 
    471 		if (is_even(w)) {
    472 			sum = ip_ocsum(w, mlen >> 1, sum);
    473 			w += mlen >> 1;
    474 			/*
    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.
    480 			 */
    481 			if (is_odd(mlen)) {
    482 #ifdef _LITTLE_ENDIAN
    483 				sum += *(uchar_t *)w;
    484 #else
    485 				sum += *(uchar_t *)w << 8;
    486 #endif
    487 				byteleft = B_TRUE;
    488 			}
    489 		} else {
    490 			ushort_t swsum;
    491 #ifdef _LITTLE_ENDIAN
    492 			sum += *(uchar_t *)w;
    493 #else
    494 			sum += *(uchar_t *)w << 8;
    495 #endif
    496 			mlen--;
    497 			w = (ushort_t *)(1 + (uintptr_t)w);
    498 
    499 			/* Do a separate checksum and copy operation */
    500 			swsum = ip_ocsum(w, mlen >> 1, 0);
    501 			sum += ((swsum << 8) & 0xffff) | (swsum >> 8);
    502 			w += mlen >> 1;
    503 			/*
    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.
    510 			 */
    511 			if (is_odd(mlen)) {
    512 #ifdef _LITTLE_ENDIAN
    513 				sum += *(uchar_t *)w << 8;
    514 #else
    515 				sum += *(uchar_t *)w;
    516 #endif
    517 			} else {
    518 				byteleft = B_TRUE;
    519 			}
    520 		}
    521 	}
    522 
    523 	/*
    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.
    526 	 */
    527 	sum = (sum & 0xffff) + (sum >> 16);
    528 	sum = (sum & 0xffff) + (sum >> 16);
    529 
    530 	return (sum);
    531 }
    532 
    533 /* Return the IP checksum for the IP header at "iph". */
    534 uint16_t
    535 ip_csum_hdr(ipha_t *ipha)
    536 {
    537 	uint16_t	*uph;
    538 	uint32_t	sum;
    539 	int		opt_len;
    540 
    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];
    546 	if (opt_len > 0) {
    547 		do {
    548 			sum += uph[10];
    549 			sum += uph[11];
    550 			uph += 2;
    551 		} while (--opt_len);
    552 	}
    553 	sum = (sum & 0xFFFF) + (sum >> 16);
    554 	sum = ~(sum + (sum >> 16)) & 0xFFFF;
    555 	if (sum == 0xffff)
    556 		sum = 0;
    557 	return ((uint16_t)sum);
    558 }
    559