xref: /onnv-gate/usr/src/uts/common/inet/ip/tn_ipopt.c (revision 11457:5a39e22e536d)
11676Sjpk /*
21676Sjpk  * CDDL HEADER START
31676Sjpk  *
41676Sjpk  * The contents of this file are subject to the terms of the
51676Sjpk  * Common Development and Distribution License (the "License").
61676Sjpk  * You may not use this file except in compliance with the License.
71676Sjpk  *
81676Sjpk  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
91676Sjpk  * or http://www.opensolaris.org/os/licensing.
101676Sjpk  * See the License for the specific language governing permissions
111676Sjpk  * and limitations under the License.
121676Sjpk  *
131676Sjpk  * When distributing Covered Code, include this CDDL HEADER in each
141676Sjpk  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
151676Sjpk  * If applicable, add the following below this CDDL HEADER, with the
161676Sjpk  * fields enclosed by brackets "[]" replaced with your own identifying
171676Sjpk  * information: Portions Copyright [yyyy] [name of copyright owner]
181676Sjpk  *
191676Sjpk  * CDDL HEADER END
201676Sjpk  */
211676Sjpk /*
22*11457SErik.Nordmark@Sun.COM  * Copyright 2010 Sun Microsystems, Inc.  All rights reserved.
231676Sjpk  * Use is subject to license terms.
241676Sjpk  */
251676Sjpk 
261676Sjpk #include <sys/types.h>
271676Sjpk #include <sys/systm.h>
281676Sjpk #include <sys/kmem.h>
291676Sjpk #include <sys/disp.h>
301676Sjpk #include <sys/stream.h>
311676Sjpk #include <sys/strsubr.h>
321676Sjpk #include <sys/strsun.h>
331676Sjpk #include <sys/policy.h>
341676Sjpk #include <sys/tsol/label_macro.h>
351676Sjpk #include <sys/tsol/tndb.h>
361676Sjpk #include <sys/tsol/tnet.h>
371676Sjpk #include <inet/ip.h>
381676Sjpk #include <inet/ip6.h>
391676Sjpk #include <inet/tcp.h>
401676Sjpk #include <inet/ipclassifier.h>
411676Sjpk #include <inet/ip_ire.h>
422535Ssangeeta #include <inet/ip_ftable.h>
431676Sjpk 
441676Sjpk /*
451676Sjpk  * This routine takes a sensitivity label as input and creates a CIPSO
461676Sjpk  * option in the specified buffer.  It returns the size of the CIPSO option.
471676Sjpk  * If the sensitivity label is too large for the CIPSO option, then 0
481676Sjpk  * is returned.
491676Sjpk  *
501676Sjpk  * tsol2cipso_tt1 returns 0 for failure and greater than 0 for success
511676Sjpk  * (more accurately, success means a return value between 10 and 40).
521676Sjpk  */
531676Sjpk 
541676Sjpk static int
tsol2cipso_tt1(const bslabel_t * sl,unsigned char * cop,uint32_t doi)551676Sjpk tsol2cipso_tt1(const bslabel_t *sl, unsigned char *cop, uint32_t doi)
561676Sjpk {
571676Sjpk 	struct cipso_tag_type_1 *tt1;
581676Sjpk 	const _bslabel_impl_t *bsl;
591676Sjpk 	const uchar_t *ucp;
601676Sjpk 	int i;
611676Sjpk 
621676Sjpk 	if (doi == 0)
631676Sjpk 		return (0);
641676Sjpk 
651676Sjpk 	/* check for Admin High sensitivity label */
661676Sjpk 	if (blequal(sl, label2bslabel(l_admin_high)))
671676Sjpk 		return (0);
681676Sjpk 
691676Sjpk 	/* check whether classification will fit in one octet */
701676Sjpk 	bsl = (const _bslabel_impl_t *)sl;
711676Sjpk 	if (LCLASS(bsl) & 0xFF00)
721676Sjpk 		return (0);
731676Sjpk 
741676Sjpk 	/*
751676Sjpk 	 * Check whether compartments will fit in 30 octets.
761676Sjpk 	 * Compartments 241 - 256 are not allowed.
771676Sjpk 	 */
781676Sjpk 	if (ntohl(bsl->compartments.c8) & 0x0000FFFF)
791676Sjpk 		return (0);
801676Sjpk 
811676Sjpk 	/*
821676Sjpk 	 * Compute option length and tag length.
831676Sjpk 	 * 'p' points to the last two bytes in the Sensitivity Label's
841676Sjpk 	 * compartments; these cannot be mapped into CIPSO compartments.
851676Sjpk 	 */
861676Sjpk 	ucp = (const uchar_t *)&bsl->compartments.c8 + 2;
871676Sjpk 	while (--ucp >= (const uchar_t *)&bsl->compartments.c1)
881676Sjpk 		if (*ucp != 0)
891676Sjpk 			break;
901676Sjpk 
911676Sjpk 	i =  ucp - (const uchar_t *)&bsl->compartments.c1 + 1;
921676Sjpk 
931676Sjpk 	if (cop == NULL)
941676Sjpk 		return (10 + i);
951676Sjpk 
961676Sjpk 	doi = htonl(doi);
971676Sjpk 	ucp = (const uchar_t *)&doi;
981676Sjpk 	cop[IPOPT_OPTVAL] = IPOPT_COMSEC;
991676Sjpk 	cop[IPOPT_OLEN] = 10 + i;
1001676Sjpk 	cop[IPOPT_OLEN+1] = ucp[0];
1011676Sjpk 	cop[IPOPT_OLEN+2] = ucp[1];
1021676Sjpk 	cop[IPOPT_OLEN+3] = ucp[2];
1031676Sjpk 	cop[IPOPT_OLEN+4] = ucp[3];
1041676Sjpk 	tt1 = (struct cipso_tag_type_1 *)&cop[IPOPT_OLEN + 5];
1051676Sjpk 	tt1->tag_type = 1;
1061676Sjpk 	tt1->tag_align = 0;
1071676Sjpk 	tt1->tag_sl = LCLASS(bsl);
1081676Sjpk 	tt1->tag_length = 4 + i;
1091676Sjpk 
1101676Sjpk 	bcopy(&bsl->compartments.c1, tt1->tag_cat, i);
1111676Sjpk 
1121676Sjpk 	return (cop[IPOPT_OLEN]);
1131676Sjpk }
1141676Sjpk 
1151676Sjpk /*
11610181SKen.Powell@Sun.COM  * The following routine searches for a security label in an IPv4 datagram.
11710181SKen.Powell@Sun.COM  * It returns label_type of:
11810181SKen.Powell@Sun.COM  *    OPT_CIPSO if a CIPSO IP option is found.
11910181SKen.Powell@Sun.COM  *    OPT_NONE if no security label is found.
1201676Sjpk  *
12110181SKen.Powell@Sun.COM  * If OPT_CIPSO, a pointer to the CIPSO IP option will be returned in
12210181SKen.Powell@Sun.COM  * the buffer parameter.
12310181SKen.Powell@Sun.COM  *
12410181SKen.Powell@Sun.COM  * The function will return with B_FALSE if an IP format error
12510181SKen.Powell@Sun.COM  * is encountered.
1261676Sjpk  */
1271676Sjpk 
12810181SKen.Powell@Sun.COM boolean_t
tsol_get_option_v4(mblk_t * mp,tsol_ip_label_t * label_type,uchar_t ** buffer)12910181SKen.Powell@Sun.COM tsol_get_option_v4(mblk_t *mp, tsol_ip_label_t *label_type, uchar_t **buffer)
1301676Sjpk {
1311676Sjpk 	ipha_t	*ipha;
1321676Sjpk 	uchar_t	*opt;
1331676Sjpk 	uint32_t	totallen;
1341676Sjpk 	uint32_t	optval;
1351676Sjpk 	uint32_t	optlen;
1361676Sjpk 
13710181SKen.Powell@Sun.COM 	*label_type = OPT_NONE;
1381676Sjpk 
1391676Sjpk 	/*
1401676Sjpk 	 * Get length (in 4 byte octets) of IP header options.
14110181SKen.Powell@Sun.COM 	 * If header doesn't contain options, then return a label_type
14210181SKen.Powell@Sun.COM 	 * of OPT_NONE.
1431676Sjpk 	 */
14410181SKen.Powell@Sun.COM 	ipha = (ipha_t *)mp->b_rptr;
1451676Sjpk 	totallen = ipha->ipha_version_and_hdr_length -
14610181SKen.Powell@Sun.COM 	    (uint8_t)((IP_VERSION << 4));
14710181SKen.Powell@Sun.COM 	totallen <<= 2;
14810181SKen.Powell@Sun.COM 	if (totallen < IP_SIMPLE_HDR_LENGTH || totallen > MBLKL(mp))
14910181SKen.Powell@Sun.COM 		return (B_FALSE);
15010181SKen.Powell@Sun.COM 	totallen -= IP_SIMPLE_HDR_LENGTH;
1511676Sjpk 	if (totallen == 0)
15210181SKen.Powell@Sun.COM 		return (B_TRUE);
1531676Sjpk 
1541676Sjpk 	/*
1551676Sjpk 	 * Search for CIPSO option.
1561676Sjpk 	 * If no such option is present, then return OPT_NONE.
1571676Sjpk 	 */
1581676Sjpk 	opt = (uchar_t *)&ipha[1];
1591676Sjpk 	while (totallen != 0) {
1601676Sjpk 		switch (optval = opt[IPOPT_OPTVAL]) {
1611676Sjpk 		case IPOPT_EOL:
16210181SKen.Powell@Sun.COM 			return (B_TRUE);
1631676Sjpk 		case IPOPT_NOP:
1641676Sjpk 			optlen = 1;
1651676Sjpk 			break;
1661676Sjpk 		default:
1671676Sjpk 			if (totallen <= IPOPT_OLEN)
16810181SKen.Powell@Sun.COM 				return (B_FALSE);
1691676Sjpk 			optlen = opt[IPOPT_OLEN];
1701676Sjpk 			if (optlen < 2)
17110181SKen.Powell@Sun.COM 				return (B_FALSE);
1721676Sjpk 		}
1731676Sjpk 		if (optlen > totallen)
17410181SKen.Powell@Sun.COM 			return (B_FALSE);
1751676Sjpk 		/*
1761676Sjpk 		 * Copy pointer to option into '*buffer' and
1771676Sjpk 		 * return the option type.
1781676Sjpk 		 */
1791676Sjpk 		switch (optval) {
1801676Sjpk 		case IPOPT_COMSEC:
1811676Sjpk 			if (TSOL_CIPSO_TAG_OFFSET < optlen &&
18210181SKen.Powell@Sun.COM 			    opt[TSOL_CIPSO_TAG_OFFSET] == 1) {
18310181SKen.Powell@Sun.COM 				*label_type = OPT_CIPSO;
18410181SKen.Powell@Sun.COM 				*buffer = opt;
18510181SKen.Powell@Sun.COM 				return (B_TRUE);
18610181SKen.Powell@Sun.COM 			}
18710181SKen.Powell@Sun.COM 			return (B_FALSE);
1881676Sjpk 		}
1891676Sjpk 		totallen -= optlen;
1901676Sjpk 		opt += optlen;
1911676Sjpk 	}
19210181SKen.Powell@Sun.COM 	return (B_TRUE);
19310181SKen.Powell@Sun.COM }
19410181SKen.Powell@Sun.COM 
19510181SKen.Powell@Sun.COM /*
19610181SKen.Powell@Sun.COM  * The following routine searches for a security label in an IPv6 datagram.
19710181SKen.Powell@Sun.COM  * It returns label_type of:
19810181SKen.Powell@Sun.COM  *    OPT_CIPSO if a CIPSO IP option is found.
19910181SKen.Powell@Sun.COM  *    OPT_NONE if no security label is found.
20010181SKen.Powell@Sun.COM  *
20110181SKen.Powell@Sun.COM  * If OPT_CIPSO, a pointer to the IPv4 portion of the CIPSO IP option will
20210181SKen.Powell@Sun.COM  * be returned in the buffer parameter.
20310181SKen.Powell@Sun.COM  *
20410181SKen.Powell@Sun.COM  * The function will return with B_FALSE if an IP format error
20510181SKen.Powell@Sun.COM  * or an unexpected label content error is encountered.
20610181SKen.Powell@Sun.COM  */
20710181SKen.Powell@Sun.COM 
20810181SKen.Powell@Sun.COM boolean_t
tsol_get_option_v6(mblk_t * mp,tsol_ip_label_t * label_type,uchar_t ** buffer)20910181SKen.Powell@Sun.COM tsol_get_option_v6(mblk_t *mp, tsol_ip_label_t *label_type, uchar_t **buffer)
21010181SKen.Powell@Sun.COM {
21110181SKen.Powell@Sun.COM 	uchar_t		*opt_ptr = NULL;
21210181SKen.Powell@Sun.COM 	uchar_t		*after_secopt;
21310181SKen.Powell@Sun.COM 	boolean_t	hbh_needed;
21410181SKen.Powell@Sun.COM 	const uchar_t	*ip6hbh;
21510181SKen.Powell@Sun.COM 	size_t		optlen;
21610181SKen.Powell@Sun.COM 	uint32_t	doi;
21710181SKen.Powell@Sun.COM 	const ip6_t	*ip6h;
21810181SKen.Powell@Sun.COM 
21910181SKen.Powell@Sun.COM 	*label_type = OPT_NONE;
22010181SKen.Powell@Sun.COM 	*buffer = NULL;
22110181SKen.Powell@Sun.COM 	ip6h = (const ip6_t *)mp->b_rptr;
22210181SKen.Powell@Sun.COM 	if (ip6h->ip6_nxt != IPPROTO_HOPOPTS)
22310181SKen.Powell@Sun.COM 		return (B_TRUE);
22410181SKen.Powell@Sun.COM 	ip6hbh = (const uchar_t *)&ip6h[1];
22510181SKen.Powell@Sun.COM 	if (ip6hbh + MIN_EHDR_LEN > mp->b_wptr)
22610181SKen.Powell@Sun.COM 		return (B_FALSE);
22710181SKen.Powell@Sun.COM 	optlen = (ip6hbh[1] + 1) << 3;
22810181SKen.Powell@Sun.COM 	if (ip6hbh + optlen > mp->b_wptr)
22910181SKen.Powell@Sun.COM 		return (B_FALSE);
23010181SKen.Powell@Sun.COM 	if (!tsol_find_secopt_v6(ip6hbh, optlen,
23110181SKen.Powell@Sun.COM 	    &opt_ptr, &after_secopt, &hbh_needed))
23210181SKen.Powell@Sun.COM 		return (B_FALSE);
23310181SKen.Powell@Sun.COM 	/* tsol_find_secopt_v6 guarantees some sanity */
23410181SKen.Powell@Sun.COM 	if (opt_ptr != NULL) {
23510181SKen.Powell@Sun.COM 		/*
23610181SKen.Powell@Sun.COM 		 * IPv6 Option
23710181SKen.Powell@Sun.COM 		 *   opt_ptr[0]: Option type
23810181SKen.Powell@Sun.COM 		 *   opt_ptr[1]: Length of option data in bytes
23910181SKen.Powell@Sun.COM 		 *   opt_ptr[2]: First byte of option data
24010181SKen.Powell@Sun.COM 		 */
24110181SKen.Powell@Sun.COM 		if ((optlen = opt_ptr[1]) < 8)
24210181SKen.Powell@Sun.COM 			return (B_FALSE);
24310181SKen.Powell@Sun.COM 		opt_ptr += 2;
24410181SKen.Powell@Sun.COM 		/*
24510181SKen.Powell@Sun.COM 		 * From "Generalized Labeled Security Option for IPv6" draft
24610181SKen.Powell@Sun.COM 		 *   opt_ptr[0] - opt_ptr[4]: DOI = IP6LS_DOI_V4
24710181SKen.Powell@Sun.COM 		 *   opt_ptr[4]: Tag type = IP6LS_TT_V4
24810181SKen.Powell@Sun.COM 		 *   opt_ptr[5]: Tag length in bytes starting at Tag type field
24910181SKen.Powell@Sun.COM 		 * IPv4 CIPSO Option
25010181SKen.Powell@Sun.COM 		 *   opt_ptr[6]: option type
25110181SKen.Powell@Sun.COM 		 *   opt_ptr[7]: option length in bytes starting at type field
25210181SKen.Powell@Sun.COM 		 */
25310181SKen.Powell@Sun.COM 		bcopy(opt_ptr, &doi, sizeof (doi));
25410181SKen.Powell@Sun.COM 		doi = ntohl(doi);
25510181SKen.Powell@Sun.COM 		if (doi == IP6LS_DOI_V4 &&
25610181SKen.Powell@Sun.COM 		    opt_ptr[4] == IP6LS_TT_V4 &&
25710181SKen.Powell@Sun.COM 		    opt_ptr[5] <= optlen - 4 &&
25810181SKen.Powell@Sun.COM 		    opt_ptr[7] <= optlen - 6 &&
25910181SKen.Powell@Sun.COM 		    opt_ptr[7] <= opt_ptr[5] - 2) {
26010181SKen.Powell@Sun.COM 			opt_ptr += sizeof (doi) + 2;
26110181SKen.Powell@Sun.COM 			*label_type = OPT_CIPSO;
26210181SKen.Powell@Sun.COM 			*buffer = opt_ptr;
26310181SKen.Powell@Sun.COM 			return (B_TRUE);
26410181SKen.Powell@Sun.COM 		}
26510181SKen.Powell@Sun.COM 		return (B_FALSE);
26610181SKen.Powell@Sun.COM 	}
26710181SKen.Powell@Sun.COM 	return (B_TRUE);
2681676Sjpk }
2691676Sjpk 
2701676Sjpk /*
2719710SKen.Powell@Sun.COM  * tsol_check_dest()
2729710SKen.Powell@Sun.COM  *
2739710SKen.Powell@Sun.COM  * This routine verifies if a destination is allowed to recieve messages
27411042SErik.Nordmark@Sun.COM  * based on the security label. If any adjustments to the label are needed
27511042SErik.Nordmark@Sun.COM  * due to the connection's MAC mode or the destination's ability
27611042SErik.Nordmark@Sun.COM  * to receive labels, an "effective label" will be returned.
2779710SKen.Powell@Sun.COM  *
27811042SErik.Nordmark@Sun.COM  * zone_is_global is set if the actual zoneid is global. That is, it is
27911042SErik.Nordmark@Sun.COM  * not set for an exclusive-IP zone.
28011042SErik.Nordmark@Sun.COM  *
28111042SErik.Nordmark@Sun.COM  * On successful return, effective_tsl will point to the new label needed
28211042SErik.Nordmark@Sun.COM  * or will be NULL if a new label isn't needed. On error, effective_tsl will
28311042SErik.Nordmark@Sun.COM  * point to NULL.
2849710SKen.Powell@Sun.COM  *
2859710SKen.Powell@Sun.COM  * Returns:
28611042SErik.Nordmark@Sun.COM  *      0		Label (was|is now) correct
28711042SErik.Nordmark@Sun.COM  *	EHOSTUNREACH	The label failed the remote host accreditation
2889710SKen.Powell@Sun.COM  *      ENOMEM		Memory allocation failure
2899710SKen.Powell@Sun.COM  */
2909710SKen.Powell@Sun.COM int
tsol_check_dest(const ts_label_t * tsl,const void * dst,uchar_t version,uint_t mac_mode,boolean_t zone_is_global,ts_label_t ** effective_tsl)29111042SErik.Nordmark@Sun.COM tsol_check_dest(const ts_label_t *tsl, const void *dst,
29211042SErik.Nordmark@Sun.COM     uchar_t version, uint_t mac_mode, boolean_t zone_is_global,
29311042SErik.Nordmark@Sun.COM     ts_label_t **effective_tsl)
2949710SKen.Powell@Sun.COM {
29511042SErik.Nordmark@Sun.COM 	ts_label_t	*newtsl = NULL;
2969710SKen.Powell@Sun.COM 	tsol_tpc_t	*dst_rhtp;
2979710SKen.Powell@Sun.COM 
29811042SErik.Nordmark@Sun.COM 	if (effective_tsl != NULL)
29911042SErik.Nordmark@Sun.COM 		*effective_tsl = NULL;
3009710SKen.Powell@Sun.COM 	ASSERT(version == IPV4_VERSION ||
3019710SKen.Powell@Sun.COM 	    (version == IPV6_VERSION &&
3029710SKen.Powell@Sun.COM 	    !IN6_IS_ADDR_V4MAPPED((in6_addr_t *)dst)));
3039710SKen.Powell@Sun.COM 
3049710SKen.Powell@Sun.COM 	/* Always pass kernel level communication (NULL label) */
30511042SErik.Nordmark@Sun.COM 	if (tsl == NULL) {
3069710SKen.Powell@Sun.COM 		DTRACE_PROBE2(tx__tnopt__log__info__labeling__mac__allownull,
30711042SErik.Nordmark@Sun.COM 		    char *, "destination ip(1) with null label was passed",
3089710SKen.Powell@Sun.COM 		    ipaddr_t, dst);
3099710SKen.Powell@Sun.COM 		return (0);
3109710SKen.Powell@Sun.COM 	}
3119710SKen.Powell@Sun.COM 
31210934Ssommerfeld@sun.com 	if (tsl->tsl_flags & TSLF_IMPLICIT_IN) {
31310934Ssommerfeld@sun.com 		DTRACE_PROBE3(tx__tnopt__log__info__labeling__unresolved__label,
31410934Ssommerfeld@sun.com 		    char *,
31510934Ssommerfeld@sun.com 		    "implicit-in packet to ip(1) reached tsol_check_dest "
31610934Ssommerfeld@sun.com 		    "with implied security label sl(2)",
31710934Ssommerfeld@sun.com 		    ipaddr_t, dst, ts_label_t *, tsl);
31810934Ssommerfeld@sun.com 	}
31910934Ssommerfeld@sun.com 
3209710SKen.Powell@Sun.COM 	/* Always pass multicast */
3219710SKen.Powell@Sun.COM 	if (version == IPV4_VERSION &&
3229710SKen.Powell@Sun.COM 	    CLASSD(*(ipaddr_t *)dst)) {
3239710SKen.Powell@Sun.COM 		DTRACE_PROBE2(tx__tnopt__log__info__labeling__mac__allowmult,
3249710SKen.Powell@Sun.COM 		    char *, "destination ip(1) with multicast dest was passed",
3259710SKen.Powell@Sun.COM 		    ipaddr_t, dst);
3269710SKen.Powell@Sun.COM 		return (0);
3279710SKen.Powell@Sun.COM 	} else if (version == IPV6_VERSION &&
3289710SKen.Powell@Sun.COM 	    IN6_IS_ADDR_MULTICAST((in6_addr_t *)dst)) {
3299710SKen.Powell@Sun.COM 		DTRACE_PROBE2(tx__tnopt__log__info__labeling__mac__allowmult_v6,
3309710SKen.Powell@Sun.COM 		    char *, "destination ip(1) with multicast dest was passed",
3319710SKen.Powell@Sun.COM 		    in6_addr_t *, dst);
3329710SKen.Powell@Sun.COM 		return (0);
3339710SKen.Powell@Sun.COM 	}
3349710SKen.Powell@Sun.COM 
3359710SKen.Powell@Sun.COM 	/* Never pass an undefined destination */
3369710SKen.Powell@Sun.COM 	if ((dst_rhtp = find_tpc(dst, version, B_FALSE)) == NULL) {
3379710SKen.Powell@Sun.COM 		DTRACE_PROBE2(tx__tnopt__log__info__labeling__lookupdst,
3389710SKen.Powell@Sun.COM 		    char *, "destination ip(1) not in tn database.",
3399710SKen.Powell@Sun.COM 		    void *, dst);
3409710SKen.Powell@Sun.COM 		return (EHOSTUNREACH);
3419710SKen.Powell@Sun.COM 	}
3429710SKen.Powell@Sun.COM 
3439710SKen.Powell@Sun.COM 	switch (dst_rhtp->tpc_tp.host_type) {
3449710SKen.Powell@Sun.COM 	case UNLABELED:
3459710SKen.Powell@Sun.COM 		/*
3469710SKen.Powell@Sun.COM 		 * Can talk to unlabeled hosts if
3479710SKen.Powell@Sun.COM 		 * (1) zone's label matches the default label, or
34810934Ssommerfeld@sun.com 		 * (2) SO_MAC_EXEMPT is on and we
34910934Ssommerfeld@sun.com 		 * dominate the peer's label, or
35010934Ssommerfeld@sun.com 		 * (3) SO_MAC_EXEMPT is on and
35110934Ssommerfeld@sun.com 		 * this is the global zone
3529710SKen.Powell@Sun.COM 		 */
3539710SKen.Powell@Sun.COM 		if (dst_rhtp->tpc_tp.tp_doi != tsl->tsl_doi) {
35410181SKen.Powell@Sun.COM 			DTRACE_PROBE4(tx__tnopt__log__info__labeling__doi,
3559710SKen.Powell@Sun.COM 			    char *, "unlabeled dest ip(1)/tpc(2) doi does "
3569710SKen.Powell@Sun.COM 			    "not match msg label(3) doi.", void *, dst,
3579710SKen.Powell@Sun.COM 			    tsol_tpc_t *, dst_rhtp, ts_label_t *, tsl);
3589710SKen.Powell@Sun.COM 			TPC_RELE(dst_rhtp);
3599710SKen.Powell@Sun.COM 			return (EHOSTUNREACH);
3609710SKen.Powell@Sun.COM 		}
3619710SKen.Powell@Sun.COM 		if (!blequal(&dst_rhtp->tpc_tp.tp_def_label,
3629710SKen.Powell@Sun.COM 		    &tsl->tsl_label)) {
36310934Ssommerfeld@sun.com 			if (mac_mode != CONN_MAC_AWARE ||
36411042SErik.Nordmark@Sun.COM 			    !(zone_is_global ||
3659710SKen.Powell@Sun.COM 			    bldominates(&tsl->tsl_label,
3669710SKen.Powell@Sun.COM 			    &dst_rhtp->tpc_tp.tp_def_label))) {
3679710SKen.Powell@Sun.COM 				DTRACE_PROBE4(
3689710SKen.Powell@Sun.COM 				    tx__tnopt__log__info__labeling__mac,
3699710SKen.Powell@Sun.COM 				    char *, "unlabeled dest ip(1)/tpc(2) does "
3709710SKen.Powell@Sun.COM 				    "not match msg label(3).", void *, dst,
3719710SKen.Powell@Sun.COM 				    tsol_tpc_t *, dst_rhtp, ts_label_t *, tsl);
3729710SKen.Powell@Sun.COM 				TPC_RELE(dst_rhtp);
3739710SKen.Powell@Sun.COM 				return (EHOSTUNREACH);
3749710SKen.Powell@Sun.COM 			}
3759710SKen.Powell@Sun.COM 			/*
3769710SKen.Powell@Sun.COM 			 * This is a downlabel MAC-exempt exchange.
3779710SKen.Powell@Sun.COM 			 * Use the remote destination's default label
3789710SKen.Powell@Sun.COM 			 * as the label of the message data.
3799710SKen.Powell@Sun.COM 			 */
3809710SKen.Powell@Sun.COM 			if ((newtsl = labelalloc(&dst_rhtp->tpc_tp.tp_def_label,
3819710SKen.Powell@Sun.COM 			    dst_rhtp->tpc_tp.tp_doi, KM_NOSLEEP)) == NULL) {
3829710SKen.Powell@Sun.COM 				TPC_RELE(dst_rhtp);
3839710SKen.Powell@Sun.COM 				return (ENOMEM);
3849710SKen.Powell@Sun.COM 			}
3859710SKen.Powell@Sun.COM 			newtsl->tsl_flags |= TSLF_UNLABELED;
3869710SKen.Powell@Sun.COM 
3879710SKen.Powell@Sun.COM 		} else if (!(tsl->tsl_flags & TSLF_UNLABELED)) {
3889710SKen.Powell@Sun.COM 			/*
3899710SKen.Powell@Sun.COM 			 * The security labels are the same but we need
3909710SKen.Powell@Sun.COM 			 * to flag that the remote node is unlabeled.
3919710SKen.Powell@Sun.COM 			 */
3929710SKen.Powell@Sun.COM 			if ((newtsl = labeldup(tsl, KM_NOSLEEP)) == NULL) {
3939710SKen.Powell@Sun.COM 				TPC_RELE(dst_rhtp);
3949710SKen.Powell@Sun.COM 				return (ENOMEM);
3959710SKen.Powell@Sun.COM 			}
3969710SKen.Powell@Sun.COM 			newtsl->tsl_flags |= TSLF_UNLABELED;
3979710SKen.Powell@Sun.COM 		}
3989710SKen.Powell@Sun.COM 		break;
3999710SKen.Powell@Sun.COM 
4009710SKen.Powell@Sun.COM 	case SUN_CIPSO:
4019710SKen.Powell@Sun.COM 		/*
4029710SKen.Powell@Sun.COM 		 * Can talk to labeled hosts if zone's label is within target's
4039710SKen.Powell@Sun.COM 		 * label range or set.
4049710SKen.Powell@Sun.COM 		 */
4059710SKen.Powell@Sun.COM 		if (dst_rhtp->tpc_tp.tp_cipso_doi_cipso != tsl->tsl_doi ||
4069710SKen.Powell@Sun.COM 		    (!_blinrange(&tsl->tsl_label,
4079710SKen.Powell@Sun.COM 		    &dst_rhtp->tpc_tp.tp_sl_range_cipso) &&
4089710SKen.Powell@Sun.COM 		    !blinlset(&tsl->tsl_label,
4099710SKen.Powell@Sun.COM 		    dst_rhtp->tpc_tp.tp_sl_set_cipso))) {
4109710SKen.Powell@Sun.COM 			DTRACE_PROBE4(tx__tnopt__log__info__labeling__mac,
4119710SKen.Powell@Sun.COM 			    char *, "labeled dest ip(1)/tpc(2) does not "
4129710SKen.Powell@Sun.COM 			    "match msg label(3).", void *, dst,
4139710SKen.Powell@Sun.COM 			    tsol_tpc_t *, dst_rhtp, ts_label_t *, tsl);
4149710SKen.Powell@Sun.COM 			TPC_RELE(dst_rhtp);
4159710SKen.Powell@Sun.COM 			return (EHOSTUNREACH);
4169710SKen.Powell@Sun.COM 		}
41710934Ssommerfeld@sun.com 		if ((tsl->tsl_flags & TSLF_UNLABELED) ||
41810934Ssommerfeld@sun.com 		    (mac_mode == CONN_MAC_IMPLICIT)) {
4199710SKen.Powell@Sun.COM 			/*
42010934Ssommerfeld@sun.com 			 * Copy label so we can modify the flags
4219710SKen.Powell@Sun.COM 			 */
4229710SKen.Powell@Sun.COM 			if ((newtsl = labeldup(tsl, KM_NOSLEEP)) == NULL) {
4239710SKen.Powell@Sun.COM 				TPC_RELE(dst_rhtp);
4249710SKen.Powell@Sun.COM 				return (ENOMEM);
4259710SKen.Powell@Sun.COM 			}
42610934Ssommerfeld@sun.com 			/*
42710934Ssommerfeld@sun.com 			 * The security label is a match but we need to
42810934Ssommerfeld@sun.com 			 * clear the unlabeled flag for this remote node.
42910934Ssommerfeld@sun.com 			 */
43010934Ssommerfeld@sun.com 			newtsl->tsl_flags &= ~TSLF_UNLABELED;
43110934Ssommerfeld@sun.com 			if (mac_mode == CONN_MAC_IMPLICIT)
43210934Ssommerfeld@sun.com 				newtsl->tsl_flags |= TSLF_IMPLICIT_OUT;
4339710SKen.Powell@Sun.COM 		}
4349710SKen.Powell@Sun.COM 		break;
4359710SKen.Powell@Sun.COM 
4369710SKen.Powell@Sun.COM 	default:
4379710SKen.Powell@Sun.COM 		TPC_RELE(dst_rhtp);
4389710SKen.Powell@Sun.COM 		return (EHOSTUNREACH);
4399710SKen.Powell@Sun.COM 	}
4409710SKen.Powell@Sun.COM 
4419710SKen.Powell@Sun.COM 	/*
44211042SErik.Nordmark@Sun.COM 	 * Return the new label.
4439710SKen.Powell@Sun.COM 	 */
4449710SKen.Powell@Sun.COM 	if (newtsl != NULL) {
44511042SErik.Nordmark@Sun.COM 		if (effective_tsl != NULL)
44611042SErik.Nordmark@Sun.COM 			*effective_tsl = newtsl;
44711042SErik.Nordmark@Sun.COM 		else
44811042SErik.Nordmark@Sun.COM 			label_rele(newtsl);
4499710SKen.Powell@Sun.COM 	}
4509710SKen.Powell@Sun.COM 	TPC_RELE(dst_rhtp);
4519710SKen.Powell@Sun.COM 	return (0);
4529710SKen.Powell@Sun.COM }
4539710SKen.Powell@Sun.COM 
4549710SKen.Powell@Sun.COM /*
45511042SErik.Nordmark@Sun.COM  * tsol_compute_label_v4()
4561676Sjpk  *
4571676Sjpk  * This routine computes the IP label that should be on a packet based on the
4581676Sjpk  * connection and destination information.
4591676Sjpk  *
46011042SErik.Nordmark@Sun.COM  * The zoneid is the IP zoneid (i.e., GLOBAL_ZONEID for exlusive-IP zones).
46111042SErik.Nordmark@Sun.COM  *
4621676Sjpk  * Returns:
4631676Sjpk  *      0		Fetched label
4649710SKen.Powell@Sun.COM  *	EHOSTUNREACH	No route to destination
4651676Sjpk  *	EINVAL		Label cannot be computed
4661676Sjpk  */
4671676Sjpk int
tsol_compute_label_v4(const ts_label_t * tsl,zoneid_t zoneid,ipaddr_t dst,uchar_t * opt_storage,ip_stack_t * ipst)46811042SErik.Nordmark@Sun.COM tsol_compute_label_v4(const ts_label_t *tsl, zoneid_t zoneid, ipaddr_t dst,
46911042SErik.Nordmark@Sun.COM     uchar_t *opt_storage, ip_stack_t *ipst)
4701676Sjpk {
4711676Sjpk 	uint_t		sec_opt_len;
47211042SErik.Nordmark@Sun.COM 	ire_t		*ire;
47311042SErik.Nordmark@Sun.COM 	tsol_ire_gw_secattr_t *attrp = NULL;
4748778SErik.Nordmark@Sun.COM 
4751676Sjpk 	if (opt_storage != NULL)
4761676Sjpk 		opt_storage[IPOPT_OLEN] = 0;
4771676Sjpk 
47811042SErik.Nordmark@Sun.COM 	if (tsl == NULL)
4791676Sjpk 		return (0);
4801676Sjpk 
4811676Sjpk 	/* always pass multicast */
4821676Sjpk 	if (CLASSD(dst))
4831676Sjpk 		return (0);
4841676Sjpk 
48510934Ssommerfeld@sun.com 	if (tsl->tsl_flags & TSLF_IMPLICIT_OUT)
48610934Ssommerfeld@sun.com 		return (0);
48710934Ssommerfeld@sun.com 
4889710SKen.Powell@Sun.COM 	if (tsl->tsl_flags & TSLF_UNLABELED) {
4899710SKen.Powell@Sun.COM 		/*
4909710SKen.Powell@Sun.COM 		 * The destination is unlabeled. Only add a label if the
4919710SKen.Powell@Sun.COM 		 * destination is not a broadcast/local/loopback address,
4929710SKen.Powell@Sun.COM 		 * the destination is not on the same subnet, and the
4939710SKen.Powell@Sun.COM 		 * next-hop gateway is labeled.
4949710SKen.Powell@Sun.COM 		 */
49511042SErik.Nordmark@Sun.COM 		ire = ire_route_recursive_v4(dst, 0, NULL, zoneid, tsl,
496*11457SErik.Nordmark@Sun.COM 		    MATCH_IRE_SECATTR, IRR_ALLOCATE, 0, ipst, NULL, &attrp,
497*11457SErik.Nordmark@Sun.COM 		    NULL);
49811042SErik.Nordmark@Sun.COM 		ASSERT(ire != NULL);
49911042SErik.Nordmark@Sun.COM 		if (ire->ire_flags & (RTF_REJECT|RTF_BLACKHOLE)) {
50011042SErik.Nordmark@Sun.COM 			/* no route to destination */
50111042SErik.Nordmark@Sun.COM 			ire_refrele(ire);
5029710SKen.Powell@Sun.COM 			DTRACE_PROBE3(
5031676Sjpk 			    tx__tnopt__log__info__labeling__routedst__v4,
5049710SKen.Powell@Sun.COM 			    char *, "No route to unlabeled dest ip(1) with "
50511042SErik.Nordmark@Sun.COM 			    "with label(2).", ipaddr_t, dst, ts_label_t *, tsl);
5069710SKen.Powell@Sun.COM 			return (EHOSTUNREACH);
5071676Sjpk 		}
50811042SErik.Nordmark@Sun.COM 		if (ire->ire_type & (IRE_BROADCAST | IRE_LOCAL | IRE_LOOPBACK |
50911042SErik.Nordmark@Sun.COM 		    IRE_INTERFACE)) {
51011042SErik.Nordmark@Sun.COM 			ire_refrele(ire);
51111042SErik.Nordmark@Sun.COM 			return (0);
51211042SErik.Nordmark@Sun.COM 		}
5131676Sjpk 
5141676Sjpk 		/*
51511042SErik.Nordmark@Sun.COM 		 * ire_route_recursive gives us the first attrp it finds
51611042SErik.Nordmark@Sun.COM 		 * in the recursive lookup.
5171676Sjpk 		 */
5181676Sjpk 		/*
5199710SKen.Powell@Sun.COM 		 * Return now if next hop gateway is unlabeled. There is
5209710SKen.Powell@Sun.COM 		 * no need to generate a CIPSO option for this message.
5211676Sjpk 		 */
5229710SKen.Powell@Sun.COM 		if (attrp == NULL || attrp->igsa_rhc == NULL ||
5239710SKen.Powell@Sun.COM 		    attrp->igsa_rhc->rhc_tpc->tpc_tp.host_type == UNLABELED) {
52411042SErik.Nordmark@Sun.COM 			ire_refrele(ire);
5259710SKen.Powell@Sun.COM 			return (0);
5261676Sjpk 		}
52711042SErik.Nordmark@Sun.COM 		ire_refrele(ire);
5281676Sjpk 	}
5291676Sjpk 
5301676Sjpk 	/* compute the CIPSO option */
5319710SKen.Powell@Sun.COM 	sec_opt_len = tsol2cipso_tt1(&tsl->tsl_label, opt_storage,
5329710SKen.Powell@Sun.COM 	    tsl->tsl_doi);
5331676Sjpk 
5341676Sjpk 	if (sec_opt_len == 0) {
5359710SKen.Powell@Sun.COM 		DTRACE_PROBE3(tx__tnopt__log__error__labeling__lostops__v4,
53611042SErik.Nordmark@Sun.COM 		    char *, "options lack length for dest ip(1) with label(2).",
53711042SErik.Nordmark@Sun.COM 		    ipaddr_t, dst, ts_label_t *, tsl);
5381676Sjpk 		return (EINVAL);
5391676Sjpk 	}
5401676Sjpk 
5411676Sjpk 	return (0);
5421676Sjpk }
5431676Sjpk 
5441676Sjpk /*
5451676Sjpk  * Remove any existing security option (CIPSO) from the given IP
5461676Sjpk  * header, move the 'buflen' bytes back to fill the gap, and return the number
5471676Sjpk  * of bytes removed (as zero or negative number).  Assumes that the headers are
5481676Sjpk  * sane.
54911042SErik.Nordmark@Sun.COM  *
55011042SErik.Nordmark@Sun.COM  * Note that tsol_remove_secopt does not adjust ipha_length but
55111042SErik.Nordmark@Sun.COM  * tsol_remove_secopt_v6 does adjust ip6_plen.
5521676Sjpk  */
5531676Sjpk int
tsol_remove_secopt(ipha_t * ipha,int buflen)5541676Sjpk tsol_remove_secopt(ipha_t *ipha, int buflen)
5551676Sjpk {
5561676Sjpk 	int remlen, olen, oval, delta;
5571676Sjpk 	uchar_t *fptr, *tptr;
5581676Sjpk 	boolean_t noop_keep;
5591676Sjpk 
5601676Sjpk 	remlen = IPH_HDR_LENGTH(ipha) - IP_SIMPLE_HDR_LENGTH;
5611676Sjpk 	fptr = tptr = (uchar_t *)(ipha + 1);
5621676Sjpk 	noop_keep = B_TRUE;
5631676Sjpk 	while (remlen > 0) {
5641676Sjpk 		oval = fptr[IPOPT_OPTVAL];
5651676Sjpk 
5661676Sjpk 		/* terminate on end of list */
5671676Sjpk 		if (oval == IPOPT_EOL)
5681676Sjpk 			break;
5691676Sjpk 
5701676Sjpk 		/*
5711676Sjpk 		 * Delete any no-ops following a deleted option, at least up
5721676Sjpk 		 * to a 4 octet alignment; copy others.
5731676Sjpk 		 */
5741676Sjpk 		if (oval == IPOPT_NOP) {
5751676Sjpk 			if (((fptr - (uchar_t *)ipha) & 3) == 0)
5761676Sjpk 				noop_keep = B_TRUE;
5771676Sjpk 			if (noop_keep)
5781676Sjpk 				*tptr++ = oval;
5791676Sjpk 			fptr++;
5801676Sjpk 			remlen--;
5811676Sjpk 			continue;
5821676Sjpk 		}
5831676Sjpk 
5841676Sjpk 		/* stop on corrupted list; just do nothing. */
5851676Sjpk 		if (remlen < 2)
5861676Sjpk 			return (0);
5871676Sjpk 		olen = fptr[IPOPT_OLEN];
5881676Sjpk 		if (olen < 2 || olen > remlen)
5891676Sjpk 			return (0);
5901676Sjpk 
5911676Sjpk 		/* skip over security options to delete them */
5921676Sjpk 		if (oval == IPOPT_COMSEC || oval == IPOPT_SECURITY) {
5931676Sjpk 			noop_keep = B_FALSE;
5941676Sjpk 			fptr += olen;
5951676Sjpk 			remlen -= olen;
5961676Sjpk 			continue;
5971676Sjpk 		}
5981676Sjpk 
5991676Sjpk 		/* copy the rest */
6001676Sjpk 		noop_keep = B_TRUE;
6011676Sjpk 		if (tptr != fptr)
6021676Sjpk 			ovbcopy(fptr, tptr, olen);
6031676Sjpk 		fptr += olen;
6041676Sjpk 		tptr += olen;
6051676Sjpk 		remlen -= olen;
6061676Sjpk 	}
6071676Sjpk 
6081676Sjpk 	fptr += remlen;
6091676Sjpk 
6101676Sjpk 	/* figure how much padding we'll need for header alignment */
6111676Sjpk 	olen = (tptr - (uchar_t *)ipha) & 3;
6121676Sjpk 	if (olen > 0) {
6131676Sjpk 		olen = 4 - olen;
6141676Sjpk 		/* pad with end-of-list */
6151676Sjpk 		bzero(tptr, olen);
6161676Sjpk 		tptr += olen;
6171676Sjpk 	}
6181676Sjpk 
6191676Sjpk 	/* slide back the headers that follow and update the IP header */
6201676Sjpk 	delta = fptr - tptr;
6211676Sjpk 	if (delta != 0) {
6221676Sjpk 		ovbcopy(fptr, tptr, ((uchar_t *)ipha + buflen) - fptr);
6231676Sjpk 		ipha->ipha_version_and_hdr_length -= delta / 4;
6241676Sjpk 	}
6251676Sjpk 	return (-delta);
6261676Sjpk }
6271676Sjpk 
6281676Sjpk /*
6291676Sjpk  * Insert the option in 'optbuf' into the IP header pointed to by 'ipha', and
6301676Sjpk  * move the data following the IP header (up to buflen) to accomodate the new
6311676Sjpk  * option.  Assumes that up to IP_MAX_OPT_LENGTH bytes are available (in total)
6321676Sjpk  * for IP options.  Returns the number of bytes actually inserted, or -1 if the
6331676Sjpk  * option cannot be inserted.  (Note that negative return values are possible
6341676Sjpk  * when noops must be compressed, and that only -1 indicates error.  Successful
6351676Sjpk  * return value is always evenly divisible by 4, by definition.)
63611042SErik.Nordmark@Sun.COM  *
63711042SErik.Nordmark@Sun.COM  * Note that tsol_prepend_option does not adjust ipha_length but
63811042SErik.Nordmark@Sun.COM  * tsol_prepend_option_v6 does adjust ip6_plen.
6391676Sjpk  */
6401676Sjpk int
tsol_prepend_option(uchar_t * optbuf,ipha_t * ipha,int buflen)6411676Sjpk tsol_prepend_option(uchar_t *optbuf, ipha_t *ipha, int buflen)
6421676Sjpk {
6431676Sjpk 	int remlen, padding, lastpad, totlen;
6441676Sjpk 	int oval, olen;
6451676Sjpk 	int delta;
6461676Sjpk 	uchar_t *optr;
6471676Sjpk 	uchar_t tempopt[IP_MAX_OPT_LENGTH], *toptr;
6481676Sjpk 
6491676Sjpk 	if (optbuf[IPOPT_OPTVAL] == IPOPT_EOL ||
6501676Sjpk 	    optbuf[IPOPT_OPTVAL] == IPOPT_NOP ||
6511676Sjpk 	    optbuf[IPOPT_OLEN] == 0)
6521676Sjpk 		return (0);
6531676Sjpk 
6541676Sjpk 	ASSERT(optbuf[IPOPT_OLEN] >= 2 &&
6551676Sjpk 	    optbuf[IPOPT_OLEN] <= IP_MAX_OPT_LENGTH);
6561676Sjpk 
6571676Sjpk 	/* first find the real (unpadded) length of the existing options */
6581676Sjpk 	remlen = IPH_HDR_LENGTH(ipha) - IP_SIMPLE_HDR_LENGTH;
6591676Sjpk 	padding = totlen = lastpad = 0;
6601676Sjpk 	optr = (uchar_t *)(ipha + 1);
6611676Sjpk 	while (remlen > 0) {
6621676Sjpk 		oval = optr[IPOPT_OPTVAL];
6631676Sjpk 
6641676Sjpk 		/* stop at end of list */
6651676Sjpk 		if (oval == IPOPT_EOL)
6661676Sjpk 			break;
6671676Sjpk 
6681676Sjpk 		/* skip no-ops, noting that length byte isn't present */
6691676Sjpk 		if (oval == IPOPT_NOP) {
6701676Sjpk 			optr++;
6711676Sjpk 			padding++;
6721676Sjpk 			lastpad++;
6731676Sjpk 			totlen++;
6741676Sjpk 			remlen--;
6751676Sjpk 			continue;
6761676Sjpk 		}
6771676Sjpk 
6781676Sjpk 		/* give up on a corrupted list; report failure */
6791676Sjpk 		if (remlen < 2)
6801676Sjpk 			return (-1);
6811676Sjpk 		olen = optr[IPOPT_OLEN];
6821676Sjpk 		if (olen < 2 || olen > remlen)
6831676Sjpk 			return (-1);
6841676Sjpk 
6851676Sjpk 		lastpad = 0;
6861676Sjpk 		optr += olen;
6871676Sjpk 		totlen += olen;
6881676Sjpk 		remlen -= olen;
6891676Sjpk 	}
6901676Sjpk 
6911676Sjpk 	/* completely ignore any trailing padding */
6921676Sjpk 	totlen -= lastpad;
6931676Sjpk 	padding -= lastpad;
6941676Sjpk 
6951676Sjpk 	/*
6961676Sjpk 	 * If some sort of inter-option alignment was present, try to preserve
6971676Sjpk 	 * that alignment.  If alignment pushes us out past the maximum, then
6981676Sjpk 	 * discard it and try to compress to fit.  (We just "assume" that any
6991676Sjpk 	 * padding added was attempting to get 32 bit alignment.  If that's
7001676Sjpk 	 * wrong, that's just too bad.)
7011676Sjpk 	 */
7021676Sjpk 	if (padding > 0) {
7031676Sjpk 		olen = (optbuf[IPOPT_OLEN] + 3) & ~3;
7041676Sjpk 		if (olen + totlen > IP_MAX_OPT_LENGTH) {
7051676Sjpk 			totlen -= padding;
7061676Sjpk 			if (olen + totlen > IP_MAX_OPT_LENGTH)
7071676Sjpk 				return (-1);
7081676Sjpk 			padding = 0;
7091676Sjpk 		}
7101676Sjpk 	}
7111676Sjpk 
7121676Sjpk 	/*
7131676Sjpk 	 * Since we may need to compress or expand the option list, we write to
7141676Sjpk 	 * a temporary buffer and then copy the results back to the IP header.
7151676Sjpk 	 */
7161676Sjpk 	toptr = tempopt;
7171676Sjpk 
7181676Sjpk 	/* compute actual option to insert */
7191676Sjpk 	olen = optbuf[IPOPT_OLEN];
7201676Sjpk 	bcopy(optbuf, toptr, olen);
7211676Sjpk 	toptr += olen;
7221676Sjpk 	if (padding > 0) {
7231676Sjpk 		while ((olen & 3) != 0) {
7241676Sjpk 			*toptr++ = IPOPT_NOP;
7251676Sjpk 			olen++;
7261676Sjpk 		}
7271676Sjpk 	}
7281676Sjpk 
7291676Sjpk 	/* copy over the existing options */
7301676Sjpk 	optr = (uchar_t *)(ipha + 1);
7311676Sjpk 	while (totlen > 0) {
7321676Sjpk 		oval = optr[IPOPT_OPTVAL];
7331676Sjpk 
7341676Sjpk 		/* totlen doesn't include end-of-list marker */
7351676Sjpk 		ASSERT(oval != IPOPT_EOL);
7361676Sjpk 
7371676Sjpk 		/* handle no-ops; copy if desired, ignore otherwise */
7381676Sjpk 		if (oval == IPOPT_NOP) {
7391676Sjpk 			if (padding > 0) {
7401676Sjpk 				/* note: cannot overflow due to checks above */
7411676Sjpk 				ASSERT(toptr < tempopt + IP_MAX_OPT_LENGTH);
7421676Sjpk 				*toptr++ = oval;
7431676Sjpk 			}
7441676Sjpk 			optr++;
7451676Sjpk 			totlen--;
7461676Sjpk 			continue;
7471676Sjpk 		}
7481676Sjpk 
7491676Sjpk 		/* list cannot be corrupt at this point */
7501676Sjpk 		ASSERT(totlen >= 2);
7511676Sjpk 		olen = optr[IPOPT_OLEN];
7521676Sjpk 		ASSERT(olen >= 2 && olen <= totlen);
7531676Sjpk 
7541676Sjpk 		/* cannot run out of room due to tests above */
7551676Sjpk 		ASSERT(toptr + olen <= tempopt + IP_MAX_OPT_LENGTH);
7561676Sjpk 
7571676Sjpk 		bcopy(optr, toptr, olen);
7581676Sjpk 		optr += olen;
7591676Sjpk 		toptr += olen;
7601676Sjpk 		totlen -= olen;
7611676Sjpk 	}
7621676Sjpk 
7631676Sjpk 	/* figure how much padding we'll need for header alignment */
7641676Sjpk 	olen = (toptr - tempopt) & 3;
7651676Sjpk 	if (olen > 0) {
7661676Sjpk 		olen = 4 - olen;
7671676Sjpk 		ASSERT(toptr + olen <= tempopt + IP_MAX_OPT_LENGTH);
7681676Sjpk 		/* pad with end-of-list value */
7691676Sjpk 		bzero(toptr, olen);
7701676Sjpk 		toptr += olen;
7711676Sjpk 	}
7721676Sjpk 
7731676Sjpk 	/* move the headers as needed and update IP header */
7741676Sjpk 	olen = (toptr - tempopt) + IP_SIMPLE_HDR_LENGTH;
7751676Sjpk 	remlen = IPH_HDR_LENGTH(ipha);
7761676Sjpk 	delta = olen - remlen;
7771676Sjpk 	if (delta != 0) {
7781676Sjpk 		ovbcopy((uchar_t *)ipha + remlen, (uchar_t *)ipha + olen,
7791676Sjpk 		    buflen - remlen);
7801676Sjpk 		ipha->ipha_version_and_hdr_length += delta / 4;
7811676Sjpk 	}
7821676Sjpk 
7831676Sjpk 	/* slap in the new options */
7841676Sjpk 	bcopy(tempopt, ipha + 1, olen - IP_SIMPLE_HDR_LENGTH);
7851676Sjpk 
7861676Sjpk 	return (delta);
7871676Sjpk }
7881676Sjpk 
7891676Sjpk /*
79011042SErik.Nordmark@Sun.COM  * tsol_check_label_v4()
7911676Sjpk  *
7921676Sjpk  * This routine computes the IP label that should be on the packet based on the
79311042SErik.Nordmark@Sun.COM  * connection and destination information.  It's called by the IP forwarding
79411042SErik.Nordmark@Sun.COM  * logic and by ip_output_simple. The ULPs generate the labels before calling
79511042SErik.Nordmark@Sun.COM  * conn_ip_output. If any adjustments to
79611042SErik.Nordmark@Sun.COM  * the label are needed due to the connection's MAC-exempt status or
79711042SErik.Nordmark@Sun.COM  * the destination's ability to receive labels, an "effective label"
79811042SErik.Nordmark@Sun.COM  * will be returned.
7991676Sjpk  *
8006596Skp158701  * The packet's header is clear before entering IPsec's engine.
8011676Sjpk  *
80211042SErik.Nordmark@Sun.COM  * The zoneid is the IP zoneid (i.e., GLOBAL_ZONEID for exlusive-IP zones).
80311042SErik.Nordmark@Sun.COM  * zone_is_global is set if the actual zoneid is global.
80411042SErik.Nordmark@Sun.COM  *
80511042SErik.Nordmark@Sun.COM  * On successful return, effective_tslp will point to the new label needed
80611042SErik.Nordmark@Sun.COM  * or will be NULL if a new label isn't needed. On error, effective_tsl will
80711042SErik.Nordmark@Sun.COM  * point to NULL.
80811042SErik.Nordmark@Sun.COM  *
8091676Sjpk  * Returns:
81011077SErik.Nordmark@Sun.COM  *      0		Label (was|is now) correct
8111676Sjpk  *      EACCES		The packet failed the remote host accreditation.
8121676Sjpk  *      ENOMEM		Memory allocation failure.
8131676Sjpk  *	EINVAL		Label cannot be computed
8141676Sjpk  */
8151676Sjpk int
tsol_check_label_v4(const ts_label_t * tsl,zoneid_t zoneid,mblk_t ** mpp,uint_t mac_mode,boolean_t zone_is_global,ip_stack_t * ipst,ts_label_t ** effective_tslp)81611042SErik.Nordmark@Sun.COM tsol_check_label_v4(const ts_label_t *tsl, zoneid_t zoneid, mblk_t **mpp,
81711042SErik.Nordmark@Sun.COM     uint_t mac_mode, boolean_t zone_is_global, ip_stack_t *ipst,
81811042SErik.Nordmark@Sun.COM     ts_label_t **effective_tslp)
8191676Sjpk {
8201676Sjpk 	mblk_t *mp = *mpp;
8211676Sjpk 	ipha_t  *ipha;
82211042SErik.Nordmark@Sun.COM 	ts_label_t *effective_tsl = NULL;
8231676Sjpk 	uchar_t opt_storage[IP_MAX_OPT_LENGTH];
8241676Sjpk 	uint_t hlen;
8251676Sjpk 	uint_t sec_opt_len;
8261676Sjpk 	uchar_t *optr;
8276596Skp158701 	int delta_remove = 0, delta_add, adjust;
8281676Sjpk 	int retv;
8291676Sjpk 
83011042SErik.Nordmark@Sun.COM 	*effective_tslp = NULL;
8311676Sjpk 	opt_storage[IPOPT_OPTVAL] = 0;
8321676Sjpk 
8331676Sjpk 	ipha = (ipha_t *)mp->b_rptr;
8341676Sjpk 
8359710SKen.Powell@Sun.COM 	/*
8369710SKen.Powell@Sun.COM 	 * Verify the destination is allowed to receive packets at
83711042SErik.Nordmark@Sun.COM 	 * the security label of the message data. tsol_check_dest()
83811042SErik.Nordmark@Sun.COM 	 * may create a new effective label or label flags.
8399710SKen.Powell@Sun.COM 	 */
84011042SErik.Nordmark@Sun.COM 	retv = tsol_check_dest(tsl, &ipha->ipha_dst, IPV4_VERSION,
84111042SErik.Nordmark@Sun.COM 	    mac_mode, zone_is_global, &effective_tsl);
8421676Sjpk 	if (retv != 0)
8431676Sjpk 		return (retv);
8441676Sjpk 
8459710SKen.Powell@Sun.COM 	/*
8469710SKen.Powell@Sun.COM 	 * Calculate the security label to be placed in the text
8479710SKen.Powell@Sun.COM 	 * of the message (if any).
8489710SKen.Powell@Sun.COM 	 */
84911042SErik.Nordmark@Sun.COM 	if (effective_tsl != NULL) {
85011042SErik.Nordmark@Sun.COM 		if ((retv = tsol_compute_label_v4(effective_tsl, zoneid,
8519710SKen.Powell@Sun.COM 		    ipha->ipha_dst, opt_storage, ipst)) != 0) {
85211042SErik.Nordmark@Sun.COM 			label_rele(effective_tsl);
8539710SKen.Powell@Sun.COM 			return (retv);
8549710SKen.Powell@Sun.COM 		}
85511042SErik.Nordmark@Sun.COM 		*effective_tslp = effective_tsl;
8569710SKen.Powell@Sun.COM 	} else {
85711042SErik.Nordmark@Sun.COM 		if ((retv = tsol_compute_label_v4(tsl, zoneid,
8589710SKen.Powell@Sun.COM 		    ipha->ipha_dst, opt_storage, ipst)) != 0) {
8599710SKen.Powell@Sun.COM 			return (retv);
8609710SKen.Powell@Sun.COM 		}
8619710SKen.Powell@Sun.COM 	}
8629710SKen.Powell@Sun.COM 
8631676Sjpk 	optr = (uchar_t *)(ipha + 1);
8641676Sjpk 	hlen = IPH_HDR_LENGTH(ipha) - IP_SIMPLE_HDR_LENGTH;
8651676Sjpk 	sec_opt_len = opt_storage[IPOPT_OLEN];
8661676Sjpk 
8671676Sjpk 	if (hlen >= sec_opt_len) {
8681676Sjpk 		/* If no option is supposed to be there, make sure it's not */
8691676Sjpk 		if (sec_opt_len == 0 && hlen > 0 &&
8701676Sjpk 		    optr[IPOPT_OPTVAL] != IPOPT_COMSEC &&
8711676Sjpk 		    optr[IPOPT_OPTVAL] != IPOPT_SECURITY)
8721676Sjpk 			return (0);
8731676Sjpk 		/* if the option is there, it's always first */
8741676Sjpk 		if (sec_opt_len != 0 &&
8751676Sjpk 		    bcmp(opt_storage, optr, sec_opt_len) == 0)
8761676Sjpk 			return (0);
8771676Sjpk 	}
8781676Sjpk 
8791676Sjpk 	/*
8801676Sjpk 	 * If there is an option there, then it must be the wrong one; delete.
8811676Sjpk 	 */
8826596Skp158701 	if (hlen > 0) {
8836596Skp158701 		delta_remove = tsol_remove_secopt(ipha, MBLKL(mp));
8846596Skp158701 		mp->b_wptr += delta_remove;
8856596Skp158701 	}
8861676Sjpk 
8871676Sjpk 	/* Make sure we have room for the worst-case addition */
8881676Sjpk 	hlen = IPH_HDR_LENGTH(ipha) + opt_storage[IPOPT_OLEN];
8891676Sjpk 	hlen = (hlen + 3) & ~3;
8901676Sjpk 	if (hlen > IP_MAX_HDR_LENGTH)
8911676Sjpk 		hlen = IP_MAX_HDR_LENGTH;
8921676Sjpk 	hlen -= IPH_HDR_LENGTH(ipha);
8931676Sjpk 	if (mp->b_wptr + hlen > mp->b_datap->db_lim) {
8941676Sjpk 		int copylen;
8951676Sjpk 		mblk_t *new_mp;
8961676Sjpk 
8971676Sjpk 		/* allocate enough to be meaningful, but not *too* much */
8981676Sjpk 		copylen = MBLKL(mp);
8991676Sjpk 		if (copylen > 256)
9001676Sjpk 			copylen = 256;
9018778SErik.Nordmark@Sun.COM 		new_mp = allocb_tmpl(hlen + copylen +
9028778SErik.Nordmark@Sun.COM 		    (mp->b_rptr - mp->b_datap->db_base), mp);
90311042SErik.Nordmark@Sun.COM 		if (new_mp == NULL) {
90411042SErik.Nordmark@Sun.COM 			if (effective_tsl != NULL) {
90511042SErik.Nordmark@Sun.COM 				label_rele(effective_tsl);
90611042SErik.Nordmark@Sun.COM 				*effective_tslp = NULL;
90711042SErik.Nordmark@Sun.COM 			}
9081676Sjpk 			return (ENOMEM);
90911042SErik.Nordmark@Sun.COM 		}
9101676Sjpk 
9111676Sjpk 		/* keep the bias */
9121676Sjpk 		new_mp->b_rptr += mp->b_rptr - mp->b_datap->db_base;
9131676Sjpk 		new_mp->b_wptr = new_mp->b_rptr + copylen;
9141676Sjpk 		bcopy(mp->b_rptr, new_mp->b_rptr, copylen);
9151676Sjpk 		new_mp->b_cont = mp;
9161676Sjpk 		if ((mp->b_rptr += copylen) >= mp->b_wptr) {
9171676Sjpk 			new_mp->b_cont = mp->b_cont;
9181676Sjpk 			freeb(mp);
9191676Sjpk 		}
9201676Sjpk 		*mpp = mp = new_mp;
9211676Sjpk 		ipha = (ipha_t *)mp->b_rptr;
9221676Sjpk 	}
9231676Sjpk 
9246596Skp158701 	delta_add = tsol_prepend_option(opt_storage, ipha, MBLKL(mp));
9256596Skp158701 	if (delta_add == -1)
9261676Sjpk 		goto param_prob;
9271676Sjpk 
9286596Skp158701 	ASSERT((mp->b_wptr + delta_add) <= DB_LIM(mp));
9296596Skp158701 	mp->b_wptr += delta_add;
9301676Sjpk 
9316596Skp158701 	adjust = delta_remove + delta_add;
9326596Skp158701 	adjust += ntohs(ipha->ipha_length);
9336596Skp158701 	ipha->ipha_length = htons(adjust);
9341676Sjpk 
9351676Sjpk 	return (0);
9361676Sjpk 
9371676Sjpk param_prob:
93811042SErik.Nordmark@Sun.COM 	if (effective_tsl != NULL) {
93911042SErik.Nordmark@Sun.COM 		label_rele(effective_tsl);
94011042SErik.Nordmark@Sun.COM 		*effective_tslp = NULL;
94111042SErik.Nordmark@Sun.COM 	}
9421676Sjpk 	return (EINVAL);
9431676Sjpk }
9441676Sjpk 
9451676Sjpk /*
9461676Sjpk  * IPv6 HopOpt extension header for the label option layout:
9471676Sjpk  *	- One octet giving the type of the 'next extension header'
9481676Sjpk  *	- Header extension length in 8-byte words, not including the
9491676Sjpk  *	  1st 8 bytes, but including any pad bytes at the end.
9501676Sjpk  *	  Eg. A value of 2 means 16 bytes not including the 1st 8 bytes.
9511676Sjpk  *	- Followed by TLV encoded IPv6 label option. Option layout is
9521676Sjpk  *		* One octet, IP6OPT_LS
9531676Sjpk  *		* One octet option length in bytes of the option data following
9541676Sjpk  *		  the length, but not including any pad bytes at the end.
9551676Sjpk  *		* Four-octet DOI (IP6LS_DOI_V4)
9561676Sjpk  *		* One octet suboption, IP6LS_TT_V4
9571676Sjpk  *		* One octet suboption length in bytes of the suboption
9581676Sjpk  *		  following the suboption length, including the suboption
9591676Sjpk  *		  header length, but not including any pad bytes at the end.
9601676Sjpk  *	- Pad to make the extension header a multiple of 8 bytes.
9611676Sjpk  *
9621676Sjpk  * This function returns the contents of 'IPv6 option structure' in the above.
9631676Sjpk  * i.e starting from the IP6OPT_LS but not including the pad at the end.
9641676Sjpk  * The user must prepend two octets (either padding or next header / length)
9651676Sjpk  * and append padding out to the next 8 octet boundary.
96611042SErik.Nordmark@Sun.COM  *
96711042SErik.Nordmark@Sun.COM  * The zoneid is the IP zoneid (i.e., GLOBAL_ZONEID for exlusive-IP zones).
9681676Sjpk  */
9691676Sjpk int
tsol_compute_label_v6(const ts_label_t * tsl,zoneid_t zoneid,const in6_addr_t * dst,uchar_t * opt_storage,ip_stack_t * ipst)97011042SErik.Nordmark@Sun.COM tsol_compute_label_v6(const ts_label_t *tsl, zoneid_t zoneid,
97111042SErik.Nordmark@Sun.COM     const in6_addr_t *dst, uchar_t *opt_storage, ip_stack_t *ipst)
9721676Sjpk {
9731676Sjpk 	uint_t		sec_opt_len;
9741676Sjpk 	uint32_t	doi;
97511042SErik.Nordmark@Sun.COM 	ire_t		*ire;
97611042SErik.Nordmark@Sun.COM 	tsol_ire_gw_secattr_t *attrp = NULL;
9778778SErik.Nordmark@Sun.COM 
9781676Sjpk 	if (ip6opt_ls == 0)
9791676Sjpk 		return (EINVAL);
9801676Sjpk 
9811676Sjpk 	if (opt_storage != NULL)
9821676Sjpk 		opt_storage[IPOPT_OLEN] = 0;
9831676Sjpk 
98411042SErik.Nordmark@Sun.COM 	if (tsl == NULL)
9851676Sjpk 		return (0);
9861676Sjpk 
9871676Sjpk 	/* Always pass multicast */
9881676Sjpk 	if (IN6_IS_ADDR_MULTICAST(dst))
9891676Sjpk 		return (0);
9901676Sjpk 
9911676Sjpk 	/*
9921676Sjpk 	 * Fill in a V6 label.  If a new format is added here, make certain
9931676Sjpk 	 * that the maximum size of this label is reflected in sys/tsol/tnet.h
9941676Sjpk 	 * as TSOL_MAX_IPV6_OPTION.
9951676Sjpk 	 */
99610934Ssommerfeld@sun.com 	if (tsl->tsl_flags & TSLF_IMPLICIT_OUT)
99710934Ssommerfeld@sun.com 		return (0);
99810934Ssommerfeld@sun.com 
9999710SKen.Powell@Sun.COM 	if (tsl->tsl_flags & TSLF_UNLABELED) {
10001676Sjpk 		/*
10019710SKen.Powell@Sun.COM 		 * The destination is unlabeled. Only add a label if the
100211042SErik.Nordmark@Sun.COM 		 * destination is not a broadcast/local/loopback address,
10039710SKen.Powell@Sun.COM 		 * the destination is not on the same subnet, and the
10049710SKen.Powell@Sun.COM 		 * next-hop gateway is labeled.
10051676Sjpk 		 */
100611042SErik.Nordmark@Sun.COM 		ire = ire_route_recursive_v6(dst, 0, NULL, zoneid, tsl,
1007*11457SErik.Nordmark@Sun.COM 		    MATCH_IRE_SECATTR, IRR_ALLOCATE, 0, ipst, NULL, &attrp,
1008*11457SErik.Nordmark@Sun.COM 		    NULL);
100911042SErik.Nordmark@Sun.COM 		ASSERT(ire != NULL);
101011042SErik.Nordmark@Sun.COM 		if (ire->ire_flags & (RTF_REJECT|RTF_BLACKHOLE)) {
101111042SErik.Nordmark@Sun.COM 			/* no route to destination */
101211042SErik.Nordmark@Sun.COM 			ire_refrele(ire);
10139710SKen.Powell@Sun.COM 			DTRACE_PROBE3(
10141676Sjpk 			    tx__tnopt__log__info__labeling__routedst__v6,
10159710SKen.Powell@Sun.COM 			    char *, "No route to unlabeled dest ip6(1) with "
101611042SErik.Nordmark@Sun.COM 			    "label(2).", in6_addr_t *, dst, ts_label_t *, tsl);
10179710SKen.Powell@Sun.COM 			return (EHOSTUNREACH);
10181676Sjpk 		}
101911042SErik.Nordmark@Sun.COM 		if (ire->ire_type & (IRE_LOCAL | IRE_LOOPBACK |
102011042SErik.Nordmark@Sun.COM 		    IRE_INTERFACE)) {
102111042SErik.Nordmark@Sun.COM 			ire_refrele(ire);
102211042SErik.Nordmark@Sun.COM 			return (0);
102311042SErik.Nordmark@Sun.COM 		}
10241676Sjpk 		/*
102511042SErik.Nordmark@Sun.COM 		 * ire_route_recursive gives us the first attrp it finds
102611042SErik.Nordmark@Sun.COM 		 * in the recursive lookup.
10271676Sjpk 		 */
10289710SKen.Powell@Sun.COM 		/*
10299710SKen.Powell@Sun.COM 		 * Return now if next hop gateway is unlabeled. There is
10309710SKen.Powell@Sun.COM 		 * no need to generate a CIPSO option for this message.
10319710SKen.Powell@Sun.COM 		 */
10329710SKen.Powell@Sun.COM 		if (attrp == NULL || attrp->igsa_rhc == NULL ||
10339710SKen.Powell@Sun.COM 		    attrp->igsa_rhc->rhc_tpc->tpc_tp.host_type == UNLABELED) {
103411042SErik.Nordmark@Sun.COM 			ire_refrele(ire);
10359710SKen.Powell@Sun.COM 			return (0);
10361676Sjpk 		}
103711042SErik.Nordmark@Sun.COM 		ire_refrele(ire);
10381676Sjpk 	}
10391676Sjpk 
10401676Sjpk 	/* compute the CIPSO option */
10411676Sjpk 	if (opt_storage != NULL)
10421676Sjpk 		opt_storage += 8;
10439710SKen.Powell@Sun.COM 	sec_opt_len = tsol2cipso_tt1(&tsl->tsl_label, opt_storage,
10449710SKen.Powell@Sun.COM 	    tsl->tsl_doi);
10451676Sjpk 
10461676Sjpk 	if (sec_opt_len == 0) {
10479710SKen.Powell@Sun.COM 		DTRACE_PROBE3(tx__tnopt__log__error__labeling__lostops__v6,
10489710SKen.Powell@Sun.COM 		    char *, "options lack length for dest ip6(1) with "
104911042SErik.Nordmark@Sun.COM 		    "label(2).", in6_addr_t *, dst, ts_label_t *, tsl);
10501676Sjpk 		return (EINVAL);
10511676Sjpk 	}
10521676Sjpk 
10531676Sjpk 	if (opt_storage == NULL)
10541676Sjpk 		return (0);
10551676Sjpk 
10561676Sjpk 	if (sec_opt_len < IP_MAX_OPT_LENGTH)
10571676Sjpk 		opt_storage[sec_opt_len] = IPOPT_EOL;
10581676Sjpk 
10591676Sjpk 	/*
10601676Sjpk 	 * Just in case the option length is odd, round it up to the next even
10611676Sjpk 	 * multiple.  The IPv6 option definition doesn't like odd numbers for
10621676Sjpk 	 * some reason.
10631676Sjpk 	 *
10641676Sjpk 	 * Length in the overall option header (IP6OPT_LS) does not include the
10651676Sjpk 	 * option header itself, but the length in the suboption does include
10661676Sjpk 	 * the suboption header.  Thus, when there's just one suboption, the
10671676Sjpk 	 * length in the option header is the suboption length plus 4 (for the
10681676Sjpk 	 * DOI value).
10691676Sjpk 	 */
10701676Sjpk 	opt_storage[-2] = IP6LS_TT_V4;
10711676Sjpk 	opt_storage[-1] = (sec_opt_len + 2 + 1) & ~1;
10721676Sjpk 	opt_storage[-8] = ip6opt_ls;
10731676Sjpk 	opt_storage[-7] = opt_storage[-1] + 4;
10741676Sjpk 	doi = htons(IP6LS_DOI_V4);
10751676Sjpk 	bcopy(&doi, opt_storage - 6, 4);
10761676Sjpk 
10771676Sjpk 	return (0);
10781676Sjpk }
10791676Sjpk 
10801676Sjpk /*
10811676Sjpk  * Locate the start of the IP6OPT_LS label option and return it.
10821676Sjpk  * Also return the start of the next non-pad option in after_secoptp.
10831676Sjpk  * Usually the label option is the first option at least when packets
10841676Sjpk  * are generated, but for generality we don't assume that on received packets.
108510181SKen.Powell@Sun.COM  *
108610181SKen.Powell@Sun.COM  * The function will return with B_FALSE if an IP format error
108710181SKen.Powell@Sun.COM  * or an unexpected label content error is encountered.
10881676Sjpk  */
108910181SKen.Powell@Sun.COM boolean_t
tsol_find_secopt_v6(const uchar_t * ip6hbh,uint_t hbhlen,uchar_t ** secoptp,uchar_t ** after_secoptp,boolean_t * hbh_needed)10901676Sjpk tsol_find_secopt_v6(
10911676Sjpk     const uchar_t *ip6hbh,	/* Start of the hop-by-hop extension header */
10921676Sjpk     uint_t hbhlen,		/* Length of the hop-by-hop extension header */
109310181SKen.Powell@Sun.COM     uchar_t **secoptp,		/* Location of IP6OPT_LS label option */
10941676Sjpk     uchar_t **after_secoptp,	/* Non-pad option following the label option */
10951676Sjpk     boolean_t *hbh_needed)	/* Is hop-by-hop hdr needed w/o label */
10961676Sjpk {
10971676Sjpk 	uint_t	optlen;
10981676Sjpk 	uint_t	optused;
10991676Sjpk 	const uchar_t *optptr;
11001676Sjpk 	uchar_t	opt_type;
11011676Sjpk 
110210181SKen.Powell@Sun.COM 	*secoptp = NULL;
11031676Sjpk 	*hbh_needed = B_FALSE;
11041676Sjpk 	*after_secoptp = NULL;
11051676Sjpk 	optlen = hbhlen - 2;
11061676Sjpk 	optptr = ip6hbh + 2;
11071676Sjpk 	while (optlen != 0) {
11081676Sjpk 		opt_type = *optptr;
11091676Sjpk 		if (opt_type == IP6OPT_PAD1) {
11101676Sjpk 			optptr++;
11111676Sjpk 			optlen--;
11121676Sjpk 			continue;
11131676Sjpk 		}
11141676Sjpk 		if (optlen == 1)
111510181SKen.Powell@Sun.COM 			return (B_FALSE);
11161676Sjpk 		optused = 2 + optptr[1];
11171676Sjpk 		if (optused > optlen)
111810181SKen.Powell@Sun.COM 			return (B_FALSE);
11191676Sjpk 		/*
11201676Sjpk 		 * if we get here, ip6opt_ls can
11211676Sjpk 		 * not be 0 because it will always
11221676Sjpk 		 * match the IP6OPT_PAD1 above.
11231676Sjpk 		 * Therefore ip6opt_ls == 0 forces
11241676Sjpk 		 * this test to always fail here.
11251676Sjpk 		 */
112610181SKen.Powell@Sun.COM 		if (opt_type == ip6opt_ls) {
112710181SKen.Powell@Sun.COM 			if (*secoptp != NULL)
112810181SKen.Powell@Sun.COM 				/* More than one security option found */
112910181SKen.Powell@Sun.COM 				return (B_FALSE);
113010181SKen.Powell@Sun.COM 			*secoptp = (uchar_t *)optptr;
113110181SKen.Powell@Sun.COM 		} else switch (opt_type) {
11321676Sjpk 		case IP6OPT_PADN:
11331676Sjpk 			break;
11341676Sjpk 		default:
11351676Sjpk 			/*
11361676Sjpk 			 * There is at least 1 option other than
11371676Sjpk 			 * the label option. So the hop-by-hop header is needed
11381676Sjpk 			 */
11391676Sjpk 			*hbh_needed = B_TRUE;
114010181SKen.Powell@Sun.COM 			if (*secoptp != NULL) {
11411676Sjpk 				*after_secoptp = (uchar_t *)optptr;
114210181SKen.Powell@Sun.COM 				return (B_TRUE);
11431676Sjpk 			}
11441676Sjpk 			break;
11451676Sjpk 		}
11461676Sjpk 		optlen -= optused;
11471676Sjpk 		optptr += optused;
11481676Sjpk 	}
114910181SKen.Powell@Sun.COM 	return (B_TRUE);
11501676Sjpk }
11511676Sjpk 
11521676Sjpk /*
11531676Sjpk  * Remove the label option from the hop-by-hop options header if it exists.
11541676Sjpk  * 'buflen' is the total length of the packet typically b_wptr - b_rptr.
11551676Sjpk  * Header and data following the label option that is deleted are copied
11564564Swy83408  * (i.e. slid backward) to the right position, and returns the number
11574564Swy83408  * of bytes removed (as zero or negative number.)
115811042SErik.Nordmark@Sun.COM  *
115911042SErik.Nordmark@Sun.COM  * Note that tsol_remove_secopt does not adjust ipha_length but
116011042SErik.Nordmark@Sun.COM  * tsol_remove_secopt_v6 does adjust ip6_plen.
11611676Sjpk  */
11621676Sjpk int
tsol_remove_secopt_v6(ip6_t * ip6h,int buflen)11631676Sjpk tsol_remove_secopt_v6(ip6_t *ip6h, int buflen)
11641676Sjpk {
11651676Sjpk 	uchar_t	*ip6hbh;	/* hop-by-hop header */
11661676Sjpk 	uint_t	hbhlen;		/* hop-by-hop extension header length */
11671676Sjpk 	uchar_t *secopt = NULL;
11681676Sjpk 	uchar_t *after_secopt;
11691676Sjpk 	uint_t	pad;
11701676Sjpk 	uint_t	delta;
11711676Sjpk 	boolean_t hbh_needed;
11721676Sjpk 
11731676Sjpk 	/*
11741676Sjpk 	 * hop-by-hop extension header must appear first, if it does not
11751676Sjpk 	 * exist, there is no label option.
11761676Sjpk 	 */
11771676Sjpk 	if (ip6h->ip6_nxt != IPPROTO_HOPOPTS)
11781676Sjpk 		return (0);
11791676Sjpk 
11801676Sjpk 	ip6hbh = (uchar_t *)&ip6h[1];
11811676Sjpk 	hbhlen = (ip6hbh[1] + 1) << 3;
11821676Sjpk 	/*
11831676Sjpk 	 * Locate the start of the label option if it exists and the end
11841676Sjpk 	 * of the label option including pads if any.
11851676Sjpk 	 */
118610181SKen.Powell@Sun.COM 	if (!tsol_find_secopt_v6(ip6hbh, hbhlen, &secopt, &after_secopt,
118710181SKen.Powell@Sun.COM 	    &hbh_needed)) {
118810181SKen.Powell@Sun.COM 		/*
118910181SKen.Powell@Sun.COM 		 * This function should not see invalid messages.
119010181SKen.Powell@Sun.COM 		 * If one occurs, it would indicate either an
119110181SKen.Powell@Sun.COM 		 * option previously verified in the forwarding
119210181SKen.Powell@Sun.COM 		 * path has been corrupted or an option was
119310181SKen.Powell@Sun.COM 		 * incorrectly generated locally.
119410181SKen.Powell@Sun.COM 		 */
119510181SKen.Powell@Sun.COM 		ASSERT(0);
119610181SKen.Powell@Sun.COM 		return (0);
119710181SKen.Powell@Sun.COM 	}
11981676Sjpk 	if (secopt == NULL)
11991676Sjpk 		return (0);
12001676Sjpk 	if (!hbh_needed) {
12011676Sjpk 		uchar_t	next_hdr;
12021676Sjpk 		/*
12031676Sjpk 		 * The label option was the only option in the hop-by-hop
12041676Sjpk 		 * header. We don't need the hop-by-hop header itself any
12051676Sjpk 		 * longer.
12061676Sjpk 		 */
12071676Sjpk 		next_hdr = ip6hbh[0];
12081676Sjpk 		ovbcopy(ip6hbh + hbhlen, ip6hbh,
12091676Sjpk 		    buflen - (IPV6_HDR_LEN + hbhlen));
12102776Skp158701 		ip6h->ip6_plen = htons(ntohs(ip6h->ip6_plen) - hbhlen);
12111676Sjpk 		ip6h->ip6_nxt = next_hdr;
12124564Swy83408 		return (-hbhlen);
12131676Sjpk 	}
12141676Sjpk 
12151676Sjpk 	if (after_secopt == NULL) {
12161676Sjpk 		/* There is no option following the label option */
12171676Sjpk 		after_secopt = ip6hbh + hbhlen;
12181676Sjpk 	}
12191676Sjpk 
12201676Sjpk 	/*
12211676Sjpk 	 * After deleting the label option, we need to slide the headers
12221676Sjpk 	 * and data back, while still maintaining the same alignment (module 8)
12231676Sjpk 	 * for the other options. So we slide the headers and data back only
12241676Sjpk 	 * by an integral multiple of 8 bytes, and fill the remaining bytes
12251676Sjpk 	 * with pads.
12261676Sjpk 	 */
12271676Sjpk 	delta = after_secopt - secopt;
12281676Sjpk 	pad = delta % 8;
12291676Sjpk 	if (pad == 1) {
12301676Sjpk 		secopt[0] = IP6OPT_PAD1;
12311676Sjpk 	} else if (pad > 1) {
12321676Sjpk 		secopt[0] = IP6OPT_PADN;
12331676Sjpk 		secopt[1] = pad - 2;
12341676Sjpk 		if (pad > 2)
12351676Sjpk 			bzero(&secopt[2], pad - 2);
12361676Sjpk 	}
12371676Sjpk 	secopt += pad;
12381676Sjpk 	delta -= pad;
12391676Sjpk 	ovbcopy(after_secopt, secopt,
12401676Sjpk 	    (uchar_t *)ip6h + buflen - after_secopt);
12411676Sjpk 	ip6hbh[1] -= delta/8;
12422776Skp158701 	ip6h->ip6_plen = htons(ntohs(ip6h->ip6_plen) - delta);
12431676Sjpk 
12444564Swy83408 	return (-delta);
12451676Sjpk }
12461676Sjpk 
12471676Sjpk /*
12481676Sjpk  * 'optbuf' contains a CIPSO label embedded in an IPv6 hop-by-hop option,
12491676Sjpk  * starting with the IP6OPT_LS option type. The format of this hop-by-hop
12501676Sjpk  * option is described in the block comment above tsol_compute_label_v6.
12511676Sjpk  * This function prepends this hop-by-hop option before any other hop-by-hop
12521676Sjpk  * options in the hop-by-hop header if one already exists, else a new
12531676Sjpk  * hop-by-hop header is created and stuffed into the packet following
12541676Sjpk  * the IPv6 header. 'buflen' is the total length of the packet i.e.
12551676Sjpk  * b_wptr - b_rptr. The caller ensures that there is enough space for the
12561676Sjpk  * extra option being added. Header and data following the position where
12571676Sjpk  * the label option is inserted are copied (i.e. slid forward) to the right
12581676Sjpk  * position.
125911042SErik.Nordmark@Sun.COM  *
126011042SErik.Nordmark@Sun.COM  * Note that tsol_prepend_option does not adjust ipha_length but
126111042SErik.Nordmark@Sun.COM  * tsol_prepend_option_v6 does adjust ip6_plen.
12621676Sjpk  */
12631676Sjpk int
tsol_prepend_option_v6(uchar_t * optbuf,ip6_t * ip6h,int buflen)12641676Sjpk tsol_prepend_option_v6(uchar_t *optbuf, ip6_t *ip6h, int buflen)
12651676Sjpk {
12661676Sjpk 	/*
12671676Sjpk 	 * rawlen is the length of the label option in bytes, not including
12681676Sjpk 	 * any pads, starting from the IP6OPT_LS (option type) byte.
12691676Sjpk 	 */
12701676Sjpk 	uint_t	rawlen;
12711676Sjpk 
12721676Sjpk 	uint_t	optlen;		/* rawlen rounded to an 8 byte multiple */
12731676Sjpk 	uchar_t	*ip6hbh;	/* start of the hop-by-hop extension header */
12741676Sjpk 	uint_t	hbhlen;		/* Length of the hop-by-hop extension header */
12751676Sjpk 	uint_t	pad_len;
12761676Sjpk 	uchar_t	*pad_position;
12771676Sjpk 	int	delta;		/* Actual number of bytes inserted */
12781676Sjpk 
12791676Sjpk 	rawlen = optbuf[1] + 2;	/* Add 2 for the option type, option length */
12801676Sjpk 	ip6hbh = (uchar_t *)&ip6h[1];
12811676Sjpk 	if (ip6h->ip6_nxt == IPPROTO_HOPOPTS) {
12821676Sjpk 		/*
12831676Sjpk 		 * There is a hop-by-hop header present already. In order to
12841676Sjpk 		 * preserve the alignment of the other options at the existing
12851676Sjpk 		 * value (modulo 8) we need to pad the label option to a
12861676Sjpk 		 * multiple of 8 bytes before prepending it to the other
12871676Sjpk 		 * options. Slide the extension headers and data forward to
12881676Sjpk 		 * accomodate the label option at the start of the hop-by-hop
12891676Sjpk 		 * header
12901676Sjpk 		 */
12911676Sjpk 		delta = optlen = (rawlen + 7) & ~7;
12921676Sjpk 		pad_len = optlen - rawlen;
12931676Sjpk 		pad_position = ip6hbh + 2 + rawlen;
12941676Sjpk 		ovbcopy(ip6hbh + 2, ip6hbh + 2 + optlen,
12951676Sjpk 		    buflen - (IPV6_HDR_LEN + 2));
12961676Sjpk 		/*
12971676Sjpk 		 * Bump up the hop-by-hop extension header length by
12981676Sjpk 		 * the number of 8-byte words added
12991676Sjpk 		 */
13001676Sjpk 		optlen >>= 3;
13011676Sjpk 		if (ip6hbh[1] + optlen > 255)
13021676Sjpk 			return (-1);
13031676Sjpk 		ip6hbh[1] += optlen;
13041676Sjpk 	} else {
13051676Sjpk 		/*
13061676Sjpk 		 * There is no hop-by-hop header in the packet. Construct a
13071676Sjpk 		 * new Hop-by-hop extension header (a multiple of 8 bytes).
13081676Sjpk 		 * Slide any other extension headers and data forward to
13091676Sjpk 		 * accomodate this hop-by-hop header
13101676Sjpk 		 */
13111676Sjpk 		delta = hbhlen = (2 + rawlen + 7) & ~7; /* +2 for nxthdr, len */
13121676Sjpk 		pad_len = hbhlen - (2 + rawlen);
13131676Sjpk 		pad_position = ip6hbh + 2 + rawlen;
13141676Sjpk 		ovbcopy(ip6hbh, ip6hbh + hbhlen, buflen - IPV6_HDR_LEN);
13151676Sjpk 		ip6hbh[0] = ip6h->ip6_nxt;
13161676Sjpk 		/*
13171676Sjpk 		 * hop-by-hop extension header length in 8-byte words, not
13181676Sjpk 		 * including the 1st 8 bytes of the hop-by-hop header.
13191676Sjpk 		 */
13201676Sjpk 		ip6hbh[1] = (hbhlen >> 3) - 1;
13211676Sjpk 		ip6h->ip6_nxt = IPPROTO_HOPOPTS;
13221676Sjpk 	}
13231676Sjpk 	/*
13241676Sjpk 	 * Copy the label option into the hop-by-hop header and insert any
13251676Sjpk 	 * needed pads
13261676Sjpk 	 */
13271676Sjpk 	bcopy(optbuf, ip6hbh + 2, rawlen);
13281676Sjpk 	if (pad_len == 1) {
13291676Sjpk 		pad_position[0] = IP6OPT_PAD1;
13301676Sjpk 	} else if (pad_len > 1) {
13311676Sjpk 		pad_position[0] = IP6OPT_PADN;
13321676Sjpk 		pad_position[1] = pad_len - 2;
13331676Sjpk 		if (pad_len > 2)
13341676Sjpk 			bzero(pad_position + 2, pad_len - 2);
13351676Sjpk 	}
13362776Skp158701 	ip6h->ip6_plen = htons(ntohs(ip6h->ip6_plen) + delta);
13371676Sjpk 	return (delta);
13381676Sjpk }
13391676Sjpk 
13401676Sjpk /*
13411676Sjpk  * tsol_check_label_v6()
13421676Sjpk  *
13431676Sjpk  * This routine computes the IP label that should be on the packet based on the
134411042SErik.Nordmark@Sun.COM  * connection and destination information.  It's called by the IP forwarding
134511042SErik.Nordmark@Sun.COM  * logic and by ip_output_simple. The ULPs generate the labels before calling
134611042SErik.Nordmark@Sun.COM  * conn_ip_output. If any adjustments to
134711042SErik.Nordmark@Sun.COM  * the label are needed due to the connection's MAC-exempt status or
134811042SErik.Nordmark@Sun.COM  * the destination's ability to receive labels, an "effective label"
134911042SErik.Nordmark@Sun.COM  * will be returned.
135011042SErik.Nordmark@Sun.COM  *
135111042SErik.Nordmark@Sun.COM  * The packet's header is clear before entering IPsec's engine.
135211042SErik.Nordmark@Sun.COM  *
135311042SErik.Nordmark@Sun.COM  * The zoneid is the IP zoneid (i.e., GLOBAL_ZONEID for exlusive-IP zones).
135411042SErik.Nordmark@Sun.COM  * zone_is_global is set if the actual zoneid is global.
135511042SErik.Nordmark@Sun.COM  *
135611042SErik.Nordmark@Sun.COM  * On successful return, effective_tslp will point to the new label needed
135711042SErik.Nordmark@Sun.COM  * or will be NULL if a new label isn't needed. On error, effective_tsl will
135811042SErik.Nordmark@Sun.COM  * point to NULL.
13591676Sjpk  *
13601676Sjpk  * Returns:
136111077SErik.Nordmark@Sun.COM  *      0		Label (was|is now) correct
13629710SKen.Powell@Sun.COM  *      EACCES		The packet failed the remote host accreditation.
13631676Sjpk  *      ENOMEM		Memory allocation failure.
136411042SErik.Nordmark@Sun.COM  *	EINVAL		Label cannot be computed
13651676Sjpk  */
13661676Sjpk int
tsol_check_label_v6(const ts_label_t * tsl,zoneid_t zoneid,mblk_t ** mpp,uint_t mac_mode,boolean_t zone_is_global,ip_stack_t * ipst,ts_label_t ** effective_tslp)136711042SErik.Nordmark@Sun.COM tsol_check_label_v6(const ts_label_t *tsl, zoneid_t zoneid, mblk_t **mpp,
136811042SErik.Nordmark@Sun.COM     uint_t mac_mode, boolean_t zone_is_global, ip_stack_t *ipst,
136911042SErik.Nordmark@Sun.COM     ts_label_t **effective_tslp)
13701676Sjpk {
13711676Sjpk 	mblk_t *mp = *mpp;
13721676Sjpk 	ip6_t  *ip6h;
137311042SErik.Nordmark@Sun.COM 	ts_label_t *effective_tsl = NULL;
13741676Sjpk 	/*
13751676Sjpk 	 * Label option length is limited to IP_MAX_OPT_LENGTH for
13761676Sjpk 	 * symmetry with IPv4. Can be relaxed if needed
13771676Sjpk 	 */
13781676Sjpk 	uchar_t opt_storage[TSOL_MAX_IPV6_OPTION];
13791676Sjpk 	uint_t hlen;
13801676Sjpk 	uint_t sec_opt_len; /* label option length not including type, len */
13816596Skp158701 	int delta_remove = 0, delta_add;
13821676Sjpk 	int retv;
13831676Sjpk 	uchar_t	*after_secopt;
13841676Sjpk 	uchar_t	*secopt = NULL;
13851676Sjpk 	uchar_t	*ip6hbh;
13861676Sjpk 	uint_t	hbhlen;
13871676Sjpk 	boolean_t hbh_needed;
13881676Sjpk 
138911042SErik.Nordmark@Sun.COM 	*effective_tslp = NULL;
139011042SErik.Nordmark@Sun.COM 
13919710SKen.Powell@Sun.COM 	/*
13929710SKen.Powell@Sun.COM 	 * Verify the destination is allowed to receive packets at
139311042SErik.Nordmark@Sun.COM 	 * the security label of the message data. tsol_check_dest()
139411042SErik.Nordmark@Sun.COM 	 * may create a new effective label or label flags.
13959710SKen.Powell@Sun.COM 	 */
13961676Sjpk 	ip6h = (ip6_t *)mp->b_rptr;
139711042SErik.Nordmark@Sun.COM 	retv = tsol_check_dest(tsl, &ip6h->ip6_dst, IPV6_VERSION,
139811042SErik.Nordmark@Sun.COM 	    mac_mode, zone_is_global, &effective_tsl);
13991676Sjpk 	if (retv != 0)
14001676Sjpk 		return (retv);
14011676Sjpk 
14029710SKen.Powell@Sun.COM 	/*
14039710SKen.Powell@Sun.COM 	 * Calculate the security label to be placed in the text
14049710SKen.Powell@Sun.COM 	 * of the message (if any).
14059710SKen.Powell@Sun.COM 	 */
140611042SErik.Nordmark@Sun.COM 	if (effective_tsl != NULL) {
140711042SErik.Nordmark@Sun.COM 		if ((retv = tsol_compute_label_v6(effective_tsl, zoneid,
14089710SKen.Powell@Sun.COM 		    &ip6h->ip6_dst, opt_storage, ipst)) != 0) {
140911042SErik.Nordmark@Sun.COM 			label_rele(effective_tsl);
14109710SKen.Powell@Sun.COM 			return (retv);
14119710SKen.Powell@Sun.COM 		}
141211042SErik.Nordmark@Sun.COM 		*effective_tslp = effective_tsl;
14139710SKen.Powell@Sun.COM 	} else {
141411042SErik.Nordmark@Sun.COM 		if ((retv = tsol_compute_label_v6(tsl, zoneid,
14159710SKen.Powell@Sun.COM 		    &ip6h->ip6_dst, opt_storage, ipst)) != 0)
14169710SKen.Powell@Sun.COM 			return (retv);
14179710SKen.Powell@Sun.COM 	}
14189710SKen.Powell@Sun.COM 
14191676Sjpk 	sec_opt_len = opt_storage[1];
14201676Sjpk 
14211676Sjpk 	if (ip6h->ip6_nxt == IPPROTO_HOPOPTS) {
14221676Sjpk 		ip6hbh = (uchar_t *)&ip6h[1];
14231676Sjpk 		hbhlen = (ip6hbh[1] + 1) << 3;
142410181SKen.Powell@Sun.COM 		if (!tsol_find_secopt_v6(ip6hbh, hbhlen, &secopt,
142510181SKen.Powell@Sun.COM 		    &after_secopt, &hbh_needed)) {
142610181SKen.Powell@Sun.COM 			/*
142710181SKen.Powell@Sun.COM 			 * This function should not see invalid messages.
142810181SKen.Powell@Sun.COM 			 * If one occurs, it would indicate either an
142910181SKen.Powell@Sun.COM 			 * option previously verified in the forwarding
143010181SKen.Powell@Sun.COM 			 * path has been corrupted or an option was
143110181SKen.Powell@Sun.COM 			 * incorrectly generated locally.
143210181SKen.Powell@Sun.COM 			 */
143310181SKen.Powell@Sun.COM 			ASSERT(0);
143410181SKen.Powell@Sun.COM 			return (EACCES);
143510181SKen.Powell@Sun.COM 		}
14361676Sjpk 	}
14371676Sjpk 
14381676Sjpk 	if (sec_opt_len == 0 && secopt == NULL) {
14391676Sjpk 		/*
14401676Sjpk 		 * The packet is not supposed to have a label, and it
14411676Sjpk 		 * does not have one currently
14421676Sjpk 		 */
14431676Sjpk 		return (0);
14441676Sjpk 	}
144510934Ssommerfeld@sun.com 
14461676Sjpk 	if (secopt != NULL && sec_opt_len != 0 &&
14471676Sjpk 	    (bcmp(opt_storage, secopt, sec_opt_len + 2) == 0)) {
14481676Sjpk 		/* The packet has the correct label already */
14491676Sjpk 		return (0);
14501676Sjpk 	}
14511676Sjpk 
14521676Sjpk 	/*
14531676Sjpk 	 * If there is an option there, then it must be the wrong one; delete.
14541676Sjpk 	 */
14556596Skp158701 	if (secopt != NULL) {
14566596Skp158701 		delta_remove = tsol_remove_secopt_v6(ip6h, MBLKL(mp));
14576596Skp158701 		mp->b_wptr += delta_remove;
14586596Skp158701 	}
14591676Sjpk 
14601676Sjpk 	/*
14611676Sjpk 	 * Make sure we have room for the worst-case addition. Add 2 bytes for
14621676Sjpk 	 * the hop-by-hop ext header's next header and length fields. Add
14631676Sjpk 	 * another 2 bytes for the label option type, len and then round
14641676Sjpk 	 * up to the next 8-byte multiple.
14651676Sjpk 	 */
14661676Sjpk 	hlen = (4 + sec_opt_len + 7) & ~7;
14671676Sjpk 	if (mp->b_wptr + hlen > mp->b_datap->db_lim) {
14681676Sjpk 		int copylen;
14691676Sjpk 		mblk_t *new_mp;
14701676Sjpk 		uint16_t hdr_len;
14711676Sjpk 
14721676Sjpk 		hdr_len = ip_hdr_length_v6(mp, ip6h);
14731676Sjpk 		/*
14741676Sjpk 		 * Allocate enough to be meaningful, but not *too* much.
14751676Sjpk 		 * Also all the IPv6 extension headers must be in the same mblk
14761676Sjpk 		 */
14771676Sjpk 		copylen = MBLKL(mp);
14781676Sjpk 		if (copylen > 256)
14791676Sjpk 			copylen = 256;
14801676Sjpk 		if (copylen < hdr_len)
14811676Sjpk 			copylen = hdr_len;
14828778SErik.Nordmark@Sun.COM 		new_mp = allocb_tmpl(hlen + copylen +
14838778SErik.Nordmark@Sun.COM 		    (mp->b_rptr - mp->b_datap->db_base), mp);
148411042SErik.Nordmark@Sun.COM 		if (new_mp == NULL) {
148511042SErik.Nordmark@Sun.COM 			if (effective_tsl != NULL) {
148611042SErik.Nordmark@Sun.COM 				label_rele(effective_tsl);
148711042SErik.Nordmark@Sun.COM 				*effective_tslp = NULL;
148811042SErik.Nordmark@Sun.COM 			}
14891676Sjpk 			return (ENOMEM);
149011042SErik.Nordmark@Sun.COM 		}
14911676Sjpk 
14921676Sjpk 		/* keep the bias */
14931676Sjpk 		new_mp->b_rptr += mp->b_rptr - mp->b_datap->db_base;
14941676Sjpk 		new_mp->b_wptr = new_mp->b_rptr + copylen;
14951676Sjpk 		bcopy(mp->b_rptr, new_mp->b_rptr, copylen);
14961676Sjpk 		new_mp->b_cont = mp;
14971676Sjpk 		if ((mp->b_rptr += copylen) >= mp->b_wptr) {
14981676Sjpk 			new_mp->b_cont = mp->b_cont;
14991676Sjpk 			freeb(mp);
15001676Sjpk 		}
15011676Sjpk 		*mpp = mp = new_mp;
15021676Sjpk 		ip6h = (ip6_t *)mp->b_rptr;
15031676Sjpk 	}
15041676Sjpk 
15056596Skp158701 	delta_add = tsol_prepend_option_v6(opt_storage, ip6h, MBLKL(mp));
15066596Skp158701 	if (delta_add == -1)
15071676Sjpk 		goto param_prob;
15081676Sjpk 
15096596Skp158701 	ASSERT(mp->b_wptr + delta_add <= DB_LIM(mp));
15106596Skp158701 	mp->b_wptr += delta_add;
15111676Sjpk 
151211042SErik.Nordmark@Sun.COM 	/* tsol_prepend_option_v6 has adjusted ip6_plen */
15131676Sjpk 	return (0);
15141676Sjpk 
15151676Sjpk param_prob:
151611042SErik.Nordmark@Sun.COM 	if (effective_tsl != NULL) {
151711042SErik.Nordmark@Sun.COM 		label_rele(effective_tsl);
151811042SErik.Nordmark@Sun.COM 		*effective_tslp = NULL;
151911042SErik.Nordmark@Sun.COM 	}
15201676Sjpk 	return (EINVAL);
15211676Sjpk }
1522