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