xref: /onnv-gate/usr/src/uts/common/inet/ip/tn_ipopt.c (revision 11042:2d6e217af1b4)
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 /*
228778SErik.Nordmark@Sun.COM  * Copyright 2009 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
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
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
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
274*11042SErik.Nordmark@Sun.COM  * based on the security label. If any adjustments to the label are needed
275*11042SErik.Nordmark@Sun.COM  * due to the connection's MAC mode or the destination's ability
276*11042SErik.Nordmark@Sun.COM  * to receive labels, an "effective label" will be returned.
2779710SKen.Powell@Sun.COM  *
278*11042SErik.Nordmark@Sun.COM  * zone_is_global is set if the actual zoneid is global. That is, it is
279*11042SErik.Nordmark@Sun.COM  * not set for an exclusive-IP zone.
280*11042SErik.Nordmark@Sun.COM  *
281*11042SErik.Nordmark@Sun.COM  * On successful return, effective_tsl will point to the new label needed
282*11042SErik.Nordmark@Sun.COM  * or will be NULL if a new label isn't needed. On error, effective_tsl will
283*11042SErik.Nordmark@Sun.COM  * point to NULL.
2849710SKen.Powell@Sun.COM  *
2859710SKen.Powell@Sun.COM  * Returns:
286*11042SErik.Nordmark@Sun.COM  *      0		Label (was|is now) correct
287*11042SErik.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
291*11042SErik.Nordmark@Sun.COM tsol_check_dest(const ts_label_t *tsl, const void *dst,
292*11042SErik.Nordmark@Sun.COM     uchar_t version, uint_t mac_mode, boolean_t zone_is_global,
293*11042SErik.Nordmark@Sun.COM     ts_label_t **effective_tsl)
2949710SKen.Powell@Sun.COM {
295*11042SErik.Nordmark@Sun.COM 	ts_label_t	*newtsl = NULL;
2969710SKen.Powell@Sun.COM 	tsol_tpc_t	*dst_rhtp;
2979710SKen.Powell@Sun.COM 
298*11042SErik.Nordmark@Sun.COM 	if (effective_tsl != NULL)
299*11042SErik.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) */
305*11042SErik.Nordmark@Sun.COM 	if (tsl == NULL) {
3069710SKen.Powell@Sun.COM 		DTRACE_PROBE2(tx__tnopt__log__info__labeling__mac__allownull,
307*11042SErik.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 ||
364*11042SErik.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 	/*
442*11042SErik.Nordmark@Sun.COM 	 * Return the new label.
4439710SKen.Powell@Sun.COM 	 */
4449710SKen.Powell@Sun.COM 	if (newtsl != NULL) {
445*11042SErik.Nordmark@Sun.COM 		if (effective_tsl != NULL)
446*11042SErik.Nordmark@Sun.COM 			*effective_tsl = newtsl;
447*11042SErik.Nordmark@Sun.COM 		else
448*11042SErik.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 /*
455*11042SErik.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  *
460*11042SErik.Nordmark@Sun.COM  * The zoneid is the IP zoneid (i.e., GLOBAL_ZONEID for exlusive-IP zones).
461*11042SErik.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
468*11042SErik.Nordmark@Sun.COM tsol_compute_label_v4(const ts_label_t *tsl, zoneid_t zoneid, ipaddr_t dst,
469*11042SErik.Nordmark@Sun.COM     uchar_t *opt_storage, ip_stack_t *ipst)
4701676Sjpk {
4711676Sjpk 	uint_t		sec_opt_len;
472*11042SErik.Nordmark@Sun.COM 	ire_t		*ire;
473*11042SErik.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 
478*11042SErik.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 		 */
495*11042SErik.Nordmark@Sun.COM 		ire = ire_route_recursive_v4(dst, 0, NULL, zoneid, tsl,
496*11042SErik.Nordmark@Sun.COM 		    MATCH_IRE_SECATTR, B_TRUE, 0, ipst, NULL, &attrp, NULL);
497*11042SErik.Nordmark@Sun.COM 		ASSERT(ire != NULL);
498*11042SErik.Nordmark@Sun.COM 		if (ire->ire_flags & (RTF_REJECT|RTF_BLACKHOLE)) {
499*11042SErik.Nordmark@Sun.COM 			/* no route to destination */
500*11042SErik.Nordmark@Sun.COM 			ire_refrele(ire);
5019710SKen.Powell@Sun.COM 			DTRACE_PROBE3(
5021676Sjpk 			    tx__tnopt__log__info__labeling__routedst__v4,
5039710SKen.Powell@Sun.COM 			    char *, "No route to unlabeled dest ip(1) with "
504*11042SErik.Nordmark@Sun.COM 			    "with label(2).", ipaddr_t, dst, ts_label_t *, tsl);
5059710SKen.Powell@Sun.COM 			return (EHOSTUNREACH);
5061676Sjpk 		}
507*11042SErik.Nordmark@Sun.COM 		if (ire->ire_type & (IRE_BROADCAST | IRE_LOCAL | IRE_LOOPBACK |
508*11042SErik.Nordmark@Sun.COM 		    IRE_INTERFACE)) {
509*11042SErik.Nordmark@Sun.COM 			ire_refrele(ire);
510*11042SErik.Nordmark@Sun.COM 			return (0);
511*11042SErik.Nordmark@Sun.COM 		}
5121676Sjpk 
5131676Sjpk 		/*
514*11042SErik.Nordmark@Sun.COM 		 * ire_route_recursive gives us the first attrp it finds
515*11042SErik.Nordmark@Sun.COM 		 * in the recursive lookup.
5161676Sjpk 		 */
5171676Sjpk 		/*
5189710SKen.Powell@Sun.COM 		 * Return now if next hop gateway is unlabeled. There is
5199710SKen.Powell@Sun.COM 		 * no need to generate a CIPSO option for this message.
5201676Sjpk 		 */
5219710SKen.Powell@Sun.COM 		if (attrp == NULL || attrp->igsa_rhc == NULL ||
5229710SKen.Powell@Sun.COM 		    attrp->igsa_rhc->rhc_tpc->tpc_tp.host_type == UNLABELED) {
523*11042SErik.Nordmark@Sun.COM 			ire_refrele(ire);
5249710SKen.Powell@Sun.COM 			return (0);
5251676Sjpk 		}
526*11042SErik.Nordmark@Sun.COM 		ire_refrele(ire);
5271676Sjpk 	}
5281676Sjpk 
5291676Sjpk 	/* compute the CIPSO option */
5309710SKen.Powell@Sun.COM 	sec_opt_len = tsol2cipso_tt1(&tsl->tsl_label, opt_storage,
5319710SKen.Powell@Sun.COM 	    tsl->tsl_doi);
5321676Sjpk 
5331676Sjpk 	if (sec_opt_len == 0) {
5349710SKen.Powell@Sun.COM 		DTRACE_PROBE3(tx__tnopt__log__error__labeling__lostops__v4,
535*11042SErik.Nordmark@Sun.COM 		    char *, "options lack length for dest ip(1) with label(2).",
536*11042SErik.Nordmark@Sun.COM 		    ipaddr_t, dst, ts_label_t *, tsl);
5371676Sjpk 		return (EINVAL);
5381676Sjpk 	}
5391676Sjpk 
5401676Sjpk 	return (0);
5411676Sjpk }
5421676Sjpk 
5431676Sjpk /*
5441676Sjpk  * Remove any existing security option (CIPSO) from the given IP
5451676Sjpk  * header, move the 'buflen' bytes back to fill the gap, and return the number
5461676Sjpk  * of bytes removed (as zero or negative number).  Assumes that the headers are
5471676Sjpk  * sane.
548*11042SErik.Nordmark@Sun.COM  *
549*11042SErik.Nordmark@Sun.COM  * Note that tsol_remove_secopt does not adjust ipha_length but
550*11042SErik.Nordmark@Sun.COM  * tsol_remove_secopt_v6 does adjust ip6_plen.
5511676Sjpk  */
5521676Sjpk int
5531676Sjpk tsol_remove_secopt(ipha_t *ipha, int buflen)
5541676Sjpk {
5551676Sjpk 	int remlen, olen, oval, delta;
5561676Sjpk 	uchar_t *fptr, *tptr;
5571676Sjpk 	boolean_t noop_keep;
5581676Sjpk 
5591676Sjpk 	remlen = IPH_HDR_LENGTH(ipha) - IP_SIMPLE_HDR_LENGTH;
5601676Sjpk 	fptr = tptr = (uchar_t *)(ipha + 1);
5611676Sjpk 	noop_keep = B_TRUE;
5621676Sjpk 	while (remlen > 0) {
5631676Sjpk 		oval = fptr[IPOPT_OPTVAL];
5641676Sjpk 
5651676Sjpk 		/* terminate on end of list */
5661676Sjpk 		if (oval == IPOPT_EOL)
5671676Sjpk 			break;
5681676Sjpk 
5691676Sjpk 		/*
5701676Sjpk 		 * Delete any no-ops following a deleted option, at least up
5711676Sjpk 		 * to a 4 octet alignment; copy others.
5721676Sjpk 		 */
5731676Sjpk 		if (oval == IPOPT_NOP) {
5741676Sjpk 			if (((fptr - (uchar_t *)ipha) & 3) == 0)
5751676Sjpk 				noop_keep = B_TRUE;
5761676Sjpk 			if (noop_keep)
5771676Sjpk 				*tptr++ = oval;
5781676Sjpk 			fptr++;
5791676Sjpk 			remlen--;
5801676Sjpk 			continue;
5811676Sjpk 		}
5821676Sjpk 
5831676Sjpk 		/* stop on corrupted list; just do nothing. */
5841676Sjpk 		if (remlen < 2)
5851676Sjpk 			return (0);
5861676Sjpk 		olen = fptr[IPOPT_OLEN];
5871676Sjpk 		if (olen < 2 || olen > remlen)
5881676Sjpk 			return (0);
5891676Sjpk 
5901676Sjpk 		/* skip over security options to delete them */
5911676Sjpk 		if (oval == IPOPT_COMSEC || oval == IPOPT_SECURITY) {
5921676Sjpk 			noop_keep = B_FALSE;
5931676Sjpk 			fptr += olen;
5941676Sjpk 			remlen -= olen;
5951676Sjpk 			continue;
5961676Sjpk 		}
5971676Sjpk 
5981676Sjpk 		/* copy the rest */
5991676Sjpk 		noop_keep = B_TRUE;
6001676Sjpk 		if (tptr != fptr)
6011676Sjpk 			ovbcopy(fptr, tptr, olen);
6021676Sjpk 		fptr += olen;
6031676Sjpk 		tptr += olen;
6041676Sjpk 		remlen -= olen;
6051676Sjpk 	}
6061676Sjpk 
6071676Sjpk 	fptr += remlen;
6081676Sjpk 
6091676Sjpk 	/* figure how much padding we'll need for header alignment */
6101676Sjpk 	olen = (tptr - (uchar_t *)ipha) & 3;
6111676Sjpk 	if (olen > 0) {
6121676Sjpk 		olen = 4 - olen;
6131676Sjpk 		/* pad with end-of-list */
6141676Sjpk 		bzero(tptr, olen);
6151676Sjpk 		tptr += olen;
6161676Sjpk 	}
6171676Sjpk 
6181676Sjpk 	/* slide back the headers that follow and update the IP header */
6191676Sjpk 	delta = fptr - tptr;
6201676Sjpk 	if (delta != 0) {
6211676Sjpk 		ovbcopy(fptr, tptr, ((uchar_t *)ipha + buflen) - fptr);
6221676Sjpk 		ipha->ipha_version_and_hdr_length -= delta / 4;
6231676Sjpk 	}
6241676Sjpk 	return (-delta);
6251676Sjpk }
6261676Sjpk 
6271676Sjpk /*
6281676Sjpk  * Insert the option in 'optbuf' into the IP header pointed to by 'ipha', and
6291676Sjpk  * move the data following the IP header (up to buflen) to accomodate the new
6301676Sjpk  * option.  Assumes that up to IP_MAX_OPT_LENGTH bytes are available (in total)
6311676Sjpk  * for IP options.  Returns the number of bytes actually inserted, or -1 if the
6321676Sjpk  * option cannot be inserted.  (Note that negative return values are possible
6331676Sjpk  * when noops must be compressed, and that only -1 indicates error.  Successful
6341676Sjpk  * return value is always evenly divisible by 4, by definition.)
635*11042SErik.Nordmark@Sun.COM  *
636*11042SErik.Nordmark@Sun.COM  * Note that tsol_prepend_option does not adjust ipha_length but
637*11042SErik.Nordmark@Sun.COM  * tsol_prepend_option_v6 does adjust ip6_plen.
6381676Sjpk  */
6391676Sjpk int
6401676Sjpk tsol_prepend_option(uchar_t *optbuf, ipha_t *ipha, int buflen)
6411676Sjpk {
6421676Sjpk 	int remlen, padding, lastpad, totlen;
6431676Sjpk 	int oval, olen;
6441676Sjpk 	int delta;
6451676Sjpk 	uchar_t *optr;
6461676Sjpk 	uchar_t tempopt[IP_MAX_OPT_LENGTH], *toptr;
6471676Sjpk 
6481676Sjpk 	if (optbuf[IPOPT_OPTVAL] == IPOPT_EOL ||
6491676Sjpk 	    optbuf[IPOPT_OPTVAL] == IPOPT_NOP ||
6501676Sjpk 	    optbuf[IPOPT_OLEN] == 0)
6511676Sjpk 		return (0);
6521676Sjpk 
6531676Sjpk 	ASSERT(optbuf[IPOPT_OLEN] >= 2 &&
6541676Sjpk 	    optbuf[IPOPT_OLEN] <= IP_MAX_OPT_LENGTH);
6551676Sjpk 
6561676Sjpk 	/* first find the real (unpadded) length of the existing options */
6571676Sjpk 	remlen = IPH_HDR_LENGTH(ipha) - IP_SIMPLE_HDR_LENGTH;
6581676Sjpk 	padding = totlen = lastpad = 0;
6591676Sjpk 	optr = (uchar_t *)(ipha + 1);
6601676Sjpk 	while (remlen > 0) {
6611676Sjpk 		oval = optr[IPOPT_OPTVAL];
6621676Sjpk 
6631676Sjpk 		/* stop at end of list */
6641676Sjpk 		if (oval == IPOPT_EOL)
6651676Sjpk 			break;
6661676Sjpk 
6671676Sjpk 		/* skip no-ops, noting that length byte isn't present */
6681676Sjpk 		if (oval == IPOPT_NOP) {
6691676Sjpk 			optr++;
6701676Sjpk 			padding++;
6711676Sjpk 			lastpad++;
6721676Sjpk 			totlen++;
6731676Sjpk 			remlen--;
6741676Sjpk 			continue;
6751676Sjpk 		}
6761676Sjpk 
6771676Sjpk 		/* give up on a corrupted list; report failure */
6781676Sjpk 		if (remlen < 2)
6791676Sjpk 			return (-1);
6801676Sjpk 		olen = optr[IPOPT_OLEN];
6811676Sjpk 		if (olen < 2 || olen > remlen)
6821676Sjpk 			return (-1);
6831676Sjpk 
6841676Sjpk 		lastpad = 0;
6851676Sjpk 		optr += olen;
6861676Sjpk 		totlen += olen;
6871676Sjpk 		remlen -= olen;
6881676Sjpk 	}
6891676Sjpk 
6901676Sjpk 	/* completely ignore any trailing padding */
6911676Sjpk 	totlen -= lastpad;
6921676Sjpk 	padding -= lastpad;
6931676Sjpk 
6941676Sjpk 	/*
6951676Sjpk 	 * If some sort of inter-option alignment was present, try to preserve
6961676Sjpk 	 * that alignment.  If alignment pushes us out past the maximum, then
6971676Sjpk 	 * discard it and try to compress to fit.  (We just "assume" that any
6981676Sjpk 	 * padding added was attempting to get 32 bit alignment.  If that's
6991676Sjpk 	 * wrong, that's just too bad.)
7001676Sjpk 	 */
7011676Sjpk 	if (padding > 0) {
7021676Sjpk 		olen = (optbuf[IPOPT_OLEN] + 3) & ~3;
7031676Sjpk 		if (olen + totlen > IP_MAX_OPT_LENGTH) {
7041676Sjpk 			totlen -= padding;
7051676Sjpk 			if (olen + totlen > IP_MAX_OPT_LENGTH)
7061676Sjpk 				return (-1);
7071676Sjpk 			padding = 0;
7081676Sjpk 		}
7091676Sjpk 	}
7101676Sjpk 
7111676Sjpk 	/*
7121676Sjpk 	 * Since we may need to compress or expand the option list, we write to
7131676Sjpk 	 * a temporary buffer and then copy the results back to the IP header.
7141676Sjpk 	 */
7151676Sjpk 	toptr = tempopt;
7161676Sjpk 
7171676Sjpk 	/* compute actual option to insert */
7181676Sjpk 	olen = optbuf[IPOPT_OLEN];
7191676Sjpk 	bcopy(optbuf, toptr, olen);
7201676Sjpk 	toptr += olen;
7211676Sjpk 	if (padding > 0) {
7221676Sjpk 		while ((olen & 3) != 0) {
7231676Sjpk 			*toptr++ = IPOPT_NOP;
7241676Sjpk 			olen++;
7251676Sjpk 		}
7261676Sjpk 	}
7271676Sjpk 
7281676Sjpk 	/* copy over the existing options */
7291676Sjpk 	optr = (uchar_t *)(ipha + 1);
7301676Sjpk 	while (totlen > 0) {
7311676Sjpk 		oval = optr[IPOPT_OPTVAL];
7321676Sjpk 
7331676Sjpk 		/* totlen doesn't include end-of-list marker */
7341676Sjpk 		ASSERT(oval != IPOPT_EOL);
7351676Sjpk 
7361676Sjpk 		/* handle no-ops; copy if desired, ignore otherwise */
7371676Sjpk 		if (oval == IPOPT_NOP) {
7381676Sjpk 			if (padding > 0) {
7391676Sjpk 				/* note: cannot overflow due to checks above */
7401676Sjpk 				ASSERT(toptr < tempopt + IP_MAX_OPT_LENGTH);
7411676Sjpk 				*toptr++ = oval;
7421676Sjpk 			}
7431676Sjpk 			optr++;
7441676Sjpk 			totlen--;
7451676Sjpk 			continue;
7461676Sjpk 		}
7471676Sjpk 
7481676Sjpk 		/* list cannot be corrupt at this point */
7491676Sjpk 		ASSERT(totlen >= 2);
7501676Sjpk 		olen = optr[IPOPT_OLEN];
7511676Sjpk 		ASSERT(olen >= 2 && olen <= totlen);
7521676Sjpk 
7531676Sjpk 		/* cannot run out of room due to tests above */
7541676Sjpk 		ASSERT(toptr + olen <= tempopt + IP_MAX_OPT_LENGTH);
7551676Sjpk 
7561676Sjpk 		bcopy(optr, toptr, olen);
7571676Sjpk 		optr += olen;
7581676Sjpk 		toptr += olen;
7591676Sjpk 		totlen -= olen;
7601676Sjpk 	}
7611676Sjpk 
7621676Sjpk 	/* figure how much padding we'll need for header alignment */
7631676Sjpk 	olen = (toptr - tempopt) & 3;
7641676Sjpk 	if (olen > 0) {
7651676Sjpk 		olen = 4 - olen;
7661676Sjpk 		ASSERT(toptr + olen <= tempopt + IP_MAX_OPT_LENGTH);
7671676Sjpk 		/* pad with end-of-list value */
7681676Sjpk 		bzero(toptr, olen);
7691676Sjpk 		toptr += olen;
7701676Sjpk 	}
7711676Sjpk 
7721676Sjpk 	/* move the headers as needed and update IP header */
7731676Sjpk 	olen = (toptr - tempopt) + IP_SIMPLE_HDR_LENGTH;
7741676Sjpk 	remlen = IPH_HDR_LENGTH(ipha);
7751676Sjpk 	delta = olen - remlen;
7761676Sjpk 	if (delta != 0) {
7771676Sjpk 		ovbcopy((uchar_t *)ipha + remlen, (uchar_t *)ipha + olen,
7781676Sjpk 		    buflen - remlen);
7791676Sjpk 		ipha->ipha_version_and_hdr_length += delta / 4;
7801676Sjpk 	}
7811676Sjpk 
7821676Sjpk 	/* slap in the new options */
7831676Sjpk 	bcopy(tempopt, ipha + 1, olen - IP_SIMPLE_HDR_LENGTH);
7841676Sjpk 
7851676Sjpk 	return (delta);
7861676Sjpk }
7871676Sjpk 
7881676Sjpk /*
789*11042SErik.Nordmark@Sun.COM  * tsol_check_label_v4()
7901676Sjpk  *
7911676Sjpk  * This routine computes the IP label that should be on the packet based on the
792*11042SErik.Nordmark@Sun.COM  * connection and destination information.  It's called by the IP forwarding
793*11042SErik.Nordmark@Sun.COM  * logic and by ip_output_simple. The ULPs generate the labels before calling
794*11042SErik.Nordmark@Sun.COM  * conn_ip_output. If any adjustments to
795*11042SErik.Nordmark@Sun.COM  * the label are needed due to the connection's MAC-exempt status or
796*11042SErik.Nordmark@Sun.COM  * the destination's ability to receive labels, an "effective label"
797*11042SErik.Nordmark@Sun.COM  * will be returned.
7981676Sjpk  *
7996596Skp158701  * The packet's header is clear before entering IPsec's engine.
8001676Sjpk  *
801*11042SErik.Nordmark@Sun.COM  * The zoneid is the IP zoneid (i.e., GLOBAL_ZONEID for exlusive-IP zones).
802*11042SErik.Nordmark@Sun.COM  * zone_is_global is set if the actual zoneid is global.
803*11042SErik.Nordmark@Sun.COM  *
804*11042SErik.Nordmark@Sun.COM  * On successful return, effective_tslp will point to the new label needed
805*11042SErik.Nordmark@Sun.COM  * or will be NULL if a new label isn't needed. On error, effective_tsl will
806*11042SErik.Nordmark@Sun.COM  * point to NULL.
807*11042SErik.Nordmark@Sun.COM  *
8081676Sjpk  * Returns:
809*11042SErik.Nordmark@Sun.COM  *      0		Label on (was|is now) correct
8101676Sjpk  *      EACCES		The packet failed the remote host accreditation.
8111676Sjpk  *      ENOMEM		Memory allocation failure.
8121676Sjpk  *	EINVAL		Label cannot be computed
8131676Sjpk  */
8141676Sjpk int
815*11042SErik.Nordmark@Sun.COM tsol_check_label_v4(const ts_label_t *tsl, zoneid_t zoneid, mblk_t **mpp,
816*11042SErik.Nordmark@Sun.COM     uint_t mac_mode, boolean_t zone_is_global, ip_stack_t *ipst,
817*11042SErik.Nordmark@Sun.COM     ts_label_t **effective_tslp)
8181676Sjpk {
8191676Sjpk 	mblk_t *mp = *mpp;
8201676Sjpk 	ipha_t  *ipha;
821*11042SErik.Nordmark@Sun.COM 	ts_label_t *effective_tsl = NULL;
8221676Sjpk 	uchar_t opt_storage[IP_MAX_OPT_LENGTH];
8231676Sjpk 	uint_t hlen;
8241676Sjpk 	uint_t sec_opt_len;
8251676Sjpk 	uchar_t *optr;
8266596Skp158701 	int delta_remove = 0, delta_add, adjust;
8271676Sjpk 	int retv;
8281676Sjpk 
829*11042SErik.Nordmark@Sun.COM 	*effective_tslp = NULL;
8301676Sjpk 	opt_storage[IPOPT_OPTVAL] = 0;
8311676Sjpk 
8321676Sjpk 	ipha = (ipha_t *)mp->b_rptr;
8331676Sjpk 
8349710SKen.Powell@Sun.COM 	/*
8359710SKen.Powell@Sun.COM 	 * Verify the destination is allowed to receive packets at
836*11042SErik.Nordmark@Sun.COM 	 * the security label of the message data. tsol_check_dest()
837*11042SErik.Nordmark@Sun.COM 	 * may create a new effective label or label flags.
8389710SKen.Powell@Sun.COM 	 */
839*11042SErik.Nordmark@Sun.COM 	retv = tsol_check_dest(tsl, &ipha->ipha_dst, IPV4_VERSION,
840*11042SErik.Nordmark@Sun.COM 	    mac_mode, zone_is_global, &effective_tsl);
8411676Sjpk 	if (retv != 0)
8421676Sjpk 		return (retv);
8431676Sjpk 
8449710SKen.Powell@Sun.COM 	/*
8459710SKen.Powell@Sun.COM 	 * Calculate the security label to be placed in the text
8469710SKen.Powell@Sun.COM 	 * of the message (if any).
8479710SKen.Powell@Sun.COM 	 */
848*11042SErik.Nordmark@Sun.COM 	if (effective_tsl != NULL) {
849*11042SErik.Nordmark@Sun.COM 		if ((retv = tsol_compute_label_v4(effective_tsl, zoneid,
8509710SKen.Powell@Sun.COM 		    ipha->ipha_dst, opt_storage, ipst)) != 0) {
851*11042SErik.Nordmark@Sun.COM 			label_rele(effective_tsl);
8529710SKen.Powell@Sun.COM 			return (retv);
8539710SKen.Powell@Sun.COM 		}
854*11042SErik.Nordmark@Sun.COM 		*effective_tslp = effective_tsl;
8559710SKen.Powell@Sun.COM 	} else {
856*11042SErik.Nordmark@Sun.COM 		if ((retv = tsol_compute_label_v4(tsl, zoneid,
8579710SKen.Powell@Sun.COM 		    ipha->ipha_dst, opt_storage, ipst)) != 0) {
8589710SKen.Powell@Sun.COM 			return (retv);
8599710SKen.Powell@Sun.COM 		}
8609710SKen.Powell@Sun.COM 	}
8619710SKen.Powell@Sun.COM 
8621676Sjpk 	optr = (uchar_t *)(ipha + 1);
8631676Sjpk 	hlen = IPH_HDR_LENGTH(ipha) - IP_SIMPLE_HDR_LENGTH;
8641676Sjpk 	sec_opt_len = opt_storage[IPOPT_OLEN];
8651676Sjpk 
8661676Sjpk 	if (hlen >= sec_opt_len) {
8671676Sjpk 		/* If no option is supposed to be there, make sure it's not */
8681676Sjpk 		if (sec_opt_len == 0 && hlen > 0 &&
8691676Sjpk 		    optr[IPOPT_OPTVAL] != IPOPT_COMSEC &&
8701676Sjpk 		    optr[IPOPT_OPTVAL] != IPOPT_SECURITY)
8711676Sjpk 			return (0);
8721676Sjpk 		/* if the option is there, it's always first */
8731676Sjpk 		if (sec_opt_len != 0 &&
8741676Sjpk 		    bcmp(opt_storage, optr, sec_opt_len) == 0)
8751676Sjpk 			return (0);
8761676Sjpk 	}
8771676Sjpk 
8781676Sjpk 	/*
8791676Sjpk 	 * If there is an option there, then it must be the wrong one; delete.
8801676Sjpk 	 */
8816596Skp158701 	if (hlen > 0) {
8826596Skp158701 		delta_remove = tsol_remove_secopt(ipha, MBLKL(mp));
8836596Skp158701 		mp->b_wptr += delta_remove;
8846596Skp158701 	}
8851676Sjpk 
8861676Sjpk 	/* Make sure we have room for the worst-case addition */
8871676Sjpk 	hlen = IPH_HDR_LENGTH(ipha) + opt_storage[IPOPT_OLEN];
8881676Sjpk 	hlen = (hlen + 3) & ~3;
8891676Sjpk 	if (hlen > IP_MAX_HDR_LENGTH)
8901676Sjpk 		hlen = IP_MAX_HDR_LENGTH;
8911676Sjpk 	hlen -= IPH_HDR_LENGTH(ipha);
8921676Sjpk 	if (mp->b_wptr + hlen > mp->b_datap->db_lim) {
8931676Sjpk 		int copylen;
8941676Sjpk 		mblk_t *new_mp;
8951676Sjpk 
8961676Sjpk 		/* allocate enough to be meaningful, but not *too* much */
8971676Sjpk 		copylen = MBLKL(mp);
8981676Sjpk 		if (copylen > 256)
8991676Sjpk 			copylen = 256;
9008778SErik.Nordmark@Sun.COM 		new_mp = allocb_tmpl(hlen + copylen +
9018778SErik.Nordmark@Sun.COM 		    (mp->b_rptr - mp->b_datap->db_base), mp);
902*11042SErik.Nordmark@Sun.COM 		if (new_mp == NULL) {
903*11042SErik.Nordmark@Sun.COM 			if (effective_tsl != NULL) {
904*11042SErik.Nordmark@Sun.COM 				label_rele(effective_tsl);
905*11042SErik.Nordmark@Sun.COM 				*effective_tslp = NULL;
906*11042SErik.Nordmark@Sun.COM 			}
9071676Sjpk 			return (ENOMEM);
908*11042SErik.Nordmark@Sun.COM 		}
9091676Sjpk 
9101676Sjpk 		/* keep the bias */
9111676Sjpk 		new_mp->b_rptr += mp->b_rptr - mp->b_datap->db_base;
9121676Sjpk 		new_mp->b_wptr = new_mp->b_rptr + copylen;
9131676Sjpk 		bcopy(mp->b_rptr, new_mp->b_rptr, copylen);
9141676Sjpk 		new_mp->b_cont = mp;
9151676Sjpk 		if ((mp->b_rptr += copylen) >= mp->b_wptr) {
9161676Sjpk 			new_mp->b_cont = mp->b_cont;
9171676Sjpk 			freeb(mp);
9181676Sjpk 		}
9191676Sjpk 		*mpp = mp = new_mp;
9201676Sjpk 		ipha = (ipha_t *)mp->b_rptr;
9211676Sjpk 	}
9221676Sjpk 
9236596Skp158701 	delta_add = tsol_prepend_option(opt_storage, ipha, MBLKL(mp));
9246596Skp158701 	if (delta_add == -1)
9251676Sjpk 		goto param_prob;
9261676Sjpk 
9276596Skp158701 	ASSERT((mp->b_wptr + delta_add) <= DB_LIM(mp));
9286596Skp158701 	mp->b_wptr += delta_add;
9291676Sjpk 
9306596Skp158701 	adjust = delta_remove + delta_add;
9316596Skp158701 	adjust += ntohs(ipha->ipha_length);
9326596Skp158701 	ipha->ipha_length = htons(adjust);
9331676Sjpk 
9341676Sjpk 	return (0);
9351676Sjpk 
9361676Sjpk param_prob:
937*11042SErik.Nordmark@Sun.COM 	if (effective_tsl != NULL) {
938*11042SErik.Nordmark@Sun.COM 		label_rele(effective_tsl);
939*11042SErik.Nordmark@Sun.COM 		*effective_tslp = NULL;
940*11042SErik.Nordmark@Sun.COM 	}
9411676Sjpk 	return (EINVAL);
9421676Sjpk }
9431676Sjpk 
9441676Sjpk /*
9451676Sjpk  * IPv6 HopOpt extension header for the label option layout:
9461676Sjpk  *	- One octet giving the type of the 'next extension header'
9471676Sjpk  *	- Header extension length in 8-byte words, not including the
9481676Sjpk  *	  1st 8 bytes, but including any pad bytes at the end.
9491676Sjpk  *	  Eg. A value of 2 means 16 bytes not including the 1st 8 bytes.
9501676Sjpk  *	- Followed by TLV encoded IPv6 label option. Option layout is
9511676Sjpk  *		* One octet, IP6OPT_LS
9521676Sjpk  *		* One octet option length in bytes of the option data following
9531676Sjpk  *		  the length, but not including any pad bytes at the end.
9541676Sjpk  *		* Four-octet DOI (IP6LS_DOI_V4)
9551676Sjpk  *		* One octet suboption, IP6LS_TT_V4
9561676Sjpk  *		* One octet suboption length in bytes of the suboption
9571676Sjpk  *		  following the suboption length, including the suboption
9581676Sjpk  *		  header length, but not including any pad bytes at the end.
9591676Sjpk  *	- Pad to make the extension header a multiple of 8 bytes.
9601676Sjpk  *
9611676Sjpk  * This function returns the contents of 'IPv6 option structure' in the above.
9621676Sjpk  * i.e starting from the IP6OPT_LS but not including the pad at the end.
9631676Sjpk  * The user must prepend two octets (either padding or next header / length)
9641676Sjpk  * and append padding out to the next 8 octet boundary.
965*11042SErik.Nordmark@Sun.COM  *
966*11042SErik.Nordmark@Sun.COM  * The zoneid is the IP zoneid (i.e., GLOBAL_ZONEID for exlusive-IP zones).
9671676Sjpk  */
9681676Sjpk int
969*11042SErik.Nordmark@Sun.COM tsol_compute_label_v6(const ts_label_t *tsl, zoneid_t zoneid,
970*11042SErik.Nordmark@Sun.COM     const in6_addr_t *dst, uchar_t *opt_storage, ip_stack_t *ipst)
9711676Sjpk {
9721676Sjpk 	uint_t		sec_opt_len;
9731676Sjpk 	uint32_t	doi;
974*11042SErik.Nordmark@Sun.COM 	ire_t		*ire;
975*11042SErik.Nordmark@Sun.COM 	tsol_ire_gw_secattr_t *attrp = NULL;
9768778SErik.Nordmark@Sun.COM 
9771676Sjpk 	if (ip6opt_ls == 0)
9781676Sjpk 		return (EINVAL);
9791676Sjpk 
9801676Sjpk 	if (opt_storage != NULL)
9811676Sjpk 		opt_storage[IPOPT_OLEN] = 0;
9821676Sjpk 
983*11042SErik.Nordmark@Sun.COM 	if (tsl == NULL)
9841676Sjpk 		return (0);
9851676Sjpk 
9861676Sjpk 	/* Always pass multicast */
9871676Sjpk 	if (IN6_IS_ADDR_MULTICAST(dst))
9881676Sjpk 		return (0);
9891676Sjpk 
9901676Sjpk 	/*
9911676Sjpk 	 * Fill in a V6 label.  If a new format is added here, make certain
9921676Sjpk 	 * that the maximum size of this label is reflected in sys/tsol/tnet.h
9931676Sjpk 	 * as TSOL_MAX_IPV6_OPTION.
9941676Sjpk 	 */
99510934Ssommerfeld@sun.com 	if (tsl->tsl_flags & TSLF_IMPLICIT_OUT)
99610934Ssommerfeld@sun.com 		return (0);
99710934Ssommerfeld@sun.com 
9989710SKen.Powell@Sun.COM 	if (tsl->tsl_flags & TSLF_UNLABELED) {
9991676Sjpk 		/*
10009710SKen.Powell@Sun.COM 		 * The destination is unlabeled. Only add a label if the
1001*11042SErik.Nordmark@Sun.COM 		 * destination is not a broadcast/local/loopback address,
10029710SKen.Powell@Sun.COM 		 * the destination is not on the same subnet, and the
10039710SKen.Powell@Sun.COM 		 * next-hop gateway is labeled.
10041676Sjpk 		 */
1005*11042SErik.Nordmark@Sun.COM 		ire = ire_route_recursive_v6(dst, 0, NULL, zoneid, tsl,
1006*11042SErik.Nordmark@Sun.COM 		    MATCH_IRE_SECATTR, B_TRUE, 0, ipst, NULL, &attrp, NULL);
1007*11042SErik.Nordmark@Sun.COM 		ASSERT(ire != NULL);
1008*11042SErik.Nordmark@Sun.COM 		if (ire->ire_flags & (RTF_REJECT|RTF_BLACKHOLE)) {
1009*11042SErik.Nordmark@Sun.COM 			/* no route to destination */
1010*11042SErik.Nordmark@Sun.COM 			ire_refrele(ire);
10119710SKen.Powell@Sun.COM 			DTRACE_PROBE3(
10121676Sjpk 			    tx__tnopt__log__info__labeling__routedst__v6,
10139710SKen.Powell@Sun.COM 			    char *, "No route to unlabeled dest ip6(1) with "
1014*11042SErik.Nordmark@Sun.COM 			    "label(2).", in6_addr_t *, dst, ts_label_t *, tsl);
10159710SKen.Powell@Sun.COM 			return (EHOSTUNREACH);
10161676Sjpk 		}
1017*11042SErik.Nordmark@Sun.COM 		if (ire->ire_type & (IRE_LOCAL | IRE_LOOPBACK |
1018*11042SErik.Nordmark@Sun.COM 		    IRE_INTERFACE)) {
1019*11042SErik.Nordmark@Sun.COM 			ire_refrele(ire);
1020*11042SErik.Nordmark@Sun.COM 			return (0);
1021*11042SErik.Nordmark@Sun.COM 		}
10221676Sjpk 		/*
1023*11042SErik.Nordmark@Sun.COM 		 * ire_route_recursive gives us the first attrp it finds
1024*11042SErik.Nordmark@Sun.COM 		 * in the recursive lookup.
10251676Sjpk 		 */
10269710SKen.Powell@Sun.COM 		/*
10279710SKen.Powell@Sun.COM 		 * Return now if next hop gateway is unlabeled. There is
10289710SKen.Powell@Sun.COM 		 * no need to generate a CIPSO option for this message.
10299710SKen.Powell@Sun.COM 		 */
10309710SKen.Powell@Sun.COM 		if (attrp == NULL || attrp->igsa_rhc == NULL ||
10319710SKen.Powell@Sun.COM 		    attrp->igsa_rhc->rhc_tpc->tpc_tp.host_type == UNLABELED) {
1032*11042SErik.Nordmark@Sun.COM 			ire_refrele(ire);
10339710SKen.Powell@Sun.COM 			return (0);
10341676Sjpk 		}
1035*11042SErik.Nordmark@Sun.COM 		ire_refrele(ire);
10361676Sjpk 	}
10371676Sjpk 
10381676Sjpk 	/* compute the CIPSO option */
10391676Sjpk 	if (opt_storage != NULL)
10401676Sjpk 		opt_storage += 8;
10419710SKen.Powell@Sun.COM 	sec_opt_len = tsol2cipso_tt1(&tsl->tsl_label, opt_storage,
10429710SKen.Powell@Sun.COM 	    tsl->tsl_doi);
10431676Sjpk 
10441676Sjpk 	if (sec_opt_len == 0) {
10459710SKen.Powell@Sun.COM 		DTRACE_PROBE3(tx__tnopt__log__error__labeling__lostops__v6,
10469710SKen.Powell@Sun.COM 		    char *, "options lack length for dest ip6(1) with "
1047*11042SErik.Nordmark@Sun.COM 		    "label(2).", in6_addr_t *, dst, ts_label_t *, tsl);
10481676Sjpk 		return (EINVAL);
10491676Sjpk 	}
10501676Sjpk 
10511676Sjpk 	if (opt_storage == NULL)
10521676Sjpk 		return (0);
10531676Sjpk 
10541676Sjpk 	if (sec_opt_len < IP_MAX_OPT_LENGTH)
10551676Sjpk 		opt_storage[sec_opt_len] = IPOPT_EOL;
10561676Sjpk 
10571676Sjpk 	/*
10581676Sjpk 	 * Just in case the option length is odd, round it up to the next even
10591676Sjpk 	 * multiple.  The IPv6 option definition doesn't like odd numbers for
10601676Sjpk 	 * some reason.
10611676Sjpk 	 *
10621676Sjpk 	 * Length in the overall option header (IP6OPT_LS) does not include the
10631676Sjpk 	 * option header itself, but the length in the suboption does include
10641676Sjpk 	 * the suboption header.  Thus, when there's just one suboption, the
10651676Sjpk 	 * length in the option header is the suboption length plus 4 (for the
10661676Sjpk 	 * DOI value).
10671676Sjpk 	 */
10681676Sjpk 	opt_storage[-2] = IP6LS_TT_V4;
10691676Sjpk 	opt_storage[-1] = (sec_opt_len + 2 + 1) & ~1;
10701676Sjpk 	opt_storage[-8] = ip6opt_ls;
10711676Sjpk 	opt_storage[-7] = opt_storage[-1] + 4;
10721676Sjpk 	doi = htons(IP6LS_DOI_V4);
10731676Sjpk 	bcopy(&doi, opt_storage - 6, 4);
10741676Sjpk 
10751676Sjpk 	return (0);
10761676Sjpk }
10771676Sjpk 
10781676Sjpk /*
10791676Sjpk  * Locate the start of the IP6OPT_LS label option and return it.
10801676Sjpk  * Also return the start of the next non-pad option in after_secoptp.
10811676Sjpk  * Usually the label option is the first option at least when packets
10821676Sjpk  * are generated, but for generality we don't assume that on received packets.
108310181SKen.Powell@Sun.COM  *
108410181SKen.Powell@Sun.COM  * The function will return with B_FALSE if an IP format error
108510181SKen.Powell@Sun.COM  * or an unexpected label content error is encountered.
10861676Sjpk  */
108710181SKen.Powell@Sun.COM boolean_t
10881676Sjpk tsol_find_secopt_v6(
10891676Sjpk     const uchar_t *ip6hbh,	/* Start of the hop-by-hop extension header */
10901676Sjpk     uint_t hbhlen,		/* Length of the hop-by-hop extension header */
109110181SKen.Powell@Sun.COM     uchar_t **secoptp,		/* Location of IP6OPT_LS label option */
10921676Sjpk     uchar_t **after_secoptp,	/* Non-pad option following the label option */
10931676Sjpk     boolean_t *hbh_needed)	/* Is hop-by-hop hdr needed w/o label */
10941676Sjpk {
10951676Sjpk 	uint_t	optlen;
10961676Sjpk 	uint_t	optused;
10971676Sjpk 	const uchar_t *optptr;
10981676Sjpk 	uchar_t	opt_type;
10991676Sjpk 
110010181SKen.Powell@Sun.COM 	*secoptp = NULL;
11011676Sjpk 	*hbh_needed = B_FALSE;
11021676Sjpk 	*after_secoptp = NULL;
11031676Sjpk 	optlen = hbhlen - 2;
11041676Sjpk 	optptr = ip6hbh + 2;
11051676Sjpk 	while (optlen != 0) {
11061676Sjpk 		opt_type = *optptr;
11071676Sjpk 		if (opt_type == IP6OPT_PAD1) {
11081676Sjpk 			optptr++;
11091676Sjpk 			optlen--;
11101676Sjpk 			continue;
11111676Sjpk 		}
11121676Sjpk 		if (optlen == 1)
111310181SKen.Powell@Sun.COM 			return (B_FALSE);
11141676Sjpk 		optused = 2 + optptr[1];
11151676Sjpk 		if (optused > optlen)
111610181SKen.Powell@Sun.COM 			return (B_FALSE);
11171676Sjpk 		/*
11181676Sjpk 		 * if we get here, ip6opt_ls can
11191676Sjpk 		 * not be 0 because it will always
11201676Sjpk 		 * match the IP6OPT_PAD1 above.
11211676Sjpk 		 * Therefore ip6opt_ls == 0 forces
11221676Sjpk 		 * this test to always fail here.
11231676Sjpk 		 */
112410181SKen.Powell@Sun.COM 		if (opt_type == ip6opt_ls) {
112510181SKen.Powell@Sun.COM 			if (*secoptp != NULL)
112610181SKen.Powell@Sun.COM 				/* More than one security option found */
112710181SKen.Powell@Sun.COM 				return (B_FALSE);
112810181SKen.Powell@Sun.COM 			*secoptp = (uchar_t *)optptr;
112910181SKen.Powell@Sun.COM 		} else switch (opt_type) {
11301676Sjpk 		case IP6OPT_PADN:
11311676Sjpk 			break;
11321676Sjpk 		default:
11331676Sjpk 			/*
11341676Sjpk 			 * There is at least 1 option other than
11351676Sjpk 			 * the label option. So the hop-by-hop header is needed
11361676Sjpk 			 */
11371676Sjpk 			*hbh_needed = B_TRUE;
113810181SKen.Powell@Sun.COM 			if (*secoptp != NULL) {
11391676Sjpk 				*after_secoptp = (uchar_t *)optptr;
114010181SKen.Powell@Sun.COM 				return (B_TRUE);
11411676Sjpk 			}
11421676Sjpk 			break;
11431676Sjpk 		}
11441676Sjpk 		optlen -= optused;
11451676Sjpk 		optptr += optused;
11461676Sjpk 	}
114710181SKen.Powell@Sun.COM 	return (B_TRUE);
11481676Sjpk }
11491676Sjpk 
11501676Sjpk /*
11511676Sjpk  * Remove the label option from the hop-by-hop options header if it exists.
11521676Sjpk  * 'buflen' is the total length of the packet typically b_wptr - b_rptr.
11531676Sjpk  * Header and data following the label option that is deleted are copied
11544564Swy83408  * (i.e. slid backward) to the right position, and returns the number
11554564Swy83408  * of bytes removed (as zero or negative number.)
1156*11042SErik.Nordmark@Sun.COM  *
1157*11042SErik.Nordmark@Sun.COM  * Note that tsol_remove_secopt does not adjust ipha_length but
1158*11042SErik.Nordmark@Sun.COM  * tsol_remove_secopt_v6 does adjust ip6_plen.
11591676Sjpk  */
11601676Sjpk int
11611676Sjpk tsol_remove_secopt_v6(ip6_t *ip6h, int buflen)
11621676Sjpk {
11631676Sjpk 	uchar_t	*ip6hbh;	/* hop-by-hop header */
11641676Sjpk 	uint_t	hbhlen;		/* hop-by-hop extension header length */
11651676Sjpk 	uchar_t *secopt = NULL;
11661676Sjpk 	uchar_t *after_secopt;
11671676Sjpk 	uint_t	pad;
11681676Sjpk 	uint_t	delta;
11691676Sjpk 	boolean_t hbh_needed;
11701676Sjpk 
11711676Sjpk 	/*
11721676Sjpk 	 * hop-by-hop extension header must appear first, if it does not
11731676Sjpk 	 * exist, there is no label option.
11741676Sjpk 	 */
11751676Sjpk 	if (ip6h->ip6_nxt != IPPROTO_HOPOPTS)
11761676Sjpk 		return (0);
11771676Sjpk 
11781676Sjpk 	ip6hbh = (uchar_t *)&ip6h[1];
11791676Sjpk 	hbhlen = (ip6hbh[1] + 1) << 3;
11801676Sjpk 	/*
11811676Sjpk 	 * Locate the start of the label option if it exists and the end
11821676Sjpk 	 * of the label option including pads if any.
11831676Sjpk 	 */
118410181SKen.Powell@Sun.COM 	if (!tsol_find_secopt_v6(ip6hbh, hbhlen, &secopt, &after_secopt,
118510181SKen.Powell@Sun.COM 	    &hbh_needed)) {
118610181SKen.Powell@Sun.COM 		/*
118710181SKen.Powell@Sun.COM 		 * This function should not see invalid messages.
118810181SKen.Powell@Sun.COM 		 * If one occurs, it would indicate either an
118910181SKen.Powell@Sun.COM 		 * option previously verified in the forwarding
119010181SKen.Powell@Sun.COM 		 * path has been corrupted or an option was
119110181SKen.Powell@Sun.COM 		 * incorrectly generated locally.
119210181SKen.Powell@Sun.COM 		 */
119310181SKen.Powell@Sun.COM 		ASSERT(0);
119410181SKen.Powell@Sun.COM 		return (0);
119510181SKen.Powell@Sun.COM 	}
11961676Sjpk 	if (secopt == NULL)
11971676Sjpk 		return (0);
11981676Sjpk 	if (!hbh_needed) {
11991676Sjpk 		uchar_t	next_hdr;
12001676Sjpk 		/*
12011676Sjpk 		 * The label option was the only option in the hop-by-hop
12021676Sjpk 		 * header. We don't need the hop-by-hop header itself any
12031676Sjpk 		 * longer.
12041676Sjpk 		 */
12051676Sjpk 		next_hdr = ip6hbh[0];
12061676Sjpk 		ovbcopy(ip6hbh + hbhlen, ip6hbh,
12071676Sjpk 		    buflen - (IPV6_HDR_LEN + hbhlen));
12082776Skp158701 		ip6h->ip6_plen = htons(ntohs(ip6h->ip6_plen) - hbhlen);
12091676Sjpk 		ip6h->ip6_nxt = next_hdr;
12104564Swy83408 		return (-hbhlen);
12111676Sjpk 	}
12121676Sjpk 
12131676Sjpk 	if (after_secopt == NULL) {
12141676Sjpk 		/* There is no option following the label option */
12151676Sjpk 		after_secopt = ip6hbh + hbhlen;
12161676Sjpk 	}
12171676Sjpk 
12181676Sjpk 	/*
12191676Sjpk 	 * After deleting the label option, we need to slide the headers
12201676Sjpk 	 * and data back, while still maintaining the same alignment (module 8)
12211676Sjpk 	 * for the other options. So we slide the headers and data back only
12221676Sjpk 	 * by an integral multiple of 8 bytes, and fill the remaining bytes
12231676Sjpk 	 * with pads.
12241676Sjpk 	 */
12251676Sjpk 	delta = after_secopt - secopt;
12261676Sjpk 	pad = delta % 8;
12271676Sjpk 	if (pad == 1) {
12281676Sjpk 		secopt[0] = IP6OPT_PAD1;
12291676Sjpk 	} else if (pad > 1) {
12301676Sjpk 		secopt[0] = IP6OPT_PADN;
12311676Sjpk 		secopt[1] = pad - 2;
12321676Sjpk 		if (pad > 2)
12331676Sjpk 			bzero(&secopt[2], pad - 2);
12341676Sjpk 	}
12351676Sjpk 	secopt += pad;
12361676Sjpk 	delta -= pad;
12371676Sjpk 	ovbcopy(after_secopt, secopt,
12381676Sjpk 	    (uchar_t *)ip6h + buflen - after_secopt);
12391676Sjpk 	ip6hbh[1] -= delta/8;
12402776Skp158701 	ip6h->ip6_plen = htons(ntohs(ip6h->ip6_plen) - delta);
12411676Sjpk 
12424564Swy83408 	return (-delta);
12431676Sjpk }
12441676Sjpk 
12451676Sjpk /*
12461676Sjpk  * 'optbuf' contains a CIPSO label embedded in an IPv6 hop-by-hop option,
12471676Sjpk  * starting with the IP6OPT_LS option type. The format of this hop-by-hop
12481676Sjpk  * option is described in the block comment above tsol_compute_label_v6.
12491676Sjpk  * This function prepends this hop-by-hop option before any other hop-by-hop
12501676Sjpk  * options in the hop-by-hop header if one already exists, else a new
12511676Sjpk  * hop-by-hop header is created and stuffed into the packet following
12521676Sjpk  * the IPv6 header. 'buflen' is the total length of the packet i.e.
12531676Sjpk  * b_wptr - b_rptr. The caller ensures that there is enough space for the
12541676Sjpk  * extra option being added. Header and data following the position where
12551676Sjpk  * the label option is inserted are copied (i.e. slid forward) to the right
12561676Sjpk  * position.
1257*11042SErik.Nordmark@Sun.COM  *
1258*11042SErik.Nordmark@Sun.COM  * Note that tsol_prepend_option does not adjust ipha_length but
1259*11042SErik.Nordmark@Sun.COM  * tsol_prepend_option_v6 does adjust ip6_plen.
12601676Sjpk  */
12611676Sjpk int
12621676Sjpk tsol_prepend_option_v6(uchar_t *optbuf, ip6_t *ip6h, int buflen)
12631676Sjpk {
12641676Sjpk 	/*
12651676Sjpk 	 * rawlen is the length of the label option in bytes, not including
12661676Sjpk 	 * any pads, starting from the IP6OPT_LS (option type) byte.
12671676Sjpk 	 */
12681676Sjpk 	uint_t	rawlen;
12691676Sjpk 
12701676Sjpk 	uint_t	optlen;		/* rawlen rounded to an 8 byte multiple */
12711676Sjpk 	uchar_t	*ip6hbh;	/* start of the hop-by-hop extension header */
12721676Sjpk 	uint_t	hbhlen;		/* Length of the hop-by-hop extension header */
12731676Sjpk 	uint_t	pad_len;
12741676Sjpk 	uchar_t	*pad_position;
12751676Sjpk 	int	delta;		/* Actual number of bytes inserted */
12761676Sjpk 
12771676Sjpk 	rawlen = optbuf[1] + 2;	/* Add 2 for the option type, option length */
12781676Sjpk 	ip6hbh = (uchar_t *)&ip6h[1];
12791676Sjpk 	if (ip6h->ip6_nxt == IPPROTO_HOPOPTS) {
12801676Sjpk 		/*
12811676Sjpk 		 * There is a hop-by-hop header present already. In order to
12821676Sjpk 		 * preserve the alignment of the other options at the existing
12831676Sjpk 		 * value (modulo 8) we need to pad the label option to a
12841676Sjpk 		 * multiple of 8 bytes before prepending it to the other
12851676Sjpk 		 * options. Slide the extension headers and data forward to
12861676Sjpk 		 * accomodate the label option at the start of the hop-by-hop
12871676Sjpk 		 * header
12881676Sjpk 		 */
12891676Sjpk 		delta = optlen = (rawlen + 7) & ~7;
12901676Sjpk 		pad_len = optlen - rawlen;
12911676Sjpk 		pad_position = ip6hbh + 2 + rawlen;
12921676Sjpk 		ovbcopy(ip6hbh + 2, ip6hbh + 2 + optlen,
12931676Sjpk 		    buflen - (IPV6_HDR_LEN + 2));
12941676Sjpk 		/*
12951676Sjpk 		 * Bump up the hop-by-hop extension header length by
12961676Sjpk 		 * the number of 8-byte words added
12971676Sjpk 		 */
12981676Sjpk 		optlen >>= 3;
12991676Sjpk 		if (ip6hbh[1] + optlen > 255)
13001676Sjpk 			return (-1);
13011676Sjpk 		ip6hbh[1] += optlen;
13021676Sjpk 	} else {
13031676Sjpk 		/*
13041676Sjpk 		 * There is no hop-by-hop header in the packet. Construct a
13051676Sjpk 		 * new Hop-by-hop extension header (a multiple of 8 bytes).
13061676Sjpk 		 * Slide any other extension headers and data forward to
13071676Sjpk 		 * accomodate this hop-by-hop header
13081676Sjpk 		 */
13091676Sjpk 		delta = hbhlen = (2 + rawlen + 7) & ~7; /* +2 for nxthdr, len */
13101676Sjpk 		pad_len = hbhlen - (2 + rawlen);
13111676Sjpk 		pad_position = ip6hbh + 2 + rawlen;
13121676Sjpk 		ovbcopy(ip6hbh, ip6hbh + hbhlen, buflen - IPV6_HDR_LEN);
13131676Sjpk 		ip6hbh[0] = ip6h->ip6_nxt;
13141676Sjpk 		/*
13151676Sjpk 		 * hop-by-hop extension header length in 8-byte words, not
13161676Sjpk 		 * including the 1st 8 bytes of the hop-by-hop header.
13171676Sjpk 		 */
13181676Sjpk 		ip6hbh[1] = (hbhlen >> 3) - 1;
13191676Sjpk 		ip6h->ip6_nxt = IPPROTO_HOPOPTS;
13201676Sjpk 	}
13211676Sjpk 	/*
13221676Sjpk 	 * Copy the label option into the hop-by-hop header and insert any
13231676Sjpk 	 * needed pads
13241676Sjpk 	 */
13251676Sjpk 	bcopy(optbuf, ip6hbh + 2, rawlen);
13261676Sjpk 	if (pad_len == 1) {
13271676Sjpk 		pad_position[0] = IP6OPT_PAD1;
13281676Sjpk 	} else if (pad_len > 1) {
13291676Sjpk 		pad_position[0] = IP6OPT_PADN;
13301676Sjpk 		pad_position[1] = pad_len - 2;
13311676Sjpk 		if (pad_len > 2)
13321676Sjpk 			bzero(pad_position + 2, pad_len - 2);
13331676Sjpk 	}
13342776Skp158701 	ip6h->ip6_plen = htons(ntohs(ip6h->ip6_plen) + delta);
13351676Sjpk 	return (delta);
13361676Sjpk }
13371676Sjpk 
13381676Sjpk /*
13391676Sjpk  * tsol_check_label_v6()
13401676Sjpk  *
13411676Sjpk  * This routine computes the IP label that should be on the packet based on the
1342*11042SErik.Nordmark@Sun.COM  * connection and destination information.  It's called by the IP forwarding
1343*11042SErik.Nordmark@Sun.COM  * logic and by ip_output_simple. The ULPs generate the labels before calling
1344*11042SErik.Nordmark@Sun.COM  * conn_ip_output. If any adjustments to
1345*11042SErik.Nordmark@Sun.COM  * the label are needed due to the connection's MAC-exempt status or
1346*11042SErik.Nordmark@Sun.COM  * the destination's ability to receive labels, an "effective label"
1347*11042SErik.Nordmark@Sun.COM  * will be returned.
1348*11042SErik.Nordmark@Sun.COM  *
1349*11042SErik.Nordmark@Sun.COM  * The packet's header is clear before entering IPsec's engine.
1350*11042SErik.Nordmark@Sun.COM  *
1351*11042SErik.Nordmark@Sun.COM  * The zoneid is the IP zoneid (i.e., GLOBAL_ZONEID for exlusive-IP zones).
1352*11042SErik.Nordmark@Sun.COM  * zone_is_global is set if the actual zoneid is global.
1353*11042SErik.Nordmark@Sun.COM  *
1354*11042SErik.Nordmark@Sun.COM  * On successful return, effective_tslp will point to the new label needed
1355*11042SErik.Nordmark@Sun.COM  * or will be NULL if a new label isn't needed. On error, effective_tsl will
1356*11042SErik.Nordmark@Sun.COM  * point to NULL.
13571676Sjpk  *
13581676Sjpk  * Returns:
1359*11042SErik.Nordmark@Sun.COM  *      0		Label on (was|is now) correct
13609710SKen.Powell@Sun.COM  *      EACCES		The packet failed the remote host accreditation.
13611676Sjpk  *      ENOMEM		Memory allocation failure.
1362*11042SErik.Nordmark@Sun.COM  *	EINVAL		Label cannot be computed
13631676Sjpk  */
13641676Sjpk int
1365*11042SErik.Nordmark@Sun.COM tsol_check_label_v6(const ts_label_t *tsl, zoneid_t zoneid, mblk_t **mpp,
1366*11042SErik.Nordmark@Sun.COM     uint_t mac_mode, boolean_t zone_is_global, ip_stack_t *ipst,
1367*11042SErik.Nordmark@Sun.COM     ts_label_t **effective_tslp)
13681676Sjpk {
13691676Sjpk 	mblk_t *mp = *mpp;
13701676Sjpk 	ip6_t  *ip6h;
1371*11042SErik.Nordmark@Sun.COM 	ts_label_t *effective_tsl = NULL;
13721676Sjpk 	/*
13731676Sjpk 	 * Label option length is limited to IP_MAX_OPT_LENGTH for
13741676Sjpk 	 * symmetry with IPv4. Can be relaxed if needed
13751676Sjpk 	 */
13761676Sjpk 	uchar_t opt_storage[TSOL_MAX_IPV6_OPTION];
13771676Sjpk 	uint_t hlen;
13781676Sjpk 	uint_t sec_opt_len; /* label option length not including type, len */
13796596Skp158701 	int delta_remove = 0, delta_add;
13801676Sjpk 	int retv;
13811676Sjpk 	uchar_t	*after_secopt;
13821676Sjpk 	uchar_t	*secopt = NULL;
13831676Sjpk 	uchar_t	*ip6hbh;
13841676Sjpk 	uint_t	hbhlen;
13851676Sjpk 	boolean_t hbh_needed;
13861676Sjpk 
1387*11042SErik.Nordmark@Sun.COM 	*effective_tslp = NULL;
1388*11042SErik.Nordmark@Sun.COM 
13899710SKen.Powell@Sun.COM 	/*
13909710SKen.Powell@Sun.COM 	 * Verify the destination is allowed to receive packets at
1391*11042SErik.Nordmark@Sun.COM 	 * the security label of the message data. tsol_check_dest()
1392*11042SErik.Nordmark@Sun.COM 	 * may create a new effective label or label flags.
13939710SKen.Powell@Sun.COM 	 */
13941676Sjpk 	ip6h = (ip6_t *)mp->b_rptr;
1395*11042SErik.Nordmark@Sun.COM 	retv = tsol_check_dest(tsl, &ip6h->ip6_dst, IPV6_VERSION,
1396*11042SErik.Nordmark@Sun.COM 	    mac_mode, zone_is_global, &effective_tsl);
13971676Sjpk 	if (retv != 0)
13981676Sjpk 		return (retv);
13991676Sjpk 
14009710SKen.Powell@Sun.COM 	/*
14019710SKen.Powell@Sun.COM 	 * Calculate the security label to be placed in the text
14029710SKen.Powell@Sun.COM 	 * of the message (if any).
14039710SKen.Powell@Sun.COM 	 */
1404*11042SErik.Nordmark@Sun.COM 	if (effective_tsl != NULL) {
1405*11042SErik.Nordmark@Sun.COM 		if ((retv = tsol_compute_label_v6(effective_tsl, zoneid,
14069710SKen.Powell@Sun.COM 		    &ip6h->ip6_dst, opt_storage, ipst)) != 0) {
1407*11042SErik.Nordmark@Sun.COM 			label_rele(effective_tsl);
14089710SKen.Powell@Sun.COM 			return (retv);
14099710SKen.Powell@Sun.COM 		}
1410*11042SErik.Nordmark@Sun.COM 		*effective_tslp = effective_tsl;
14119710SKen.Powell@Sun.COM 	} else {
1412*11042SErik.Nordmark@Sun.COM 		if ((retv = tsol_compute_label_v6(tsl, zoneid,
14139710SKen.Powell@Sun.COM 		    &ip6h->ip6_dst, opt_storage, ipst)) != 0)
14149710SKen.Powell@Sun.COM 			return (retv);
14159710SKen.Powell@Sun.COM 	}
14169710SKen.Powell@Sun.COM 
14171676Sjpk 	sec_opt_len = opt_storage[1];
14181676Sjpk 
14191676Sjpk 	if (ip6h->ip6_nxt == IPPROTO_HOPOPTS) {
14201676Sjpk 		ip6hbh = (uchar_t *)&ip6h[1];
14211676Sjpk 		hbhlen = (ip6hbh[1] + 1) << 3;
142210181SKen.Powell@Sun.COM 		if (!tsol_find_secopt_v6(ip6hbh, hbhlen, &secopt,
142310181SKen.Powell@Sun.COM 		    &after_secopt, &hbh_needed)) {
142410181SKen.Powell@Sun.COM 			/*
142510181SKen.Powell@Sun.COM 			 * This function should not see invalid messages.
142610181SKen.Powell@Sun.COM 			 * If one occurs, it would indicate either an
142710181SKen.Powell@Sun.COM 			 * option previously verified in the forwarding
142810181SKen.Powell@Sun.COM 			 * path has been corrupted or an option was
142910181SKen.Powell@Sun.COM 			 * incorrectly generated locally.
143010181SKen.Powell@Sun.COM 			 */
143110181SKen.Powell@Sun.COM 			ASSERT(0);
143210181SKen.Powell@Sun.COM 			return (EACCES);
143310181SKen.Powell@Sun.COM 		}
14341676Sjpk 	}
14351676Sjpk 
14361676Sjpk 	if (sec_opt_len == 0 && secopt == NULL) {
14371676Sjpk 		/*
14381676Sjpk 		 * The packet is not supposed to have a label, and it
14391676Sjpk 		 * does not have one currently
14401676Sjpk 		 */
14411676Sjpk 		return (0);
14421676Sjpk 	}
144310934Ssommerfeld@sun.com 
14441676Sjpk 	if (secopt != NULL && sec_opt_len != 0 &&
14451676Sjpk 	    (bcmp(opt_storage, secopt, sec_opt_len + 2) == 0)) {
14461676Sjpk 		/* The packet has the correct label already */
14471676Sjpk 		return (0);
14481676Sjpk 	}
14491676Sjpk 
14501676Sjpk 	/*
14511676Sjpk 	 * If there is an option there, then it must be the wrong one; delete.
14521676Sjpk 	 */
14536596Skp158701 	if (secopt != NULL) {
14546596Skp158701 		delta_remove = tsol_remove_secopt_v6(ip6h, MBLKL(mp));
14556596Skp158701 		mp->b_wptr += delta_remove;
14566596Skp158701 	}
14571676Sjpk 
14581676Sjpk 	/*
14591676Sjpk 	 * Make sure we have room for the worst-case addition. Add 2 bytes for
14601676Sjpk 	 * the hop-by-hop ext header's next header and length fields. Add
14611676Sjpk 	 * another 2 bytes for the label option type, len and then round
14621676Sjpk 	 * up to the next 8-byte multiple.
14631676Sjpk 	 */
14641676Sjpk 	hlen = (4 + sec_opt_len + 7) & ~7;
14651676Sjpk 	if (mp->b_wptr + hlen > mp->b_datap->db_lim) {
14661676Sjpk 		int copylen;
14671676Sjpk 		mblk_t *new_mp;
14681676Sjpk 		uint16_t hdr_len;
14691676Sjpk 
14701676Sjpk 		hdr_len = ip_hdr_length_v6(mp, ip6h);
14711676Sjpk 		/*
14721676Sjpk 		 * Allocate enough to be meaningful, but not *too* much.
14731676Sjpk 		 * Also all the IPv6 extension headers must be in the same mblk
14741676Sjpk 		 */
14751676Sjpk 		copylen = MBLKL(mp);
14761676Sjpk 		if (copylen > 256)
14771676Sjpk 			copylen = 256;
14781676Sjpk 		if (copylen < hdr_len)
14791676Sjpk 			copylen = hdr_len;
14808778SErik.Nordmark@Sun.COM 		new_mp = allocb_tmpl(hlen + copylen +
14818778SErik.Nordmark@Sun.COM 		    (mp->b_rptr - mp->b_datap->db_base), mp);
1482*11042SErik.Nordmark@Sun.COM 		if (new_mp == NULL) {
1483*11042SErik.Nordmark@Sun.COM 			if (effective_tsl != NULL) {
1484*11042SErik.Nordmark@Sun.COM 				label_rele(effective_tsl);
1485*11042SErik.Nordmark@Sun.COM 				*effective_tslp = NULL;
1486*11042SErik.Nordmark@Sun.COM 			}
14871676Sjpk 			return (ENOMEM);
1488*11042SErik.Nordmark@Sun.COM 		}
14891676Sjpk 
14901676Sjpk 		/* keep the bias */
14911676Sjpk 		new_mp->b_rptr += mp->b_rptr - mp->b_datap->db_base;
14921676Sjpk 		new_mp->b_wptr = new_mp->b_rptr + copylen;
14931676Sjpk 		bcopy(mp->b_rptr, new_mp->b_rptr, copylen);
14941676Sjpk 		new_mp->b_cont = mp;
14951676Sjpk 		if ((mp->b_rptr += copylen) >= mp->b_wptr) {
14961676Sjpk 			new_mp->b_cont = mp->b_cont;
14971676Sjpk 			freeb(mp);
14981676Sjpk 		}
14991676Sjpk 		*mpp = mp = new_mp;
15001676Sjpk 		ip6h = (ip6_t *)mp->b_rptr;
15011676Sjpk 	}
15021676Sjpk 
15036596Skp158701 	delta_add = tsol_prepend_option_v6(opt_storage, ip6h, MBLKL(mp));
15046596Skp158701 	if (delta_add == -1)
15051676Sjpk 		goto param_prob;
15061676Sjpk 
15076596Skp158701 	ASSERT(mp->b_wptr + delta_add <= DB_LIM(mp));
15086596Skp158701 	mp->b_wptr += delta_add;
15091676Sjpk 
1510*11042SErik.Nordmark@Sun.COM 	/* tsol_prepend_option_v6 has adjusted ip6_plen */
15111676Sjpk 	return (0);
15121676Sjpk 
15131676Sjpk param_prob:
1514*11042SErik.Nordmark@Sun.COM 	if (effective_tsl != NULL) {
1515*11042SErik.Nordmark@Sun.COM 		label_rele(effective_tsl);
1516*11042SErik.Nordmark@Sun.COM 		*effective_tslp = NULL;
1517*11042SErik.Nordmark@Sun.COM 	}
15181676Sjpk 	return (EINVAL);
15191676Sjpk }
1520