xref: /illumos-gate/usr/src/uts/common/inet/ip/tn_ipopt.c (revision 9e3469d3)
145916cd2Sjpk /*
245916cd2Sjpk  * CDDL HEADER START
345916cd2Sjpk  *
445916cd2Sjpk  * The contents of this file are subject to the terms of the
545916cd2Sjpk  * Common Development and Distribution License (the "License").
645916cd2Sjpk  * You may not use this file except in compliance with the License.
745916cd2Sjpk  *
845916cd2Sjpk  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
945916cd2Sjpk  * or http://www.opensolaris.org/os/licensing.
1045916cd2Sjpk  * See the License for the specific language governing permissions
1145916cd2Sjpk  * and limitations under the License.
1245916cd2Sjpk  *
1345916cd2Sjpk  * When distributing Covered Code, include this CDDL HEADER in each
1445916cd2Sjpk  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
1545916cd2Sjpk  * If applicable, add the following below this CDDL HEADER, with the
1645916cd2Sjpk  * fields enclosed by brackets "[]" replaced with your own identifying
1745916cd2Sjpk  * information: Portions Copyright [yyyy] [name of copyright owner]
1845916cd2Sjpk  *
1945916cd2Sjpk  * CDDL HEADER END
2045916cd2Sjpk  */
2145916cd2Sjpk /*
22*9e3469d3SErik Nordmark  * Copyright 2010 Sun Microsystems, Inc.  All rights reserved.
2345916cd2Sjpk  * Use is subject to license terms.
2445916cd2Sjpk  */
2545916cd2Sjpk 
2645916cd2Sjpk #include <sys/types.h>
2745916cd2Sjpk #include <sys/systm.h>
2845916cd2Sjpk #include <sys/kmem.h>
2945916cd2Sjpk #include <sys/disp.h>
3045916cd2Sjpk #include <sys/stream.h>
3145916cd2Sjpk #include <sys/strsubr.h>
3245916cd2Sjpk #include <sys/strsun.h>
3345916cd2Sjpk #include <sys/policy.h>
3445916cd2Sjpk #include <sys/tsol/label_macro.h>
3545916cd2Sjpk #include <sys/tsol/tndb.h>
3645916cd2Sjpk #include <sys/tsol/tnet.h>
3745916cd2Sjpk #include <inet/ip.h>
3845916cd2Sjpk #include <inet/ip6.h>
3945916cd2Sjpk #include <inet/tcp.h>
4045916cd2Sjpk #include <inet/ipclassifier.h>
4145916cd2Sjpk #include <inet/ip_ire.h>
42c793af95Ssangeeta #include <inet/ip_ftable.h>
4345916cd2Sjpk 
4445916cd2Sjpk /*
4545916cd2Sjpk  * This routine takes a sensitivity label as input and creates a CIPSO
4645916cd2Sjpk  * option in the specified buffer.  It returns the size of the CIPSO option.
4745916cd2Sjpk  * If the sensitivity label is too large for the CIPSO option, then 0
4845916cd2Sjpk  * is returned.
4945916cd2Sjpk  *
5045916cd2Sjpk  * tsol2cipso_tt1 returns 0 for failure and greater than 0 for success
5145916cd2Sjpk  * (more accurately, success means a return value between 10 and 40).
5245916cd2Sjpk  */
5345916cd2Sjpk 
5445916cd2Sjpk static int
tsol2cipso_tt1(const bslabel_t * sl,unsigned char * cop,uint32_t doi)5545916cd2Sjpk tsol2cipso_tt1(const bslabel_t *sl, unsigned char *cop, uint32_t doi)
5645916cd2Sjpk {
5745916cd2Sjpk 	struct cipso_tag_type_1 *tt1;
5845916cd2Sjpk 	const _bslabel_impl_t *bsl;
5945916cd2Sjpk 	const uchar_t *ucp;
6045916cd2Sjpk 	int i;
6145916cd2Sjpk 
6245916cd2Sjpk 	if (doi == 0)
6345916cd2Sjpk 		return (0);
6445916cd2Sjpk 
6545916cd2Sjpk 	/* check for Admin High sensitivity label */
6645916cd2Sjpk 	if (blequal(sl, label2bslabel(l_admin_high)))
6745916cd2Sjpk 		return (0);
6845916cd2Sjpk 
6945916cd2Sjpk 	/* check whether classification will fit in one octet */
7045916cd2Sjpk 	bsl = (const _bslabel_impl_t *)sl;
7145916cd2Sjpk 	if (LCLASS(bsl) & 0xFF00)
7245916cd2Sjpk 		return (0);
7345916cd2Sjpk 
7445916cd2Sjpk 	/*
7545916cd2Sjpk 	 * Check whether compartments will fit in 30 octets.
7645916cd2Sjpk 	 * Compartments 241 - 256 are not allowed.
7745916cd2Sjpk 	 */
7845916cd2Sjpk 	if (ntohl(bsl->compartments.c8) & 0x0000FFFF)
7945916cd2Sjpk 		return (0);
8045916cd2Sjpk 
8145916cd2Sjpk 	/*
8245916cd2Sjpk 	 * Compute option length and tag length.
8345916cd2Sjpk 	 * 'p' points to the last two bytes in the Sensitivity Label's
8445916cd2Sjpk 	 * compartments; these cannot be mapped into CIPSO compartments.
8545916cd2Sjpk 	 */
8645916cd2Sjpk 	ucp = (const uchar_t *)&bsl->compartments.c8 + 2;
8745916cd2Sjpk 	while (--ucp >= (const uchar_t *)&bsl->compartments.c1)
8845916cd2Sjpk 		if (*ucp != 0)
8945916cd2Sjpk 			break;
9045916cd2Sjpk 
9145916cd2Sjpk 	i =  ucp - (const uchar_t *)&bsl->compartments.c1 + 1;
9245916cd2Sjpk 
9345916cd2Sjpk 	if (cop == NULL)
9445916cd2Sjpk 		return (10 + i);
9545916cd2Sjpk 
9645916cd2Sjpk 	doi = htonl(doi);
9745916cd2Sjpk 	ucp = (const uchar_t *)&doi;
9845916cd2Sjpk 	cop[IPOPT_OPTVAL] = IPOPT_COMSEC;
9945916cd2Sjpk 	cop[IPOPT_OLEN] = 10 + i;
10045916cd2Sjpk 	cop[IPOPT_OLEN+1] = ucp[0];
10145916cd2Sjpk 	cop[IPOPT_OLEN+2] = ucp[1];
10245916cd2Sjpk 	cop[IPOPT_OLEN+3] = ucp[2];
10345916cd2Sjpk 	cop[IPOPT_OLEN+4] = ucp[3];
10445916cd2Sjpk 	tt1 = (struct cipso_tag_type_1 *)&cop[IPOPT_OLEN + 5];
10545916cd2Sjpk 	tt1->tag_type = 1;
10645916cd2Sjpk 	tt1->tag_align = 0;
10745916cd2Sjpk 	tt1->tag_sl = LCLASS(bsl);
10845916cd2Sjpk 	tt1->tag_length = 4 + i;
10945916cd2Sjpk 
11045916cd2Sjpk 	bcopy(&bsl->compartments.c1, tt1->tag_cat, i);
11145916cd2Sjpk 
11245916cd2Sjpk 	return (cop[IPOPT_OLEN]);
11345916cd2Sjpk }
11445916cd2Sjpk 
11545916cd2Sjpk /*
116c4e55c13Sken Powell - Sun Microsystem  * The following routine searches for a security label in an IPv4 datagram.
117c4e55c13Sken Powell - Sun Microsystem  * It returns label_type of:
118c4e55c13Sken Powell - Sun Microsystem  *    OPT_CIPSO if a CIPSO IP option is found.
119c4e55c13Sken Powell - Sun Microsystem  *    OPT_NONE if no security label is found.
12045916cd2Sjpk  *
121c4e55c13Sken Powell - Sun Microsystem  * If OPT_CIPSO, a pointer to the CIPSO IP option will be returned in
122c4e55c13Sken Powell - Sun Microsystem  * the buffer parameter.
123c4e55c13Sken Powell - Sun Microsystem  *
124c4e55c13Sken Powell - Sun Microsystem  * The function will return with B_FALSE if an IP format error
125c4e55c13Sken Powell - Sun Microsystem  * is encountered.
12645916cd2Sjpk  */
12745916cd2Sjpk 
128c4e55c13Sken Powell - Sun Microsystem boolean_t
tsol_get_option_v4(mblk_t * mp,tsol_ip_label_t * label_type,uchar_t ** buffer)129c4e55c13Sken Powell - Sun Microsystem tsol_get_option_v4(mblk_t *mp, tsol_ip_label_t *label_type, uchar_t **buffer)
13045916cd2Sjpk {
13145916cd2Sjpk 	ipha_t	*ipha;
13245916cd2Sjpk 	uchar_t	*opt;
13345916cd2Sjpk 	uint32_t	totallen;
13445916cd2Sjpk 	uint32_t	optval;
13545916cd2Sjpk 	uint32_t	optlen;
13645916cd2Sjpk 
137c4e55c13Sken Powell - Sun Microsystem 	*label_type = OPT_NONE;
13845916cd2Sjpk 
13945916cd2Sjpk 	/*
14045916cd2Sjpk 	 * Get length (in 4 byte octets) of IP header options.
141c4e55c13Sken Powell - Sun Microsystem 	 * If header doesn't contain options, then return a label_type
142c4e55c13Sken Powell - Sun Microsystem 	 * of OPT_NONE.
14345916cd2Sjpk 	 */
144c4e55c13Sken Powell - Sun Microsystem 	ipha = (ipha_t *)mp->b_rptr;
14545916cd2Sjpk 	totallen = ipha->ipha_version_and_hdr_length -
146c4e55c13Sken Powell - Sun Microsystem 	    (uint8_t)((IP_VERSION << 4));
14745916cd2Sjpk 	totallen <<= 2;
148c4e55c13Sken Powell - Sun Microsystem 	if (totallen < IP_SIMPLE_HDR_LENGTH || totallen > MBLKL(mp))
149c4e55c13Sken Powell - Sun Microsystem 		return (B_FALSE);
150c4e55c13Sken Powell - Sun Microsystem 	totallen -= IP_SIMPLE_HDR_LENGTH;
151c4e55c13Sken Powell - Sun Microsystem 	if (totallen == 0)
152c4e55c13Sken Powell - Sun Microsystem 		return (B_TRUE);
15345916cd2Sjpk 
15445916cd2Sjpk 	/*
15545916cd2Sjpk 	 * Search for CIPSO option.
15645916cd2Sjpk 	 * If no such option is present, then return OPT_NONE.
15745916cd2Sjpk 	 */
15845916cd2Sjpk 	opt = (uchar_t *)&ipha[1];
15945916cd2Sjpk 	while (totallen != 0) {
16045916cd2Sjpk 		switch (optval = opt[IPOPT_OPTVAL]) {
16145916cd2Sjpk 		case IPOPT_EOL:
162c4e55c13Sken Powell - Sun Microsystem 			return (B_TRUE);
16345916cd2Sjpk 		case IPOPT_NOP:
16445916cd2Sjpk 			optlen = 1;
16545916cd2Sjpk 			break;
16645916cd2Sjpk 		default:
16745916cd2Sjpk 			if (totallen <= IPOPT_OLEN)
168c4e55c13Sken Powell - Sun Microsystem 				return (B_FALSE);
16945916cd2Sjpk 			optlen = opt[IPOPT_OLEN];
17045916cd2Sjpk 			if (optlen < 2)
171c4e55c13Sken Powell - Sun Microsystem 				return (B_FALSE);
17245916cd2Sjpk 		}
17345916cd2Sjpk 		if (optlen > totallen)
174c4e55c13Sken Powell - Sun Microsystem 			return (B_FALSE);
17545916cd2Sjpk 		/*
17645916cd2Sjpk 		 * Copy pointer to option into '*buffer' and
17745916cd2Sjpk 		 * return the option type.
17845916cd2Sjpk 		 */
17945916cd2Sjpk 		switch (optval) {
18045916cd2Sjpk 		case IPOPT_COMSEC:
18145916cd2Sjpk 			if (TSOL_CIPSO_TAG_OFFSET < optlen &&
182c4e55c13Sken Powell - Sun Microsystem 			    opt[TSOL_CIPSO_TAG_OFFSET] == 1) {
183c4e55c13Sken Powell - Sun Microsystem 				*label_type = OPT_CIPSO;
184c4e55c13Sken Powell - Sun Microsystem 				*buffer = opt;
185c4e55c13Sken Powell - Sun Microsystem 				return (B_TRUE);
186c4e55c13Sken Powell - Sun Microsystem 			}
187c4e55c13Sken Powell - Sun Microsystem 			return (B_FALSE);
18845916cd2Sjpk 		}
18945916cd2Sjpk 		totallen -= optlen;
19045916cd2Sjpk 		opt += optlen;
19145916cd2Sjpk 	}
192c4e55c13Sken Powell - Sun Microsystem 	return (B_TRUE);
193c4e55c13Sken Powell - Sun Microsystem }
194c4e55c13Sken Powell - Sun Microsystem 
195c4e55c13Sken Powell - Sun Microsystem /*
196c4e55c13Sken Powell - Sun Microsystem  * The following routine searches for a security label in an IPv6 datagram.
197c4e55c13Sken Powell - Sun Microsystem  * It returns label_type of:
198c4e55c13Sken Powell - Sun Microsystem  *    OPT_CIPSO if a CIPSO IP option is found.
199c4e55c13Sken Powell - Sun Microsystem  *    OPT_NONE if no security label is found.
200c4e55c13Sken Powell - Sun Microsystem  *
201c4e55c13Sken Powell - Sun Microsystem  * If OPT_CIPSO, a pointer to the IPv4 portion of the CIPSO IP option will
202c4e55c13Sken Powell - Sun Microsystem  * be returned in the buffer parameter.
203c4e55c13Sken Powell - Sun Microsystem  *
204c4e55c13Sken Powell - Sun Microsystem  * The function will return with B_FALSE if an IP format error
205c4e55c13Sken Powell - Sun Microsystem  * or an unexpected label content error is encountered.
206c4e55c13Sken Powell - Sun Microsystem  */
207c4e55c13Sken Powell - Sun Microsystem 
208c4e55c13Sken Powell - Sun Microsystem boolean_t
tsol_get_option_v6(mblk_t * mp,tsol_ip_label_t * label_type,uchar_t ** buffer)209c4e55c13Sken Powell - Sun Microsystem tsol_get_option_v6(mblk_t *mp, tsol_ip_label_t *label_type, uchar_t **buffer)
210c4e55c13Sken Powell - Sun Microsystem {
211c4e55c13Sken Powell - Sun Microsystem 	uchar_t		*opt_ptr = NULL;
212c4e55c13Sken Powell - Sun Microsystem 	uchar_t		*after_secopt;
213c4e55c13Sken Powell - Sun Microsystem 	boolean_t	hbh_needed;
214c4e55c13Sken Powell - Sun Microsystem 	const uchar_t	*ip6hbh;
215c4e55c13Sken Powell - Sun Microsystem 	size_t		optlen;
216c4e55c13Sken Powell - Sun Microsystem 	uint32_t	doi;
217c4e55c13Sken Powell - Sun Microsystem 	const ip6_t	*ip6h;
218c4e55c13Sken Powell - Sun Microsystem 
219c4e55c13Sken Powell - Sun Microsystem 	*label_type = OPT_NONE;
220c4e55c13Sken Powell - Sun Microsystem 	*buffer = NULL;
221c4e55c13Sken Powell - Sun Microsystem 	ip6h = (const ip6_t *)mp->b_rptr;
222c4e55c13Sken Powell - Sun Microsystem 	if (ip6h->ip6_nxt != IPPROTO_HOPOPTS)
223c4e55c13Sken Powell - Sun Microsystem 		return (B_TRUE);
224c4e55c13Sken Powell - Sun Microsystem 	ip6hbh = (const uchar_t *)&ip6h[1];
225c4e55c13Sken Powell - Sun Microsystem 	if (ip6hbh + MIN_EHDR_LEN > mp->b_wptr)
226c4e55c13Sken Powell - Sun Microsystem 		return (B_FALSE);
227c4e55c13Sken Powell - Sun Microsystem 	optlen = (ip6hbh[1] + 1) << 3;
228c4e55c13Sken Powell - Sun Microsystem 	if (ip6hbh + optlen > mp->b_wptr)
229c4e55c13Sken Powell - Sun Microsystem 		return (B_FALSE);
230c4e55c13Sken Powell - Sun Microsystem 	if (!tsol_find_secopt_v6(ip6hbh, optlen,
231c4e55c13Sken Powell - Sun Microsystem 	    &opt_ptr, &after_secopt, &hbh_needed))
232c4e55c13Sken Powell - Sun Microsystem 		return (B_FALSE);
233c4e55c13Sken Powell - Sun Microsystem 	/* tsol_find_secopt_v6 guarantees some sanity */
234c4e55c13Sken Powell - Sun Microsystem 	if (opt_ptr != NULL) {
235c4e55c13Sken Powell - Sun Microsystem 		/*
236c4e55c13Sken Powell - Sun Microsystem 		 * IPv6 Option
237c4e55c13Sken Powell - Sun Microsystem 		 *   opt_ptr[0]: Option type
238c4e55c13Sken Powell - Sun Microsystem 		 *   opt_ptr[1]: Length of option data in bytes
239c4e55c13Sken Powell - Sun Microsystem 		 *   opt_ptr[2]: First byte of option data
240c4e55c13Sken Powell - Sun Microsystem 		 */
241c4e55c13Sken Powell - Sun Microsystem 		if ((optlen = opt_ptr[1]) < 8)
242c4e55c13Sken Powell - Sun Microsystem 			return (B_FALSE);
243c4e55c13Sken Powell - Sun Microsystem 		opt_ptr += 2;
244c4e55c13Sken Powell - Sun Microsystem 		/*
245c4e55c13Sken Powell - Sun Microsystem 		 * From "Generalized Labeled Security Option for IPv6" draft
246c4e55c13Sken Powell - Sun Microsystem 		 *   opt_ptr[0] - opt_ptr[4]: DOI = IP6LS_DOI_V4
247c4e55c13Sken Powell - Sun Microsystem 		 *   opt_ptr[4]: Tag type = IP6LS_TT_V4
248c4e55c13Sken Powell - Sun Microsystem 		 *   opt_ptr[5]: Tag length in bytes starting at Tag type field
249c4e55c13Sken Powell - Sun Microsystem 		 * IPv4 CIPSO Option
250c4e55c13Sken Powell - Sun Microsystem 		 *   opt_ptr[6]: option type
251c4e55c13Sken Powell - Sun Microsystem 		 *   opt_ptr[7]: option length in bytes starting at type field
252c4e55c13Sken Powell - Sun Microsystem 		 */
253c4e55c13Sken Powell - Sun Microsystem 		bcopy(opt_ptr, &doi, sizeof (doi));
254c4e55c13Sken Powell - Sun Microsystem 		doi = ntohl(doi);
255c4e55c13Sken Powell - Sun Microsystem 		if (doi == IP6LS_DOI_V4 &&
256c4e55c13Sken Powell - Sun Microsystem 		    opt_ptr[4] == IP6LS_TT_V4 &&
257c4e55c13Sken Powell - Sun Microsystem 		    opt_ptr[5] <= optlen - 4 &&
258c4e55c13Sken Powell - Sun Microsystem 		    opt_ptr[7] <= optlen - 6 &&
259c4e55c13Sken Powell - Sun Microsystem 		    opt_ptr[7] <= opt_ptr[5] - 2) {
260c4e55c13Sken Powell - Sun Microsystem 			opt_ptr += sizeof (doi) + 2;
261c4e55c13Sken Powell - Sun Microsystem 			*label_type = OPT_CIPSO;
262c4e55c13Sken Powell - Sun Microsystem 			*buffer = opt_ptr;
263c4e55c13Sken Powell - Sun Microsystem 			return (B_TRUE);
264c4e55c13Sken Powell - Sun Microsystem 		}
265c4e55c13Sken Powell - Sun Microsystem 		return (B_FALSE);
266c4e55c13Sken Powell - Sun Microsystem 	}
267c4e55c13Sken Powell - Sun Microsystem 	return (B_TRUE);
26845916cd2Sjpk }
26945916cd2Sjpk 
2705f9878b0Sken Powell - Sun Microsystem /*
2715f9878b0Sken Powell - Sun Microsystem  * tsol_check_dest()
2725f9878b0Sken Powell - Sun Microsystem  *
2735f9878b0Sken Powell - Sun Microsystem  * This routine verifies if a destination is allowed to recieve messages
274bd670b35SErik Nordmark  * based on the security label. If any adjustments to the label are needed
275bd670b35SErik Nordmark  * due to the connection's MAC mode or the destination's ability
276bd670b35SErik Nordmark  * to receive labels, an "effective label" will be returned.
277bd670b35SErik Nordmark  *
278bd670b35SErik Nordmark  * zone_is_global is set if the actual zoneid is global. That is, it is
279bd670b35SErik Nordmark  * not set for an exclusive-IP zone.
2805f9878b0Sken Powell - Sun Microsystem  *
281bd670b35SErik Nordmark  * On successful return, effective_tsl will point to the new label needed
282bd670b35SErik Nordmark  * or will be NULL if a new label isn't needed. On error, effective_tsl will
283bd670b35SErik Nordmark  * point to NULL.
2845f9878b0Sken Powell - Sun Microsystem  *
2855f9878b0Sken Powell - Sun Microsystem  * Returns:
286bd670b35SErik Nordmark  *      0		Label (was|is now) correct
287bd670b35SErik Nordmark  *	EHOSTUNREACH	The label failed the remote host accreditation
2885f9878b0Sken Powell - Sun Microsystem  *      ENOMEM		Memory allocation failure
2895f9878b0Sken Powell - Sun Microsystem  */
2905f9878b0Sken Powell - Sun Microsystem int
tsol_check_dest(const ts_label_t * tsl,const void * dst,uchar_t version,uint_t mac_mode,boolean_t zone_is_global,ts_label_t ** effective_tsl)291bd670b35SErik Nordmark tsol_check_dest(const ts_label_t *tsl, const void *dst,
292bd670b35SErik Nordmark     uchar_t version, uint_t mac_mode, boolean_t zone_is_global,
293bd670b35SErik Nordmark     ts_label_t **effective_tsl)
2945f9878b0Sken Powell - Sun Microsystem {
295bd670b35SErik Nordmark 	ts_label_t	*newtsl = NULL;
2965f9878b0Sken Powell - Sun Microsystem 	tsol_tpc_t	*dst_rhtp;
2975f9878b0Sken Powell - Sun Microsystem 
298bd670b35SErik Nordmark 	if (effective_tsl != NULL)
299bd670b35SErik Nordmark 		*effective_tsl = NULL;
3005f9878b0Sken Powell - Sun Microsystem 	ASSERT(version == IPV4_VERSION ||
3015f9878b0Sken Powell - Sun Microsystem 	    (version == IPV6_VERSION &&
3025f9878b0Sken Powell - Sun Microsystem 	    !IN6_IS_ADDR_V4MAPPED((in6_addr_t *)dst)));
3035f9878b0Sken Powell - Sun Microsystem 
3045f9878b0Sken Powell - Sun Microsystem 	/* Always pass kernel level communication (NULL label) */
305bd670b35SErik Nordmark 	if (tsl == NULL) {
3065f9878b0Sken Powell - Sun Microsystem 		DTRACE_PROBE2(tx__tnopt__log__info__labeling__mac__allownull,
307bd670b35SErik Nordmark 		    char *, "destination ip(1) with null label was passed",
3085f9878b0Sken Powell - Sun Microsystem 		    ipaddr_t, dst);
3095f9878b0Sken Powell - Sun Microsystem 		return (0);
3105f9878b0Sken Powell - Sun Microsystem 	}
3115f9878b0Sken Powell - Sun Microsystem 
3125d3b8cb7SBill Sommerfeld 	if (tsl->tsl_flags & TSLF_IMPLICIT_IN) {
3135d3b8cb7SBill Sommerfeld 		DTRACE_PROBE3(tx__tnopt__log__info__labeling__unresolved__label,
3145d3b8cb7SBill Sommerfeld 		    char *,
3155d3b8cb7SBill Sommerfeld 		    "implicit-in packet to ip(1) reached tsol_check_dest "
3165d3b8cb7SBill Sommerfeld 		    "with implied security label sl(2)",
3175d3b8cb7SBill Sommerfeld 		    ipaddr_t, dst, ts_label_t *, tsl);
3185d3b8cb7SBill Sommerfeld 	}
3195d3b8cb7SBill Sommerfeld 
3205f9878b0Sken Powell - Sun Microsystem 	/* Always pass multicast */
3215f9878b0Sken Powell - Sun Microsystem 	if (version == IPV4_VERSION &&
3225f9878b0Sken Powell - Sun Microsystem 	    CLASSD(*(ipaddr_t *)dst)) {
3235f9878b0Sken Powell - Sun Microsystem 		DTRACE_PROBE2(tx__tnopt__log__info__labeling__mac__allowmult,
3245f9878b0Sken Powell - Sun Microsystem 		    char *, "destination ip(1) with multicast dest was passed",
3255f9878b0Sken Powell - Sun Microsystem 		    ipaddr_t, dst);
3265f9878b0Sken Powell - Sun Microsystem 		return (0);
3275f9878b0Sken Powell - Sun Microsystem 	} else if (version == IPV6_VERSION &&
3285f9878b0Sken Powell - Sun Microsystem 	    IN6_IS_ADDR_MULTICAST((in6_addr_t *)dst)) {
3295f9878b0Sken Powell - Sun Microsystem 		DTRACE_PROBE2(tx__tnopt__log__info__labeling__mac__allowmult_v6,
3305f9878b0Sken Powell - Sun Microsystem 		    char *, "destination ip(1) with multicast dest was passed",
3315f9878b0Sken Powell - Sun Microsystem 		    in6_addr_t *, dst);
3325f9878b0Sken Powell - Sun Microsystem 		return (0);
3335f9878b0Sken Powell - Sun Microsystem 	}
3345f9878b0Sken Powell - Sun Microsystem 
3355f9878b0Sken Powell - Sun Microsystem 	/* Never pass an undefined destination */
3365f9878b0Sken Powell - Sun Microsystem 	if ((dst_rhtp = find_tpc(dst, version, B_FALSE)) == NULL) {
3375f9878b0Sken Powell - Sun Microsystem 		DTRACE_PROBE2(tx__tnopt__log__info__labeling__lookupdst,
3385f9878b0Sken Powell - Sun Microsystem 		    char *, "destination ip(1) not in tn database.",
3395f9878b0Sken Powell - Sun Microsystem 		    void *, dst);
3405f9878b0Sken Powell - Sun Microsystem 		return (EHOSTUNREACH);
3415f9878b0Sken Powell - Sun Microsystem 	}
3425f9878b0Sken Powell - Sun Microsystem 
3435f9878b0Sken Powell - Sun Microsystem 	switch (dst_rhtp->tpc_tp.host_type) {
3445f9878b0Sken Powell - Sun Microsystem 	case UNLABELED:
3455f9878b0Sken Powell - Sun Microsystem 		/*
3465f9878b0Sken Powell - Sun Microsystem 		 * Can talk to unlabeled hosts if
3475f9878b0Sken Powell - Sun Microsystem 		 * (1) zone's label matches the default label, or
3485d3b8cb7SBill Sommerfeld 		 * (2) SO_MAC_EXEMPT is on and we
3495d3b8cb7SBill Sommerfeld 		 * dominate the peer's label, or
3505d3b8cb7SBill Sommerfeld 		 * (3) SO_MAC_EXEMPT is on and
3515d3b8cb7SBill Sommerfeld 		 * this is the global zone
3525f9878b0Sken Powell - Sun Microsystem 		 */
3535f9878b0Sken Powell - Sun Microsystem 		if (dst_rhtp->tpc_tp.tp_doi != tsl->tsl_doi) {
354c4e55c13Sken Powell - Sun Microsystem 			DTRACE_PROBE4(tx__tnopt__log__info__labeling__doi,
3555f9878b0Sken Powell - Sun Microsystem 			    char *, "unlabeled dest ip(1)/tpc(2) doi does "
3565f9878b0Sken Powell - Sun Microsystem 			    "not match msg label(3) doi.", void *, dst,
3575f9878b0Sken Powell - Sun Microsystem 			    tsol_tpc_t *, dst_rhtp, ts_label_t *, tsl);
3585f9878b0Sken Powell - Sun Microsystem 			TPC_RELE(dst_rhtp);
3595f9878b0Sken Powell - Sun Microsystem 			return (EHOSTUNREACH);
3605f9878b0Sken Powell - Sun Microsystem 		}
3615f9878b0Sken Powell - Sun Microsystem 		if (!blequal(&dst_rhtp->tpc_tp.tp_def_label,
3625f9878b0Sken Powell - Sun Microsystem 		    &tsl->tsl_label)) {
3635d3b8cb7SBill Sommerfeld 			if (mac_mode != CONN_MAC_AWARE ||
364bd670b35SErik Nordmark 			    !(zone_is_global ||
3655f9878b0Sken Powell - Sun Microsystem 			    bldominates(&tsl->tsl_label,
3665f9878b0Sken Powell - Sun Microsystem 			    &dst_rhtp->tpc_tp.tp_def_label))) {
3675f9878b0Sken Powell - Sun Microsystem 				DTRACE_PROBE4(
3685f9878b0Sken Powell - Sun Microsystem 				    tx__tnopt__log__info__labeling__mac,
3695f9878b0Sken Powell - Sun Microsystem 				    char *, "unlabeled dest ip(1)/tpc(2) does "
3705f9878b0Sken Powell - Sun Microsystem 				    "not match msg label(3).", void *, dst,
3715f9878b0Sken Powell - Sun Microsystem 				    tsol_tpc_t *, dst_rhtp, ts_label_t *, tsl);
3725f9878b0Sken Powell - Sun Microsystem 				TPC_RELE(dst_rhtp);
3735f9878b0Sken Powell - Sun Microsystem 				return (EHOSTUNREACH);
3745f9878b0Sken Powell - Sun Microsystem 			}
3755f9878b0Sken Powell - Sun Microsystem 			/*
3765f9878b0Sken Powell - Sun Microsystem 			 * This is a downlabel MAC-exempt exchange.
3775f9878b0Sken Powell - Sun Microsystem 			 * Use the remote destination's default label
3785f9878b0Sken Powell - Sun Microsystem 			 * as the label of the message data.
3795f9878b0Sken Powell - Sun Microsystem 			 */
3805f9878b0Sken Powell - Sun Microsystem 			if ((newtsl = labelalloc(&dst_rhtp->tpc_tp.tp_def_label,
3815f9878b0Sken Powell - Sun Microsystem 			    dst_rhtp->tpc_tp.tp_doi, KM_NOSLEEP)) == NULL) {
3825f9878b0Sken Powell - Sun Microsystem 				TPC_RELE(dst_rhtp);
3835f9878b0Sken Powell - Sun Microsystem 				return (ENOMEM);
3845f9878b0Sken Powell - Sun Microsystem 			}
3855f9878b0Sken Powell - Sun Microsystem 			newtsl->tsl_flags |= TSLF_UNLABELED;
3865f9878b0Sken Powell - Sun Microsystem 
3875f9878b0Sken Powell - Sun Microsystem 		} else if (!(tsl->tsl_flags & TSLF_UNLABELED)) {
3885f9878b0Sken Powell - Sun Microsystem 			/*
3895f9878b0Sken Powell - Sun Microsystem 			 * The security labels are the same but we need
3905f9878b0Sken Powell - Sun Microsystem 			 * to flag that the remote node is unlabeled.
3915f9878b0Sken Powell - Sun Microsystem 			 */
3925f9878b0Sken Powell - Sun Microsystem 			if ((newtsl = labeldup(tsl, KM_NOSLEEP)) == NULL) {
3935f9878b0Sken Powell - Sun Microsystem 				TPC_RELE(dst_rhtp);
3945f9878b0Sken Powell - Sun Microsystem 				return (ENOMEM);
3955f9878b0Sken Powell - Sun Microsystem 			}
3965f9878b0Sken Powell - Sun Microsystem 			newtsl->tsl_flags |= TSLF_UNLABELED;
3975f9878b0Sken Powell - Sun Microsystem 		}
3985f9878b0Sken Powell - Sun Microsystem 		break;
3995f9878b0Sken Powell - Sun Microsystem 
4005f9878b0Sken Powell - Sun Microsystem 	case SUN_CIPSO:
4015f9878b0Sken Powell - Sun Microsystem 		/*
4025f9878b0Sken Powell - Sun Microsystem 		 * Can talk to labeled hosts if zone's label is within target's
4035f9878b0Sken Powell - Sun Microsystem 		 * label range or set.
4045f9878b0Sken Powell - Sun Microsystem 		 */
4055f9878b0Sken Powell - Sun Microsystem 		if (dst_rhtp->tpc_tp.tp_cipso_doi_cipso != tsl->tsl_doi ||
4065f9878b0Sken Powell - Sun Microsystem 		    (!_blinrange(&tsl->tsl_label,
4075f9878b0Sken Powell - Sun Microsystem 		    &dst_rhtp->tpc_tp.tp_sl_range_cipso) &&
4085f9878b0Sken Powell - Sun Microsystem 		    !blinlset(&tsl->tsl_label,
4095f9878b0Sken Powell - Sun Microsystem 		    dst_rhtp->tpc_tp.tp_sl_set_cipso))) {
4105f9878b0Sken Powell - Sun Microsystem 			DTRACE_PROBE4(tx__tnopt__log__info__labeling__mac,
4115f9878b0Sken Powell - Sun Microsystem 			    char *, "labeled dest ip(1)/tpc(2) does not "
4125f9878b0Sken Powell - Sun Microsystem 			    "match msg label(3).", void *, dst,
4135f9878b0Sken Powell - Sun Microsystem 			    tsol_tpc_t *, dst_rhtp, ts_label_t *, tsl);
4145f9878b0Sken Powell - Sun Microsystem 			TPC_RELE(dst_rhtp);
4155f9878b0Sken Powell - Sun Microsystem 			return (EHOSTUNREACH);
4165f9878b0Sken Powell - Sun Microsystem 		}
4175d3b8cb7SBill Sommerfeld 		if ((tsl->tsl_flags & TSLF_UNLABELED) ||
4185d3b8cb7SBill Sommerfeld 		    (mac_mode == CONN_MAC_IMPLICIT)) {
4195f9878b0Sken Powell - Sun Microsystem 			/*
4205d3b8cb7SBill Sommerfeld 			 * Copy label so we can modify the flags
4215f9878b0Sken Powell - Sun Microsystem 			 */
4225f9878b0Sken Powell - Sun Microsystem 			if ((newtsl = labeldup(tsl, KM_NOSLEEP)) == NULL) {
4235f9878b0Sken Powell - Sun Microsystem 				TPC_RELE(dst_rhtp);
4245f9878b0Sken Powell - Sun Microsystem 				return (ENOMEM);
4255f9878b0Sken Powell - Sun Microsystem 			}
4265d3b8cb7SBill Sommerfeld 			/*
4275d3b8cb7SBill Sommerfeld 			 * The security label is a match but we need to
4285d3b8cb7SBill Sommerfeld 			 * clear the unlabeled flag for this remote node.
4295d3b8cb7SBill Sommerfeld 			 */
4305d3b8cb7SBill Sommerfeld 			newtsl->tsl_flags &= ~TSLF_UNLABELED;
4315d3b8cb7SBill Sommerfeld 			if (mac_mode == CONN_MAC_IMPLICIT)
4325d3b8cb7SBill Sommerfeld 				newtsl->tsl_flags |= TSLF_IMPLICIT_OUT;
4335f9878b0Sken Powell - Sun Microsystem 		}
4345f9878b0Sken Powell - Sun Microsystem 		break;
4355f9878b0Sken Powell - Sun Microsystem 
4365f9878b0Sken Powell - Sun Microsystem 	default:
4375f9878b0Sken Powell - Sun Microsystem 		TPC_RELE(dst_rhtp);
4385f9878b0Sken Powell - Sun Microsystem 		return (EHOSTUNREACH);
4395f9878b0Sken Powell - Sun Microsystem 	}
4405f9878b0Sken Powell - Sun Microsystem 
4415f9878b0Sken Powell - Sun Microsystem 	/*
442bd670b35SErik Nordmark 	 * Return the new label.
4435f9878b0Sken Powell - Sun Microsystem 	 */
4445f9878b0Sken Powell - Sun Microsystem 	if (newtsl != NULL) {
445bd670b35SErik Nordmark 		if (effective_tsl != NULL)
446bd670b35SErik Nordmark 			*effective_tsl = newtsl;
447bd670b35SErik Nordmark 		else
448bd670b35SErik Nordmark 			label_rele(newtsl);
4495f9878b0Sken Powell - Sun Microsystem 	}
4505f9878b0Sken Powell - Sun Microsystem 	TPC_RELE(dst_rhtp);
4515f9878b0Sken Powell - Sun Microsystem 	return (0);
4525f9878b0Sken Powell - Sun Microsystem }
4535f9878b0Sken Powell - Sun Microsystem 
45445916cd2Sjpk /*
455bd670b35SErik Nordmark  * tsol_compute_label_v4()
45645916cd2Sjpk  *
45745916cd2Sjpk  * This routine computes the IP label that should be on a packet based on the
45845916cd2Sjpk  * connection and destination information.
45945916cd2Sjpk  *
460bd670b35SErik Nordmark  * The zoneid is the IP zoneid (i.e., GLOBAL_ZONEID for exlusive-IP zones).
461bd670b35SErik Nordmark  *
46245916cd2Sjpk  * Returns:
46345916cd2Sjpk  *      0		Fetched label
4645f9878b0Sken Powell - Sun Microsystem  *	EHOSTUNREACH	No route to destination
46545916cd2Sjpk  *	EINVAL		Label cannot be computed
46645916cd2Sjpk  */
46745916cd2Sjpk int
tsol_compute_label_v4(const ts_label_t * tsl,zoneid_t zoneid,ipaddr_t dst,uchar_t * opt_storage,ip_stack_t * ipst)468bd670b35SErik Nordmark tsol_compute_label_v4(const ts_label_t *tsl, zoneid_t zoneid, ipaddr_t dst,
469bd670b35SErik Nordmark     uchar_t *opt_storage, ip_stack_t *ipst)
47045916cd2Sjpk {
47145916cd2Sjpk 	uint_t		sec_opt_len;
472bd670b35SErik Nordmark 	ire_t		*ire;
473bd670b35SErik Nordmark 	tsol_ire_gw_secattr_t *attrp = NULL;
474de8c4a14SErik Nordmark 
47545916cd2Sjpk 	if (opt_storage != NULL)
47645916cd2Sjpk 		opt_storage[IPOPT_OLEN] = 0;
47745916cd2Sjpk 
478bd670b35SErik Nordmark 	if (tsl == NULL)
47945916cd2Sjpk 		return (0);
48045916cd2Sjpk 
48145916cd2Sjpk 	/* always pass multicast */
48245916cd2Sjpk 	if (CLASSD(dst))
48345916cd2Sjpk 		return (0);
48445916cd2Sjpk 
4855d3b8cb7SBill Sommerfeld 	if (tsl->tsl_flags & TSLF_IMPLICIT_OUT)
4865d3b8cb7SBill Sommerfeld 		return (0);
4875d3b8cb7SBill Sommerfeld 
4885f9878b0Sken Powell - Sun Microsystem 	if (tsl->tsl_flags & TSLF_UNLABELED) {
48945916cd2Sjpk 		/*
4905f9878b0Sken Powell - Sun Microsystem 		 * The destination is unlabeled. Only add a label if the
4915f9878b0Sken Powell - Sun Microsystem 		 * destination is not a broadcast/local/loopback address,
4925f9878b0Sken Powell - Sun Microsystem 		 * the destination is not on the same subnet, and the
4935f9878b0Sken Powell - Sun Microsystem 		 * next-hop gateway is labeled.
49445916cd2Sjpk 		 */
495bd670b35SErik Nordmark 		ire = ire_route_recursive_v4(dst, 0, NULL, zoneid, tsl,
496*9e3469d3SErik Nordmark 		    MATCH_IRE_SECATTR, IRR_ALLOCATE, 0, ipst, NULL, &attrp,
497*9e3469d3SErik Nordmark 		    NULL);
498bd670b35SErik Nordmark 		ASSERT(ire != NULL);
499bd670b35SErik Nordmark 		if (ire->ire_flags & (RTF_REJECT|RTF_BLACKHOLE)) {
500bd670b35SErik Nordmark 			/* no route to destination */
501bd670b35SErik Nordmark 			ire_refrele(ire);
5025f9878b0Sken Powell - Sun Microsystem 			DTRACE_PROBE3(
50345916cd2Sjpk 			    tx__tnopt__log__info__labeling__routedst__v4,
5045f9878b0Sken Powell - Sun Microsystem 			    char *, "No route to unlabeled dest ip(1) with "
505bd670b35SErik Nordmark 			    "with label(2).", ipaddr_t, dst, ts_label_t *, tsl);
5065f9878b0Sken Powell - Sun Microsystem 			return (EHOSTUNREACH);
50745916cd2Sjpk 		}
508bd670b35SErik Nordmark 		if (ire->ire_type & (IRE_BROADCAST | IRE_LOCAL | IRE_LOOPBACK |
509bd670b35SErik Nordmark 		    IRE_INTERFACE)) {
510bd670b35SErik Nordmark 			ire_refrele(ire);
511bd670b35SErik Nordmark 			return (0);
512bd670b35SErik Nordmark 		}
51345916cd2Sjpk 
51445916cd2Sjpk 		/*
515bd670b35SErik Nordmark 		 * ire_route_recursive gives us the first attrp it finds
516bd670b35SErik Nordmark 		 * in the recursive lookup.
51745916cd2Sjpk 		 */
51845916cd2Sjpk 		/*
5195f9878b0Sken Powell - Sun Microsystem 		 * Return now if next hop gateway is unlabeled. There is
5205f9878b0Sken Powell - Sun Microsystem 		 * no need to generate a CIPSO option for this message.
52145916cd2Sjpk 		 */
5225f9878b0Sken Powell - Sun Microsystem 		if (attrp == NULL || attrp->igsa_rhc == NULL ||
5235f9878b0Sken Powell - Sun Microsystem 		    attrp->igsa_rhc->rhc_tpc->tpc_tp.host_type == UNLABELED) {
524bd670b35SErik Nordmark 			ire_refrele(ire);
5255f9878b0Sken Powell - Sun Microsystem 			return (0);
52645916cd2Sjpk 		}
527bd670b35SErik Nordmark 		ire_refrele(ire);
52845916cd2Sjpk 	}
52945916cd2Sjpk 
53045916cd2Sjpk 	/* compute the CIPSO option */
5315f9878b0Sken Powell - Sun Microsystem 	sec_opt_len = tsol2cipso_tt1(&tsl->tsl_label, opt_storage,
5325f9878b0Sken Powell - Sun Microsystem 	    tsl->tsl_doi);
53345916cd2Sjpk 
53445916cd2Sjpk 	if (sec_opt_len == 0) {
5355f9878b0Sken Powell - Sun Microsystem 		DTRACE_PROBE3(tx__tnopt__log__error__labeling__lostops__v4,
536bd670b35SErik Nordmark 		    char *, "options lack length for dest ip(1) with label(2).",
537bd670b35SErik Nordmark 		    ipaddr_t, dst, ts_label_t *, tsl);
53845916cd2Sjpk 		return (EINVAL);
53945916cd2Sjpk 	}
54045916cd2Sjpk 
54145916cd2Sjpk 	return (0);
54245916cd2Sjpk }
54345916cd2Sjpk 
54445916cd2Sjpk /*
54545916cd2Sjpk  * Remove any existing security option (CIPSO) from the given IP
54645916cd2Sjpk  * header, move the 'buflen' bytes back to fill the gap, and return the number
54745916cd2Sjpk  * of bytes removed (as zero or negative number).  Assumes that the headers are
54845916cd2Sjpk  * sane.
549bd670b35SErik Nordmark  *
550bd670b35SErik Nordmark  * Note that tsol_remove_secopt does not adjust ipha_length but
551bd670b35SErik Nordmark  * tsol_remove_secopt_v6 does adjust ip6_plen.
55245916cd2Sjpk  */
55345916cd2Sjpk int
tsol_remove_secopt(ipha_t * ipha,int buflen)55445916cd2Sjpk tsol_remove_secopt(ipha_t *ipha, int buflen)
55545916cd2Sjpk {
55645916cd2Sjpk 	int remlen, olen, oval, delta;
55745916cd2Sjpk 	uchar_t *fptr, *tptr;
55845916cd2Sjpk 	boolean_t noop_keep;
55945916cd2Sjpk 
56045916cd2Sjpk 	remlen = IPH_HDR_LENGTH(ipha) - IP_SIMPLE_HDR_LENGTH;
56145916cd2Sjpk 	fptr = tptr = (uchar_t *)(ipha + 1);
56245916cd2Sjpk 	noop_keep = B_TRUE;
56345916cd2Sjpk 	while (remlen > 0) {
56445916cd2Sjpk 		oval = fptr[IPOPT_OPTVAL];
56545916cd2Sjpk 
56645916cd2Sjpk 		/* terminate on end of list */
56745916cd2Sjpk 		if (oval == IPOPT_EOL)
56845916cd2Sjpk 			break;
56945916cd2Sjpk 
57045916cd2Sjpk 		/*
57145916cd2Sjpk 		 * Delete any no-ops following a deleted option, at least up
57245916cd2Sjpk 		 * to a 4 octet alignment; copy others.
57345916cd2Sjpk 		 */
57445916cd2Sjpk 		if (oval == IPOPT_NOP) {
57545916cd2Sjpk 			if (((fptr - (uchar_t *)ipha) & 3) == 0)
57645916cd2Sjpk 				noop_keep = B_TRUE;
57745916cd2Sjpk 			if (noop_keep)
57845916cd2Sjpk 				*tptr++ = oval;
57945916cd2Sjpk 			fptr++;
58045916cd2Sjpk 			remlen--;
58145916cd2Sjpk 			continue;
58245916cd2Sjpk 		}
58345916cd2Sjpk 
58445916cd2Sjpk 		/* stop on corrupted list; just do nothing. */
58545916cd2Sjpk 		if (remlen < 2)
58645916cd2Sjpk 			return (0);
58745916cd2Sjpk 		olen = fptr[IPOPT_OLEN];
58845916cd2Sjpk 		if (olen < 2 || olen > remlen)
58945916cd2Sjpk 			return (0);
59045916cd2Sjpk 
59145916cd2Sjpk 		/* skip over security options to delete them */
59245916cd2Sjpk 		if (oval == IPOPT_COMSEC || oval == IPOPT_SECURITY) {
59345916cd2Sjpk 			noop_keep = B_FALSE;
59445916cd2Sjpk 			fptr += olen;
59545916cd2Sjpk 			remlen -= olen;
59645916cd2Sjpk 			continue;
59745916cd2Sjpk 		}
59845916cd2Sjpk 
59945916cd2Sjpk 		/* copy the rest */
60045916cd2Sjpk 		noop_keep = B_TRUE;
60145916cd2Sjpk 		if (tptr != fptr)
60245916cd2Sjpk 			ovbcopy(fptr, tptr, olen);
60345916cd2Sjpk 		fptr += olen;
60445916cd2Sjpk 		tptr += olen;
60545916cd2Sjpk 		remlen -= olen;
60645916cd2Sjpk 	}
60745916cd2Sjpk 
60845916cd2Sjpk 	fptr += remlen;
60945916cd2Sjpk 
61045916cd2Sjpk 	/* figure how much padding we'll need for header alignment */
61145916cd2Sjpk 	olen = (tptr - (uchar_t *)ipha) & 3;
61245916cd2Sjpk 	if (olen > 0) {
61345916cd2Sjpk 		olen = 4 - olen;
61445916cd2Sjpk 		/* pad with end-of-list */
61545916cd2Sjpk 		bzero(tptr, olen);
61645916cd2Sjpk 		tptr += olen;
61745916cd2Sjpk 	}
61845916cd2Sjpk 
61945916cd2Sjpk 	/* slide back the headers that follow and update the IP header */
62045916cd2Sjpk 	delta = fptr - tptr;
62145916cd2Sjpk 	if (delta != 0) {
62245916cd2Sjpk 		ovbcopy(fptr, tptr, ((uchar_t *)ipha + buflen) - fptr);
62345916cd2Sjpk 		ipha->ipha_version_and_hdr_length -= delta / 4;
62445916cd2Sjpk 	}
62545916cd2Sjpk 	return (-delta);
62645916cd2Sjpk }
62745916cd2Sjpk 
62845916cd2Sjpk /*
62945916cd2Sjpk  * Insert the option in 'optbuf' into the IP header pointed to by 'ipha', and
63045916cd2Sjpk  * move the data following the IP header (up to buflen) to accomodate the new
63145916cd2Sjpk  * option.  Assumes that up to IP_MAX_OPT_LENGTH bytes are available (in total)
63245916cd2Sjpk  * for IP options.  Returns the number of bytes actually inserted, or -1 if the
63345916cd2Sjpk  * option cannot be inserted.  (Note that negative return values are possible
63445916cd2Sjpk  * when noops must be compressed, and that only -1 indicates error.  Successful
63545916cd2Sjpk  * return value is always evenly divisible by 4, by definition.)
636bd670b35SErik Nordmark  *
637bd670b35SErik Nordmark  * Note that tsol_prepend_option does not adjust ipha_length but
638bd670b35SErik Nordmark  * tsol_prepend_option_v6 does adjust ip6_plen.
63945916cd2Sjpk  */
64045916cd2Sjpk int
tsol_prepend_option(uchar_t * optbuf,ipha_t * ipha,int buflen)64145916cd2Sjpk tsol_prepend_option(uchar_t *optbuf, ipha_t *ipha, int buflen)
64245916cd2Sjpk {
64345916cd2Sjpk 	int remlen, padding, lastpad, totlen;
64445916cd2Sjpk 	int oval, olen;
64545916cd2Sjpk 	int delta;
64645916cd2Sjpk 	uchar_t *optr;
64745916cd2Sjpk 	uchar_t tempopt[IP_MAX_OPT_LENGTH], *toptr;
64845916cd2Sjpk 
64945916cd2Sjpk 	if (optbuf[IPOPT_OPTVAL] == IPOPT_EOL ||
65045916cd2Sjpk 	    optbuf[IPOPT_OPTVAL] == IPOPT_NOP ||
65145916cd2Sjpk 	    optbuf[IPOPT_OLEN] == 0)
65245916cd2Sjpk 		return (0);
65345916cd2Sjpk 
65445916cd2Sjpk 	ASSERT(optbuf[IPOPT_OLEN] >= 2 &&
65545916cd2Sjpk 	    optbuf[IPOPT_OLEN] <= IP_MAX_OPT_LENGTH);
65645916cd2Sjpk 
65745916cd2Sjpk 	/* first find the real (unpadded) length of the existing options */
65845916cd2Sjpk 	remlen = IPH_HDR_LENGTH(ipha) - IP_SIMPLE_HDR_LENGTH;
65945916cd2Sjpk 	padding = totlen = lastpad = 0;
66045916cd2Sjpk 	optr = (uchar_t *)(ipha + 1);
66145916cd2Sjpk 	while (remlen > 0) {
66245916cd2Sjpk 		oval = optr[IPOPT_OPTVAL];
66345916cd2Sjpk 
66445916cd2Sjpk 		/* stop at end of list */
66545916cd2Sjpk 		if (oval == IPOPT_EOL)
66645916cd2Sjpk 			break;
66745916cd2Sjpk 
66845916cd2Sjpk 		/* skip no-ops, noting that length byte isn't present */
66945916cd2Sjpk 		if (oval == IPOPT_NOP) {
67045916cd2Sjpk 			optr++;
67145916cd2Sjpk 			padding++;
67245916cd2Sjpk 			lastpad++;
67345916cd2Sjpk 			totlen++;
67445916cd2Sjpk 			remlen--;
67545916cd2Sjpk 			continue;
67645916cd2Sjpk 		}
67745916cd2Sjpk 
67845916cd2Sjpk 		/* give up on a corrupted list; report failure */
67945916cd2Sjpk 		if (remlen < 2)
68045916cd2Sjpk 			return (-1);
68145916cd2Sjpk 		olen = optr[IPOPT_OLEN];
68245916cd2Sjpk 		if (olen < 2 || olen > remlen)
68345916cd2Sjpk 			return (-1);
68445916cd2Sjpk 
68545916cd2Sjpk 		lastpad = 0;
68645916cd2Sjpk 		optr += olen;
68745916cd2Sjpk 		totlen += olen;
68845916cd2Sjpk 		remlen -= olen;
68945916cd2Sjpk 	}
69045916cd2Sjpk 
69145916cd2Sjpk 	/* completely ignore any trailing padding */
69245916cd2Sjpk 	totlen -= lastpad;
69345916cd2Sjpk 	padding -= lastpad;
69445916cd2Sjpk 
69545916cd2Sjpk 	/*
69645916cd2Sjpk 	 * If some sort of inter-option alignment was present, try to preserve
69745916cd2Sjpk 	 * that alignment.  If alignment pushes us out past the maximum, then
69845916cd2Sjpk 	 * discard it and try to compress to fit.  (We just "assume" that any
69945916cd2Sjpk 	 * padding added was attempting to get 32 bit alignment.  If that's
70045916cd2Sjpk 	 * wrong, that's just too bad.)
70145916cd2Sjpk 	 */
70245916cd2Sjpk 	if (padding > 0) {
70345916cd2Sjpk 		olen = (optbuf[IPOPT_OLEN] + 3) & ~3;
70445916cd2Sjpk 		if (olen + totlen > IP_MAX_OPT_LENGTH) {
70545916cd2Sjpk 			totlen -= padding;
70645916cd2Sjpk 			if (olen + totlen > IP_MAX_OPT_LENGTH)
70745916cd2Sjpk 				return (-1);
70845916cd2Sjpk 			padding = 0;
70945916cd2Sjpk 		}
71045916cd2Sjpk 	}
71145916cd2Sjpk 
71245916cd2Sjpk 	/*
71345916cd2Sjpk 	 * Since we may need to compress or expand the option list, we write to
71445916cd2Sjpk 	 * a temporary buffer and then copy the results back to the IP header.
71545916cd2Sjpk 	 */
71645916cd2Sjpk 	toptr = tempopt;
71745916cd2Sjpk 
71845916cd2Sjpk 	/* compute actual option to insert */
71945916cd2Sjpk 	olen = optbuf[IPOPT_OLEN];
72045916cd2Sjpk 	bcopy(optbuf, toptr, olen);
72145916cd2Sjpk 	toptr += olen;
72245916cd2Sjpk 	if (padding > 0) {
72345916cd2Sjpk 		while ((olen & 3) != 0) {
72445916cd2Sjpk 			*toptr++ = IPOPT_NOP;
72545916cd2Sjpk 			olen++;
72645916cd2Sjpk 		}
72745916cd2Sjpk 	}
72845916cd2Sjpk 
72945916cd2Sjpk 	/* copy over the existing options */
73045916cd2Sjpk 	optr = (uchar_t *)(ipha + 1);
73145916cd2Sjpk 	while (totlen > 0) {
73245916cd2Sjpk 		oval = optr[IPOPT_OPTVAL];
73345916cd2Sjpk 
73445916cd2Sjpk 		/* totlen doesn't include end-of-list marker */
73545916cd2Sjpk 		ASSERT(oval != IPOPT_EOL);
73645916cd2Sjpk 
73745916cd2Sjpk 		/* handle no-ops; copy if desired, ignore otherwise */
73845916cd2Sjpk 		if (oval == IPOPT_NOP) {
73945916cd2Sjpk 			if (padding > 0) {
74045916cd2Sjpk 				/* note: cannot overflow due to checks above */
74145916cd2Sjpk 				ASSERT(toptr < tempopt + IP_MAX_OPT_LENGTH);
74245916cd2Sjpk 				*toptr++ = oval;
74345916cd2Sjpk 			}
74445916cd2Sjpk 			optr++;
74545916cd2Sjpk 			totlen--;
74645916cd2Sjpk 			continue;
74745916cd2Sjpk 		}
74845916cd2Sjpk 
74945916cd2Sjpk 		/* list cannot be corrupt at this point */
75045916cd2Sjpk 		ASSERT(totlen >= 2);
75145916cd2Sjpk 		olen = optr[IPOPT_OLEN];
75245916cd2Sjpk 		ASSERT(olen >= 2 && olen <= totlen);
75345916cd2Sjpk 
75445916cd2Sjpk 		/* cannot run out of room due to tests above */
75545916cd2Sjpk 		ASSERT(toptr + olen <= tempopt + IP_MAX_OPT_LENGTH);
75645916cd2Sjpk 
75745916cd2Sjpk 		bcopy(optr, toptr, olen);
75845916cd2Sjpk 		optr += olen;
75945916cd2Sjpk 		toptr += olen;
76045916cd2Sjpk 		totlen -= olen;
76145916cd2Sjpk 	}
76245916cd2Sjpk 
76345916cd2Sjpk 	/* figure how much padding we'll need for header alignment */
76445916cd2Sjpk 	olen = (toptr - tempopt) & 3;
76545916cd2Sjpk 	if (olen > 0) {
76645916cd2Sjpk 		olen = 4 - olen;
76745916cd2Sjpk 		ASSERT(toptr + olen <= tempopt + IP_MAX_OPT_LENGTH);
76845916cd2Sjpk 		/* pad with end-of-list value */
76945916cd2Sjpk 		bzero(toptr, olen);
77045916cd2Sjpk 		toptr += olen;
77145916cd2Sjpk 	}
77245916cd2Sjpk 
77345916cd2Sjpk 	/* move the headers as needed and update IP header */
77445916cd2Sjpk 	olen = (toptr - tempopt) + IP_SIMPLE_HDR_LENGTH;
77545916cd2Sjpk 	remlen = IPH_HDR_LENGTH(ipha);
77645916cd2Sjpk 	delta = olen - remlen;
77745916cd2Sjpk 	if (delta != 0) {
77845916cd2Sjpk 		ovbcopy((uchar_t *)ipha + remlen, (uchar_t *)ipha + olen,
77945916cd2Sjpk 		    buflen - remlen);
78045916cd2Sjpk 		ipha->ipha_version_and_hdr_length += delta / 4;
78145916cd2Sjpk 	}
78245916cd2Sjpk 
78345916cd2Sjpk 	/* slap in the new options */
78445916cd2Sjpk 	bcopy(tempopt, ipha + 1, olen - IP_SIMPLE_HDR_LENGTH);
78545916cd2Sjpk 
78645916cd2Sjpk 	return (delta);
78745916cd2Sjpk }
78845916cd2Sjpk 
78945916cd2Sjpk /*
790bd670b35SErik Nordmark  * tsol_check_label_v4()
79145916cd2Sjpk  *
79245916cd2Sjpk  * This routine computes the IP label that should be on the packet based on the
793bd670b35SErik Nordmark  * connection and destination information.  It's called by the IP forwarding
794bd670b35SErik Nordmark  * logic and by ip_output_simple. The ULPs generate the labels before calling
795bd670b35SErik Nordmark  * conn_ip_output. If any adjustments to
796bd670b35SErik Nordmark  * the label are needed due to the connection's MAC-exempt status or
797bd670b35SErik Nordmark  * the destination's ability to receive labels, an "effective label"
798bd670b35SErik Nordmark  * will be returned.
79945916cd2Sjpk  *
800222c5bceSkp  * The packet's header is clear before entering IPsec's engine.
80145916cd2Sjpk  *
802bd670b35SErik Nordmark  * The zoneid is the IP zoneid (i.e., GLOBAL_ZONEID for exlusive-IP zones).
803bd670b35SErik Nordmark  * zone_is_global is set if the actual zoneid is global.
804bd670b35SErik Nordmark  *
805bd670b35SErik Nordmark  * On successful return, effective_tslp will point to the new label needed
806bd670b35SErik Nordmark  * or will be NULL if a new label isn't needed. On error, effective_tsl will
807bd670b35SErik Nordmark  * point to NULL.
808bd670b35SErik Nordmark  *
80945916cd2Sjpk  * Returns:
8100e0e37a8SErik Nordmark  *      0		Label (was|is now) correct
81145916cd2Sjpk  *      EACCES		The packet failed the remote host accreditation.
81245916cd2Sjpk  *      ENOMEM		Memory allocation failure.
81345916cd2Sjpk  *	EINVAL		Label cannot be computed
81445916cd2Sjpk  */
81545916cd2Sjpk int
tsol_check_label_v4(const ts_label_t * tsl,zoneid_t zoneid,mblk_t ** mpp,uint_t mac_mode,boolean_t zone_is_global,ip_stack_t * ipst,ts_label_t ** effective_tslp)816bd670b35SErik Nordmark tsol_check_label_v4(const ts_label_t *tsl, zoneid_t zoneid, mblk_t **mpp,
817bd670b35SErik Nordmark     uint_t mac_mode, boolean_t zone_is_global, ip_stack_t *ipst,
818bd670b35SErik Nordmark     ts_label_t **effective_tslp)
81945916cd2Sjpk {
82045916cd2Sjpk 	mblk_t *mp = *mpp;
82145916cd2Sjpk 	ipha_t  *ipha;
822bd670b35SErik Nordmark 	ts_label_t *effective_tsl = NULL;
82345916cd2Sjpk 	uchar_t opt_storage[IP_MAX_OPT_LENGTH];
82445916cd2Sjpk 	uint_t hlen;
82545916cd2Sjpk 	uint_t sec_opt_len;
82645916cd2Sjpk 	uchar_t *optr;
827222c5bceSkp 	int delta_remove = 0, delta_add, adjust;
82845916cd2Sjpk 	int retv;
82945916cd2Sjpk 
830bd670b35SErik Nordmark 	*effective_tslp = NULL;
83145916cd2Sjpk 	opt_storage[IPOPT_OPTVAL] = 0;
83245916cd2Sjpk 
83345916cd2Sjpk 	ipha = (ipha_t *)mp->b_rptr;
83445916cd2Sjpk 
8355f9878b0Sken Powell - Sun Microsystem 	/*
8365f9878b0Sken Powell - Sun Microsystem 	 * Verify the destination is allowed to receive packets at
837bd670b35SErik Nordmark 	 * the security label of the message data. tsol_check_dest()
838bd670b35SErik Nordmark 	 * may create a new effective label or label flags.
8395f9878b0Sken Powell - Sun Microsystem 	 */
840bd670b35SErik Nordmark 	retv = tsol_check_dest(tsl, &ipha->ipha_dst, IPV4_VERSION,
841bd670b35SErik Nordmark 	    mac_mode, zone_is_global, &effective_tsl);
84245916cd2Sjpk 	if (retv != 0)
84345916cd2Sjpk 		return (retv);
84445916cd2Sjpk 
8455f9878b0Sken Powell - Sun Microsystem 	/*
8465f9878b0Sken Powell - Sun Microsystem 	 * Calculate the security label to be placed in the text
8475f9878b0Sken Powell - Sun Microsystem 	 * of the message (if any).
8485f9878b0Sken Powell - Sun Microsystem 	 */
849bd670b35SErik Nordmark 	if (effective_tsl != NULL) {
850bd670b35SErik Nordmark 		if ((retv = tsol_compute_label_v4(effective_tsl, zoneid,
8515f9878b0Sken Powell - Sun Microsystem 		    ipha->ipha_dst, opt_storage, ipst)) != 0) {
852bd670b35SErik Nordmark 			label_rele(effective_tsl);
8535f9878b0Sken Powell - Sun Microsystem 			return (retv);
8545f9878b0Sken Powell - Sun Microsystem 		}
855bd670b35SErik Nordmark 		*effective_tslp = effective_tsl;
8565f9878b0Sken Powell - Sun Microsystem 	} else {
857bd670b35SErik Nordmark 		if ((retv = tsol_compute_label_v4(tsl, zoneid,
8585f9878b0Sken Powell - Sun Microsystem 		    ipha->ipha_dst, opt_storage, ipst)) != 0) {
8595f9878b0Sken Powell - Sun Microsystem 			return (retv);
8605f9878b0Sken Powell - Sun Microsystem 		}
8615f9878b0Sken Powell - Sun Microsystem 	}
8625f9878b0Sken Powell - Sun Microsystem 
86345916cd2Sjpk 	optr = (uchar_t *)(ipha + 1);
86445916cd2Sjpk 	hlen = IPH_HDR_LENGTH(ipha) - IP_SIMPLE_HDR_LENGTH;
86545916cd2Sjpk 	sec_opt_len = opt_storage[IPOPT_OLEN];
86645916cd2Sjpk 
86745916cd2Sjpk 	if (hlen >= sec_opt_len) {
86845916cd2Sjpk 		/* If no option is supposed to be there, make sure it's not */
86945916cd2Sjpk 		if (sec_opt_len == 0 && hlen > 0 &&
87045916cd2Sjpk 		    optr[IPOPT_OPTVAL] != IPOPT_COMSEC &&
87145916cd2Sjpk 		    optr[IPOPT_OPTVAL] != IPOPT_SECURITY)
87245916cd2Sjpk 			return (0);
87345916cd2Sjpk 		/* if the option is there, it's always first */
87445916cd2Sjpk 		if (sec_opt_len != 0 &&
87545916cd2Sjpk 		    bcmp(opt_storage, optr, sec_opt_len) == 0)
87645916cd2Sjpk 			return (0);
87745916cd2Sjpk 	}
87845916cd2Sjpk 
87945916cd2Sjpk 	/*
88045916cd2Sjpk 	 * If there is an option there, then it must be the wrong one; delete.
88145916cd2Sjpk 	 */
882222c5bceSkp 	if (hlen > 0) {
883222c5bceSkp 		delta_remove = tsol_remove_secopt(ipha, MBLKL(mp));
884222c5bceSkp 		mp->b_wptr += delta_remove;
885222c5bceSkp 	}
88645916cd2Sjpk 
88745916cd2Sjpk 	/* Make sure we have room for the worst-case addition */
88845916cd2Sjpk 	hlen = IPH_HDR_LENGTH(ipha) + opt_storage[IPOPT_OLEN];
88945916cd2Sjpk 	hlen = (hlen + 3) & ~3;
89045916cd2Sjpk 	if (hlen > IP_MAX_HDR_LENGTH)
89145916cd2Sjpk 		hlen = IP_MAX_HDR_LENGTH;
89245916cd2Sjpk 	hlen -= IPH_HDR_LENGTH(ipha);
89345916cd2Sjpk 	if (mp->b_wptr + hlen > mp->b_datap->db_lim) {
89445916cd2Sjpk 		int copylen;
89545916cd2Sjpk 		mblk_t *new_mp;
89645916cd2Sjpk 
89745916cd2Sjpk 		/* allocate enough to be meaningful, but not *too* much */
89845916cd2Sjpk 		copylen = MBLKL(mp);
89945916cd2Sjpk 		if (copylen > 256)
90045916cd2Sjpk 			copylen = 256;
901de8c4a14SErik Nordmark 		new_mp = allocb_tmpl(hlen + copylen +
902de8c4a14SErik Nordmark 		    (mp->b_rptr - mp->b_datap->db_base), mp);
903bd670b35SErik Nordmark 		if (new_mp == NULL) {
904bd670b35SErik Nordmark 			if (effective_tsl != NULL) {
905bd670b35SErik Nordmark 				label_rele(effective_tsl);
906bd670b35SErik Nordmark 				*effective_tslp = NULL;
907bd670b35SErik Nordmark 			}
90845916cd2Sjpk 			return (ENOMEM);
909bd670b35SErik Nordmark 		}
91045916cd2Sjpk 
91145916cd2Sjpk 		/* keep the bias */
91245916cd2Sjpk 		new_mp->b_rptr += mp->b_rptr - mp->b_datap->db_base;
91345916cd2Sjpk 		new_mp->b_wptr = new_mp->b_rptr + copylen;
91445916cd2Sjpk 		bcopy(mp->b_rptr, new_mp->b_rptr, copylen);
91545916cd2Sjpk 		new_mp->b_cont = mp;
91645916cd2Sjpk 		if ((mp->b_rptr += copylen) >= mp->b_wptr) {
91745916cd2Sjpk 			new_mp->b_cont = mp->b_cont;
91845916cd2Sjpk 			freeb(mp);
91945916cd2Sjpk 		}
92045916cd2Sjpk 		*mpp = mp = new_mp;
92145916cd2Sjpk 		ipha = (ipha_t *)mp->b_rptr;
92245916cd2Sjpk 	}
92345916cd2Sjpk 
924222c5bceSkp 	delta_add = tsol_prepend_option(opt_storage, ipha, MBLKL(mp));
925222c5bceSkp 	if (delta_add == -1)
92645916cd2Sjpk 		goto param_prob;
92745916cd2Sjpk 
928222c5bceSkp 	ASSERT((mp->b_wptr + delta_add) <= DB_LIM(mp));
929222c5bceSkp 	mp->b_wptr += delta_add;
93045916cd2Sjpk 
931222c5bceSkp 	adjust = delta_remove + delta_add;
932222c5bceSkp 	adjust += ntohs(ipha->ipha_length);
933222c5bceSkp 	ipha->ipha_length = htons(adjust);
93445916cd2Sjpk 
93545916cd2Sjpk 	return (0);
93645916cd2Sjpk 
93745916cd2Sjpk param_prob:
938bd670b35SErik Nordmark 	if (effective_tsl != NULL) {
939bd670b35SErik Nordmark 		label_rele(effective_tsl);
940bd670b35SErik Nordmark 		*effective_tslp = NULL;
941bd670b35SErik Nordmark 	}
94245916cd2Sjpk 	return (EINVAL);
94345916cd2Sjpk }
94445916cd2Sjpk 
94545916cd2Sjpk /*
94645916cd2Sjpk  * IPv6 HopOpt extension header for the label option layout:
94745916cd2Sjpk  *	- One octet giving the type of the 'next extension header'
94845916cd2Sjpk  *	- Header extension length in 8-byte words, not including the
94945916cd2Sjpk  *	  1st 8 bytes, but including any pad bytes at the end.
95045916cd2Sjpk  *	  Eg. A value of 2 means 16 bytes not including the 1st 8 bytes.
95145916cd2Sjpk  *	- Followed by TLV encoded IPv6 label option. Option layout is
95245916cd2Sjpk  *		* One octet, IP6OPT_LS
95345916cd2Sjpk  *		* One octet option length in bytes of the option data following
95445916cd2Sjpk  *		  the length, but not including any pad bytes at the end.
95545916cd2Sjpk  *		* Four-octet DOI (IP6LS_DOI_V4)
95645916cd2Sjpk  *		* One octet suboption, IP6LS_TT_V4
95745916cd2Sjpk  *		* One octet suboption length in bytes of the suboption
95845916cd2Sjpk  *		  following the suboption length, including the suboption
95945916cd2Sjpk  *		  header length, but not including any pad bytes at the end.
96045916cd2Sjpk  *	- Pad to make the extension header a multiple of 8 bytes.
96145916cd2Sjpk  *
96245916cd2Sjpk  * This function returns the contents of 'IPv6 option structure' in the above.
96345916cd2Sjpk  * i.e starting from the IP6OPT_LS but not including the pad at the end.
96445916cd2Sjpk  * The user must prepend two octets (either padding or next header / length)
96545916cd2Sjpk  * and append padding out to the next 8 octet boundary.
966bd670b35SErik Nordmark  *
967bd670b35SErik Nordmark  * The zoneid is the IP zoneid (i.e., GLOBAL_ZONEID for exlusive-IP zones).
96845916cd2Sjpk  */
96945916cd2Sjpk int
tsol_compute_label_v6(const ts_label_t * tsl,zoneid_t zoneid,const in6_addr_t * dst,uchar_t * opt_storage,ip_stack_t * ipst)970bd670b35SErik Nordmark tsol_compute_label_v6(const ts_label_t *tsl, zoneid_t zoneid,
971bd670b35SErik Nordmark     const in6_addr_t *dst, uchar_t *opt_storage, ip_stack_t *ipst)
97245916cd2Sjpk {
97345916cd2Sjpk 	uint_t		sec_opt_len;
97445916cd2Sjpk 	uint32_t	doi;
975bd670b35SErik Nordmark 	ire_t		*ire;
976bd670b35SErik Nordmark 	tsol_ire_gw_secattr_t *attrp = NULL;
977de8c4a14SErik Nordmark 
97845916cd2Sjpk 	if (ip6opt_ls == 0)
97945916cd2Sjpk 		return (EINVAL);
98045916cd2Sjpk 
98145916cd2Sjpk 	if (opt_storage != NULL)
98245916cd2Sjpk 		opt_storage[IPOPT_OLEN] = 0;
98345916cd2Sjpk 
984bd670b35SErik Nordmark 	if (tsl == NULL)
98545916cd2Sjpk 		return (0);
98645916cd2Sjpk 
98745916cd2Sjpk 	/* Always pass multicast */
98845916cd2Sjpk 	if (IN6_IS_ADDR_MULTICAST(dst))
98945916cd2Sjpk 		return (0);
99045916cd2Sjpk 
99145916cd2Sjpk 	/*
99245916cd2Sjpk 	 * Fill in a V6 label.  If a new format is added here, make certain
99345916cd2Sjpk 	 * that the maximum size of this label is reflected in sys/tsol/tnet.h
99445916cd2Sjpk 	 * as TSOL_MAX_IPV6_OPTION.
99545916cd2Sjpk 	 */
9965d3b8cb7SBill Sommerfeld 	if (tsl->tsl_flags & TSLF_IMPLICIT_OUT)
9975d3b8cb7SBill Sommerfeld 		return (0);
9985d3b8cb7SBill Sommerfeld 
9995f9878b0Sken Powell - Sun Microsystem 	if (tsl->tsl_flags & TSLF_UNLABELED) {
100045916cd2Sjpk 		/*
10015f9878b0Sken Powell - Sun Microsystem 		 * The destination is unlabeled. Only add a label if the
1002bd670b35SErik Nordmark 		 * destination is not a broadcast/local/loopback address,
10035f9878b0Sken Powell - Sun Microsystem 		 * the destination is not on the same subnet, and the
10045f9878b0Sken Powell - Sun Microsystem 		 * next-hop gateway is labeled.
100545916cd2Sjpk 		 */
1006bd670b35SErik Nordmark 		ire = ire_route_recursive_v6(dst, 0, NULL, zoneid, tsl,
1007*9e3469d3SErik Nordmark 		    MATCH_IRE_SECATTR, IRR_ALLOCATE, 0, ipst, NULL, &attrp,
1008*9e3469d3SErik Nordmark 		    NULL);
1009bd670b35SErik Nordmark 		ASSERT(ire != NULL);
1010bd670b35SErik Nordmark 		if (ire->ire_flags & (RTF_REJECT|RTF_BLACKHOLE)) {
1011bd670b35SErik Nordmark 			/* no route to destination */
1012bd670b35SErik Nordmark 			ire_refrele(ire);
10135f9878b0Sken Powell - Sun Microsystem 			DTRACE_PROBE3(
101445916cd2Sjpk 			    tx__tnopt__log__info__labeling__routedst__v6,
10155f9878b0Sken Powell - Sun Microsystem 			    char *, "No route to unlabeled dest ip6(1) with "
1016bd670b35SErik Nordmark 			    "label(2).", in6_addr_t *, dst, ts_label_t *, tsl);
10175f9878b0Sken Powell - Sun Microsystem 			return (EHOSTUNREACH);
101845916cd2Sjpk 		}
1019bd670b35SErik Nordmark 		if (ire->ire_type & (IRE_LOCAL | IRE_LOOPBACK |
1020bd670b35SErik Nordmark 		    IRE_INTERFACE)) {
1021bd670b35SErik Nordmark 			ire_refrele(ire);
1022bd670b35SErik Nordmark 			return (0);
1023bd670b35SErik Nordmark 		}
102445916cd2Sjpk 		/*
1025bd670b35SErik Nordmark 		 * ire_route_recursive gives us the first attrp it finds
1026bd670b35SErik Nordmark 		 * in the recursive lookup.
102745916cd2Sjpk 		 */
10285f9878b0Sken Powell - Sun Microsystem 		/*
10295f9878b0Sken Powell - Sun Microsystem 		 * Return now if next hop gateway is unlabeled. There is
10305f9878b0Sken Powell - Sun Microsystem 		 * no need to generate a CIPSO option for this message.
10315f9878b0Sken Powell - Sun Microsystem 		 */
10325f9878b0Sken Powell - Sun Microsystem 		if (attrp == NULL || attrp->igsa_rhc == NULL ||
10335f9878b0Sken Powell - Sun Microsystem 		    attrp->igsa_rhc->rhc_tpc->tpc_tp.host_type == UNLABELED) {
1034bd670b35SErik Nordmark 			ire_refrele(ire);
10355f9878b0Sken Powell - Sun Microsystem 			return (0);
103645916cd2Sjpk 		}
1037bd670b35SErik Nordmark 		ire_refrele(ire);
103845916cd2Sjpk 	}
103945916cd2Sjpk 
104045916cd2Sjpk 	/* compute the CIPSO option */
104145916cd2Sjpk 	if (opt_storage != NULL)
104245916cd2Sjpk 		opt_storage += 8;
10435f9878b0Sken Powell - Sun Microsystem 	sec_opt_len = tsol2cipso_tt1(&tsl->tsl_label, opt_storage,
10445f9878b0Sken Powell - Sun Microsystem 	    tsl->tsl_doi);
104545916cd2Sjpk 
104645916cd2Sjpk 	if (sec_opt_len == 0) {
10475f9878b0Sken Powell - Sun Microsystem 		DTRACE_PROBE3(tx__tnopt__log__error__labeling__lostops__v6,
10485f9878b0Sken Powell - Sun Microsystem 		    char *, "options lack length for dest ip6(1) with "
1049bd670b35SErik Nordmark 		    "label(2).", in6_addr_t *, dst, ts_label_t *, tsl);
105045916cd2Sjpk 		return (EINVAL);
105145916cd2Sjpk 	}
105245916cd2Sjpk 
105345916cd2Sjpk 	if (opt_storage == NULL)
105445916cd2Sjpk 		return (0);
105545916cd2Sjpk 
105645916cd2Sjpk 	if (sec_opt_len < IP_MAX_OPT_LENGTH)
105745916cd2Sjpk 		opt_storage[sec_opt_len] = IPOPT_EOL;
105845916cd2Sjpk 
105945916cd2Sjpk 	/*
106045916cd2Sjpk 	 * Just in case the option length is odd, round it up to the next even
106145916cd2Sjpk 	 * multiple.  The IPv6 option definition doesn't like odd numbers for
106245916cd2Sjpk 	 * some reason.
106345916cd2Sjpk 	 *
106445916cd2Sjpk 	 * Length in the overall option header (IP6OPT_LS) does not include the
106545916cd2Sjpk 	 * option header itself, but the length in the suboption does include
106645916cd2Sjpk 	 * the suboption header.  Thus, when there's just one suboption, the
106745916cd2Sjpk 	 * length in the option header is the suboption length plus 4 (for the
106845916cd2Sjpk 	 * DOI value).
106945916cd2Sjpk 	 */
107045916cd2Sjpk 	opt_storage[-2] = IP6LS_TT_V4;
107145916cd2Sjpk 	opt_storage[-1] = (sec_opt_len + 2 + 1) & ~1;
107245916cd2Sjpk 	opt_storage[-8] = ip6opt_ls;
107345916cd2Sjpk 	opt_storage[-7] = opt_storage[-1] + 4;
107445916cd2Sjpk 	doi = htons(IP6LS_DOI_V4);
107545916cd2Sjpk 	bcopy(&doi, opt_storage - 6, 4);
107645916cd2Sjpk 
107745916cd2Sjpk 	return (0);
107845916cd2Sjpk }
107945916cd2Sjpk 
108045916cd2Sjpk /*
108145916cd2Sjpk  * Locate the start of the IP6OPT_LS label option and return it.
108245916cd2Sjpk  * Also return the start of the next non-pad option in after_secoptp.
108345916cd2Sjpk  * Usually the label option is the first option at least when packets
108445916cd2Sjpk  * are generated, but for generality we don't assume that on received packets.
1085c4e55c13Sken Powell - Sun Microsystem  *
1086c4e55c13Sken Powell - Sun Microsystem  * The function will return with B_FALSE if an IP format error
1087c4e55c13Sken Powell - Sun Microsystem  * or an unexpected label content error is encountered.
108845916cd2Sjpk  */
1089c4e55c13Sken Powell - Sun Microsystem boolean_t
tsol_find_secopt_v6(const uchar_t * ip6hbh,uint_t hbhlen,uchar_t ** secoptp,uchar_t ** after_secoptp,boolean_t * hbh_needed)109045916cd2Sjpk tsol_find_secopt_v6(
109145916cd2Sjpk     const uchar_t *ip6hbh,	/* Start of the hop-by-hop extension header */
109245916cd2Sjpk     uint_t hbhlen,		/* Length of the hop-by-hop extension header */
1093c4e55c13Sken Powell - Sun Microsystem     uchar_t **secoptp,		/* Location of IP6OPT_LS label option */
109445916cd2Sjpk     uchar_t **after_secoptp,	/* Non-pad option following the label option */
109545916cd2Sjpk     boolean_t *hbh_needed)	/* Is hop-by-hop hdr needed w/o label */
109645916cd2Sjpk {
109745916cd2Sjpk 	uint_t	optlen;
109845916cd2Sjpk 	uint_t	optused;
109945916cd2Sjpk 	const uchar_t *optptr;
110045916cd2Sjpk 	uchar_t	opt_type;
110145916cd2Sjpk 
1102c4e55c13Sken Powell - Sun Microsystem 	*secoptp = NULL;
110345916cd2Sjpk 	*hbh_needed = B_FALSE;
110445916cd2Sjpk 	*after_secoptp = NULL;
110545916cd2Sjpk 	optlen = hbhlen - 2;
110645916cd2Sjpk 	optptr = ip6hbh + 2;
110745916cd2Sjpk 	while (optlen != 0) {
110845916cd2Sjpk 		opt_type = *optptr;
110945916cd2Sjpk 		if (opt_type == IP6OPT_PAD1) {
111045916cd2Sjpk 			optptr++;
111145916cd2Sjpk 			optlen--;
111245916cd2Sjpk 			continue;
111345916cd2Sjpk 		}
111445916cd2Sjpk 		if (optlen == 1)
1115c4e55c13Sken Powell - Sun Microsystem 			return (B_FALSE);
111645916cd2Sjpk 		optused = 2 + optptr[1];
111745916cd2Sjpk 		if (optused > optlen)
1118c4e55c13Sken Powell - Sun Microsystem 			return (B_FALSE);
111945916cd2Sjpk 		/*
112045916cd2Sjpk 		 * if we get here, ip6opt_ls can
112145916cd2Sjpk 		 * not be 0 because it will always
112245916cd2Sjpk 		 * match the IP6OPT_PAD1 above.
112345916cd2Sjpk 		 * Therefore ip6opt_ls == 0 forces
112445916cd2Sjpk 		 * this test to always fail here.
112545916cd2Sjpk 		 */
1126c4e55c13Sken Powell - Sun Microsystem 		if (opt_type == ip6opt_ls) {
1127c4e55c13Sken Powell - Sun Microsystem 			if (*secoptp != NULL)
1128c4e55c13Sken Powell - Sun Microsystem 				/* More than one security option found */
1129c4e55c13Sken Powell - Sun Microsystem 				return (B_FALSE);
1130c4e55c13Sken Powell - Sun Microsystem 			*secoptp = (uchar_t *)optptr;
1131c4e55c13Sken Powell - Sun Microsystem 		} else switch (opt_type) {
113245916cd2Sjpk 		case IP6OPT_PADN:
113345916cd2Sjpk 			break;
113445916cd2Sjpk 		default:
113545916cd2Sjpk 			/*
113645916cd2Sjpk 			 * There is at least 1 option other than
113745916cd2Sjpk 			 * the label option. So the hop-by-hop header is needed
113845916cd2Sjpk 			 */
113945916cd2Sjpk 			*hbh_needed = B_TRUE;
1140c4e55c13Sken Powell - Sun Microsystem 			if (*secoptp != NULL) {
114145916cd2Sjpk 				*after_secoptp = (uchar_t *)optptr;
1142c4e55c13Sken Powell - Sun Microsystem 				return (B_TRUE);
114345916cd2Sjpk 			}
114445916cd2Sjpk 			break;
114545916cd2Sjpk 		}
114645916cd2Sjpk 		optlen -= optused;
114745916cd2Sjpk 		optptr += optused;
114845916cd2Sjpk 	}
1149c4e55c13Sken Powell - Sun Microsystem 	return (B_TRUE);
115045916cd2Sjpk }
115145916cd2Sjpk 
115245916cd2Sjpk /*
115345916cd2Sjpk  * Remove the label option from the hop-by-hop options header if it exists.
115445916cd2Sjpk  * 'buflen' is the total length of the packet typically b_wptr - b_rptr.
115545916cd2Sjpk  * Header and data following the label option that is deleted are copied
11560ec92a15Swy  * (i.e. slid backward) to the right position, and returns the number
11570ec92a15Swy  * of bytes removed (as zero or negative number.)
1158bd670b35SErik Nordmark  *
1159bd670b35SErik Nordmark  * Note that tsol_remove_secopt does not adjust ipha_length but
1160bd670b35SErik Nordmark  * tsol_remove_secopt_v6 does adjust ip6_plen.
116145916cd2Sjpk  */
116245916cd2Sjpk int
tsol_remove_secopt_v6(ip6_t * ip6h,int buflen)116345916cd2Sjpk tsol_remove_secopt_v6(ip6_t *ip6h, int buflen)
116445916cd2Sjpk {
116545916cd2Sjpk 	uchar_t	*ip6hbh;	/* hop-by-hop header */
116645916cd2Sjpk 	uint_t	hbhlen;		/* hop-by-hop extension header length */
116745916cd2Sjpk 	uchar_t *secopt = NULL;
116845916cd2Sjpk 	uchar_t *after_secopt;
116945916cd2Sjpk 	uint_t	pad;
117045916cd2Sjpk 	uint_t	delta;
117145916cd2Sjpk 	boolean_t hbh_needed;
117245916cd2Sjpk 
117345916cd2Sjpk 	/*
117445916cd2Sjpk 	 * hop-by-hop extension header must appear first, if it does not
117545916cd2Sjpk 	 * exist, there is no label option.
117645916cd2Sjpk 	 */
117745916cd2Sjpk 	if (ip6h->ip6_nxt != IPPROTO_HOPOPTS)
117845916cd2Sjpk 		return (0);
117945916cd2Sjpk 
118045916cd2Sjpk 	ip6hbh = (uchar_t *)&ip6h[1];
118145916cd2Sjpk 	hbhlen = (ip6hbh[1] + 1) << 3;
118245916cd2Sjpk 	/*
118345916cd2Sjpk 	 * Locate the start of the label option if it exists and the end
118445916cd2Sjpk 	 * of the label option including pads if any.
118545916cd2Sjpk 	 */
1186c4e55c13Sken Powell - Sun Microsystem 	if (!tsol_find_secopt_v6(ip6hbh, hbhlen, &secopt, &after_secopt,
1187c4e55c13Sken Powell - Sun Microsystem 	    &hbh_needed)) {
1188c4e55c13Sken Powell - Sun Microsystem 		/*
1189c4e55c13Sken Powell - Sun Microsystem 		 * This function should not see invalid messages.
1190c4e55c13Sken Powell - Sun Microsystem 		 * If one occurs, it would indicate either an
1191c4e55c13Sken Powell - Sun Microsystem 		 * option previously verified in the forwarding
1192c4e55c13Sken Powell - Sun Microsystem 		 * path has been corrupted or an option was
1193c4e55c13Sken Powell - Sun Microsystem 		 * incorrectly generated locally.
1194c4e55c13Sken Powell - Sun Microsystem 		 */
1195c4e55c13Sken Powell - Sun Microsystem 		ASSERT(0);
1196c4e55c13Sken Powell - Sun Microsystem 		return (0);
1197c4e55c13Sken Powell - Sun Microsystem 	}
119845916cd2Sjpk 	if (secopt == NULL)
119945916cd2Sjpk 		return (0);
120045916cd2Sjpk 	if (!hbh_needed) {
120145916cd2Sjpk 		uchar_t	next_hdr;
120245916cd2Sjpk 		/*
120345916cd2Sjpk 		 * The label option was the only option in the hop-by-hop
120445916cd2Sjpk 		 * header. We don't need the hop-by-hop header itself any
120545916cd2Sjpk 		 * longer.
120645916cd2Sjpk 		 */
120745916cd2Sjpk 		next_hdr = ip6hbh[0];
120845916cd2Sjpk 		ovbcopy(ip6hbh + hbhlen, ip6hbh,
120945916cd2Sjpk 		    buflen - (IPV6_HDR_LEN + hbhlen));
1210d7ab25acSkp 		ip6h->ip6_plen = htons(ntohs(ip6h->ip6_plen) - hbhlen);
121145916cd2Sjpk 		ip6h->ip6_nxt = next_hdr;
12120ec92a15Swy 		return (-hbhlen);
121345916cd2Sjpk 	}
121445916cd2Sjpk 
121545916cd2Sjpk 	if (after_secopt == NULL) {
121645916cd2Sjpk 		/* There is no option following the label option */
121745916cd2Sjpk 		after_secopt = ip6hbh + hbhlen;
121845916cd2Sjpk 	}
121945916cd2Sjpk 
122045916cd2Sjpk 	/*
122145916cd2Sjpk 	 * After deleting the label option, we need to slide the headers
122245916cd2Sjpk 	 * and data back, while still maintaining the same alignment (module 8)
122345916cd2Sjpk 	 * for the other options. So we slide the headers and data back only
122445916cd2Sjpk 	 * by an integral multiple of 8 bytes, and fill the remaining bytes
122545916cd2Sjpk 	 * with pads.
122645916cd2Sjpk 	 */
122745916cd2Sjpk 	delta = after_secopt - secopt;
122845916cd2Sjpk 	pad = delta % 8;
122945916cd2Sjpk 	if (pad == 1) {
123045916cd2Sjpk 		secopt[0] = IP6OPT_PAD1;
123145916cd2Sjpk 	} else if (pad > 1) {
123245916cd2Sjpk 		secopt[0] = IP6OPT_PADN;
123345916cd2Sjpk 		secopt[1] = pad - 2;
123445916cd2Sjpk 		if (pad > 2)
123545916cd2Sjpk 			bzero(&secopt[2], pad - 2);
123645916cd2Sjpk 	}
123745916cd2Sjpk 	secopt += pad;
123845916cd2Sjpk 	delta -= pad;
123945916cd2Sjpk 	ovbcopy(after_secopt, secopt,
124045916cd2Sjpk 	    (uchar_t *)ip6h + buflen - after_secopt);
124145916cd2Sjpk 	ip6hbh[1] -= delta/8;
1242d7ab25acSkp 	ip6h->ip6_plen = htons(ntohs(ip6h->ip6_plen) - delta);
124345916cd2Sjpk 
12440ec92a15Swy 	return (-delta);
124545916cd2Sjpk }
124645916cd2Sjpk 
124745916cd2Sjpk /*
124845916cd2Sjpk  * 'optbuf' contains a CIPSO label embedded in an IPv6 hop-by-hop option,
124945916cd2Sjpk  * starting with the IP6OPT_LS option type. The format of this hop-by-hop
125045916cd2Sjpk  * option is described in the block comment above tsol_compute_label_v6.
125145916cd2Sjpk  * This function prepends this hop-by-hop option before any other hop-by-hop
125245916cd2Sjpk  * options in the hop-by-hop header if one already exists, else a new
125345916cd2Sjpk  * hop-by-hop header is created and stuffed into the packet following
125445916cd2Sjpk  * the IPv6 header. 'buflen' is the total length of the packet i.e.
125545916cd2Sjpk  * b_wptr - b_rptr. The caller ensures that there is enough space for the
125645916cd2Sjpk  * extra option being added. Header and data following the position where
125745916cd2Sjpk  * the label option is inserted are copied (i.e. slid forward) to the right
125845916cd2Sjpk  * position.
1259bd670b35SErik Nordmark  *
1260bd670b35SErik Nordmark  * Note that tsol_prepend_option does not adjust ipha_length but
1261bd670b35SErik Nordmark  * tsol_prepend_option_v6 does adjust ip6_plen.
126245916cd2Sjpk  */
126345916cd2Sjpk int
tsol_prepend_option_v6(uchar_t * optbuf,ip6_t * ip6h,int buflen)126445916cd2Sjpk tsol_prepend_option_v6(uchar_t *optbuf, ip6_t *ip6h, int buflen)
126545916cd2Sjpk {
126645916cd2Sjpk 	/*
126745916cd2Sjpk 	 * rawlen is the length of the label option in bytes, not including
126845916cd2Sjpk 	 * any pads, starting from the IP6OPT_LS (option type) byte.
126945916cd2Sjpk 	 */
127045916cd2Sjpk 	uint_t	rawlen;
127145916cd2Sjpk 
127245916cd2Sjpk 	uint_t	optlen;		/* rawlen rounded to an 8 byte multiple */
127345916cd2Sjpk 	uchar_t	*ip6hbh;	/* start of the hop-by-hop extension header */
127445916cd2Sjpk 	uint_t	hbhlen;		/* Length of the hop-by-hop extension header */
127545916cd2Sjpk 	uint_t	pad_len;
127645916cd2Sjpk 	uchar_t	*pad_position;
127745916cd2Sjpk 	int	delta;		/* Actual number of bytes inserted */
127845916cd2Sjpk 
127945916cd2Sjpk 	rawlen = optbuf[1] + 2;	/* Add 2 for the option type, option length */
128045916cd2Sjpk 	ip6hbh = (uchar_t *)&ip6h[1];
128145916cd2Sjpk 	if (ip6h->ip6_nxt == IPPROTO_HOPOPTS) {
128245916cd2Sjpk 		/*
128345916cd2Sjpk 		 * There is a hop-by-hop header present already. In order to
128445916cd2Sjpk 		 * preserve the alignment of the other options at the existing
128545916cd2Sjpk 		 * value (modulo 8) we need to pad the label option to a
128645916cd2Sjpk 		 * multiple of 8 bytes before prepending it to the other
128745916cd2Sjpk 		 * options. Slide the extension headers and data forward to
128845916cd2Sjpk 		 * accomodate the label option at the start of the hop-by-hop
128945916cd2Sjpk 		 * header
129045916cd2Sjpk 		 */
129145916cd2Sjpk 		delta = optlen = (rawlen + 7) & ~7;
129245916cd2Sjpk 		pad_len = optlen - rawlen;
129345916cd2Sjpk 		pad_position = ip6hbh + 2 + rawlen;
129445916cd2Sjpk 		ovbcopy(ip6hbh + 2, ip6hbh + 2 + optlen,
129545916cd2Sjpk 		    buflen - (IPV6_HDR_LEN + 2));
129645916cd2Sjpk 		/*
129745916cd2Sjpk 		 * Bump up the hop-by-hop extension header length by
129845916cd2Sjpk 		 * the number of 8-byte words added
129945916cd2Sjpk 		 */
130045916cd2Sjpk 		optlen >>= 3;
130145916cd2Sjpk 		if (ip6hbh[1] + optlen > 255)
130245916cd2Sjpk 			return (-1);
130345916cd2Sjpk 		ip6hbh[1] += optlen;
130445916cd2Sjpk 	} else {
130545916cd2Sjpk 		/*
130645916cd2Sjpk 		 * There is no hop-by-hop header in the packet. Construct a
130745916cd2Sjpk 		 * new Hop-by-hop extension header (a multiple of 8 bytes).
130845916cd2Sjpk 		 * Slide any other extension headers and data forward to
130945916cd2Sjpk 		 * accomodate this hop-by-hop header
131045916cd2Sjpk 		 */
131145916cd2Sjpk 		delta = hbhlen = (2 + rawlen + 7) & ~7; /* +2 for nxthdr, len */
131245916cd2Sjpk 		pad_len = hbhlen - (2 + rawlen);
131345916cd2Sjpk 		pad_position = ip6hbh + 2 + rawlen;
131445916cd2Sjpk 		ovbcopy(ip6hbh, ip6hbh + hbhlen, buflen - IPV6_HDR_LEN);
131545916cd2Sjpk 		ip6hbh[0] = ip6h->ip6_nxt;
131645916cd2Sjpk 		/*
131745916cd2Sjpk 		 * hop-by-hop extension header length in 8-byte words, not
131845916cd2Sjpk 		 * including the 1st 8 bytes of the hop-by-hop header.
131945916cd2Sjpk 		 */
132045916cd2Sjpk 		ip6hbh[1] = (hbhlen >> 3) - 1;
132145916cd2Sjpk 		ip6h->ip6_nxt = IPPROTO_HOPOPTS;
132245916cd2Sjpk 	}
132345916cd2Sjpk 	/*
132445916cd2Sjpk 	 * Copy the label option into the hop-by-hop header and insert any
132545916cd2Sjpk 	 * needed pads
132645916cd2Sjpk 	 */
132745916cd2Sjpk 	bcopy(optbuf, ip6hbh + 2, rawlen);
132845916cd2Sjpk 	if (pad_len == 1) {
132945916cd2Sjpk 		pad_position[0] = IP6OPT_PAD1;
133045916cd2Sjpk 	} else if (pad_len > 1) {
133145916cd2Sjpk 		pad_position[0] = IP6OPT_PADN;
133245916cd2Sjpk 		pad_position[1] = pad_len - 2;
133345916cd2Sjpk 		if (pad_len > 2)
133445916cd2Sjpk 			bzero(pad_position + 2, pad_len - 2);
133545916cd2Sjpk 	}
1336d7ab25acSkp 	ip6h->ip6_plen = htons(ntohs(ip6h->ip6_plen) + delta);
133745916cd2Sjpk 	return (delta);
133845916cd2Sjpk }
133945916cd2Sjpk 
134045916cd2Sjpk /*
134145916cd2Sjpk  * tsol_check_label_v6()
134245916cd2Sjpk  *
134345916cd2Sjpk  * This routine computes the IP label that should be on the packet based on the
1344bd670b35SErik Nordmark  * connection and destination information.  It's called by the IP forwarding
1345bd670b35SErik Nordmark  * logic and by ip_output_simple. The ULPs generate the labels before calling
1346bd670b35SErik Nordmark  * conn_ip_output. If any adjustments to
1347bd670b35SErik Nordmark  * the label are needed due to the connection's MAC-exempt status or
1348bd670b35SErik Nordmark  * the destination's ability to receive labels, an "effective label"
1349bd670b35SErik Nordmark  * will be returned.
1350bd670b35SErik Nordmark  *
1351bd670b35SErik Nordmark  * The packet's header is clear before entering IPsec's engine.
1352bd670b35SErik Nordmark  *
1353bd670b35SErik Nordmark  * The zoneid is the IP zoneid (i.e., GLOBAL_ZONEID for exlusive-IP zones).
1354bd670b35SErik Nordmark  * zone_is_global is set if the actual zoneid is global.
1355bd670b35SErik Nordmark  *
1356bd670b35SErik Nordmark  * On successful return, effective_tslp will point to the new label needed
1357bd670b35SErik Nordmark  * or will be NULL if a new label isn't needed. On error, effective_tsl will
1358bd670b35SErik Nordmark  * point to NULL.
135945916cd2Sjpk  *
136045916cd2Sjpk  * Returns:
13610e0e37a8SErik Nordmark  *      0		Label (was|is now) correct
13625f9878b0Sken Powell - Sun Microsystem  *      EACCES		The packet failed the remote host accreditation.
136345916cd2Sjpk  *      ENOMEM		Memory allocation failure.
1364bd670b35SErik Nordmark  *	EINVAL		Label cannot be computed
136545916cd2Sjpk  */
136645916cd2Sjpk int
tsol_check_label_v6(const ts_label_t * tsl,zoneid_t zoneid,mblk_t ** mpp,uint_t mac_mode,boolean_t zone_is_global,ip_stack_t * ipst,ts_label_t ** effective_tslp)1367bd670b35SErik Nordmark tsol_check_label_v6(const ts_label_t *tsl, zoneid_t zoneid, mblk_t **mpp,
1368bd670b35SErik Nordmark     uint_t mac_mode, boolean_t zone_is_global, ip_stack_t *ipst,
1369bd670b35SErik Nordmark     ts_label_t **effective_tslp)
137045916cd2Sjpk {
137145916cd2Sjpk 	mblk_t *mp = *mpp;
137245916cd2Sjpk 	ip6_t  *ip6h;
1373bd670b35SErik Nordmark 	ts_label_t *effective_tsl = NULL;
137445916cd2Sjpk 	/*
137545916cd2Sjpk 	 * Label option length is limited to IP_MAX_OPT_LENGTH for
137645916cd2Sjpk 	 * symmetry with IPv4. Can be relaxed if needed
137745916cd2Sjpk 	 */
137845916cd2Sjpk 	uchar_t opt_storage[TSOL_MAX_IPV6_OPTION];
137945916cd2Sjpk 	uint_t hlen;
138045916cd2Sjpk 	uint_t sec_opt_len; /* label option length not including type, len */
1381222c5bceSkp 	int delta_remove = 0, delta_add;
138245916cd2Sjpk 	int retv;
138345916cd2Sjpk 	uchar_t	*after_secopt;
138445916cd2Sjpk 	uchar_t	*secopt = NULL;
138545916cd2Sjpk 	uchar_t	*ip6hbh;
138645916cd2Sjpk 	uint_t	hbhlen;
138745916cd2Sjpk 	boolean_t hbh_needed;
138845916cd2Sjpk 
1389bd670b35SErik Nordmark 	*effective_tslp = NULL;
1390bd670b35SErik Nordmark 
13915f9878b0Sken Powell - Sun Microsystem 	/*
13925f9878b0Sken Powell - Sun Microsystem 	 * Verify the destination is allowed to receive packets at
1393bd670b35SErik Nordmark 	 * the security label of the message data. tsol_check_dest()
1394bd670b35SErik Nordmark 	 * may create a new effective label or label flags.
13955f9878b0Sken Powell - Sun Microsystem 	 */
139645916cd2Sjpk 	ip6h = (ip6_t *)mp->b_rptr;
1397bd670b35SErik Nordmark 	retv = tsol_check_dest(tsl, &ip6h->ip6_dst, IPV6_VERSION,
1398bd670b35SErik Nordmark 	    mac_mode, zone_is_global, &effective_tsl);
139945916cd2Sjpk 	if (retv != 0)
140045916cd2Sjpk 		return (retv);
140145916cd2Sjpk 
14025f9878b0Sken Powell - Sun Microsystem 	/*
14035f9878b0Sken Powell - Sun Microsystem 	 * Calculate the security label to be placed in the text
14045f9878b0Sken Powell - Sun Microsystem 	 * of the message (if any).
14055f9878b0Sken Powell - Sun Microsystem 	 */
1406bd670b35SErik Nordmark 	if (effective_tsl != NULL) {
1407bd670b35SErik Nordmark 		if ((retv = tsol_compute_label_v6(effective_tsl, zoneid,
14085f9878b0Sken Powell - Sun Microsystem 		    &ip6h->ip6_dst, opt_storage, ipst)) != 0) {
1409bd670b35SErik Nordmark 			label_rele(effective_tsl);
14105f9878b0Sken Powell - Sun Microsystem 			return (retv);
14115f9878b0Sken Powell - Sun Microsystem 		}
1412bd670b35SErik Nordmark 		*effective_tslp = effective_tsl;
14135f9878b0Sken Powell - Sun Microsystem 	} else {
1414bd670b35SErik Nordmark 		if ((retv = tsol_compute_label_v6(tsl, zoneid,
14155f9878b0Sken Powell - Sun Microsystem 		    &ip6h->ip6_dst, opt_storage, ipst)) != 0)
14165f9878b0Sken Powell - Sun Microsystem 			return (retv);
14175f9878b0Sken Powell - Sun Microsystem 	}
14185f9878b0Sken Powell - Sun Microsystem 
141945916cd2Sjpk 	sec_opt_len = opt_storage[1];
142045916cd2Sjpk 
142145916cd2Sjpk 	if (ip6h->ip6_nxt == IPPROTO_HOPOPTS) {
142245916cd2Sjpk 		ip6hbh = (uchar_t *)&ip6h[1];
142345916cd2Sjpk 		hbhlen = (ip6hbh[1] + 1) << 3;
1424c4e55c13Sken Powell - Sun Microsystem 		if (!tsol_find_secopt_v6(ip6hbh, hbhlen, &secopt,
1425c4e55c13Sken Powell - Sun Microsystem 		    &after_secopt, &hbh_needed)) {
1426c4e55c13Sken Powell - Sun Microsystem 			/*
1427c4e55c13Sken Powell - Sun Microsystem 			 * This function should not see invalid messages.
1428c4e55c13Sken Powell - Sun Microsystem 			 * If one occurs, it would indicate either an
1429c4e55c13Sken Powell - Sun Microsystem 			 * option previously verified in the forwarding
1430c4e55c13Sken Powell - Sun Microsystem 			 * path has been corrupted or an option was
1431c4e55c13Sken Powell - Sun Microsystem 			 * incorrectly generated locally.
1432c4e55c13Sken Powell - Sun Microsystem 			 */
1433c4e55c13Sken Powell - Sun Microsystem 			ASSERT(0);
1434c4e55c13Sken Powell - Sun Microsystem 			return (EACCES);
1435c4e55c13Sken Powell - Sun Microsystem 		}
143645916cd2Sjpk 	}
143745916cd2Sjpk 
143845916cd2Sjpk 	if (sec_opt_len == 0 && secopt == NULL) {
143945916cd2Sjpk 		/*
144045916cd2Sjpk 		 * The packet is not supposed to have a label, and it
144145916cd2Sjpk 		 * does not have one currently
144245916cd2Sjpk 		 */
144345916cd2Sjpk 		return (0);
144445916cd2Sjpk 	}
14455d3b8cb7SBill Sommerfeld 
144645916cd2Sjpk 	if (secopt != NULL && sec_opt_len != 0 &&
144745916cd2Sjpk 	    (bcmp(opt_storage, secopt, sec_opt_len + 2) == 0)) {
144845916cd2Sjpk 		/* The packet has the correct label already */
144945916cd2Sjpk 		return (0);
145045916cd2Sjpk 	}
145145916cd2Sjpk 
145245916cd2Sjpk 	/*
145345916cd2Sjpk 	 * If there is an option there, then it must be the wrong one; delete.
145445916cd2Sjpk 	 */
1455222c5bceSkp 	if (secopt != NULL) {
1456222c5bceSkp 		delta_remove = tsol_remove_secopt_v6(ip6h, MBLKL(mp));
1457222c5bceSkp 		mp->b_wptr += delta_remove;
1458222c5bceSkp 	}
145945916cd2Sjpk 
146045916cd2Sjpk 	/*
146145916cd2Sjpk 	 * Make sure we have room for the worst-case addition. Add 2 bytes for
146245916cd2Sjpk 	 * the hop-by-hop ext header's next header and length fields. Add
146345916cd2Sjpk 	 * another 2 bytes for the label option type, len and then round
146445916cd2Sjpk 	 * up to the next 8-byte multiple.
146545916cd2Sjpk 	 */
146645916cd2Sjpk 	hlen = (4 + sec_opt_len + 7) & ~7;
146745916cd2Sjpk 	if (mp->b_wptr + hlen > mp->b_datap->db_lim) {
146845916cd2Sjpk 		int copylen;
146945916cd2Sjpk 		mblk_t *new_mp;
147045916cd2Sjpk 		uint16_t hdr_len;
147145916cd2Sjpk 
147245916cd2Sjpk 		hdr_len = ip_hdr_length_v6(mp, ip6h);
147345916cd2Sjpk 		/*
147445916cd2Sjpk 		 * Allocate enough to be meaningful, but not *too* much.
147545916cd2Sjpk 		 * Also all the IPv6 extension headers must be in the same mblk
147645916cd2Sjpk 		 */
147745916cd2Sjpk 		copylen = MBLKL(mp);
147845916cd2Sjpk 		if (copylen > 256)
147945916cd2Sjpk 			copylen = 256;
148045916cd2Sjpk 		if (copylen < hdr_len)
148145916cd2Sjpk 			copylen = hdr_len;
1482de8c4a14SErik Nordmark 		new_mp = allocb_tmpl(hlen + copylen +
1483de8c4a14SErik Nordmark 		    (mp->b_rptr - mp->b_datap->db_base), mp);
1484bd670b35SErik Nordmark 		if (new_mp == NULL) {
1485bd670b35SErik Nordmark 			if (effective_tsl != NULL) {
1486bd670b35SErik Nordmark 				label_rele(effective_tsl);
1487bd670b35SErik Nordmark 				*effective_tslp = NULL;
1488bd670b35SErik Nordmark 			}
148945916cd2Sjpk 			return (ENOMEM);
1490bd670b35SErik Nordmark 		}
149145916cd2Sjpk 
149245916cd2Sjpk 		/* keep the bias */
149345916cd2Sjpk 		new_mp->b_rptr += mp->b_rptr - mp->b_datap->db_base;
149445916cd2Sjpk 		new_mp->b_wptr = new_mp->b_rptr + copylen;
149545916cd2Sjpk 		bcopy(mp->b_rptr, new_mp->b_rptr, copylen);
149645916cd2Sjpk 		new_mp->b_cont = mp;
149745916cd2Sjpk 		if ((mp->b_rptr += copylen) >= mp->b_wptr) {
149845916cd2Sjpk 			new_mp->b_cont = mp->b_cont;
149945916cd2Sjpk 			freeb(mp);
150045916cd2Sjpk 		}
150145916cd2Sjpk 		*mpp = mp = new_mp;
150245916cd2Sjpk 		ip6h = (ip6_t *)mp->b_rptr;
150345916cd2Sjpk 	}
150445916cd2Sjpk 
1505222c5bceSkp 	delta_add = tsol_prepend_option_v6(opt_storage, ip6h, MBLKL(mp));
1506222c5bceSkp 	if (delta_add == -1)
150745916cd2Sjpk 		goto param_prob;
150845916cd2Sjpk 
1509222c5bceSkp 	ASSERT(mp->b_wptr + delta_add <= DB_LIM(mp));
1510222c5bceSkp 	mp->b_wptr += delta_add;
151145916cd2Sjpk 
1512bd670b35SErik Nordmark 	/* tsol_prepend_option_v6 has adjusted ip6_plen */
151345916cd2Sjpk 	return (0);
151445916cd2Sjpk 
151545916cd2Sjpk param_prob:
1516bd670b35SErik Nordmark 	if (effective_tsl != NULL) {
1517bd670b35SErik Nordmark 		label_rele(effective_tsl);
1518bd670b35SErik Nordmark 		*effective_tslp = NULL;
151945916cd2Sjpk 	}
1520bd670b35SErik Nordmark 	return (EINVAL);
152145916cd2Sjpk }
1522