xref: /onnv-gate/usr/src/uts/common/inet/ip/tn_ipopt.c (revision 4564:534db075062d)
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 /*
223448Sdh155122  * Copyright 2007 Sun Microsystems, Inc.  All rights reserved.
231676Sjpk  * Use is subject to license terms.
241676Sjpk  */
251676Sjpk 
261676Sjpk #pragma ident	"%Z%%M%	%I%	%E% SMI"
271676Sjpk 
281676Sjpk #include <sys/types.h>
291676Sjpk #include <sys/systm.h>
301676Sjpk #include <sys/kmem.h>
311676Sjpk #include <sys/disp.h>
321676Sjpk #include <sys/stream.h>
331676Sjpk #include <sys/strsubr.h>
341676Sjpk #include <sys/strsun.h>
351676Sjpk #include <sys/policy.h>
361676Sjpk #include <sys/tsol/label_macro.h>
371676Sjpk #include <sys/tsol/tndb.h>
381676Sjpk #include <sys/tsol/tnet.h>
391676Sjpk #include <inet/ip.h>
401676Sjpk #include <inet/ip6.h>
411676Sjpk #include <inet/tcp.h>
421676Sjpk #include <inet/ipclassifier.h>
431676Sjpk #include <inet/ip_ire.h>
442535Ssangeeta #include <inet/ip_ftable.h>
451676Sjpk 
461676Sjpk /*
471676Sjpk  * This routine takes a sensitivity label as input and creates a CIPSO
481676Sjpk  * option in the specified buffer.  It returns the size of the CIPSO option.
491676Sjpk  * If the sensitivity label is too large for the CIPSO option, then 0
501676Sjpk  * is returned.
511676Sjpk  *
521676Sjpk  * tsol2cipso_tt1 returns 0 for failure and greater than 0 for success
531676Sjpk  * (more accurately, success means a return value between 10 and 40).
541676Sjpk  */
551676Sjpk 
561676Sjpk static int
571676Sjpk tsol2cipso_tt1(const bslabel_t *sl, unsigned char *cop, uint32_t doi)
581676Sjpk {
591676Sjpk 	struct cipso_tag_type_1 *tt1;
601676Sjpk 	const _bslabel_impl_t *bsl;
611676Sjpk 	const uchar_t *ucp;
621676Sjpk 	int i;
631676Sjpk 
641676Sjpk 	if (doi == 0)
651676Sjpk 		return (0);
661676Sjpk 
671676Sjpk 	/* check for Admin High sensitivity label */
681676Sjpk 	if (blequal(sl, label2bslabel(l_admin_high)))
691676Sjpk 		return (0);
701676Sjpk 
711676Sjpk 	/* check whether classification will fit in one octet */
721676Sjpk 	bsl = (const _bslabel_impl_t *)sl;
731676Sjpk 	if (LCLASS(bsl) & 0xFF00)
741676Sjpk 		return (0);
751676Sjpk 
761676Sjpk 	/*
771676Sjpk 	 * Check whether compartments will fit in 30 octets.
781676Sjpk 	 * Compartments 241 - 256 are not allowed.
791676Sjpk 	 */
801676Sjpk 	if (ntohl(bsl->compartments.c8) & 0x0000FFFF)
811676Sjpk 		return (0);
821676Sjpk 
831676Sjpk 	/*
841676Sjpk 	 * Compute option length and tag length.
851676Sjpk 	 * 'p' points to the last two bytes in the Sensitivity Label's
861676Sjpk 	 * compartments; these cannot be mapped into CIPSO compartments.
871676Sjpk 	 */
881676Sjpk 	ucp = (const uchar_t *)&bsl->compartments.c8 + 2;
891676Sjpk 	while (--ucp >= (const uchar_t *)&bsl->compartments.c1)
901676Sjpk 		if (*ucp != 0)
911676Sjpk 			break;
921676Sjpk 
931676Sjpk 	i =  ucp - (const uchar_t *)&bsl->compartments.c1 + 1;
941676Sjpk 
951676Sjpk 	if (cop == NULL)
961676Sjpk 		return (10 + i);
971676Sjpk 
981676Sjpk 	doi = htonl(doi);
991676Sjpk 	ucp = (const uchar_t *)&doi;
1001676Sjpk 	cop[IPOPT_OPTVAL] = IPOPT_COMSEC;
1011676Sjpk 	cop[IPOPT_OLEN] = 10 + i;
1021676Sjpk 	cop[IPOPT_OLEN+1] = ucp[0];
1031676Sjpk 	cop[IPOPT_OLEN+2] = ucp[1];
1041676Sjpk 	cop[IPOPT_OLEN+3] = ucp[2];
1051676Sjpk 	cop[IPOPT_OLEN+4] = ucp[3];
1061676Sjpk 	tt1 = (struct cipso_tag_type_1 *)&cop[IPOPT_OLEN + 5];
1071676Sjpk 	tt1->tag_type = 1;
1081676Sjpk 	tt1->tag_align = 0;
1091676Sjpk 	tt1->tag_sl = LCLASS(bsl);
1101676Sjpk 	tt1->tag_length = 4 + i;
1111676Sjpk 
1121676Sjpk 	bcopy(&bsl->compartments.c1, tt1->tag_cat, i);
1131676Sjpk 
1141676Sjpk 	return (cop[IPOPT_OLEN]);
1151676Sjpk }
1161676Sjpk 
1171676Sjpk /*
1181676Sjpk  * The following routine copies a datagram's option into the specified buffer
1191676Sjpk  * (if buffer pointer is non-null), or returns a pointer to the label within
1201676Sjpk  * the streams message (if buffer is null).  In both cases, tsol_get_option
1211676Sjpk  * returns the option's type.
1221676Sjpk  *
1231676Sjpk  * tsol_get_option assumes that the specified buffer is large enough to
1241676Sjpk  * hold the largest valid CIPSO option.  Since the total number of
1251676Sjpk  * IP header options cannot exceed 40 bytes, a 40 byte buffer is a good choice.
1261676Sjpk  */
1271676Sjpk 
1281676Sjpk tsol_ip_label_t
1291676Sjpk tsol_get_option(mblk_t *mp, 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 
1371676Sjpk 	ipha = (ipha_t *)mp->b_rptr;
1381676Sjpk 
1391676Sjpk 	/*
1401676Sjpk 	 * Get length (in 4 byte octets) of IP header options.
1411676Sjpk 	 * If header doesn't contain options, then return OPT_NONE.
1421676Sjpk 	 */
1431676Sjpk 	totallen = ipha->ipha_version_and_hdr_length -
1441676Sjpk 	    (uint8_t)((IP_VERSION << 4) + IP_SIMPLE_HDR_LENGTH_IN_WORDS);
1451676Sjpk 
1461676Sjpk 	if (totallen == 0)
1471676Sjpk 		return (OPT_NONE);
1481676Sjpk 
1491676Sjpk 	totallen <<= 2;
1501676Sjpk 
1511676Sjpk 	/*
1521676Sjpk 	 * Search for CIPSO option.
1531676Sjpk 	 * If no such option is present, then return OPT_NONE.
1541676Sjpk 	 */
1551676Sjpk 	opt = (uchar_t *)&ipha[1];
1561676Sjpk 	while (totallen != 0) {
1571676Sjpk 		switch (optval = opt[IPOPT_OPTVAL]) {
1581676Sjpk 		case IPOPT_EOL:
1591676Sjpk 			return (OPT_NONE);
1601676Sjpk 		case IPOPT_NOP:
1611676Sjpk 			optlen = 1;
1621676Sjpk 			break;
1631676Sjpk 		default:
1641676Sjpk 			if (totallen <= IPOPT_OLEN)
1651676Sjpk 				return (OPT_NONE);
1661676Sjpk 			optlen = opt[IPOPT_OLEN];
1671676Sjpk 			if (optlen < 2)
1681676Sjpk 				return (OPT_NONE);
1691676Sjpk 		}
1701676Sjpk 		if (optlen > totallen)
1711676Sjpk 			return (OPT_NONE);
1721676Sjpk 		/*
1731676Sjpk 		 * Copy pointer to option into '*buffer' and
1741676Sjpk 		 * return the option type.
1751676Sjpk 		 */
1761676Sjpk 		switch (optval) {
1771676Sjpk 		case IPOPT_COMSEC:
1781676Sjpk 			*buffer = opt;
1791676Sjpk 			if (TSOL_CIPSO_TAG_OFFSET < optlen &&
1801676Sjpk 			    opt[TSOL_CIPSO_TAG_OFFSET] == 1)
1811676Sjpk 				return (OPT_CIPSO);
1821676Sjpk 			return (OPT_NONE);
1831676Sjpk 		}
1841676Sjpk 		totallen -= optlen;
1851676Sjpk 		opt += optlen;
1861676Sjpk 	}
1871676Sjpk 	return (OPT_NONE);
1881676Sjpk }
1891676Sjpk 
1901676Sjpk /*
1911676Sjpk  * tsol_compute_label()
1921676Sjpk  *
1931676Sjpk  * This routine computes the IP label that should be on a packet based on the
1941676Sjpk  * connection and destination information.
1951676Sjpk  *
1961676Sjpk  * Returns:
1971676Sjpk  *      0		Fetched label
1981676Sjpk  *      EACCES		The packet failed the remote host accreditation
1991676Sjpk  *      ENOMEM		Memory allocation failure
2001676Sjpk  *	EINVAL		Label cannot be computed
2011676Sjpk  */
2021676Sjpk int
2031676Sjpk tsol_compute_label(const cred_t *credp, ipaddr_t dst, uchar_t *opt_storage,
2043448Sdh155122     boolean_t isexempt, ip_stack_t *ipst)
2051676Sjpk {
2061676Sjpk 	uint_t		sec_opt_len;
2071676Sjpk 	ts_label_t	*tsl;
2081676Sjpk 	tsol_tpc_t	*dst_rhtp;
2091676Sjpk 	ire_t		*ire, *sire = NULL;
2101676Sjpk 	boolean_t	compute_label = B_FALSE;
2111676Sjpk 	tsol_ire_gw_secattr_t *attrp;
2123448Sdh155122 	zoneid_t	zoneid, ip_zoneid;
2131676Sjpk 
2141676Sjpk 	if (opt_storage != NULL)
2151676Sjpk 		opt_storage[IPOPT_OLEN] = 0;
2161676Sjpk 
2171676Sjpk 	if ((tsl = crgetlabel(credp)) == NULL)
2181676Sjpk 		return (0);
2191676Sjpk 
2201676Sjpk 	/* always pass multicast */
2211676Sjpk 	if (CLASSD(dst))
2221676Sjpk 		return (0);
2231676Sjpk 
2241676Sjpk 	if ((dst_rhtp = find_tpc(&dst, IPV4_VERSION, B_FALSE)) == NULL) {
2251676Sjpk 		DTRACE_PROBE3(tx__tnopt__log__info__labeling__lookupdst__v4,
2261676Sjpk 		    char *, "destination ip(1) not in database (with creds(2))",
2271676Sjpk 		    ipaddr_t, dst, cred_t *, credp);
2281676Sjpk 		return (EINVAL);
2291676Sjpk 	}
2301676Sjpk 
2311676Sjpk 	zoneid = crgetzoneid(credp);
2321676Sjpk 
2333448Sdh155122 	/*
2343448Sdh155122 	 * For exclusive stacks we set the zoneid to zero
2353448Sdh155122 	 * to operate as if in the global zone for IRE and conn_t comparisons.
2363448Sdh155122 	 */
2373448Sdh155122 	if (ipst->ips_netstack->netstack_stackid != GLOBAL_NETSTACKID)
2383448Sdh155122 		ip_zoneid = GLOBAL_ZONEID;
2393448Sdh155122 	else
2403448Sdh155122 		ip_zoneid = zoneid;
2413448Sdh155122 
2421676Sjpk 	switch (dst_rhtp->tpc_tp.host_type) {
2431676Sjpk 	case UNLABELED:
2441676Sjpk 		/*
2451676Sjpk 		 * Only add a label if the unlabeled destination is
2461676Sjpk 		 * not broadcast/local/loopback address, that it is
2471676Sjpk 		 * not on the same subnet, and that the next-hop
2481676Sjpk 		 * gateway is labeled.
2491676Sjpk 		 */
2503448Sdh155122 		ire = ire_cache_lookup(dst, ip_zoneid, tsl, ipst);
2511676Sjpk 
2521676Sjpk 		if (ire != NULL && (ire->ire_type & (IRE_BROADCAST | IRE_LOCAL |
2531676Sjpk 		    IRE_LOOPBACK | IRE_INTERFACE)) != 0) {
2541676Sjpk 			IRE_REFRELE(ire);
2551676Sjpk 			TPC_RELE(dst_rhtp);
2561676Sjpk 			return (0);
2571676Sjpk 		} else if (ire == NULL) {
2581676Sjpk 			ire = ire_ftable_lookup(dst, 0, 0, 0, NULL, &sire,
2593448Sdh155122 			    ip_zoneid, 0, tsl, (MATCH_IRE_RECURSIVE |
2603448Sdh155122 				MATCH_IRE_DEFAULT | MATCH_IRE_SECATTR), ipst);
2611676Sjpk 		}
2621676Sjpk 
2631676Sjpk 		/* no route to destination */
2641676Sjpk 		if (ire == NULL) {
2651676Sjpk 			DTRACE_PROBE4(
2661676Sjpk 			    tx__tnopt__log__info__labeling__routedst__v4,
2671676Sjpk 			    char *, "No route to unlabeled dest ip(1)/tpc(2) "
2681676Sjpk 			    "with creds(3).", ipaddr_t, dst, tsol_tpc_t *,
2691676Sjpk 			    dst_rhtp, cred_t *, credp);
2701676Sjpk 			TPC_RELE(dst_rhtp);
2711676Sjpk 			return (EINVAL);
2721676Sjpk 		}
2731676Sjpk 
2741676Sjpk 		/*
2751676Sjpk 		 * Prefix IRE from f-table lookup means that the destination
2761676Sjpk 		 * is not directly connected; check the next-hop attributes.
2771676Sjpk 		 */
2781676Sjpk 		if (sire != NULL) {
2791676Sjpk 			ASSERT(ire != NULL);
2801676Sjpk 			IRE_REFRELE(ire);
2811676Sjpk 			ire = sire;
2821676Sjpk 		}
2831676Sjpk 
2841676Sjpk 		attrp = ire->ire_gw_secattr;
2851676Sjpk 		if (attrp != NULL && attrp->igsa_rhc != NULL &&
2861676Sjpk 		    attrp->igsa_rhc->rhc_tpc->tpc_tp.host_type != UNLABELED)
2871676Sjpk 			compute_label = B_TRUE;
2881676Sjpk 
2891676Sjpk 		/*
2901676Sjpk 		 * Can talk to unlabeled hosts if
2911676Sjpk 		 * (1) zone's label matches the default label, or
2921676Sjpk 		 * (2) SO_MAC_EXEMPT is on and we dominate the peer's label
2931676Sjpk 		 * (3) SO_MAC_EXEMPT is on and this is the global zone
2941676Sjpk 		 */
2951676Sjpk 		if (dst_rhtp->tpc_tp.tp_doi != tsl->tsl_doi ||
2961676Sjpk 		    (!blequal(&dst_rhtp->tpc_tp.tp_def_label,
2971676Sjpk 		    &tsl->tsl_label) && (!isexempt ||
2981676Sjpk 		    (zoneid != GLOBAL_ZONEID && !bldominates(&tsl->tsl_label,
2991676Sjpk 		    &dst_rhtp->tpc_tp.tp_def_label))))) {
3001676Sjpk 			DTRACE_PROBE4(tx__tnopt__log__info__labeling__mac__v4,
3011676Sjpk 			    char *, "unlabeled dest ip(1)/tpc(2) "
3021676Sjpk 			    "non-matching creds(3).", ipaddr_t, dst,
3031676Sjpk 			    tsol_tpc_t *, dst_rhtp, cred_t *, credp);
3041676Sjpk 			IRE_REFRELE(ire);
3051676Sjpk 			TPC_RELE(dst_rhtp);
3061676Sjpk 			return (EACCES);
3071676Sjpk 		}
3081676Sjpk 
3091676Sjpk 		IRE_REFRELE(ire);
3101676Sjpk 		break;
3111676Sjpk 
3121676Sjpk 	case SUN_CIPSO:
3131676Sjpk 		/*
3141676Sjpk 		 * Can talk to labeled hosts if zone's label is within target's
3151676Sjpk 		 * label range or set.
3161676Sjpk 		 */
3171676Sjpk 		if (dst_rhtp->tpc_tp.tp_cipso_doi_cipso != tsl->tsl_doi ||
3181676Sjpk 		    (!_blinrange(&tsl->tsl_label,
3191676Sjpk 		    &dst_rhtp->tpc_tp.tp_sl_range_cipso) &&
3201676Sjpk 		    !blinlset(&tsl->tsl_label,
3211676Sjpk 		    dst_rhtp->tpc_tp.tp_sl_set_cipso))) {
3221676Sjpk 			DTRACE_PROBE4(tx__tnopt__log__info__labeling__mac__v4,
3231676Sjpk 			    char *, "labeled dest ip(1)/tpc(2) "
3241676Sjpk 			    "non-matching creds(3).", ipaddr_t, dst,
3251676Sjpk 			    tsol_tpc_t *, dst_rhtp, cred_t *, credp);
3261676Sjpk 			TPC_RELE(dst_rhtp);
3271676Sjpk 			return (EACCES);
3281676Sjpk 		}
3291676Sjpk 		compute_label = B_TRUE;
3301676Sjpk 		break;
3311676Sjpk 
3321676Sjpk 	default:
3331676Sjpk 		TPC_RELE(dst_rhtp);
3341676Sjpk 		return (EACCES);
3351676Sjpk 	}
3361676Sjpk 
3371676Sjpk 	if (!compute_label) {
3381676Sjpk 		TPC_RELE(dst_rhtp);
3391676Sjpk 		return (0);
3401676Sjpk 	}
3411676Sjpk 
3421676Sjpk 	/* compute the CIPSO option */
3431676Sjpk 	if (dst_rhtp->tpc_tp.host_type != UNLABELED)
3441676Sjpk 		sec_opt_len = tsol2cipso_tt1(&tsl->tsl_label, opt_storage,
3451676Sjpk 		    tsl->tsl_doi);
3461676Sjpk 	else
3471676Sjpk 		sec_opt_len = tsol2cipso_tt1(&dst_rhtp->tpc_tp.tp_def_label,
3481676Sjpk 		    opt_storage, tsl->tsl_doi);
3491676Sjpk 	TPC_RELE(dst_rhtp);
3501676Sjpk 
3511676Sjpk 	if (sec_opt_len == 0) {
3521676Sjpk 		DTRACE_PROBE4(tx__tnopt__log__error__labeling__lostops__v4,
3531676Sjpk 		    char *,
3541676Sjpk 		    "options lack length for dest ip(1)/tpc(2) with creds(3).",
3551676Sjpk 		    ipaddr_t, dst, tsol_tpc_t *, dst_rhtp, cred_t *, credp);
3561676Sjpk 		return (EINVAL);
3571676Sjpk 	}
3581676Sjpk 
3591676Sjpk 	return (0);
3601676Sjpk }
3611676Sjpk 
3621676Sjpk /*
3631676Sjpk  * Remove any existing security option (CIPSO) from the given IP
3641676Sjpk  * header, move the 'buflen' bytes back to fill the gap, and return the number
3651676Sjpk  * of bytes removed (as zero or negative number).  Assumes that the headers are
3661676Sjpk  * sane.
3671676Sjpk  */
3681676Sjpk int
3691676Sjpk tsol_remove_secopt(ipha_t *ipha, int buflen)
3701676Sjpk {
3711676Sjpk 	int remlen, olen, oval, delta;
3721676Sjpk 	uchar_t *fptr, *tptr;
3731676Sjpk 	boolean_t noop_keep;
3741676Sjpk 
3751676Sjpk 	remlen = IPH_HDR_LENGTH(ipha) - IP_SIMPLE_HDR_LENGTH;
3761676Sjpk 	fptr = tptr = (uchar_t *)(ipha + 1);
3771676Sjpk 	noop_keep = B_TRUE;
3781676Sjpk 	while (remlen > 0) {
3791676Sjpk 		oval = fptr[IPOPT_OPTVAL];
3801676Sjpk 
3811676Sjpk 		/* terminate on end of list */
3821676Sjpk 		if (oval == IPOPT_EOL)
3831676Sjpk 			break;
3841676Sjpk 
3851676Sjpk 		/*
3861676Sjpk 		 * Delete any no-ops following a deleted option, at least up
3871676Sjpk 		 * to a 4 octet alignment; copy others.
3881676Sjpk 		 */
3891676Sjpk 		if (oval == IPOPT_NOP) {
3901676Sjpk 			if (((fptr - (uchar_t *)ipha) & 3) == 0)
3911676Sjpk 				noop_keep = B_TRUE;
3921676Sjpk 			if (noop_keep)
3931676Sjpk 				*tptr++ = oval;
3941676Sjpk 			fptr++;
3951676Sjpk 			remlen--;
3961676Sjpk 			continue;
3971676Sjpk 		}
3981676Sjpk 
3991676Sjpk 		/* stop on corrupted list; just do nothing. */
4001676Sjpk 		if (remlen < 2)
4011676Sjpk 			return (0);
4021676Sjpk 		olen = fptr[IPOPT_OLEN];
4031676Sjpk 		if (olen < 2 || olen > remlen)
4041676Sjpk 			return (0);
4051676Sjpk 
4061676Sjpk 		/* skip over security options to delete them */
4071676Sjpk 		if (oval == IPOPT_COMSEC || oval == IPOPT_SECURITY) {
4081676Sjpk 			noop_keep = B_FALSE;
4091676Sjpk 			fptr += olen;
4101676Sjpk 			remlen -= olen;
4111676Sjpk 			continue;
4121676Sjpk 		}
4131676Sjpk 
4141676Sjpk 		/* copy the rest */
4151676Sjpk 		noop_keep = B_TRUE;
4161676Sjpk 		if (tptr != fptr)
4171676Sjpk 			ovbcopy(fptr, tptr, olen);
4181676Sjpk 		fptr += olen;
4191676Sjpk 		tptr += olen;
4201676Sjpk 		remlen -= olen;
4211676Sjpk 	}
4221676Sjpk 
4231676Sjpk 	fptr += remlen;
4241676Sjpk 
4251676Sjpk 	/* figure how much padding we'll need for header alignment */
4261676Sjpk 	olen = (tptr - (uchar_t *)ipha) & 3;
4271676Sjpk 	if (olen > 0) {
4281676Sjpk 		olen = 4 - olen;
4291676Sjpk 		/* pad with end-of-list */
4301676Sjpk 		bzero(tptr, olen);
4311676Sjpk 		tptr += olen;
4321676Sjpk 	}
4331676Sjpk 
4341676Sjpk 	/* slide back the headers that follow and update the IP header */
4351676Sjpk 	delta = fptr - tptr;
4361676Sjpk 	if (delta != 0) {
4371676Sjpk 		ovbcopy(fptr, tptr, ((uchar_t *)ipha + buflen) - fptr);
4381676Sjpk 		ipha->ipha_version_and_hdr_length -= delta / 4;
4391676Sjpk 	}
4401676Sjpk 	return (-delta);
4411676Sjpk }
4421676Sjpk 
4431676Sjpk /*
4441676Sjpk  * Insert the option in 'optbuf' into the IP header pointed to by 'ipha', and
4451676Sjpk  * move the data following the IP header (up to buflen) to accomodate the new
4461676Sjpk  * option.  Assumes that up to IP_MAX_OPT_LENGTH bytes are available (in total)
4471676Sjpk  * for IP options.  Returns the number of bytes actually inserted, or -1 if the
4481676Sjpk  * option cannot be inserted.  (Note that negative return values are possible
4491676Sjpk  * when noops must be compressed, and that only -1 indicates error.  Successful
4501676Sjpk  * return value is always evenly divisible by 4, by definition.)
4511676Sjpk  */
4521676Sjpk int
4531676Sjpk tsol_prepend_option(uchar_t *optbuf, ipha_t *ipha, int buflen)
4541676Sjpk {
4551676Sjpk 	int remlen, padding, lastpad, totlen;
4561676Sjpk 	int oval, olen;
4571676Sjpk 	int delta;
4581676Sjpk 	uchar_t *optr;
4591676Sjpk 	uchar_t tempopt[IP_MAX_OPT_LENGTH], *toptr;
4601676Sjpk 
4611676Sjpk 	if (optbuf[IPOPT_OPTVAL] == IPOPT_EOL ||
4621676Sjpk 	    optbuf[IPOPT_OPTVAL] == IPOPT_NOP ||
4631676Sjpk 	    optbuf[IPOPT_OLEN] == 0)
4641676Sjpk 		return (0);
4651676Sjpk 
4661676Sjpk 	ASSERT(optbuf[IPOPT_OLEN] >= 2 &&
4671676Sjpk 	    optbuf[IPOPT_OLEN] <= IP_MAX_OPT_LENGTH);
4681676Sjpk 
4691676Sjpk 	/* first find the real (unpadded) length of the existing options */
4701676Sjpk 	remlen = IPH_HDR_LENGTH(ipha) - IP_SIMPLE_HDR_LENGTH;
4711676Sjpk 	padding = totlen = lastpad = 0;
4721676Sjpk 	optr = (uchar_t *)(ipha + 1);
4731676Sjpk 	while (remlen > 0) {
4741676Sjpk 		oval = optr[IPOPT_OPTVAL];
4751676Sjpk 
4761676Sjpk 		/* stop at end of list */
4771676Sjpk 		if (oval == IPOPT_EOL)
4781676Sjpk 			break;
4791676Sjpk 
4801676Sjpk 		/* skip no-ops, noting that length byte isn't present */
4811676Sjpk 		if (oval == IPOPT_NOP) {
4821676Sjpk 			optr++;
4831676Sjpk 			padding++;
4841676Sjpk 			lastpad++;
4851676Sjpk 			totlen++;
4861676Sjpk 			remlen--;
4871676Sjpk 			continue;
4881676Sjpk 		}
4891676Sjpk 
4901676Sjpk 		/* give up on a corrupted list; report failure */
4911676Sjpk 		if (remlen < 2)
4921676Sjpk 			return (-1);
4931676Sjpk 		olen = optr[IPOPT_OLEN];
4941676Sjpk 		if (olen < 2 || olen > remlen)
4951676Sjpk 			return (-1);
4961676Sjpk 
4971676Sjpk 		lastpad = 0;
4981676Sjpk 		optr += olen;
4991676Sjpk 		totlen += olen;
5001676Sjpk 		remlen -= olen;
5011676Sjpk 	}
5021676Sjpk 
5031676Sjpk 	/* completely ignore any trailing padding */
5041676Sjpk 	totlen -= lastpad;
5051676Sjpk 	padding -= lastpad;
5061676Sjpk 
5071676Sjpk 	/*
5081676Sjpk 	 * If some sort of inter-option alignment was present, try to preserve
5091676Sjpk 	 * that alignment.  If alignment pushes us out past the maximum, then
5101676Sjpk 	 * discard it and try to compress to fit.  (We just "assume" that any
5111676Sjpk 	 * padding added was attempting to get 32 bit alignment.  If that's
5121676Sjpk 	 * wrong, that's just too bad.)
5131676Sjpk 	 */
5141676Sjpk 	if (padding > 0) {
5151676Sjpk 		olen = (optbuf[IPOPT_OLEN] + 3) & ~3;
5161676Sjpk 		if (olen + totlen > IP_MAX_OPT_LENGTH) {
5171676Sjpk 			totlen -= padding;
5181676Sjpk 			if (olen + totlen > IP_MAX_OPT_LENGTH)
5191676Sjpk 				return (-1);
5201676Sjpk 			padding = 0;
5211676Sjpk 		}
5221676Sjpk 	}
5231676Sjpk 
5241676Sjpk 	/*
5251676Sjpk 	 * Since we may need to compress or expand the option list, we write to
5261676Sjpk 	 * a temporary buffer and then copy the results back to the IP header.
5271676Sjpk 	 */
5281676Sjpk 	toptr = tempopt;
5291676Sjpk 
5301676Sjpk 	/* compute actual option to insert */
5311676Sjpk 	olen = optbuf[IPOPT_OLEN];
5321676Sjpk 	bcopy(optbuf, toptr, olen);
5331676Sjpk 	toptr += olen;
5341676Sjpk 	if (padding > 0) {
5351676Sjpk 		while ((olen & 3) != 0) {
5361676Sjpk 			*toptr++ = IPOPT_NOP;
5371676Sjpk 			olen++;
5381676Sjpk 		}
5391676Sjpk 	}
5401676Sjpk 
5411676Sjpk 	/* copy over the existing options */
5421676Sjpk 	optr = (uchar_t *)(ipha + 1);
5431676Sjpk 	while (totlen > 0) {
5441676Sjpk 		oval = optr[IPOPT_OPTVAL];
5451676Sjpk 
5461676Sjpk 		/* totlen doesn't include end-of-list marker */
5471676Sjpk 		ASSERT(oval != IPOPT_EOL);
5481676Sjpk 
5491676Sjpk 		/* handle no-ops; copy if desired, ignore otherwise */
5501676Sjpk 		if (oval == IPOPT_NOP) {
5511676Sjpk 			if (padding > 0) {
5521676Sjpk 				/* note: cannot overflow due to checks above */
5531676Sjpk 				ASSERT(toptr < tempopt + IP_MAX_OPT_LENGTH);
5541676Sjpk 				*toptr++ = oval;
5551676Sjpk 			}
5561676Sjpk 			optr++;
5571676Sjpk 			totlen--;
5581676Sjpk 			continue;
5591676Sjpk 		}
5601676Sjpk 
5611676Sjpk 		/* list cannot be corrupt at this point */
5621676Sjpk 		ASSERT(totlen >= 2);
5631676Sjpk 		olen = optr[IPOPT_OLEN];
5641676Sjpk 		ASSERT(olen >= 2 && olen <= totlen);
5651676Sjpk 
5661676Sjpk 		/* cannot run out of room due to tests above */
5671676Sjpk 		ASSERT(toptr + olen <= tempopt + IP_MAX_OPT_LENGTH);
5681676Sjpk 
5691676Sjpk 		bcopy(optr, toptr, olen);
5701676Sjpk 		optr += olen;
5711676Sjpk 		toptr += olen;
5721676Sjpk 		totlen -= olen;
5731676Sjpk 	}
5741676Sjpk 
5751676Sjpk 	/* figure how much padding we'll need for header alignment */
5761676Sjpk 	olen = (toptr - tempopt) & 3;
5771676Sjpk 	if (olen > 0) {
5781676Sjpk 		olen = 4 - olen;
5791676Sjpk 		ASSERT(toptr + olen <= tempopt + IP_MAX_OPT_LENGTH);
5801676Sjpk 		/* pad with end-of-list value */
5811676Sjpk 		bzero(toptr, olen);
5821676Sjpk 		toptr += olen;
5831676Sjpk 	}
5841676Sjpk 
5851676Sjpk 	/* move the headers as needed and update IP header */
5861676Sjpk 	olen = (toptr - tempopt) + IP_SIMPLE_HDR_LENGTH;
5871676Sjpk 	remlen = IPH_HDR_LENGTH(ipha);
5881676Sjpk 	delta = olen - remlen;
5891676Sjpk 	if (delta != 0) {
5901676Sjpk 		ovbcopy((uchar_t *)ipha + remlen, (uchar_t *)ipha + olen,
5911676Sjpk 		    buflen - remlen);
5921676Sjpk 		ipha->ipha_version_and_hdr_length += delta / 4;
5931676Sjpk 	}
5941676Sjpk 
5951676Sjpk 	/* slap in the new options */
5961676Sjpk 	bcopy(tempopt, ipha + 1, olen - IP_SIMPLE_HDR_LENGTH);
5971676Sjpk 
5981676Sjpk 	return (delta);
5991676Sjpk }
6001676Sjpk 
6011676Sjpk /*
6021676Sjpk  * tsol_check_label()
6031676Sjpk  *
6041676Sjpk  * This routine computes the IP label that should be on the packet based on the
6051676Sjpk  * connection and destination information.  If the label is there, it returns
6061676Sjpk  * zero, so the caller knows that the label is syncronized, and further calls
6071676Sjpk  * are not required.  If the label isn't right, then the right one is inserted.
6081676Sjpk  *
6091676Sjpk  * The packet's header is clear, before entering IPSec's engine.
6101676Sjpk  *
6111676Sjpk  * Returns:
6121676Sjpk  *      0		Label on packet (was|is now) correct
6131676Sjpk  *      EACCES		The packet failed the remote host accreditation.
6141676Sjpk  *      ENOMEM		Memory allocation failure.
6151676Sjpk  *	EINVAL		Label cannot be computed
6161676Sjpk  */
6171676Sjpk int
6181676Sjpk tsol_check_label(const cred_t *credp, mblk_t **mpp, int *addedp,
6193448Sdh155122     boolean_t isexempt, ip_stack_t *ipst)
6201676Sjpk {
6211676Sjpk 	mblk_t *mp = *mpp;
6221676Sjpk 	ipha_t  *ipha;
6231676Sjpk 	uchar_t opt_storage[IP_MAX_OPT_LENGTH];
6241676Sjpk 	uint_t hlen;
6251676Sjpk 	uint_t sec_opt_len;
6261676Sjpk 	uchar_t *optr;
6271676Sjpk 	int added;
6281676Sjpk 	int retv;
6291676Sjpk 
6301676Sjpk 	if (addedp != NULL)
6311676Sjpk 		*addedp = 0;
6321676Sjpk 
6331676Sjpk 	opt_storage[IPOPT_OPTVAL] = 0;
6341676Sjpk 
6351676Sjpk 	ipha = (ipha_t *)mp->b_rptr;
6361676Sjpk 
6373448Sdh155122 	retv = tsol_compute_label(credp, ipha->ipha_dst, opt_storage, isexempt,
6383448Sdh155122 	    ipst);
6391676Sjpk 	if (retv != 0)
6401676Sjpk 		return (retv);
6411676Sjpk 
6421676Sjpk 	optr = (uchar_t *)(ipha + 1);
6431676Sjpk 	hlen = IPH_HDR_LENGTH(ipha) - IP_SIMPLE_HDR_LENGTH;
6441676Sjpk 	sec_opt_len = opt_storage[IPOPT_OLEN];
6451676Sjpk 
6461676Sjpk 	if (hlen >= sec_opt_len) {
6471676Sjpk 		/* If no option is supposed to be there, make sure it's not */
6481676Sjpk 		if (sec_opt_len == 0 && hlen > 0 &&
6491676Sjpk 		    optr[IPOPT_OPTVAL] != IPOPT_COMSEC &&
6501676Sjpk 		    optr[IPOPT_OPTVAL] != IPOPT_SECURITY)
6511676Sjpk 			return (0);
6521676Sjpk 		/* if the option is there, it's always first */
6531676Sjpk 		if (sec_opt_len != 0 &&
6541676Sjpk 		    bcmp(opt_storage, optr, sec_opt_len) == 0)
6551676Sjpk 			return (0);
6561676Sjpk 	}
6571676Sjpk 
6581676Sjpk 	/*
6591676Sjpk 	 * If there is an option there, then it must be the wrong one; delete.
6601676Sjpk 	 */
6611676Sjpk 	if (hlen > 0)
6621676Sjpk 		mp->b_wptr += tsol_remove_secopt(ipha, MBLKL(mp));
6631676Sjpk 
6641676Sjpk 	/* Make sure we have room for the worst-case addition */
6651676Sjpk 	hlen = IPH_HDR_LENGTH(ipha) + opt_storage[IPOPT_OLEN];
6661676Sjpk 	hlen = (hlen + 3) & ~3;
6671676Sjpk 	if (hlen > IP_MAX_HDR_LENGTH)
6681676Sjpk 		hlen = IP_MAX_HDR_LENGTH;
6691676Sjpk 	hlen -= IPH_HDR_LENGTH(ipha);
6701676Sjpk 	if (mp->b_wptr + hlen > mp->b_datap->db_lim) {
6711676Sjpk 		int copylen;
6721676Sjpk 		mblk_t *new_mp;
6731676Sjpk 
6741676Sjpk 		/* allocate enough to be meaningful, but not *too* much */
6751676Sjpk 		copylen = MBLKL(mp);
6761676Sjpk 		if (copylen > 256)
6771676Sjpk 			copylen = 256;
6781676Sjpk 		new_mp = allocb(hlen + copylen +
6791676Sjpk 		    (mp->b_rptr - mp->b_datap->db_base), BPRI_HI);
6801676Sjpk 		if (new_mp == NULL)
6811676Sjpk 			return (ENOMEM);
6821676Sjpk 		mblk_setcred(new_mp, DB_CRED(mp));
6831676Sjpk 
6841676Sjpk 		/* keep the bias */
6851676Sjpk 		new_mp->b_rptr += mp->b_rptr - mp->b_datap->db_base;
6861676Sjpk 		new_mp->b_wptr = new_mp->b_rptr + copylen;
6871676Sjpk 		bcopy(mp->b_rptr, new_mp->b_rptr, copylen);
6881676Sjpk 		new_mp->b_cont = mp;
6891676Sjpk 		if ((mp->b_rptr += copylen) >= mp->b_wptr) {
6901676Sjpk 			new_mp->b_cont = mp->b_cont;
6911676Sjpk 			freeb(mp);
6921676Sjpk 		}
6931676Sjpk 		*mpp = mp = new_mp;
6941676Sjpk 		ipha = (ipha_t *)mp->b_rptr;
6951676Sjpk 	}
6961676Sjpk 
6971676Sjpk 	added = tsol_prepend_option(opt_storage, ipha, MBLKL(mp));
6981676Sjpk 	if (added == -1)
6991676Sjpk 		goto param_prob;
7001676Sjpk 
7011676Sjpk 	if (addedp != NULL)
7021676Sjpk 		*addedp = added;
7031676Sjpk 
7041676Sjpk 	ASSERT((mp->b_wptr + added) <= DB_LIM(mp));
7051676Sjpk 	mp->b_wptr += added;
7061676Sjpk 
7071676Sjpk 	return (0);
7081676Sjpk 
7091676Sjpk param_prob:
7101676Sjpk 	return (EINVAL);
7111676Sjpk }
7121676Sjpk 
7131676Sjpk /*
7141676Sjpk  * IPv6 HopOpt extension header for the label option layout:
7151676Sjpk  *	- One octet giving the type of the 'next extension header'
7161676Sjpk  *	- Header extension length in 8-byte words, not including the
7171676Sjpk  *	  1st 8 bytes, but including any pad bytes at the end.
7181676Sjpk  *	  Eg. A value of 2 means 16 bytes not including the 1st 8 bytes.
7191676Sjpk  *	- Followed by TLV encoded IPv6 label option. Option layout is
7201676Sjpk  *		* One octet, IP6OPT_LS
7211676Sjpk  *		* One octet option length in bytes of the option data following
7221676Sjpk  *		  the length, but not including any pad bytes at the end.
7231676Sjpk  *		* Four-octet DOI (IP6LS_DOI_V4)
7241676Sjpk  *		* One octet suboption, IP6LS_TT_V4
7251676Sjpk  *		* One octet suboption length in bytes of the suboption
7261676Sjpk  *		  following the suboption length, including the suboption
7271676Sjpk  *		  header length, but not including any pad bytes at the end.
7281676Sjpk  *	- Pad to make the extension header a multiple of 8 bytes.
7291676Sjpk  *
7301676Sjpk  * This function returns the contents of 'IPv6 option structure' in the above.
7311676Sjpk  * i.e starting from the IP6OPT_LS but not including the pad at the end.
7321676Sjpk  * The user must prepend two octets (either padding or next header / length)
7331676Sjpk  * and append padding out to the next 8 octet boundary.
7341676Sjpk  */
7351676Sjpk int
7361676Sjpk tsol_compute_label_v6(const cred_t *credp, const in6_addr_t *dst,
7373448Sdh155122     uchar_t *opt_storage, boolean_t isexempt, ip_stack_t *ipst)
7381676Sjpk {
7391676Sjpk 	tsol_tpc_t	*dst_rhtp;
7401676Sjpk 	ts_label_t	*tsl;
7411676Sjpk 	uint_t		sec_opt_len;
7421676Sjpk 	uint32_t	doi;
7433448Sdh155122 	zoneid_t	zoneid, ip_zoneid;
7441676Sjpk 	ire_t		*ire, *sire;
7451676Sjpk 	tsol_ire_gw_secattr_t *attrp;
7461676Sjpk 	boolean_t	compute_label;
7471676Sjpk 
7481676Sjpk 	if (ip6opt_ls == 0)
7491676Sjpk 		return (EINVAL);
7501676Sjpk 
7511676Sjpk 	if (opt_storage != NULL)
7521676Sjpk 		opt_storage[IPOPT_OLEN] = 0;
7531676Sjpk 
7541676Sjpk 	if ((tsl = crgetlabel(credp)) == NULL)
7551676Sjpk 		return (0);
7561676Sjpk 
7571676Sjpk 	/* Always pass multicast */
7581676Sjpk 	if (IN6_IS_ADDR_MULTICAST(dst))
7591676Sjpk 		return (0);
7601676Sjpk 
7611676Sjpk 	if ((dst_rhtp = find_tpc(dst, IPV6_VERSION, B_FALSE)) == NULL) {
7621676Sjpk 		DTRACE_PROBE3(tx__tnopt__log__info__labeling__lookupdst__v6,
7631676Sjpk 		    char *, "destination ip6(1) not in database with creds(2)",
7641676Sjpk 		    in6_addr_t *, dst, cred_t *, credp);
7651676Sjpk 		return (EINVAL);
7661676Sjpk 	}
7671676Sjpk 
7681676Sjpk 	zoneid = crgetzoneid(credp);
7691676Sjpk 
7701676Sjpk 	/*
7713448Sdh155122 	 * For exclusive stacks we set the zoneid to zero
7723448Sdh155122 	 * to operate as if in the global zone for IRE and conn_t comparisons.
7733448Sdh155122 	 */
7743448Sdh155122 	if (ipst->ips_netstack->netstack_stackid != GLOBAL_NETSTACKID)
7753448Sdh155122 		ip_zoneid = GLOBAL_ZONEID;
7763448Sdh155122 	else
7773448Sdh155122 		ip_zoneid = zoneid;
7783448Sdh155122 
7793448Sdh155122 	/*
7801676Sjpk 	 * Fill in a V6 label.  If a new format is added here, make certain
7811676Sjpk 	 * that the maximum size of this label is reflected in sys/tsol/tnet.h
7821676Sjpk 	 * as TSOL_MAX_IPV6_OPTION.
7831676Sjpk 	 */
7841676Sjpk 	compute_label = B_FALSE;
7851676Sjpk 	switch (dst_rhtp->tpc_tp.host_type) {
7861676Sjpk 	case UNLABELED:
7871676Sjpk 		/*
7881676Sjpk 		 * Only add a label if the unlabeled destination is
7891676Sjpk 		 * not local or loopback address, that it is
7901676Sjpk 		 * not on the same subnet, and that the next-hop
7911676Sjpk 		 * gateway is labeled.
7921676Sjpk 		 */
7931676Sjpk 		sire = NULL;
7943448Sdh155122 		ire = ire_cache_lookup_v6(dst, ip_zoneid, tsl, ipst);
7951676Sjpk 
7961676Sjpk 		if (ire != NULL && (ire->ire_type & (IRE_LOCAL |
7971676Sjpk 		    IRE_LOOPBACK | IRE_INTERFACE)) != 0) {
7981676Sjpk 			IRE_REFRELE(ire);
7991676Sjpk 			TPC_RELE(dst_rhtp);
8001676Sjpk 			return (0);
8011676Sjpk 		} else if (ire == NULL) {
8021676Sjpk 			ire = ire_ftable_lookup_v6(dst, NULL, NULL, 0, NULL,
8033448Sdh155122 			    &sire, ip_zoneid, 0, tsl, (MATCH_IRE_RECURSIVE |
8043448Sdh155122 			    MATCH_IRE_DEFAULT | MATCH_IRE_SECATTR), ipst);
8051676Sjpk 		}
8061676Sjpk 
8071676Sjpk 		/* no route to destination */
8081676Sjpk 		if (ire == NULL) {
8091676Sjpk 			DTRACE_PROBE4(
8101676Sjpk 			    tx__tnopt__log__info__labeling__routedst__v6,
8111676Sjpk 			    char *, "No route to unlabeled dest ip6(1)/tpc(2) "
8121676Sjpk 			    "with creds(3).", in6_addr_t *, dst, tsol_tpc_t *,
8131676Sjpk 			    dst_rhtp, cred_t *, credp);
8141676Sjpk 			TPC_RELE(dst_rhtp);
8151676Sjpk 			return (EINVAL);
8161676Sjpk 		}
8171676Sjpk 
8181676Sjpk 		/*
8191676Sjpk 		 * Prefix IRE from f-table lookup means that the destination
8201676Sjpk 		 * is not directly connected; check the next-hop attributes.
8211676Sjpk 		 */
8221676Sjpk 		if (sire != NULL) {
8231676Sjpk 			ASSERT(ire != NULL);
8241676Sjpk 			IRE_REFRELE(ire);
8251676Sjpk 			ire = sire;
8261676Sjpk 		}
8271676Sjpk 
8281676Sjpk 		attrp = ire->ire_gw_secattr;
8291676Sjpk 		if (attrp != NULL && attrp->igsa_rhc != NULL &&
8301676Sjpk 		    attrp->igsa_rhc->rhc_tpc->tpc_tp.host_type != UNLABELED)
8311676Sjpk 			compute_label = B_TRUE;
8321676Sjpk 
8331676Sjpk 		if (dst_rhtp->tpc_tp.tp_doi != tsl->tsl_doi ||
8341676Sjpk 		    (!blequal(&dst_rhtp->tpc_tp.tp_def_label,
8351676Sjpk 		    &tsl->tsl_label) && (!isexempt ||
8361676Sjpk 		    (zoneid != GLOBAL_ZONEID && !bldominates(&tsl->tsl_label,
8371676Sjpk 		    &dst_rhtp->tpc_tp.tp_def_label))))) {
8381676Sjpk 			DTRACE_PROBE4(tx__tnopt__log__info__labeling__mac__v6,
8391676Sjpk 			    char *, "unlabeled dest ip6(1)/tpc(2) "
8401676Sjpk 			    "non-matching creds(3)", in6_addr_t *, dst,
8411676Sjpk 			    tsol_tpc_t *, dst_rhtp, cred_t *, credp);
8421676Sjpk 			IRE_REFRELE(ire);
8431676Sjpk 			TPC_RELE(dst_rhtp);
8441676Sjpk 			return (EACCES);
8451676Sjpk 		}
8461676Sjpk 
8471676Sjpk 		IRE_REFRELE(ire);
8481676Sjpk 		break;
8491676Sjpk 
8501676Sjpk 	case SUN_CIPSO:
8511676Sjpk 		if (dst_rhtp->tpc_tp.tp_cipso_doi_cipso != tsl->tsl_doi ||
8521676Sjpk 		    (!_blinrange(&tsl->tsl_label,
8531676Sjpk 		    &dst_rhtp->tpc_tp.tp_sl_range_cipso) &&
8541676Sjpk 		    !blinlset(&tsl->tsl_label,
8551676Sjpk 		    dst_rhtp->tpc_tp.tp_sl_set_cipso))) {
8561676Sjpk 			DTRACE_PROBE4(tx__tnopt__log__info__labeling__mac__v6,
8571676Sjpk 			    char *,
8581676Sjpk 			    "labeled dest ip6(1)/tpc(2) non-matching creds(3).",
8591676Sjpk 			    in6_addr_t *, dst, tsol_tpc_t *, dst_rhtp,
8601676Sjpk 			    cred_t *, credp);
8611676Sjpk 			TPC_RELE(dst_rhtp);
8621676Sjpk 			return (EACCES);
8631676Sjpk 		}
8641676Sjpk 		compute_label = B_TRUE;
8651676Sjpk 		break;
8661676Sjpk 
8671676Sjpk 	default:
8681676Sjpk 		TPC_RELE(dst_rhtp);
8691676Sjpk 		return (EACCES);
8701676Sjpk 	}
8711676Sjpk 
8721676Sjpk 	if (!compute_label) {
8731676Sjpk 		TPC_RELE(dst_rhtp);
8741676Sjpk 		return (0);
8751676Sjpk 	}
8761676Sjpk 
8771676Sjpk 	/* compute the CIPSO option */
8781676Sjpk 	if (opt_storage != NULL)
8791676Sjpk 		opt_storage += 8;
8801676Sjpk 	if (dst_rhtp->tpc_tp.host_type != UNLABELED) {
8811676Sjpk 		sec_opt_len = tsol2cipso_tt1(&tsl->tsl_label, opt_storage,
8821676Sjpk 		    tsl->tsl_doi);
8831676Sjpk 	} else {
8841676Sjpk 		sec_opt_len = tsol2cipso_tt1(&dst_rhtp->tpc_tp.tp_def_label,
8851676Sjpk 		    opt_storage, tsl->tsl_doi);
8861676Sjpk 	}
8871676Sjpk 	TPC_RELE(dst_rhtp);
8881676Sjpk 
8891676Sjpk 	if (sec_opt_len == 0) {
8901676Sjpk 		DTRACE_PROBE4(tx__tnopt__log__error__labeling__lostops__v6,
8911676Sjpk 		    char *,
8921676Sjpk 		    "options lack length for dest ip6(1)/tpc(2) with creds(3).",
8931676Sjpk 		    in6_addr_t *, dst, tsol_tpc_t *, dst_rhtp, cred_t *, credp);
8941676Sjpk 		return (EINVAL);
8951676Sjpk 	}
8961676Sjpk 
8971676Sjpk 	if (opt_storage == NULL)
8981676Sjpk 		return (0);
8991676Sjpk 
9001676Sjpk 	if (sec_opt_len < IP_MAX_OPT_LENGTH)
9011676Sjpk 		opt_storage[sec_opt_len] = IPOPT_EOL;
9021676Sjpk 
9031676Sjpk 	/*
9041676Sjpk 	 * Just in case the option length is odd, round it up to the next even
9051676Sjpk 	 * multiple.  The IPv6 option definition doesn't like odd numbers for
9061676Sjpk 	 * some reason.
9071676Sjpk 	 *
9081676Sjpk 	 * Length in the overall option header (IP6OPT_LS) does not include the
9091676Sjpk 	 * option header itself, but the length in the suboption does include
9101676Sjpk 	 * the suboption header.  Thus, when there's just one suboption, the
9111676Sjpk 	 * length in the option header is the suboption length plus 4 (for the
9121676Sjpk 	 * DOI value).
9131676Sjpk 	 */
9141676Sjpk 	opt_storage[-2] = IP6LS_TT_V4;
9151676Sjpk 	opt_storage[-1] = (sec_opt_len + 2 + 1) & ~1;
9161676Sjpk 	opt_storage[-8] = ip6opt_ls;
9171676Sjpk 	opt_storage[-7] = opt_storage[-1] + 4;
9181676Sjpk 	doi = htons(IP6LS_DOI_V4);
9191676Sjpk 	bcopy(&doi, opt_storage - 6, 4);
9201676Sjpk 
9211676Sjpk 	return (0);
9221676Sjpk }
9231676Sjpk 
9241676Sjpk /*
9251676Sjpk  * Locate the start of the IP6OPT_LS label option and return it.
9261676Sjpk  * Also return the start of the next non-pad option in after_secoptp.
9271676Sjpk  * Usually the label option is the first option at least when packets
9281676Sjpk  * are generated, but for generality we don't assume that on received packets.
9291676Sjpk  */
9301676Sjpk uchar_t *
9311676Sjpk tsol_find_secopt_v6(
9321676Sjpk     const uchar_t *ip6hbh,	/* Start of the hop-by-hop extension header */
9331676Sjpk     uint_t hbhlen,		/* Length of the hop-by-hop extension header */
9341676Sjpk     uchar_t **after_secoptp,	/* Non-pad option following the label option */
9351676Sjpk     boolean_t *hbh_needed)	/* Is hop-by-hop hdr needed w/o label */
9361676Sjpk {
9371676Sjpk 	uint_t	optlen;
9381676Sjpk 	uint_t	optused;
9391676Sjpk 	const uchar_t *optptr;
9401676Sjpk 	uchar_t	opt_type;
9411676Sjpk 	const uchar_t *secopt = NULL;
9421676Sjpk 
9431676Sjpk 	*hbh_needed = B_FALSE;
9441676Sjpk 	*after_secoptp = NULL;
9451676Sjpk 	optlen = hbhlen - 2;
9461676Sjpk 	optptr = ip6hbh + 2;
9471676Sjpk 	while (optlen != 0) {
9481676Sjpk 		opt_type = *optptr;
9491676Sjpk 		if (opt_type == IP6OPT_PAD1) {
9501676Sjpk 			optptr++;
9511676Sjpk 			optlen--;
9521676Sjpk 			continue;
9531676Sjpk 		}
9541676Sjpk 		if (optlen == 1)
9551676Sjpk 			break;
9561676Sjpk 		optused = 2 + optptr[1];
9571676Sjpk 		if (optused > optlen)
9581676Sjpk 			break;
9591676Sjpk 		/*
9601676Sjpk 		 * if we get here, ip6opt_ls can
9611676Sjpk 		 * not be 0 because it will always
9621676Sjpk 		 * match the IP6OPT_PAD1 above.
9631676Sjpk 		 * Therefore ip6opt_ls == 0 forces
9641676Sjpk 		 * this test to always fail here.
9651676Sjpk 		 */
9661676Sjpk 		if (opt_type == ip6opt_ls)
9671676Sjpk 			secopt = optptr;
9681676Sjpk 		else switch (opt_type) {
9691676Sjpk 		case IP6OPT_PADN:
9701676Sjpk 			break;
9711676Sjpk 		default:
9721676Sjpk 			/*
9731676Sjpk 			 * There is at least 1 option other than
9741676Sjpk 			 * the label option. So the hop-by-hop header is needed
9751676Sjpk 			 */
9761676Sjpk 			*hbh_needed = B_TRUE;
9771676Sjpk 			if (secopt != NULL) {
9781676Sjpk 				*after_secoptp = (uchar_t *)optptr;
9791676Sjpk 				return ((uchar_t *)secopt);
9801676Sjpk 			}
9811676Sjpk 			break;
9821676Sjpk 		}
9831676Sjpk 		optlen -= optused;
9841676Sjpk 		optptr += optused;
9851676Sjpk 	}
9861676Sjpk 	return ((uchar_t *)secopt);
9871676Sjpk }
9881676Sjpk 
9891676Sjpk /*
9901676Sjpk  * Remove the label option from the hop-by-hop options header if it exists.
9911676Sjpk  * 'buflen' is the total length of the packet typically b_wptr - b_rptr.
9921676Sjpk  * Header and data following the label option that is deleted are copied
993*4564Swy83408  * (i.e. slid backward) to the right position, and returns the number
994*4564Swy83408  * of bytes removed (as zero or negative number.)
9951676Sjpk  */
9961676Sjpk int
9971676Sjpk tsol_remove_secopt_v6(ip6_t *ip6h, int buflen)
9981676Sjpk {
9991676Sjpk 	uchar_t	*ip6hbh;	/* hop-by-hop header */
10001676Sjpk 	uint_t	hbhlen;		/* hop-by-hop extension header length */
10011676Sjpk 	uchar_t *secopt = NULL;
10021676Sjpk 	uchar_t *after_secopt;
10031676Sjpk 	uint_t	pad;
10041676Sjpk 	uint_t	delta;
10051676Sjpk 	boolean_t hbh_needed;
10061676Sjpk 
10071676Sjpk 	/*
10081676Sjpk 	 * hop-by-hop extension header must appear first, if it does not
10091676Sjpk 	 * exist, there is no label option.
10101676Sjpk 	 */
10111676Sjpk 	if (ip6h->ip6_nxt != IPPROTO_HOPOPTS)
10121676Sjpk 		return (0);
10131676Sjpk 
10141676Sjpk 	ip6hbh = (uchar_t *)&ip6h[1];
10151676Sjpk 	hbhlen = (ip6hbh[1] + 1) << 3;
10161676Sjpk 	/*
10171676Sjpk 	 * Locate the start of the label option if it exists and the end
10181676Sjpk 	 * of the label option including pads if any.
10191676Sjpk 	 */
10201676Sjpk 	secopt = tsol_find_secopt_v6(ip6hbh, hbhlen, &after_secopt,
10211676Sjpk 	    &hbh_needed);
10221676Sjpk 	if (secopt == NULL)
10231676Sjpk 		return (0);
10241676Sjpk 	if (!hbh_needed) {
10251676Sjpk 		uchar_t	next_hdr;
10261676Sjpk 		/*
10271676Sjpk 		 * The label option was the only option in the hop-by-hop
10281676Sjpk 		 * header. We don't need the hop-by-hop header itself any
10291676Sjpk 		 * longer.
10301676Sjpk 		 */
10311676Sjpk 		next_hdr = ip6hbh[0];
10321676Sjpk 		ovbcopy(ip6hbh + hbhlen, ip6hbh,
10331676Sjpk 		    buflen - (IPV6_HDR_LEN + hbhlen));
10342776Skp158701 		ip6h->ip6_plen = htons(ntohs(ip6h->ip6_plen) - hbhlen);
10351676Sjpk 		ip6h->ip6_nxt = next_hdr;
1036*4564Swy83408 		return (-hbhlen);
10371676Sjpk 	}
10381676Sjpk 
10391676Sjpk 	if (after_secopt == NULL) {
10401676Sjpk 		/* There is no option following the label option */
10411676Sjpk 		after_secopt = ip6hbh + hbhlen;
10421676Sjpk 	}
10431676Sjpk 
10441676Sjpk 	/*
10451676Sjpk 	 * After deleting the label option, we need to slide the headers
10461676Sjpk 	 * and data back, while still maintaining the same alignment (module 8)
10471676Sjpk 	 * for the other options. So we slide the headers and data back only
10481676Sjpk 	 * by an integral multiple of 8 bytes, and fill the remaining bytes
10491676Sjpk 	 * with pads.
10501676Sjpk 	 */
10511676Sjpk 	delta = after_secopt - secopt;
10521676Sjpk 	pad = delta % 8;
10531676Sjpk 	if (pad == 1) {
10541676Sjpk 		secopt[0] = IP6OPT_PAD1;
10551676Sjpk 	} else if (pad > 1) {
10561676Sjpk 		secopt[0] = IP6OPT_PADN;
10571676Sjpk 		secopt[1] = pad - 2;
10581676Sjpk 		if (pad > 2)
10591676Sjpk 			bzero(&secopt[2], pad - 2);
10601676Sjpk 	}
10611676Sjpk 	secopt += pad;
10621676Sjpk 	delta -= pad;
10631676Sjpk 	ovbcopy(after_secopt, secopt,
10641676Sjpk 	    (uchar_t *)ip6h + buflen - after_secopt);
10651676Sjpk 	ip6hbh[1] -= delta/8;
10662776Skp158701 	ip6h->ip6_plen = htons(ntohs(ip6h->ip6_plen) - delta);
10671676Sjpk 
1068*4564Swy83408 	return (-delta);
10691676Sjpk }
10701676Sjpk 
10711676Sjpk /*
10721676Sjpk  * 'optbuf' contains a CIPSO label embedded in an IPv6 hop-by-hop option,
10731676Sjpk  * starting with the IP6OPT_LS option type. The format of this hop-by-hop
10741676Sjpk  * option is described in the block comment above tsol_compute_label_v6.
10751676Sjpk  * This function prepends this hop-by-hop option before any other hop-by-hop
10761676Sjpk  * options in the hop-by-hop header if one already exists, else a new
10771676Sjpk  * hop-by-hop header is created and stuffed into the packet following
10781676Sjpk  * the IPv6 header. 'buflen' is the total length of the packet i.e.
10791676Sjpk  * b_wptr - b_rptr. The caller ensures that there is enough space for the
10801676Sjpk  * extra option being added. Header and data following the position where
10811676Sjpk  * the label option is inserted are copied (i.e. slid forward) to the right
10821676Sjpk  * position.
10831676Sjpk  */
10841676Sjpk int
10851676Sjpk tsol_prepend_option_v6(uchar_t *optbuf, ip6_t *ip6h, int buflen)
10861676Sjpk {
10871676Sjpk 	/*
10881676Sjpk 	 * rawlen is the length of the label option in bytes, not including
10891676Sjpk 	 * any pads, starting from the IP6OPT_LS (option type) byte.
10901676Sjpk 	 */
10911676Sjpk 	uint_t	rawlen;
10921676Sjpk 
10931676Sjpk 	uint_t	optlen;		/* rawlen rounded to an 8 byte multiple */
10941676Sjpk 	uchar_t	*ip6hbh;	/* start of the hop-by-hop extension header */
10951676Sjpk 	uint_t	hbhlen;		/* Length of the hop-by-hop extension header */
10961676Sjpk 	uint_t	pad_len;
10971676Sjpk 	uchar_t	*pad_position;
10981676Sjpk 	int	delta;		/* Actual number of bytes inserted */
10991676Sjpk 
11001676Sjpk 	rawlen = optbuf[1] + 2;	/* Add 2 for the option type, option length */
11011676Sjpk 	ip6hbh = (uchar_t *)&ip6h[1];
11021676Sjpk 	if (ip6h->ip6_nxt == IPPROTO_HOPOPTS) {
11031676Sjpk 		/*
11041676Sjpk 		 * There is a hop-by-hop header present already. In order to
11051676Sjpk 		 * preserve the alignment of the other options at the existing
11061676Sjpk 		 * value (modulo 8) we need to pad the label option to a
11071676Sjpk 		 * multiple of 8 bytes before prepending it to the other
11081676Sjpk 		 * options. Slide the extension headers and data forward to
11091676Sjpk 		 * accomodate the label option at the start of the hop-by-hop
11101676Sjpk 		 * header
11111676Sjpk 		 */
11121676Sjpk 		delta = optlen = (rawlen + 7) & ~7;
11131676Sjpk 		pad_len = optlen - rawlen;
11141676Sjpk 		pad_position = ip6hbh + 2 + rawlen;
11151676Sjpk 		ovbcopy(ip6hbh + 2, ip6hbh + 2 + optlen,
11161676Sjpk 		    buflen - (IPV6_HDR_LEN + 2));
11171676Sjpk 		/*
11181676Sjpk 		 * Bump up the hop-by-hop extension header length by
11191676Sjpk 		 * the number of 8-byte words added
11201676Sjpk 		 */
11211676Sjpk 		optlen >>= 3;
11221676Sjpk 		if (ip6hbh[1] + optlen > 255)
11231676Sjpk 			return (-1);
11241676Sjpk 		ip6hbh[1] += optlen;
11251676Sjpk 	} else {
11261676Sjpk 		/*
11271676Sjpk 		 * There is no hop-by-hop header in the packet. Construct a
11281676Sjpk 		 * new Hop-by-hop extension header (a multiple of 8 bytes).
11291676Sjpk 		 * Slide any other extension headers and data forward to
11301676Sjpk 		 * accomodate this hop-by-hop header
11311676Sjpk 		 */
11321676Sjpk 		delta = hbhlen = (2 + rawlen + 7) & ~7; /* +2 for nxthdr, len */
11331676Sjpk 		pad_len = hbhlen - (2 + rawlen);
11341676Sjpk 		pad_position = ip6hbh + 2 + rawlen;
11351676Sjpk 		ovbcopy(ip6hbh, ip6hbh + hbhlen, buflen - IPV6_HDR_LEN);
11361676Sjpk 		ip6hbh[0] = ip6h->ip6_nxt;
11371676Sjpk 		/*
11381676Sjpk 		 * hop-by-hop extension header length in 8-byte words, not
11391676Sjpk 		 * including the 1st 8 bytes of the hop-by-hop header.
11401676Sjpk 		 */
11411676Sjpk 		ip6hbh[1] = (hbhlen >> 3) - 1;
11421676Sjpk 		ip6h->ip6_nxt = IPPROTO_HOPOPTS;
11431676Sjpk 	}
11441676Sjpk 	/*
11451676Sjpk 	 * Copy the label option into the hop-by-hop header and insert any
11461676Sjpk 	 * needed pads
11471676Sjpk 	 */
11481676Sjpk 	bcopy(optbuf, ip6hbh + 2, rawlen);
11491676Sjpk 	if (pad_len == 1) {
11501676Sjpk 		pad_position[0] = IP6OPT_PAD1;
11511676Sjpk 	} else if (pad_len > 1) {
11521676Sjpk 		pad_position[0] = IP6OPT_PADN;
11531676Sjpk 		pad_position[1] = pad_len - 2;
11541676Sjpk 		if (pad_len > 2)
11551676Sjpk 			bzero(pad_position + 2, pad_len - 2);
11561676Sjpk 	}
11572776Skp158701 	ip6h->ip6_plen = htons(ntohs(ip6h->ip6_plen) + delta);
11581676Sjpk 	return (delta);
11591676Sjpk }
11601676Sjpk 
11611676Sjpk /*
11621676Sjpk  * tsol_check_label_v6()
11631676Sjpk  *
11641676Sjpk  * This routine computes the IP label that should be on the packet based on the
11651676Sjpk  * connection and destination information.  It's called only by the IP
11661676Sjpk  * forwarding logic, because all internal modules atop IP know how to generate
11671676Sjpk  * their own labels.
11681676Sjpk  *
11691676Sjpk  * Returns:
11701676Sjpk  *      0		Label on packet was already correct
11711676Sjpk  *      EACCESS		The packet failed the remote host accreditation.
11721676Sjpk  *      ENOMEM		Memory allocation failure.
11731676Sjpk  */
11741676Sjpk int
11751676Sjpk tsol_check_label_v6(const cred_t *credp, mblk_t **mpp, int *addedp,
11763448Sdh155122     boolean_t isexempt, ip_stack_t *ipst)
11771676Sjpk {
11781676Sjpk 	mblk_t *mp = *mpp;
11791676Sjpk 	ip6_t  *ip6h;
11801676Sjpk 	/*
11811676Sjpk 	 * Label option length is limited to IP_MAX_OPT_LENGTH for
11821676Sjpk 	 * symmetry with IPv4. Can be relaxed if needed
11831676Sjpk 	 */
11841676Sjpk 	uchar_t opt_storage[TSOL_MAX_IPV6_OPTION];
11851676Sjpk 	uint_t hlen;
11861676Sjpk 	uint_t sec_opt_len; /* label option length not including type, len */
11871676Sjpk 	int added;
11881676Sjpk 	int retv;
11891676Sjpk 	uchar_t	*after_secopt;
11901676Sjpk 	uchar_t	*secopt = NULL;
11911676Sjpk 	uchar_t	*ip6hbh;
11921676Sjpk 	uint_t	hbhlen;
11931676Sjpk 	boolean_t hbh_needed;
11941676Sjpk 
11951676Sjpk 	if (addedp != NULL)
11961676Sjpk 		*addedp = 0;
11971676Sjpk 
11981676Sjpk 	ip6h = (ip6_t *)mp->b_rptr;
11991676Sjpk 	retv = tsol_compute_label_v6(credp, &ip6h->ip6_dst, opt_storage,
12003448Sdh155122 	    isexempt, ipst);
12011676Sjpk 	if (retv != 0)
12021676Sjpk 		return (retv);
12031676Sjpk 
12041676Sjpk 	sec_opt_len = opt_storage[1];
12051676Sjpk 
12061676Sjpk 	if (ip6h->ip6_nxt == IPPROTO_HOPOPTS) {
12071676Sjpk 		ip6hbh = (uchar_t *)&ip6h[1];
12081676Sjpk 		hbhlen = (ip6hbh[1] + 1) << 3;
12091676Sjpk 		secopt = tsol_find_secopt_v6(ip6hbh, hbhlen, &after_secopt,
12101676Sjpk 		    &hbh_needed);
12111676Sjpk 	}
12121676Sjpk 
12131676Sjpk 	if (sec_opt_len == 0 && secopt == NULL) {
12141676Sjpk 		/*
12151676Sjpk 		 * The packet is not supposed to have a label, and it
12161676Sjpk 		 * does not have one currently
12171676Sjpk 		 */
12181676Sjpk 		return (0);
12191676Sjpk 	}
12201676Sjpk 	if (secopt != NULL && sec_opt_len != 0 &&
12211676Sjpk 	    (bcmp(opt_storage, secopt, sec_opt_len + 2) == 0)) {
12221676Sjpk 		/* The packet has the correct label already */
12231676Sjpk 		return (0);
12241676Sjpk 	}
12251676Sjpk 
12261676Sjpk 	/*
12271676Sjpk 	 * If there is an option there, then it must be the wrong one; delete.
12281676Sjpk 	 */
12291676Sjpk 	if (secopt != NULL)
12301676Sjpk 		mp->b_wptr += tsol_remove_secopt_v6(ip6h, MBLKL(mp));
12311676Sjpk 
12321676Sjpk 	/*
12331676Sjpk 	 * Make sure we have room for the worst-case addition. Add 2 bytes for
12341676Sjpk 	 * the hop-by-hop ext header's next header and length fields. Add
12351676Sjpk 	 * another 2 bytes for the label option type, len and then round
12361676Sjpk 	 * up to the next 8-byte multiple.
12371676Sjpk 	 */
12381676Sjpk 	hlen = (4 + sec_opt_len + 7) & ~7;
12391676Sjpk 	if (mp->b_wptr + hlen > mp->b_datap->db_lim) {
12401676Sjpk 		int copylen;
12411676Sjpk 		mblk_t *new_mp;
12421676Sjpk 		uint16_t hdr_len;
12431676Sjpk 
12441676Sjpk 		hdr_len = ip_hdr_length_v6(mp, ip6h);
12451676Sjpk 		/*
12461676Sjpk 		 * Allocate enough to be meaningful, but not *too* much.
12471676Sjpk 		 * Also all the IPv6 extension headers must be in the same mblk
12481676Sjpk 		 */
12491676Sjpk 		copylen = MBLKL(mp);
12501676Sjpk 		if (copylen > 256)
12511676Sjpk 			copylen = 256;
12521676Sjpk 		if (copylen < hdr_len)
12531676Sjpk 			copylen = hdr_len;
12541676Sjpk 		new_mp = allocb(hlen + copylen +
12551676Sjpk 		    (mp->b_rptr - mp->b_datap->db_base), BPRI_HI);
12561676Sjpk 		if (new_mp == NULL)
12571676Sjpk 			return (ENOMEM);
12582776Skp158701 		mblk_setcred(new_mp, DB_CRED(mp));
12591676Sjpk 
12601676Sjpk 		/* keep the bias */
12611676Sjpk 		new_mp->b_rptr += mp->b_rptr - mp->b_datap->db_base;
12621676Sjpk 		new_mp->b_wptr = new_mp->b_rptr + copylen;
12631676Sjpk 		bcopy(mp->b_rptr, new_mp->b_rptr, copylen);
12641676Sjpk 		new_mp->b_cont = mp;
12651676Sjpk 		if ((mp->b_rptr += copylen) >= mp->b_wptr) {
12661676Sjpk 			new_mp->b_cont = mp->b_cont;
12671676Sjpk 			freeb(mp);
12681676Sjpk 		}
12691676Sjpk 		*mpp = mp = new_mp;
12701676Sjpk 		ip6h = (ip6_t *)mp->b_rptr;
12711676Sjpk 	}
12721676Sjpk 
12731676Sjpk 	added = tsol_prepend_option_v6(opt_storage, ip6h, MBLKL(mp));
12741676Sjpk 	if (added == -1)
12751676Sjpk 		goto param_prob;
12761676Sjpk 
12771676Sjpk 	if (addedp != NULL)
12781676Sjpk 		*addedp = added;
12791676Sjpk 
12801676Sjpk 	ASSERT(mp->b_wptr + added <= DB_LIM(mp));
12811676Sjpk 	mp->b_wptr += added;
12821676Sjpk 
12831676Sjpk 	return (0);
12841676Sjpk 
12851676Sjpk param_prob:
12861676Sjpk 	return (EINVAL);
12871676Sjpk }
12881676Sjpk 
12891676Sjpk /*
12901676Sjpk  * Update the given IPv6 "sticky options" structure to contain the provided
12911676Sjpk  * label, which is encoded as an IPv6 option.  Existing label is removed if
12921676Sjpk  * necessary, and storage is allocated/freed/resized.
12931676Sjpk  *
12941676Sjpk  * Returns 0 on success, errno on failure.
12951676Sjpk  */
12961676Sjpk int
12971676Sjpk tsol_update_sticky(ip6_pkt_t *ipp, uint_t *labellen, const uchar_t *labelopt)
12981676Sjpk {
12991676Sjpk 	int rawlen, optlen, newlen;
13001676Sjpk 	uchar_t *newopts;
13011676Sjpk 
13021676Sjpk 	/*
13031676Sjpk 	 * rawlen is the size of the IPv6 label to be inserted from labelopt.
13041676Sjpk 	 * optlen is the total length of that option, including any necessary
13051676Sjpk 	 * headers and padding.  newlen is the new size of the total hop-by-hop
13061676Sjpk 	 * options buffer, including user options.
13071676Sjpk 	 */
13082283Skp158701 	ASSERT(*labellen <= ipp->ipp_hopoptslen);
13092283Skp158701 	ASSERT((ipp->ipp_hopopts == NULL && ipp->ipp_hopoptslen == 0) ||
13102283Skp158701 	    (ipp->ipp_hopopts != NULL && ipp->ipp_hopoptslen != 0));
13112283Skp158701 
13121676Sjpk 	if ((rawlen = labelopt[1]) != 0) {
13131676Sjpk 		rawlen += 2;	/* add in header size */
13141676Sjpk 		optlen = (2 + rawlen + 7) & ~7;
13151676Sjpk 	} else {
13161676Sjpk 		optlen = 0;
13171676Sjpk 	}
13181676Sjpk 	newlen = ipp->ipp_hopoptslen + optlen - *labellen;
13192283Skp158701 	if (newlen == 0 && ipp->ipp_hopopts != NULL) {
13202283Skp158701 		/* Deleting all existing hop-by-hop options */
13212283Skp158701 		kmem_free(ipp->ipp_hopopts, ipp->ipp_hopoptslen);
13222283Skp158701 		ipp->ipp_hopopts = NULL;
13232283Skp158701 		ipp->ipp_fields &= ~IPPF_HOPOPTS;
13242283Skp158701 	} else if (optlen != *labellen) {
13252283Skp158701 		/* If the label not same size as last time, then reallocate */
13261676Sjpk 		if (newlen > IP6_MAX_OPT_LENGTH)
13271676Sjpk 			return (EHOSTUNREACH);
13281676Sjpk 		newopts = kmem_alloc(newlen, KM_NOSLEEP);
13291676Sjpk 		if (newopts == NULL)
13301676Sjpk 			return (ENOMEM);
13311676Sjpk 		/*
13321676Sjpk 		 * If the user has hop-by-hop stickyoptions set, then copy his
13331676Sjpk 		 * options in after the security label.
13341676Sjpk 		 */
13351676Sjpk 		if (ipp->ipp_hopoptslen > *labellen) {
13361676Sjpk 			bcopy(ipp->ipp_hopopts + *labellen, newopts + optlen,
13371676Sjpk 			    ipp->ipp_hopoptslen - *labellen);
13381676Sjpk 			/*
13391676Sjpk 			 * Stomp out any header gunk here - this was the
13401676Sjpk 			 * previous next-header and option length field.
13411676Sjpk 			 */
13421676Sjpk 			newopts[optlen] = IP6OPT_PADN;
13431676Sjpk 			newopts[optlen + 1] = 0;
13441676Sjpk 		}
13451676Sjpk 		if (ipp->ipp_hopopts != NULL)
13461676Sjpk 			kmem_free(ipp->ipp_hopopts, ipp->ipp_hopoptslen);
13471676Sjpk 		ipp->ipp_hopopts = (ip6_hbh_t *)newopts;
13481676Sjpk 	}
13491676Sjpk 	ipp->ipp_hopoptslen = newlen;
13501676Sjpk 	*labellen = optlen;
13511676Sjpk 
13521676Sjpk 	newopts = (uchar_t *)ipp->ipp_hopopts;
13531676Sjpk 
13541676Sjpk 	/* If there are any options, then fix up reported length */
13551676Sjpk 	if (newlen > 0) {
13561676Sjpk 		newopts[1] = (newlen + 7) / 8 - 1;
13571676Sjpk 		ipp->ipp_fields |= IPPF_HOPOPTS;
13581676Sjpk 	}
13591676Sjpk 
13601676Sjpk 	/* If there's a label, then insert it now */
13611676Sjpk 	if (optlen > 0) {
13621676Sjpk 		/* skip next-header and length fields */
13631676Sjpk 		newopts += 2;
13641676Sjpk 		bcopy(labelopt, newopts, rawlen);
13651676Sjpk 		newopts += rawlen;
13661676Sjpk 		/* make sure padding comes out right */
13671676Sjpk 		optlen -= 2 + rawlen;
13681676Sjpk 		if (optlen == 1) {
13691676Sjpk 			newopts[0] = IP6OPT_PAD1;
13701676Sjpk 		} else if (optlen > 1) {
13711676Sjpk 			newopts[0] = IP6OPT_PADN;
13721676Sjpk 			optlen -=  2;
13731676Sjpk 			newopts[1] = optlen;
13741676Sjpk 			if (optlen > 0)
13751676Sjpk 				bzero(newopts + 2, optlen);
13761676Sjpk 		}
13771676Sjpk 	}
13781676Sjpk 	return (0);
13791676Sjpk }
13801676Sjpk 
13811676Sjpk int
13821676Sjpk tsol_update_options(uchar_t **opts, uint_t *totlen, uint_t *labellen,
13831676Sjpk     const uchar_t *labelopt)
13841676Sjpk {
13851676Sjpk 	int optlen, newlen;
13861676Sjpk 	uchar_t *newopts;
13871676Sjpk 
13881676Sjpk 	optlen = (labelopt[IPOPT_OLEN] + 3) & ~3;
13891676Sjpk 	newlen = *totlen + optlen - *labellen;
13901676Sjpk 	if (optlen > *labellen) {
13911676Sjpk 		if (newlen > IP_MAX_OPT_LENGTH)
13921676Sjpk 			return (EHOSTUNREACH);
13931676Sjpk 		newopts = (uchar_t *)mi_alloc(newlen, BPRI_HI);
13941676Sjpk 		if (newopts == NULL)
13951676Sjpk 			return (ENOMEM);
13961676Sjpk 		if (*totlen > *labellen) {
13971676Sjpk 			bcopy(*opts + *labellen, newopts + optlen,
13981676Sjpk 			    *totlen - *labellen);
13991676Sjpk 		}
14001676Sjpk 		if (*opts != NULL)
14011676Sjpk 			mi_free((char *)*opts);
14021676Sjpk 		*opts = newopts;
14031676Sjpk 	} else if (optlen < *labellen) {
14041676Sjpk 		if (newlen == 0 && *opts != NULL) {
14051676Sjpk 			mi_free((char *)*opts);
14061676Sjpk 			*opts = NULL;
14071676Sjpk 		}
14081676Sjpk 		if (*totlen > *labellen) {
14091676Sjpk 			ovbcopy(*opts + *labellen, *opts + optlen,
14101676Sjpk 			    *totlen - *labellen);
14111676Sjpk 		}
14121676Sjpk 	}
14131676Sjpk 	*totlen = newlen;
14141676Sjpk 	*labellen = optlen;
14151676Sjpk 	if (optlen > 0) {
14161676Sjpk 		newopts = *opts;
14171676Sjpk 		bcopy(labelopt, newopts, optlen);
14181676Sjpk 		/* check if there are user-supplied options that follow */
14191676Sjpk 		if (optlen < newlen) {
14201676Sjpk 			/* compute amount of embedded alignment needed */
14211676Sjpk 			optlen -= newopts[IPOPT_OLEN];
14221676Sjpk 			newopts += newopts[IPOPT_OLEN];
14231676Sjpk 			while (--optlen >= 0)
14241676Sjpk 				*newopts++ = IPOPT_NOP;
14251676Sjpk 		} else if (optlen != newopts[IPOPT_OLEN]) {
14261676Sjpk 			/*
14271676Sjpk 			 * The label option is the only option and it is
14281676Sjpk 			 * not a multiple of 4 bytes.
14291676Sjpk 			 */
14301676Sjpk 			optlen -= newopts[IPOPT_OLEN];
14311676Sjpk 			newopts += newopts[IPOPT_OLEN];
14321676Sjpk 			while (--optlen >= 0)
14331676Sjpk 				*newopts++ = IPOPT_EOL;
14341676Sjpk 		}
14351676Sjpk 	}
14361676Sjpk 	return (0);
14371676Sjpk }
14381676Sjpk 
14391676Sjpk /*
14401676Sjpk  * This does the bulk of the processing for setting IPPROTO_IP {T_,}IP_OPTIONS.
14411676Sjpk  */
14421676Sjpk boolean_t
14431676Sjpk tsol_option_set(uchar_t **opts, uint_t *optlen, uint_t labellen,
14441676Sjpk     const uchar_t *useropts, uint_t userlen)
14451676Sjpk {
14461676Sjpk 	int newlen;
14471676Sjpk 	uchar_t *newopts;
14481676Sjpk 
14491676Sjpk 	newlen = userlen + labellen;
14501676Sjpk 	if (newlen > *optlen) {
14511676Sjpk 		/* need more room */
14521676Sjpk 		newopts = (uchar_t *)mi_alloc(newlen, BPRI_HI);
14531676Sjpk 		if (newopts == NULL)
14541720Spwernau 			return (B_FALSE);
14551676Sjpk 		/*
14561676Sjpk 		 * The supplied *opts can't be NULL in this case,
14571676Sjpk 		 * since there's an existing label.
14581676Sjpk 		 */
14591676Sjpk 		if (labellen > 0)
14601676Sjpk 			bcopy(*opts, newopts, labellen);
14611676Sjpk 		if (*opts != NULL)
14621676Sjpk 			mi_free((char *)*opts);
14631676Sjpk 		*opts = newopts;
14641676Sjpk 	}
14651676Sjpk 
14661676Sjpk 	if (newlen == 0) {
14671676Sjpk 		/* special case -- no remaining IP options at all */
14681676Sjpk 		if (*opts != NULL) {
14691676Sjpk 			mi_free((char *)*opts);
14701676Sjpk 			*opts = NULL;
14711676Sjpk 		}
14721676Sjpk 	} else if (userlen > 0) {
14731676Sjpk 		/* merge in the user's options */
14741676Sjpk 		newopts = *opts;
14751676Sjpk 		if (labellen > 0) {
14761676Sjpk 			int extra = labellen - newopts[IPOPT_OLEN];
14771676Sjpk 
14781676Sjpk 			newopts += newopts[IPOPT_OLEN];
14791676Sjpk 			while (--extra >= 0)
14801676Sjpk 				*newopts++ = IPOPT_NOP;
14811676Sjpk 		}
14821676Sjpk 		bcopy(useropts, newopts, userlen);
14831676Sjpk 	}
14841676Sjpk 
14851676Sjpk 	*optlen = newlen;
14861720Spwernau 	return (B_TRUE);
14871676Sjpk }
1488