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