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