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