xref: /titanic_50/usr/src/uts/common/inet/ip/tn_ipopt.c (revision d92fc07239af943d62e4e67791879707ec89e7d3)
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 
26 #include <sys/types.h>
27 #include <sys/systm.h>
28 #include <sys/kmem.h>
29 #include <sys/disp.h>
30 #include <sys/stream.h>
31 #include <sys/strsubr.h>
32 #include <sys/strsun.h>
33 #include <sys/policy.h>
34 #include <sys/tsol/label_macro.h>
35 #include <sys/tsol/tndb.h>
36 #include <sys/tsol/tnet.h>
37 #include <inet/ip.h>
38 #include <inet/ip6.h>
39 #include <inet/tcp.h>
40 #include <inet/ipclassifier.h>
41 #include <inet/ip_ire.h>
42 #include <inet/ip_ftable.h>
43 
44 /*
45  * This routine takes a sensitivity label as input and creates a CIPSO
46  * option in the specified buffer.  It returns the size of the CIPSO option.
47  * If the sensitivity label is too large for the CIPSO option, then 0
48  * is returned.
49  *
50  * tsol2cipso_tt1 returns 0 for failure and greater than 0 for success
51  * (more accurately, success means a return value between 10 and 40).
52  */
53 
54 static int
55 tsol2cipso_tt1(const bslabel_t *sl, unsigned char *cop, uint32_t doi)
56 {
57 	struct cipso_tag_type_1 *tt1;
58 	const _bslabel_impl_t *bsl;
59 	const uchar_t *ucp;
60 	int i;
61 
62 	if (doi == 0)
63 		return (0);
64 
65 	/* check for Admin High sensitivity label */
66 	if (blequal(sl, label2bslabel(l_admin_high)))
67 		return (0);
68 
69 	/* check whether classification will fit in one octet */
70 	bsl = (const _bslabel_impl_t *)sl;
71 	if (LCLASS(bsl) & 0xFF00)
72 		return (0);
73 
74 	/*
75 	 * Check whether compartments will fit in 30 octets.
76 	 * Compartments 241 - 256 are not allowed.
77 	 */
78 	if (ntohl(bsl->compartments.c8) & 0x0000FFFF)
79 		return (0);
80 
81 	/*
82 	 * Compute option length and tag length.
83 	 * 'p' points to the last two bytes in the Sensitivity Label's
84 	 * compartments; these cannot be mapped into CIPSO compartments.
85 	 */
86 	ucp = (const uchar_t *)&bsl->compartments.c8 + 2;
87 	while (--ucp >= (const uchar_t *)&bsl->compartments.c1)
88 		if (*ucp != 0)
89 			break;
90 
91 	i =  ucp - (const uchar_t *)&bsl->compartments.c1 + 1;
92 
93 	if (cop == NULL)
94 		return (10 + i);
95 
96 	doi = htonl(doi);
97 	ucp = (const uchar_t *)&doi;
98 	cop[IPOPT_OPTVAL] = IPOPT_COMSEC;
99 	cop[IPOPT_OLEN] = 10 + i;
100 	cop[IPOPT_OLEN+1] = ucp[0];
101 	cop[IPOPT_OLEN+2] = ucp[1];
102 	cop[IPOPT_OLEN+3] = ucp[2];
103 	cop[IPOPT_OLEN+4] = ucp[3];
104 	tt1 = (struct cipso_tag_type_1 *)&cop[IPOPT_OLEN + 5];
105 	tt1->tag_type = 1;
106 	tt1->tag_align = 0;
107 	tt1->tag_sl = LCLASS(bsl);
108 	tt1->tag_length = 4 + i;
109 
110 	bcopy(&bsl->compartments.c1, tt1->tag_cat, i);
111 
112 	return (cop[IPOPT_OLEN]);
113 }
114 
115 /*
116  * The following routine searches for a security label in an IPv4 datagram.
117  * It returns label_type of:
118  *    OPT_CIPSO if a CIPSO IP option is found.
119  *    OPT_NONE if no security label is found.
120  *
121  * If OPT_CIPSO, a pointer to the CIPSO IP option will be returned in
122  * the buffer parameter.
123  *
124  * The function will return with B_FALSE if an IP format error
125  * is encountered.
126  */
127 
128 boolean_t
129 tsol_get_option_v4(mblk_t *mp, tsol_ip_label_t *label_type, uchar_t **buffer)
130 {
131 	ipha_t	*ipha;
132 	uchar_t	*opt;
133 	uint32_t	totallen;
134 	uint32_t	optval;
135 	uint32_t	optlen;
136 
137 	*label_type = OPT_NONE;
138 
139 	/*
140 	 * Get length (in 4 byte octets) of IP header options.
141 	 * If header doesn't contain options, then return a label_type
142 	 * of OPT_NONE.
143 	 */
144 	ipha = (ipha_t *)mp->b_rptr;
145 	totallen = ipha->ipha_version_and_hdr_length -
146 	    (uint8_t)((IP_VERSION << 4));
147 	totallen <<= 2;
148 	if (totallen < IP_SIMPLE_HDR_LENGTH || totallen > MBLKL(mp))
149 		return (B_FALSE);
150 	totallen -= IP_SIMPLE_HDR_LENGTH;
151 	if (totallen == 0)
152 		return (B_TRUE);
153 
154 	/*
155 	 * Search for CIPSO option.
156 	 * If no such option is present, then return OPT_NONE.
157 	 */
158 	opt = (uchar_t *)&ipha[1];
159 	while (totallen != 0) {
160 		switch (optval = opt[IPOPT_OPTVAL]) {
161 		case IPOPT_EOL:
162 			return (B_TRUE);
163 		case IPOPT_NOP:
164 			optlen = 1;
165 			break;
166 		default:
167 			if (totallen <= IPOPT_OLEN)
168 				return (B_FALSE);
169 			optlen = opt[IPOPT_OLEN];
170 			if (optlen < 2)
171 				return (B_FALSE);
172 		}
173 		if (optlen > totallen)
174 			return (B_FALSE);
175 		/*
176 		 * Copy pointer to option into '*buffer' and
177 		 * return the option type.
178 		 */
179 		switch (optval) {
180 		case IPOPT_COMSEC:
181 			if (TSOL_CIPSO_TAG_OFFSET < optlen &&
182 			    opt[TSOL_CIPSO_TAG_OFFSET] == 1) {
183 				*label_type = OPT_CIPSO;
184 				*buffer = opt;
185 				return (B_TRUE);
186 			}
187 			return (B_FALSE);
188 		}
189 		totallen -= optlen;
190 		opt += optlen;
191 	}
192 	return (B_TRUE);
193 }
194 
195 /*
196  * The following routine searches for a security label in an IPv6 datagram.
197  * It returns label_type of:
198  *    OPT_CIPSO if a CIPSO IP option is found.
199  *    OPT_NONE if no security label is found.
200  *
201  * If OPT_CIPSO, a pointer to the IPv4 portion of the CIPSO IP option will
202  * be returned in the buffer parameter.
203  *
204  * The function will return with B_FALSE if an IP format error
205  * or an unexpected label content error is encountered.
206  */
207 
208 boolean_t
209 tsol_get_option_v6(mblk_t *mp, tsol_ip_label_t *label_type, uchar_t **buffer)
210 {
211 	uchar_t		*opt_ptr = NULL;
212 	uchar_t		*after_secopt;
213 	boolean_t	hbh_needed;
214 	const uchar_t	*ip6hbh;
215 	size_t		optlen;
216 	uint32_t	doi;
217 	const ip6_t	*ip6h;
218 
219 	*label_type = OPT_NONE;
220 	*buffer = NULL;
221 	ip6h = (const ip6_t *)mp->b_rptr;
222 	if (ip6h->ip6_nxt != IPPROTO_HOPOPTS)
223 		return (B_TRUE);
224 	ip6hbh = (const uchar_t *)&ip6h[1];
225 	if (ip6hbh + MIN_EHDR_LEN > mp->b_wptr)
226 		return (B_FALSE);
227 	optlen = (ip6hbh[1] + 1) << 3;
228 	if (ip6hbh + optlen > mp->b_wptr)
229 		return (B_FALSE);
230 	if (!tsol_find_secopt_v6(ip6hbh, optlen,
231 	    &opt_ptr, &after_secopt, &hbh_needed))
232 		return (B_FALSE);
233 	/* tsol_find_secopt_v6 guarantees some sanity */
234 	if (opt_ptr != NULL) {
235 		/*
236 		 * IPv6 Option
237 		 *   opt_ptr[0]: Option type
238 		 *   opt_ptr[1]: Length of option data in bytes
239 		 *   opt_ptr[2]: First byte of option data
240 		 */
241 		if ((optlen = opt_ptr[1]) < 8)
242 			return (B_FALSE);
243 		opt_ptr += 2;
244 		/*
245 		 * From "Generalized Labeled Security Option for IPv6" draft
246 		 *   opt_ptr[0] - opt_ptr[4]: DOI = IP6LS_DOI_V4
247 		 *   opt_ptr[4]: Tag type = IP6LS_TT_V4
248 		 *   opt_ptr[5]: Tag length in bytes starting at Tag type field
249 		 * IPv4 CIPSO Option
250 		 *   opt_ptr[6]: option type
251 		 *   opt_ptr[7]: option length in bytes starting at type field
252 		 */
253 		bcopy(opt_ptr, &doi, sizeof (doi));
254 		doi = ntohl(doi);
255 		if (doi == IP6LS_DOI_V4 &&
256 		    opt_ptr[4] == IP6LS_TT_V4 &&
257 		    opt_ptr[5] <= optlen - 4 &&
258 		    opt_ptr[7] <= optlen - 6 &&
259 		    opt_ptr[7] <= opt_ptr[5] - 2) {
260 			opt_ptr += sizeof (doi) + 2;
261 			*label_type = OPT_CIPSO;
262 			*buffer = opt_ptr;
263 			return (B_TRUE);
264 		}
265 		return (B_FALSE);
266 	}
267 	return (B_TRUE);
268 }
269 
270 /*
271  * tsol_check_dest()
272  *
273  * This routine verifies if a destination is allowed to recieve messages
274  * based on the message cred's security label. If any adjustments to
275  * the cred are needed due to the connection's MAC-exempt status or
276  * the destination's ability to receive labels, an "effective cred"
277  * will be returned.
278  *
279  * On successful return, effective_cred will point to the new creds needed
280  * or will be NULL if new creds aren't needed. On error, effective_cred
281  * is NULL.
282  *
283  * Returns:
284  *	0		Have or constructed appropriate credentials
285  *	EHOSTUNREACH	The credentials failed the remote host accreditation
286  *      ENOMEM		Memory allocation failure
287  */
288 int
289 tsol_check_dest(const cred_t *credp, const void *dst, uchar_t version,
290     boolean_t mac_exempt, cred_t **effective_cred)
291 {
292 	ts_label_t	*tsl, *newtsl = NULL;
293 	tsol_tpc_t	*dst_rhtp;
294 	zoneid_t	zoneid;
295 
296 	if (effective_cred != NULL)
297 		*effective_cred = NULL;
298 	ASSERT(version == IPV4_VERSION ||
299 	    (version == IPV6_VERSION &&
300 	    !IN6_IS_ADDR_V4MAPPED((in6_addr_t *)dst)));
301 
302 	/* Always pass kernel level communication (NULL label) */
303 	if ((tsl = crgetlabel(credp)) == NULL) {
304 		DTRACE_PROBE2(tx__tnopt__log__info__labeling__mac__allownull,
305 		    char *, "destination ip(1) with null cred was passed",
306 		    ipaddr_t, dst);
307 		return (0);
308 	}
309 
310 	/* Always pass multicast */
311 	if (version == IPV4_VERSION &&
312 	    CLASSD(*(ipaddr_t *)dst)) {
313 		DTRACE_PROBE2(tx__tnopt__log__info__labeling__mac__allowmult,
314 		    char *, "destination ip(1) with multicast dest was passed",
315 		    ipaddr_t, dst);
316 		return (0);
317 	} else if (version == IPV6_VERSION &&
318 	    IN6_IS_ADDR_MULTICAST((in6_addr_t *)dst)) {
319 		DTRACE_PROBE2(tx__tnopt__log__info__labeling__mac__allowmult_v6,
320 		    char *, "destination ip(1) with multicast dest was passed",
321 		    in6_addr_t *, dst);
322 		return (0);
323 	}
324 
325 	/* Never pass an undefined destination */
326 	if ((dst_rhtp = find_tpc(dst, version, B_FALSE)) == NULL) {
327 		DTRACE_PROBE2(tx__tnopt__log__info__labeling__lookupdst,
328 		    char *, "destination ip(1) not in tn database.",
329 		    void *, dst);
330 		return (EHOSTUNREACH);
331 	}
332 
333 	switch (dst_rhtp->tpc_tp.host_type) {
334 	case UNLABELED:
335 		/*
336 		 * Can talk to unlabeled hosts if
337 		 * (1) zone's label matches the default label, or
338 		 * (2) SO_MAC_EXEMPT is on and we dominate the peer's label
339 		 * (3) SO_MAC_EXEMPT is on and this is the global zone
340 		 */
341 		if (dst_rhtp->tpc_tp.tp_doi != tsl->tsl_doi) {
342 			DTRACE_PROBE4(tx__tnopt__log__info__labeling__doi,
343 			    char *, "unlabeled dest ip(1)/tpc(2) doi does "
344 			    "not match msg label(3) doi.", void *, dst,
345 			    tsol_tpc_t *, dst_rhtp, ts_label_t *, tsl);
346 			TPC_RELE(dst_rhtp);
347 			return (EHOSTUNREACH);
348 		}
349 		if (!blequal(&dst_rhtp->tpc_tp.tp_def_label,
350 		    &tsl->tsl_label)) {
351 			zoneid = crgetzoneid(credp);
352 			if (!mac_exempt ||
353 			    !(zoneid == GLOBAL_ZONEID ||
354 			    bldominates(&tsl->tsl_label,
355 			    &dst_rhtp->tpc_tp.tp_def_label))) {
356 				DTRACE_PROBE4(
357 				    tx__tnopt__log__info__labeling__mac,
358 				    char *, "unlabeled dest ip(1)/tpc(2) does "
359 				    "not match msg label(3).", void *, dst,
360 				    tsol_tpc_t *, dst_rhtp, ts_label_t *, tsl);
361 				TPC_RELE(dst_rhtp);
362 				return (EHOSTUNREACH);
363 			}
364 			/*
365 			 * This is a downlabel MAC-exempt exchange.
366 			 * Use the remote destination's default label
367 			 * as the label of the message data.
368 			 */
369 			if ((newtsl = labelalloc(&dst_rhtp->tpc_tp.tp_def_label,
370 			    dst_rhtp->tpc_tp.tp_doi, KM_NOSLEEP)) == NULL) {
371 				TPC_RELE(dst_rhtp);
372 				return (ENOMEM);
373 			}
374 			newtsl->tsl_flags |= TSLF_UNLABELED;
375 
376 		} else if (!(tsl->tsl_flags & TSLF_UNLABELED)) {
377 			/*
378 			 * The security labels are the same but we need
379 			 * to flag that the remote node is unlabeled.
380 			 */
381 			if ((newtsl = labeldup(tsl, KM_NOSLEEP)) == NULL) {
382 				TPC_RELE(dst_rhtp);
383 				return (ENOMEM);
384 			}
385 			newtsl->tsl_flags |= TSLF_UNLABELED;
386 		}
387 		break;
388 
389 	case SUN_CIPSO:
390 		/*
391 		 * Can talk to labeled hosts if zone's label is within target's
392 		 * label range or set.
393 		 */
394 		if (dst_rhtp->tpc_tp.tp_cipso_doi_cipso != tsl->tsl_doi ||
395 		    (!_blinrange(&tsl->tsl_label,
396 		    &dst_rhtp->tpc_tp.tp_sl_range_cipso) &&
397 		    !blinlset(&tsl->tsl_label,
398 		    dst_rhtp->tpc_tp.tp_sl_set_cipso))) {
399 			DTRACE_PROBE4(tx__tnopt__log__info__labeling__mac,
400 			    char *, "labeled dest ip(1)/tpc(2) does not "
401 			    "match msg label(3).", void *, dst,
402 			    tsol_tpc_t *, dst_rhtp, ts_label_t *, tsl);
403 			TPC_RELE(dst_rhtp);
404 			return (EHOSTUNREACH);
405 		}
406 		if (tsl->tsl_flags & TSLF_UNLABELED) {
407 			/*
408 			 * The security label is a match but we need to
409 			 * clear the unlabeled flag for this remote node.
410 			 */
411 			if ((newtsl = labeldup(tsl, KM_NOSLEEP)) == NULL) {
412 				TPC_RELE(dst_rhtp);
413 				return (ENOMEM);
414 			}
415 			newtsl->tsl_flags ^= TSLF_UNLABELED;
416 		}
417 		break;
418 
419 	default:
420 		TPC_RELE(dst_rhtp);
421 		return (EHOSTUNREACH);
422 	}
423 
424 	/*
425 	 * Generate a new cred if we modified the security label or
426 	 * label flags.
427 	 */
428 	if (newtsl != NULL) {
429 		if (effective_cred != NULL) {
430 			*effective_cred = copycred_from_tslabel(credp,
431 			    newtsl, KM_NOSLEEP);
432 		}
433 		label_rele(newtsl);
434 		if (effective_cred != NULL && *effective_cred == NULL) {
435 			TPC_RELE(dst_rhtp);
436 			return (ENOMEM);
437 		}
438 	}
439 	TPC_RELE(dst_rhtp);
440 	return (0);
441 }
442 
443 /*
444  * tsol_compute_label()
445  *
446  * This routine computes the IP label that should be on a packet based on the
447  * connection and destination information.
448  *
449  * Returns:
450  *      0		Fetched label
451  *	EHOSTUNREACH	No route to destination
452  *	EINVAL		Label cannot be computed
453  */
454 int
455 tsol_compute_label(const cred_t *credp, ipaddr_t dst, uchar_t *opt_storage,
456     ip_stack_t *ipst)
457 {
458 	uint_t		sec_opt_len;
459 	ts_label_t	*tsl;
460 	ire_t		*ire, *sire = NULL;
461 	tsol_ire_gw_secattr_t *attrp;
462 	zoneid_t	zoneid, ip_zoneid;
463 
464 	ASSERT(credp != NULL);
465 
466 	if (opt_storage != NULL)
467 		opt_storage[IPOPT_OLEN] = 0;
468 
469 	if ((tsl = crgetlabel(credp)) == NULL)
470 		return (0);
471 
472 	/* always pass multicast */
473 	if (CLASSD(dst))
474 		return (0);
475 
476 	if (tsl->tsl_flags & TSLF_UNLABELED) {
477 
478 		/*
479 		 * The destination is unlabeled. Only add a label if the
480 		 * destination is not a broadcast/local/loopback address,
481 		 * the destination is not on the same subnet, and the
482 		 * next-hop gateway is labeled.
483 		 *
484 		 * For exclusive stacks we set the zoneid to zero
485 		 * to operate as if we are in the global zone for
486 		 * IRE lookups.
487 		 */
488 		zoneid = crgetzoneid(credp);
489 		if (ipst->ips_netstack->netstack_stackid != GLOBAL_NETSTACKID)
490 			ip_zoneid = GLOBAL_ZONEID;
491 		else
492 			ip_zoneid = zoneid;
493 
494 		ire = ire_cache_lookup(dst, ip_zoneid, tsl, ipst);
495 
496 		if (ire != NULL && (ire->ire_type & (IRE_BROADCAST | IRE_LOCAL |
497 		    IRE_LOOPBACK | IRE_INTERFACE)) != 0) {
498 			IRE_REFRELE(ire);
499 			return (0);
500 		} else if (ire == NULL) {
501 			ire = ire_ftable_lookup(dst, 0, 0, 0, NULL, &sire,
502 			    ip_zoneid, 0, tsl, (MATCH_IRE_RECURSIVE |
503 			    MATCH_IRE_DEFAULT | MATCH_IRE_SECATTR), ipst);
504 		}
505 
506 		/* no route to destination */
507 		if (ire == NULL) {
508 			DTRACE_PROBE3(
509 			    tx__tnopt__log__info__labeling__routedst__v4,
510 			    char *, "No route to unlabeled dest ip(1) with "
511 			    "creds(2).", ipaddr_t, dst, cred_t *, credp);
512 			return (EHOSTUNREACH);
513 		}
514 
515 		/*
516 		 * Prefix IRE from f-table lookup means that the destination
517 		 * is not directly connected; check the next-hop attributes.
518 		 */
519 		if (sire != NULL) {
520 			ASSERT(ire != NULL);
521 			IRE_REFRELE(ire);
522 			ire = sire;
523 		}
524 
525 		/*
526 		 * Return now if next hop gateway is unlabeled. There is
527 		 * no need to generate a CIPSO option for this message.
528 		 */
529 		attrp = ire->ire_gw_secattr;
530 		if (attrp == NULL || attrp->igsa_rhc == NULL ||
531 		    attrp->igsa_rhc->rhc_tpc->tpc_tp.host_type == UNLABELED) {
532 			IRE_REFRELE(ire);
533 			return (0);
534 		}
535 
536 		IRE_REFRELE(ire);
537 
538 	}
539 
540 	/* compute the CIPSO option */
541 	sec_opt_len = tsol2cipso_tt1(&tsl->tsl_label, opt_storage,
542 	    tsl->tsl_doi);
543 
544 	if (sec_opt_len == 0) {
545 		DTRACE_PROBE3(tx__tnopt__log__error__labeling__lostops__v4,
546 		    char *, "options lack length for dest ip(1) with creds(2).",
547 		    ipaddr_t, dst, cred_t *, credp);
548 		return (EINVAL);
549 	}
550 
551 	return (0);
552 }
553 
554 /*
555  * Remove any existing security option (CIPSO) from the given IP
556  * header, move the 'buflen' bytes back to fill the gap, and return the number
557  * of bytes removed (as zero or negative number).  Assumes that the headers are
558  * sane.
559  */
560 int
561 tsol_remove_secopt(ipha_t *ipha, int buflen)
562 {
563 	int remlen, olen, oval, delta;
564 	uchar_t *fptr, *tptr;
565 	boolean_t noop_keep;
566 
567 	remlen = IPH_HDR_LENGTH(ipha) - IP_SIMPLE_HDR_LENGTH;
568 	fptr = tptr = (uchar_t *)(ipha + 1);
569 	noop_keep = B_TRUE;
570 	while (remlen > 0) {
571 		oval = fptr[IPOPT_OPTVAL];
572 
573 		/* terminate on end of list */
574 		if (oval == IPOPT_EOL)
575 			break;
576 
577 		/*
578 		 * Delete any no-ops following a deleted option, at least up
579 		 * to a 4 octet alignment; copy others.
580 		 */
581 		if (oval == IPOPT_NOP) {
582 			if (((fptr - (uchar_t *)ipha) & 3) == 0)
583 				noop_keep = B_TRUE;
584 			if (noop_keep)
585 				*tptr++ = oval;
586 			fptr++;
587 			remlen--;
588 			continue;
589 		}
590 
591 		/* stop on corrupted list; just do nothing. */
592 		if (remlen < 2)
593 			return (0);
594 		olen = fptr[IPOPT_OLEN];
595 		if (olen < 2 || olen > remlen)
596 			return (0);
597 
598 		/* skip over security options to delete them */
599 		if (oval == IPOPT_COMSEC || oval == IPOPT_SECURITY) {
600 			noop_keep = B_FALSE;
601 			fptr += olen;
602 			remlen -= olen;
603 			continue;
604 		}
605 
606 		/* copy the rest */
607 		noop_keep = B_TRUE;
608 		if (tptr != fptr)
609 			ovbcopy(fptr, tptr, olen);
610 		fptr += olen;
611 		tptr += olen;
612 		remlen -= olen;
613 	}
614 
615 	fptr += remlen;
616 
617 	/* figure how much padding we'll need for header alignment */
618 	olen = (tptr - (uchar_t *)ipha) & 3;
619 	if (olen > 0) {
620 		olen = 4 - olen;
621 		/* pad with end-of-list */
622 		bzero(tptr, olen);
623 		tptr += olen;
624 	}
625 
626 	/* slide back the headers that follow and update the IP header */
627 	delta = fptr - tptr;
628 	if (delta != 0) {
629 		ovbcopy(fptr, tptr, ((uchar_t *)ipha + buflen) - fptr);
630 		ipha->ipha_version_and_hdr_length -= delta / 4;
631 	}
632 	return (-delta);
633 }
634 
635 /*
636  * Insert the option in 'optbuf' into the IP header pointed to by 'ipha', and
637  * move the data following the IP header (up to buflen) to accomodate the new
638  * option.  Assumes that up to IP_MAX_OPT_LENGTH bytes are available (in total)
639  * for IP options.  Returns the number of bytes actually inserted, or -1 if the
640  * option cannot be inserted.  (Note that negative return values are possible
641  * when noops must be compressed, and that only -1 indicates error.  Successful
642  * return value is always evenly divisible by 4, by definition.)
643  */
644 int
645 tsol_prepend_option(uchar_t *optbuf, ipha_t *ipha, int buflen)
646 {
647 	int remlen, padding, lastpad, totlen;
648 	int oval, olen;
649 	int delta;
650 	uchar_t *optr;
651 	uchar_t tempopt[IP_MAX_OPT_LENGTH], *toptr;
652 
653 	if (optbuf[IPOPT_OPTVAL] == IPOPT_EOL ||
654 	    optbuf[IPOPT_OPTVAL] == IPOPT_NOP ||
655 	    optbuf[IPOPT_OLEN] == 0)
656 		return (0);
657 
658 	ASSERT(optbuf[IPOPT_OLEN] >= 2 &&
659 	    optbuf[IPOPT_OLEN] <= IP_MAX_OPT_LENGTH);
660 
661 	/* first find the real (unpadded) length of the existing options */
662 	remlen = IPH_HDR_LENGTH(ipha) - IP_SIMPLE_HDR_LENGTH;
663 	padding = totlen = lastpad = 0;
664 	optr = (uchar_t *)(ipha + 1);
665 	while (remlen > 0) {
666 		oval = optr[IPOPT_OPTVAL];
667 
668 		/* stop at end of list */
669 		if (oval == IPOPT_EOL)
670 			break;
671 
672 		/* skip no-ops, noting that length byte isn't present */
673 		if (oval == IPOPT_NOP) {
674 			optr++;
675 			padding++;
676 			lastpad++;
677 			totlen++;
678 			remlen--;
679 			continue;
680 		}
681 
682 		/* give up on a corrupted list; report failure */
683 		if (remlen < 2)
684 			return (-1);
685 		olen = optr[IPOPT_OLEN];
686 		if (olen < 2 || olen > remlen)
687 			return (-1);
688 
689 		lastpad = 0;
690 		optr += olen;
691 		totlen += olen;
692 		remlen -= olen;
693 	}
694 
695 	/* completely ignore any trailing padding */
696 	totlen -= lastpad;
697 	padding -= lastpad;
698 
699 	/*
700 	 * If some sort of inter-option alignment was present, try to preserve
701 	 * that alignment.  If alignment pushes us out past the maximum, then
702 	 * discard it and try to compress to fit.  (We just "assume" that any
703 	 * padding added was attempting to get 32 bit alignment.  If that's
704 	 * wrong, that's just too bad.)
705 	 */
706 	if (padding > 0) {
707 		olen = (optbuf[IPOPT_OLEN] + 3) & ~3;
708 		if (olen + totlen > IP_MAX_OPT_LENGTH) {
709 			totlen -= padding;
710 			if (olen + totlen > IP_MAX_OPT_LENGTH)
711 				return (-1);
712 			padding = 0;
713 		}
714 	}
715 
716 	/*
717 	 * Since we may need to compress or expand the option list, we write to
718 	 * a temporary buffer and then copy the results back to the IP header.
719 	 */
720 	toptr = tempopt;
721 
722 	/* compute actual option to insert */
723 	olen = optbuf[IPOPT_OLEN];
724 	bcopy(optbuf, toptr, olen);
725 	toptr += olen;
726 	if (padding > 0) {
727 		while ((olen & 3) != 0) {
728 			*toptr++ = IPOPT_NOP;
729 			olen++;
730 		}
731 	}
732 
733 	/* copy over the existing options */
734 	optr = (uchar_t *)(ipha + 1);
735 	while (totlen > 0) {
736 		oval = optr[IPOPT_OPTVAL];
737 
738 		/* totlen doesn't include end-of-list marker */
739 		ASSERT(oval != IPOPT_EOL);
740 
741 		/* handle no-ops; copy if desired, ignore otherwise */
742 		if (oval == IPOPT_NOP) {
743 			if (padding > 0) {
744 				/* note: cannot overflow due to checks above */
745 				ASSERT(toptr < tempopt + IP_MAX_OPT_LENGTH);
746 				*toptr++ = oval;
747 			}
748 			optr++;
749 			totlen--;
750 			continue;
751 		}
752 
753 		/* list cannot be corrupt at this point */
754 		ASSERT(totlen >= 2);
755 		olen = optr[IPOPT_OLEN];
756 		ASSERT(olen >= 2 && olen <= totlen);
757 
758 		/* cannot run out of room due to tests above */
759 		ASSERT(toptr + olen <= tempopt + IP_MAX_OPT_LENGTH);
760 
761 		bcopy(optr, toptr, olen);
762 		optr += olen;
763 		toptr += olen;
764 		totlen -= olen;
765 	}
766 
767 	/* figure how much padding we'll need for header alignment */
768 	olen = (toptr - tempopt) & 3;
769 	if (olen > 0) {
770 		olen = 4 - olen;
771 		ASSERT(toptr + olen <= tempopt + IP_MAX_OPT_LENGTH);
772 		/* pad with end-of-list value */
773 		bzero(toptr, olen);
774 		toptr += olen;
775 	}
776 
777 	/* move the headers as needed and update IP header */
778 	olen = (toptr - tempopt) + IP_SIMPLE_HDR_LENGTH;
779 	remlen = IPH_HDR_LENGTH(ipha);
780 	delta = olen - remlen;
781 	if (delta != 0) {
782 		ovbcopy((uchar_t *)ipha + remlen, (uchar_t *)ipha + olen,
783 		    buflen - remlen);
784 		ipha->ipha_version_and_hdr_length += delta / 4;
785 	}
786 
787 	/* slap in the new options */
788 	bcopy(tempopt, ipha + 1, olen - IP_SIMPLE_HDR_LENGTH);
789 
790 	return (delta);
791 }
792 
793 /*
794  * tsol_check_label()
795  *
796  * This routine computes the IP label that should be on the packet based on the
797  * connection and destination information.  If the label is there, it returns
798  * zero, so the caller knows that the label is syncronized, and further calls
799  * are not required.  If the label isn't right, then the right one is inserted.
800  *
801  * The packet's header is clear before entering IPsec's engine.
802  *
803  * Returns:
804  *      0		Label on packet (was|is now) correct
805  *      EACCES		The packet failed the remote host accreditation.
806  *      ENOMEM		Memory allocation failure.
807  *	EINVAL		Label cannot be computed
808  */
809 int
810 tsol_check_label(const cred_t *credp, mblk_t **mpp, boolean_t isexempt,
811     ip_stack_t *ipst, pid_t pid)
812 {
813 	mblk_t *mp = *mpp;
814 	ipha_t  *ipha;
815 	cred_t *effective_cred = NULL;
816 	uchar_t opt_storage[IP_MAX_OPT_LENGTH];
817 	uint_t hlen;
818 	uint_t sec_opt_len;
819 	uchar_t *optr;
820 	int delta_remove = 0, delta_add, adjust;
821 	int retv;
822 
823 	opt_storage[IPOPT_OPTVAL] = 0;
824 
825 	ipha = (ipha_t *)mp->b_rptr;
826 
827 	/*
828 	 * Verify the destination is allowed to receive packets at
829 	 * the security label of the message data. check_dest()
830 	 * may create a new effective cred with a modified label
831 	 * or label flags. Apply any such cred to the message block
832 	 * for use in future routing decisions.
833 	 */
834 	retv = tsol_check_dest(credp, &ipha->ipha_dst, IPV4_VERSION,
835 	    isexempt, &effective_cred);
836 	if (retv != 0)
837 		return (retv);
838 
839 	/*
840 	 * Calculate the security label to be placed in the text
841 	 * of the message (if any).
842 	 */
843 	if (effective_cred != NULL) {
844 		if ((retv = tsol_compute_label(effective_cred,
845 		    ipha->ipha_dst, opt_storage, ipst)) != 0) {
846 			crfree(effective_cred);
847 			return (retv);
848 		}
849 		mblk_setcred(mp, effective_cred, pid);
850 		crfree(effective_cred);
851 	} else {
852 		if ((retv = tsol_compute_label(credp,
853 		    ipha->ipha_dst, opt_storage, ipst)) != 0) {
854 			return (retv);
855 		}
856 	}
857 
858 	optr = (uchar_t *)(ipha + 1);
859 	hlen = IPH_HDR_LENGTH(ipha) - IP_SIMPLE_HDR_LENGTH;
860 	sec_opt_len = opt_storage[IPOPT_OLEN];
861 
862 	if (hlen >= sec_opt_len) {
863 		/* If no option is supposed to be there, make sure it's not */
864 		if (sec_opt_len == 0 && hlen > 0 &&
865 		    optr[IPOPT_OPTVAL] != IPOPT_COMSEC &&
866 		    optr[IPOPT_OPTVAL] != IPOPT_SECURITY)
867 			return (0);
868 		/* if the option is there, it's always first */
869 		if (sec_opt_len != 0 &&
870 		    bcmp(opt_storage, optr, sec_opt_len) == 0)
871 			return (0);
872 	}
873 
874 	/*
875 	 * If there is an option there, then it must be the wrong one; delete.
876 	 */
877 	if (hlen > 0) {
878 		delta_remove = tsol_remove_secopt(ipha, MBLKL(mp));
879 		mp->b_wptr += delta_remove;
880 	}
881 
882 	/* Make sure we have room for the worst-case addition */
883 	hlen = IPH_HDR_LENGTH(ipha) + opt_storage[IPOPT_OLEN];
884 	hlen = (hlen + 3) & ~3;
885 	if (hlen > IP_MAX_HDR_LENGTH)
886 		hlen = IP_MAX_HDR_LENGTH;
887 	hlen -= IPH_HDR_LENGTH(ipha);
888 	if (mp->b_wptr + hlen > mp->b_datap->db_lim) {
889 		int copylen;
890 		mblk_t *new_mp;
891 
892 		/* allocate enough to be meaningful, but not *too* much */
893 		copylen = MBLKL(mp);
894 		if (copylen > 256)
895 			copylen = 256;
896 		new_mp = allocb_tmpl(hlen + copylen +
897 		    (mp->b_rptr - mp->b_datap->db_base), mp);
898 		if (new_mp == NULL)
899 			return (ENOMEM);
900 
901 		/* keep the bias */
902 		new_mp->b_rptr += mp->b_rptr - mp->b_datap->db_base;
903 		new_mp->b_wptr = new_mp->b_rptr + copylen;
904 		bcopy(mp->b_rptr, new_mp->b_rptr, copylen);
905 		new_mp->b_cont = mp;
906 		if ((mp->b_rptr += copylen) >= mp->b_wptr) {
907 			new_mp->b_cont = mp->b_cont;
908 			freeb(mp);
909 		}
910 		*mpp = mp = new_mp;
911 		ipha = (ipha_t *)mp->b_rptr;
912 	}
913 
914 	delta_add = tsol_prepend_option(opt_storage, ipha, MBLKL(mp));
915 	if (delta_add == -1)
916 		goto param_prob;
917 
918 	ASSERT((mp->b_wptr + delta_add) <= DB_LIM(mp));
919 	mp->b_wptr += delta_add;
920 
921 	adjust = delta_remove + delta_add;
922 	adjust += ntohs(ipha->ipha_length);
923 	ipha->ipha_length = htons(adjust);
924 
925 	return (0);
926 
927 param_prob:
928 	return (EINVAL);
929 }
930 
931 /*
932  * IPv6 HopOpt extension header for the label option layout:
933  *	- One octet giving the type of the 'next extension header'
934  *	- Header extension length in 8-byte words, not including the
935  *	  1st 8 bytes, but including any pad bytes at the end.
936  *	  Eg. A value of 2 means 16 bytes not including the 1st 8 bytes.
937  *	- Followed by TLV encoded IPv6 label option. Option layout is
938  *		* One octet, IP6OPT_LS
939  *		* One octet option length in bytes of the option data following
940  *		  the length, but not including any pad bytes at the end.
941  *		* Four-octet DOI (IP6LS_DOI_V4)
942  *		* One octet suboption, IP6LS_TT_V4
943  *		* One octet suboption length in bytes of the suboption
944  *		  following the suboption length, including the suboption
945  *		  header length, but not including any pad bytes at the end.
946  *	- Pad to make the extension header a multiple of 8 bytes.
947  *
948  * This function returns the contents of 'IPv6 option structure' in the above.
949  * i.e starting from the IP6OPT_LS but not including the pad at the end.
950  * The user must prepend two octets (either padding or next header / length)
951  * and append padding out to the next 8 octet boundary.
952  */
953 int
954 tsol_compute_label_v6(const cred_t *credp, const in6_addr_t *dst,
955     uchar_t *opt_storage, ip_stack_t *ipst)
956 {
957 	ts_label_t	*tsl;
958 	uint_t		sec_opt_len;
959 	uint32_t	doi;
960 	zoneid_t	zoneid, ip_zoneid;
961 	ire_t		*ire, *sire;
962 	tsol_ire_gw_secattr_t *attrp;
963 
964 	ASSERT(credp != NULL);
965 
966 	if (ip6opt_ls == 0)
967 		return (EINVAL);
968 
969 	if (opt_storage != NULL)
970 		opt_storage[IPOPT_OLEN] = 0;
971 
972 	if ((tsl = crgetlabel(credp)) == NULL)
973 		return (0);
974 
975 	/* Always pass multicast */
976 	if (IN6_IS_ADDR_MULTICAST(dst))
977 		return (0);
978 
979 	zoneid = crgetzoneid(credp);
980 
981 	/*
982 	 * Fill in a V6 label.  If a new format is added here, make certain
983 	 * that the maximum size of this label is reflected in sys/tsol/tnet.h
984 	 * as TSOL_MAX_IPV6_OPTION.
985 	 */
986 	if (tsl->tsl_flags & TSLF_UNLABELED) {
987 		/*
988 		 * The destination is unlabeled. Only add a label if the
989 		 * destination is not broadcast/local/loopback address,
990 		 * the destination is not on the same subnet, and the
991 		 * next-hop gateway is labeled.
992 		 *
993 		 * For exclusive stacks we set the zoneid to zero to
994 		 * operate as if we are in the global zone when
995 		 * performing IRE lookups and conn_t comparisons.
996 		 */
997 		if (ipst->ips_netstack->netstack_stackid != GLOBAL_NETSTACKID)
998 			ip_zoneid = GLOBAL_ZONEID;
999 		else
1000 			ip_zoneid = zoneid;
1001 
1002 		sire = NULL;
1003 		ire = ire_cache_lookup_v6(dst, ip_zoneid, tsl, ipst);
1004 
1005 		if (ire != NULL && (ire->ire_type & (IRE_LOCAL |
1006 		    IRE_LOOPBACK | IRE_INTERFACE)) != 0) {
1007 			IRE_REFRELE(ire);
1008 			return (0);
1009 		} else if (ire == NULL) {
1010 			ire = ire_ftable_lookup_v6(dst, NULL, NULL, 0, NULL,
1011 			    &sire, ip_zoneid, 0, tsl, (MATCH_IRE_RECURSIVE |
1012 			    MATCH_IRE_DEFAULT | MATCH_IRE_SECATTR), ipst);
1013 		}
1014 
1015 		/* no route to destination */
1016 		if (ire == NULL) {
1017 			DTRACE_PROBE3(
1018 			    tx__tnopt__log__info__labeling__routedst__v6,
1019 			    char *, "No route to unlabeled dest ip6(1) with "
1020 			    "creds(2).", in6_addr_t *, dst, cred_t *, credp);
1021 			return (EHOSTUNREACH);
1022 		}
1023 
1024 		/*
1025 		 * Prefix IRE from f-table lookup means that the destination
1026 		 * is not directly connected; check the next-hop attributes.
1027 		 */
1028 		if (sire != NULL) {
1029 			ASSERT(ire != NULL);
1030 			IRE_REFRELE(ire);
1031 			ire = sire;
1032 		}
1033 
1034 		/*
1035 		 * Return now if next hop gateway is unlabeled. There is
1036 		 * no need to generate a CIPSO option for this message.
1037 		 */
1038 		attrp = ire->ire_gw_secattr;
1039 		if (attrp == NULL || attrp->igsa_rhc == NULL ||
1040 		    attrp->igsa_rhc->rhc_tpc->tpc_tp.host_type == UNLABELED) {
1041 			IRE_REFRELE(ire);
1042 			return (0);
1043 		}
1044 		IRE_REFRELE(ire);
1045 	}
1046 
1047 	/* compute the CIPSO option */
1048 	if (opt_storage != NULL)
1049 		opt_storage += 8;
1050 	sec_opt_len = tsol2cipso_tt1(&tsl->tsl_label, opt_storage,
1051 	    tsl->tsl_doi);
1052 
1053 	if (sec_opt_len == 0) {
1054 		DTRACE_PROBE3(tx__tnopt__log__error__labeling__lostops__v6,
1055 		    char *, "options lack length for dest ip6(1) with "
1056 		    "creds(2).", in6_addr_t *, dst, cred_t *, credp);
1057 		return (EINVAL);
1058 	}
1059 
1060 	if (opt_storage == NULL)
1061 		return (0);
1062 
1063 	if (sec_opt_len < IP_MAX_OPT_LENGTH)
1064 		opt_storage[sec_opt_len] = IPOPT_EOL;
1065 
1066 	/*
1067 	 * Just in case the option length is odd, round it up to the next even
1068 	 * multiple.  The IPv6 option definition doesn't like odd numbers for
1069 	 * some reason.
1070 	 *
1071 	 * Length in the overall option header (IP6OPT_LS) does not include the
1072 	 * option header itself, but the length in the suboption does include
1073 	 * the suboption header.  Thus, when there's just one suboption, the
1074 	 * length in the option header is the suboption length plus 4 (for the
1075 	 * DOI value).
1076 	 */
1077 	opt_storage[-2] = IP6LS_TT_V4;
1078 	opt_storage[-1] = (sec_opt_len + 2 + 1) & ~1;
1079 	opt_storage[-8] = ip6opt_ls;
1080 	opt_storage[-7] = opt_storage[-1] + 4;
1081 	doi = htons(IP6LS_DOI_V4);
1082 	bcopy(&doi, opt_storage - 6, 4);
1083 
1084 	return (0);
1085 }
1086 
1087 /*
1088  * Locate the start of the IP6OPT_LS label option and return it.
1089  * Also return the start of the next non-pad option in after_secoptp.
1090  * Usually the label option is the first option at least when packets
1091  * are generated, but for generality we don't assume that on received packets.
1092  *
1093  * The function will return with B_FALSE if an IP format error
1094  * or an unexpected label content error is encountered.
1095  */
1096 boolean_t
1097 tsol_find_secopt_v6(
1098     const uchar_t *ip6hbh,	/* Start of the hop-by-hop extension header */
1099     uint_t hbhlen,		/* Length of the hop-by-hop extension header */
1100     uchar_t **secoptp,		/* Location of IP6OPT_LS label option */
1101     uchar_t **after_secoptp,	/* Non-pad option following the label option */
1102     boolean_t *hbh_needed)	/* Is hop-by-hop hdr needed w/o label */
1103 {
1104 	uint_t	optlen;
1105 	uint_t	optused;
1106 	const uchar_t *optptr;
1107 	uchar_t	opt_type;
1108 
1109 	*secoptp = NULL;
1110 	*hbh_needed = B_FALSE;
1111 	*after_secoptp = NULL;
1112 	optlen = hbhlen - 2;
1113 	optptr = ip6hbh + 2;
1114 	while (optlen != 0) {
1115 		opt_type = *optptr;
1116 		if (opt_type == IP6OPT_PAD1) {
1117 			optptr++;
1118 			optlen--;
1119 			continue;
1120 		}
1121 		if (optlen == 1)
1122 			return (B_FALSE);
1123 		optused = 2 + optptr[1];
1124 		if (optused > optlen)
1125 			return (B_FALSE);
1126 		/*
1127 		 * if we get here, ip6opt_ls can
1128 		 * not be 0 because it will always
1129 		 * match the IP6OPT_PAD1 above.
1130 		 * Therefore ip6opt_ls == 0 forces
1131 		 * this test to always fail here.
1132 		 */
1133 		if (opt_type == ip6opt_ls) {
1134 			if (*secoptp != NULL)
1135 				/* More than one security option found */
1136 				return (B_FALSE);
1137 			*secoptp = (uchar_t *)optptr;
1138 		} else switch (opt_type) {
1139 		case IP6OPT_PADN:
1140 			break;
1141 		default:
1142 			/*
1143 			 * There is at least 1 option other than
1144 			 * the label option. So the hop-by-hop header is needed
1145 			 */
1146 			*hbh_needed = B_TRUE;
1147 			if (*secoptp != NULL) {
1148 				*after_secoptp = (uchar_t *)optptr;
1149 				return (B_TRUE);
1150 			}
1151 			break;
1152 		}
1153 		optlen -= optused;
1154 		optptr += optused;
1155 	}
1156 	return (B_TRUE);
1157 }
1158 
1159 /*
1160  * Remove the label option from the hop-by-hop options header if it exists.
1161  * 'buflen' is the total length of the packet typically b_wptr - b_rptr.
1162  * Header and data following the label option that is deleted are copied
1163  * (i.e. slid backward) to the right position, and returns the number
1164  * of bytes removed (as zero or negative number.)
1165  */
1166 int
1167 tsol_remove_secopt_v6(ip6_t *ip6h, int buflen)
1168 {
1169 	uchar_t	*ip6hbh;	/* hop-by-hop header */
1170 	uint_t	hbhlen;		/* hop-by-hop extension header length */
1171 	uchar_t *secopt = NULL;
1172 	uchar_t *after_secopt;
1173 	uint_t	pad;
1174 	uint_t	delta;
1175 	boolean_t hbh_needed;
1176 
1177 	/*
1178 	 * hop-by-hop extension header must appear first, if it does not
1179 	 * exist, there is no label option.
1180 	 */
1181 	if (ip6h->ip6_nxt != IPPROTO_HOPOPTS)
1182 		return (0);
1183 
1184 	ip6hbh = (uchar_t *)&ip6h[1];
1185 	hbhlen = (ip6hbh[1] + 1) << 3;
1186 	/*
1187 	 * Locate the start of the label option if it exists and the end
1188 	 * of the label option including pads if any.
1189 	 */
1190 	if (!tsol_find_secopt_v6(ip6hbh, hbhlen, &secopt, &after_secopt,
1191 	    &hbh_needed)) {
1192 		/*
1193 		 * This function should not see invalid messages.
1194 		 * If one occurs, it would indicate either an
1195 		 * option previously verified in the forwarding
1196 		 * path has been corrupted or an option was
1197 		 * incorrectly generated locally.
1198 		 */
1199 		ASSERT(0);
1200 		return (0);
1201 	}
1202 	if (secopt == NULL)
1203 		return (0);
1204 	if (!hbh_needed) {
1205 		uchar_t	next_hdr;
1206 		/*
1207 		 * The label option was the only option in the hop-by-hop
1208 		 * header. We don't need the hop-by-hop header itself any
1209 		 * longer.
1210 		 */
1211 		next_hdr = ip6hbh[0];
1212 		ovbcopy(ip6hbh + hbhlen, ip6hbh,
1213 		    buflen - (IPV6_HDR_LEN + hbhlen));
1214 		ip6h->ip6_plen = htons(ntohs(ip6h->ip6_plen) - hbhlen);
1215 		ip6h->ip6_nxt = next_hdr;
1216 		return (-hbhlen);
1217 	}
1218 
1219 	if (after_secopt == NULL) {
1220 		/* There is no option following the label option */
1221 		after_secopt = ip6hbh + hbhlen;
1222 	}
1223 
1224 	/*
1225 	 * After deleting the label option, we need to slide the headers
1226 	 * and data back, while still maintaining the same alignment (module 8)
1227 	 * for the other options. So we slide the headers and data back only
1228 	 * by an integral multiple of 8 bytes, and fill the remaining bytes
1229 	 * with pads.
1230 	 */
1231 	delta = after_secopt - secopt;
1232 	pad = delta % 8;
1233 	if (pad == 1) {
1234 		secopt[0] = IP6OPT_PAD1;
1235 	} else if (pad > 1) {
1236 		secopt[0] = IP6OPT_PADN;
1237 		secopt[1] = pad - 2;
1238 		if (pad > 2)
1239 			bzero(&secopt[2], pad - 2);
1240 	}
1241 	secopt += pad;
1242 	delta -= pad;
1243 	ovbcopy(after_secopt, secopt,
1244 	    (uchar_t *)ip6h + buflen - after_secopt);
1245 	ip6hbh[1] -= delta/8;
1246 	ip6h->ip6_plen = htons(ntohs(ip6h->ip6_plen) - delta);
1247 
1248 	return (-delta);
1249 }
1250 
1251 /*
1252  * 'optbuf' contains a CIPSO label embedded in an IPv6 hop-by-hop option,
1253  * starting with the IP6OPT_LS option type. The format of this hop-by-hop
1254  * option is described in the block comment above tsol_compute_label_v6.
1255  * This function prepends this hop-by-hop option before any other hop-by-hop
1256  * options in the hop-by-hop header if one already exists, else a new
1257  * hop-by-hop header is created and stuffed into the packet following
1258  * the IPv6 header. 'buflen' is the total length of the packet i.e.
1259  * b_wptr - b_rptr. The caller ensures that there is enough space for the
1260  * extra option being added. Header and data following the position where
1261  * the label option is inserted are copied (i.e. slid forward) to the right
1262  * position.
1263  */
1264 int
1265 tsol_prepend_option_v6(uchar_t *optbuf, ip6_t *ip6h, int buflen)
1266 {
1267 	/*
1268 	 * rawlen is the length of the label option in bytes, not including
1269 	 * any pads, starting from the IP6OPT_LS (option type) byte.
1270 	 */
1271 	uint_t	rawlen;
1272 
1273 	uint_t	optlen;		/* rawlen rounded to an 8 byte multiple */
1274 	uchar_t	*ip6hbh;	/* start of the hop-by-hop extension header */
1275 	uint_t	hbhlen;		/* Length of the hop-by-hop extension header */
1276 	uint_t	pad_len;
1277 	uchar_t	*pad_position;
1278 	int	delta;		/* Actual number of bytes inserted */
1279 
1280 	rawlen = optbuf[1] + 2;	/* Add 2 for the option type, option length */
1281 	ip6hbh = (uchar_t *)&ip6h[1];
1282 	if (ip6h->ip6_nxt == IPPROTO_HOPOPTS) {
1283 		/*
1284 		 * There is a hop-by-hop header present already. In order to
1285 		 * preserve the alignment of the other options at the existing
1286 		 * value (modulo 8) we need to pad the label option to a
1287 		 * multiple of 8 bytes before prepending it to the other
1288 		 * options. Slide the extension headers and data forward to
1289 		 * accomodate the label option at the start of the hop-by-hop
1290 		 * header
1291 		 */
1292 		delta = optlen = (rawlen + 7) & ~7;
1293 		pad_len = optlen - rawlen;
1294 		pad_position = ip6hbh + 2 + rawlen;
1295 		ovbcopy(ip6hbh + 2, ip6hbh + 2 + optlen,
1296 		    buflen - (IPV6_HDR_LEN + 2));
1297 		/*
1298 		 * Bump up the hop-by-hop extension header length by
1299 		 * the number of 8-byte words added
1300 		 */
1301 		optlen >>= 3;
1302 		if (ip6hbh[1] + optlen > 255)
1303 			return (-1);
1304 		ip6hbh[1] += optlen;
1305 	} else {
1306 		/*
1307 		 * There is no hop-by-hop header in the packet. Construct a
1308 		 * new Hop-by-hop extension header (a multiple of 8 bytes).
1309 		 * Slide any other extension headers and data forward to
1310 		 * accomodate this hop-by-hop header
1311 		 */
1312 		delta = hbhlen = (2 + rawlen + 7) & ~7; /* +2 for nxthdr, len */
1313 		pad_len = hbhlen - (2 + rawlen);
1314 		pad_position = ip6hbh + 2 + rawlen;
1315 		ovbcopy(ip6hbh, ip6hbh + hbhlen, buflen - IPV6_HDR_LEN);
1316 		ip6hbh[0] = ip6h->ip6_nxt;
1317 		/*
1318 		 * hop-by-hop extension header length in 8-byte words, not
1319 		 * including the 1st 8 bytes of the hop-by-hop header.
1320 		 */
1321 		ip6hbh[1] = (hbhlen >> 3) - 1;
1322 		ip6h->ip6_nxt = IPPROTO_HOPOPTS;
1323 	}
1324 	/*
1325 	 * Copy the label option into the hop-by-hop header and insert any
1326 	 * needed pads
1327 	 */
1328 	bcopy(optbuf, ip6hbh + 2, rawlen);
1329 	if (pad_len == 1) {
1330 		pad_position[0] = IP6OPT_PAD1;
1331 	} else if (pad_len > 1) {
1332 		pad_position[0] = IP6OPT_PADN;
1333 		pad_position[1] = pad_len - 2;
1334 		if (pad_len > 2)
1335 			bzero(pad_position + 2, pad_len - 2);
1336 	}
1337 	ip6h->ip6_plen = htons(ntohs(ip6h->ip6_plen) + delta);
1338 	return (delta);
1339 }
1340 
1341 /*
1342  * tsol_check_label_v6()
1343  *
1344  * This routine computes the IP label that should be on the packet based on the
1345  * connection and destination information.  It's called only by the IP
1346  * forwarding logic, because all internal modules atop IP know how to generate
1347  * their own labels.
1348  *
1349  * Returns:
1350  *      0		Label on packet was already correct
1351  *      EACCES		The packet failed the remote host accreditation.
1352  *      ENOMEM		Memory allocation failure.
1353  */
1354 int
1355 tsol_check_label_v6(const cred_t *credp, mblk_t **mpp, boolean_t isexempt,
1356     ip_stack_t *ipst, pid_t pid)
1357 {
1358 	mblk_t *mp = *mpp;
1359 	ip6_t  *ip6h;
1360 	cred_t *effective_cred;
1361 	/*
1362 	 * Label option length is limited to IP_MAX_OPT_LENGTH for
1363 	 * symmetry with IPv4. Can be relaxed if needed
1364 	 */
1365 	uchar_t opt_storage[TSOL_MAX_IPV6_OPTION];
1366 	uint_t hlen;
1367 	uint_t sec_opt_len; /* label option length not including type, len */
1368 	int delta_remove = 0, delta_add;
1369 	int retv;
1370 	uchar_t	*after_secopt;
1371 	uchar_t	*secopt = NULL;
1372 	uchar_t	*ip6hbh;
1373 	uint_t	hbhlen;
1374 	boolean_t hbh_needed;
1375 
1376 	/*
1377 	 * Verify the destination is allowed to receive packets at
1378 	 * the security label of the message data. check_dest()
1379 	 * may create a new effective cred with a modified label
1380 	 * or label flags. Apply any such cred to the message block
1381 	 * for use in future routing decisions.
1382 	 */
1383 	ip6h = (ip6_t *)mp->b_rptr;
1384 	retv = tsol_check_dest(credp, &ip6h->ip6_dst, IPV6_VERSION,
1385 	    isexempt, &effective_cred);
1386 	if (retv != 0)
1387 		return (retv);
1388 
1389 	/*
1390 	 * Calculate the security label to be placed in the text
1391 	 * of the message (if any).
1392 	 */
1393 	if (effective_cred != NULL) {
1394 		if ((retv = tsol_compute_label_v6(effective_cred,
1395 		    &ip6h->ip6_dst, opt_storage, ipst)) != 0) {
1396 			crfree(effective_cred);
1397 			return (retv);
1398 		}
1399 		mblk_setcred(mp, effective_cred, pid);
1400 		crfree(effective_cred);
1401 	} else {
1402 		if ((retv = tsol_compute_label_v6(credp,
1403 		    &ip6h->ip6_dst, opt_storage, ipst)) != 0)
1404 			return (retv);
1405 	}
1406 
1407 	sec_opt_len = opt_storage[1];
1408 
1409 	if (ip6h->ip6_nxt == IPPROTO_HOPOPTS) {
1410 		ip6hbh = (uchar_t *)&ip6h[1];
1411 		hbhlen = (ip6hbh[1] + 1) << 3;
1412 		if (!tsol_find_secopt_v6(ip6hbh, hbhlen, &secopt,
1413 		    &after_secopt, &hbh_needed)) {
1414 			/*
1415 			 * This function should not see invalid messages.
1416 			 * If one occurs, it would indicate either an
1417 			 * option previously verified in the forwarding
1418 			 * path has been corrupted or an option was
1419 			 * incorrectly generated locally.
1420 			 */
1421 			ASSERT(0);
1422 			return (EACCES);
1423 		}
1424 	}
1425 
1426 	if (sec_opt_len == 0 && secopt == NULL) {
1427 		/*
1428 		 * The packet is not supposed to have a label, and it
1429 		 * does not have one currently
1430 		 */
1431 		return (0);
1432 	}
1433 	if (secopt != NULL && sec_opt_len != 0 &&
1434 	    (bcmp(opt_storage, secopt, sec_opt_len + 2) == 0)) {
1435 		/* The packet has the correct label already */
1436 		return (0);
1437 	}
1438 
1439 	/*
1440 	 * If there is an option there, then it must be the wrong one; delete.
1441 	 */
1442 	if (secopt != NULL) {
1443 		delta_remove = tsol_remove_secopt_v6(ip6h, MBLKL(mp));
1444 		mp->b_wptr += delta_remove;
1445 	}
1446 
1447 	/*
1448 	 * Make sure we have room for the worst-case addition. Add 2 bytes for
1449 	 * the hop-by-hop ext header's next header and length fields. Add
1450 	 * another 2 bytes for the label option type, len and then round
1451 	 * up to the next 8-byte multiple.
1452 	 */
1453 	hlen = (4 + sec_opt_len + 7) & ~7;
1454 	if (mp->b_wptr + hlen > mp->b_datap->db_lim) {
1455 		int copylen;
1456 		mblk_t *new_mp;
1457 		uint16_t hdr_len;
1458 
1459 		hdr_len = ip_hdr_length_v6(mp, ip6h);
1460 		/*
1461 		 * Allocate enough to be meaningful, but not *too* much.
1462 		 * Also all the IPv6 extension headers must be in the same mblk
1463 		 */
1464 		copylen = MBLKL(mp);
1465 		if (copylen > 256)
1466 			copylen = 256;
1467 		if (copylen < hdr_len)
1468 			copylen = hdr_len;
1469 		new_mp = allocb_tmpl(hlen + copylen +
1470 		    (mp->b_rptr - mp->b_datap->db_base), mp);
1471 		if (new_mp == NULL)
1472 			return (ENOMEM);
1473 
1474 		/* keep the bias */
1475 		new_mp->b_rptr += mp->b_rptr - mp->b_datap->db_base;
1476 		new_mp->b_wptr = new_mp->b_rptr + copylen;
1477 		bcopy(mp->b_rptr, new_mp->b_rptr, copylen);
1478 		new_mp->b_cont = mp;
1479 		if ((mp->b_rptr += copylen) >= mp->b_wptr) {
1480 			new_mp->b_cont = mp->b_cont;
1481 			freeb(mp);
1482 		}
1483 		*mpp = mp = new_mp;
1484 		ip6h = (ip6_t *)mp->b_rptr;
1485 	}
1486 
1487 	delta_add = tsol_prepend_option_v6(opt_storage, ip6h, MBLKL(mp));
1488 	if (delta_add == -1)
1489 		goto param_prob;
1490 
1491 	ASSERT(mp->b_wptr + delta_add <= DB_LIM(mp));
1492 	mp->b_wptr += delta_add;
1493 
1494 	return (0);
1495 
1496 param_prob:
1497 	return (EINVAL);
1498 }
1499 
1500 /*
1501  * Update the given IPv6 "sticky options" structure to contain the provided
1502  * label, which is encoded as an IPv6 option.  Existing label is removed if
1503  * necessary, and storage is allocated/freed/resized.
1504  *
1505  * Returns 0 on success, errno on failure.
1506  */
1507 int
1508 tsol_update_sticky(ip6_pkt_t *ipp, uint_t *labellen, const uchar_t *labelopt)
1509 {
1510 	int rawlen, optlen, newlen;
1511 	uchar_t *newopts;
1512 
1513 	/*
1514 	 * rawlen is the size of the IPv6 label to be inserted from labelopt.
1515 	 * optlen is the total length of that option, including any necessary
1516 	 * headers and padding.  newlen is the new size of the total hop-by-hop
1517 	 * options buffer, including user options.
1518 	 */
1519 	ASSERT(*labellen <= ipp->ipp_hopoptslen);
1520 	ASSERT((ipp->ipp_hopopts == NULL && ipp->ipp_hopoptslen == 0) ||
1521 	    (ipp->ipp_hopopts != NULL && ipp->ipp_hopoptslen != 0));
1522 
1523 	if ((rawlen = labelopt[1]) != 0) {
1524 		rawlen += 2;	/* add in header size */
1525 		optlen = (2 + rawlen + 7) & ~7;
1526 	} else {
1527 		optlen = 0;
1528 	}
1529 	newlen = ipp->ipp_hopoptslen + optlen - *labellen;
1530 	if (newlen == 0 && ipp->ipp_hopopts != NULL) {
1531 		/* Deleting all existing hop-by-hop options */
1532 		kmem_free(ipp->ipp_hopopts, ipp->ipp_hopoptslen);
1533 		ipp->ipp_hopopts = NULL;
1534 		ipp->ipp_fields &= ~IPPF_HOPOPTS;
1535 	} else if (optlen != *labellen) {
1536 		/* If the label not same size as last time, then reallocate */
1537 		if (newlen > IP6_MAX_OPT_LENGTH)
1538 			return (EHOSTUNREACH);
1539 		newopts = kmem_alloc(newlen, KM_NOSLEEP);
1540 		if (newopts == NULL)
1541 			return (ENOMEM);
1542 		/*
1543 		 * If the user has hop-by-hop stickyoptions set, then copy his
1544 		 * options in after the security label.
1545 		 */
1546 		if (ipp->ipp_hopoptslen > *labellen) {
1547 			bcopy(ipp->ipp_hopopts + *labellen, newopts + optlen,
1548 			    ipp->ipp_hopoptslen - *labellen);
1549 			/*
1550 			 * Stomp out any header gunk here - this was the
1551 			 * previous next-header and option length field.
1552 			 */
1553 			newopts[optlen] = IP6OPT_PADN;
1554 			newopts[optlen + 1] = 0;
1555 		}
1556 		if (ipp->ipp_hopopts != NULL)
1557 			kmem_free(ipp->ipp_hopopts, ipp->ipp_hopoptslen);
1558 		ipp->ipp_hopopts = (ip6_hbh_t *)newopts;
1559 	}
1560 	ipp->ipp_hopoptslen = newlen;
1561 	*labellen = optlen;
1562 
1563 	newopts = (uchar_t *)ipp->ipp_hopopts;
1564 
1565 	/* If there are any options, then fix up reported length */
1566 	if (newlen > 0) {
1567 		newopts[1] = (newlen + 7) / 8 - 1;
1568 		ipp->ipp_fields |= IPPF_HOPOPTS;
1569 	}
1570 
1571 	/* If there's a label, then insert it now */
1572 	if (optlen > 0) {
1573 		/* skip next-header and length fields */
1574 		newopts += 2;
1575 		bcopy(labelopt, newopts, rawlen);
1576 		newopts += rawlen;
1577 		/* make sure padding comes out right */
1578 		optlen -= 2 + rawlen;
1579 		if (optlen == 1) {
1580 			newopts[0] = IP6OPT_PAD1;
1581 		} else if (optlen > 1) {
1582 			newopts[0] = IP6OPT_PADN;
1583 			optlen -=  2;
1584 			newopts[1] = optlen;
1585 			if (optlen > 0)
1586 				bzero(newopts + 2, optlen);
1587 		}
1588 	}
1589 	return (0);
1590 }
1591 
1592 int
1593 tsol_update_options(uchar_t **opts, uint_t *totlen, uint_t *labellen,
1594     const uchar_t *labelopt)
1595 {
1596 	int optlen, newlen;
1597 	uchar_t *newopts;
1598 
1599 	optlen = (labelopt[IPOPT_OLEN] + 3) & ~3;
1600 	newlen = *totlen + optlen - *labellen;
1601 	if (optlen > *labellen) {
1602 		if (newlen > IP_MAX_OPT_LENGTH)
1603 			return (EHOSTUNREACH);
1604 		newopts = (uchar_t *)mi_alloc(newlen, BPRI_HI);
1605 		if (newopts == NULL)
1606 			return (ENOMEM);
1607 		if (*totlen > *labellen) {
1608 			bcopy(*opts + *labellen, newopts + optlen,
1609 			    *totlen - *labellen);
1610 		}
1611 		if (*opts != NULL)
1612 			mi_free((char *)*opts);
1613 		*opts = newopts;
1614 	} else if (optlen < *labellen) {
1615 		if (newlen == 0 && *opts != NULL) {
1616 			mi_free((char *)*opts);
1617 			*opts = NULL;
1618 		}
1619 		if (*totlen > *labellen) {
1620 			ovbcopy(*opts + *labellen, *opts + optlen,
1621 			    *totlen - *labellen);
1622 		}
1623 	}
1624 	*totlen = newlen;
1625 	*labellen = optlen;
1626 	if (optlen > 0) {
1627 		newopts = *opts;
1628 		bcopy(labelopt, newopts, optlen);
1629 		/* check if there are user-supplied options that follow */
1630 		if (optlen < newlen) {
1631 			/* compute amount of embedded alignment needed */
1632 			optlen -= newopts[IPOPT_OLEN];
1633 			newopts += newopts[IPOPT_OLEN];
1634 			while (--optlen >= 0)
1635 				*newopts++ = IPOPT_NOP;
1636 		} else if (optlen != newopts[IPOPT_OLEN]) {
1637 			/*
1638 			 * The label option is the only option and it is
1639 			 * not a multiple of 4 bytes.
1640 			 */
1641 			optlen -= newopts[IPOPT_OLEN];
1642 			newopts += newopts[IPOPT_OLEN];
1643 			while (--optlen >= 0)
1644 				*newopts++ = IPOPT_EOL;
1645 		}
1646 	}
1647 	return (0);
1648 }
1649 
1650 /*
1651  * This does the bulk of the processing for setting IPPROTO_IP {T_,}IP_OPTIONS.
1652  */
1653 boolean_t
1654 tsol_option_set(uchar_t **opts, uint_t *optlen, uint_t labellen,
1655     const uchar_t *useropts, uint_t userlen)
1656 {
1657 	int newlen;
1658 	uchar_t *newopts;
1659 
1660 	newlen = userlen + labellen;
1661 	if (newlen > *optlen) {
1662 		/* need more room */
1663 		newopts = (uchar_t *)mi_alloc(newlen, BPRI_HI);
1664 		if (newopts == NULL)
1665 			return (B_FALSE);
1666 		/*
1667 		 * The supplied *opts can't be NULL in this case,
1668 		 * since there's an existing label.
1669 		 */
1670 		if (labellen > 0)
1671 			bcopy(*opts, newopts, labellen);
1672 		if (*opts != NULL)
1673 			mi_free((char *)*opts);
1674 		*opts = newopts;
1675 	}
1676 
1677 	if (newlen == 0) {
1678 		/* special case -- no remaining IP options at all */
1679 		if (*opts != NULL) {
1680 			mi_free((char *)*opts);
1681 			*opts = NULL;
1682 		}
1683 	} else if (userlen > 0) {
1684 		/* merge in the user's options */
1685 		newopts = *opts;
1686 		if (labellen > 0) {
1687 			int extra = labellen - newopts[IPOPT_OLEN];
1688 
1689 			newopts += newopts[IPOPT_OLEN];
1690 			while (--extra >= 0)
1691 				*newopts++ = IPOPT_NOP;
1692 		}
1693 		bcopy(useropts, newopts, userlen);
1694 	}
1695 
1696 	*optlen = newlen;
1697 	return (B_TRUE);
1698 }
1699