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