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 /* 228778SErik.Nordmark@Sun.COM * Copyright 2009 Sun Microsystems, Inc. All rights reserved. 231676Sjpk * Use is subject to license terms. 241676Sjpk */ 251676Sjpk 261676Sjpk #include <sys/types.h> 271676Sjpk #include <sys/systm.h> 281676Sjpk #include <sys/kmem.h> 291676Sjpk #include <sys/disp.h> 301676Sjpk #include <sys/stream.h> 311676Sjpk #include <sys/strsubr.h> 321676Sjpk #include <sys/strsun.h> 331676Sjpk #include <sys/policy.h> 341676Sjpk #include <sys/tsol/label_macro.h> 351676Sjpk #include <sys/tsol/tndb.h> 361676Sjpk #include <sys/tsol/tnet.h> 371676Sjpk #include <inet/ip.h> 381676Sjpk #include <inet/ip6.h> 391676Sjpk #include <inet/tcp.h> 401676Sjpk #include <inet/ipclassifier.h> 411676Sjpk #include <inet/ip_ire.h> 422535Ssangeeta #include <inet/ip_ftable.h> 431676Sjpk 441676Sjpk /* 451676Sjpk * This routine takes a sensitivity label as input and creates a CIPSO 461676Sjpk * option in the specified buffer. It returns the size of the CIPSO option. 471676Sjpk * If the sensitivity label is too large for the CIPSO option, then 0 481676Sjpk * is returned. 491676Sjpk * 501676Sjpk * tsol2cipso_tt1 returns 0 for failure and greater than 0 for success 511676Sjpk * (more accurately, success means a return value between 10 and 40). 521676Sjpk */ 531676Sjpk 541676Sjpk static int 551676Sjpk tsol2cipso_tt1(const bslabel_t *sl, unsigned char *cop, uint32_t doi) 561676Sjpk { 571676Sjpk struct cipso_tag_type_1 *tt1; 581676Sjpk const _bslabel_impl_t *bsl; 591676Sjpk const uchar_t *ucp; 601676Sjpk int i; 611676Sjpk 621676Sjpk if (doi == 0) 631676Sjpk return (0); 641676Sjpk 651676Sjpk /* check for Admin High sensitivity label */ 661676Sjpk if (blequal(sl, label2bslabel(l_admin_high))) 671676Sjpk return (0); 681676Sjpk 691676Sjpk /* check whether classification will fit in one octet */ 701676Sjpk bsl = (const _bslabel_impl_t *)sl; 711676Sjpk if (LCLASS(bsl) & 0xFF00) 721676Sjpk return (0); 731676Sjpk 741676Sjpk /* 751676Sjpk * Check whether compartments will fit in 30 octets. 761676Sjpk * Compartments 241 - 256 are not allowed. 771676Sjpk */ 781676Sjpk if (ntohl(bsl->compartments.c8) & 0x0000FFFF) 791676Sjpk return (0); 801676Sjpk 811676Sjpk /* 821676Sjpk * Compute option length and tag length. 831676Sjpk * 'p' points to the last two bytes in the Sensitivity Label's 841676Sjpk * compartments; these cannot be mapped into CIPSO compartments. 851676Sjpk */ 861676Sjpk ucp = (const uchar_t *)&bsl->compartments.c8 + 2; 871676Sjpk while (--ucp >= (const uchar_t *)&bsl->compartments.c1) 881676Sjpk if (*ucp != 0) 891676Sjpk break; 901676Sjpk 911676Sjpk i = ucp - (const uchar_t *)&bsl->compartments.c1 + 1; 921676Sjpk 931676Sjpk if (cop == NULL) 941676Sjpk return (10 + i); 951676Sjpk 961676Sjpk doi = htonl(doi); 971676Sjpk ucp = (const uchar_t *)&doi; 981676Sjpk cop[IPOPT_OPTVAL] = IPOPT_COMSEC; 991676Sjpk cop[IPOPT_OLEN] = 10 + i; 1001676Sjpk cop[IPOPT_OLEN+1] = ucp[0]; 1011676Sjpk cop[IPOPT_OLEN+2] = ucp[1]; 1021676Sjpk cop[IPOPT_OLEN+3] = ucp[2]; 1031676Sjpk cop[IPOPT_OLEN+4] = ucp[3]; 1041676Sjpk tt1 = (struct cipso_tag_type_1 *)&cop[IPOPT_OLEN + 5]; 1051676Sjpk tt1->tag_type = 1; 1061676Sjpk tt1->tag_align = 0; 1071676Sjpk tt1->tag_sl = LCLASS(bsl); 1081676Sjpk tt1->tag_length = 4 + i; 1091676Sjpk 1101676Sjpk bcopy(&bsl->compartments.c1, tt1->tag_cat, i); 1111676Sjpk 1121676Sjpk return (cop[IPOPT_OLEN]); 1131676Sjpk } 1141676Sjpk 1151676Sjpk /* 11610181SKen.Powell@Sun.COM * The following routine searches for a security label in an IPv4 datagram. 11710181SKen.Powell@Sun.COM * It returns label_type of: 11810181SKen.Powell@Sun.COM * OPT_CIPSO if a CIPSO IP option is found. 11910181SKen.Powell@Sun.COM * OPT_NONE if no security label is found. 1201676Sjpk * 12110181SKen.Powell@Sun.COM * If OPT_CIPSO, a pointer to the CIPSO IP option will be returned in 12210181SKen.Powell@Sun.COM * the buffer parameter. 12310181SKen.Powell@Sun.COM * 12410181SKen.Powell@Sun.COM * The function will return with B_FALSE if an IP format error 12510181SKen.Powell@Sun.COM * is encountered. 1261676Sjpk */ 1271676Sjpk 12810181SKen.Powell@Sun.COM boolean_t 12910181SKen.Powell@Sun.COM tsol_get_option_v4(mblk_t *mp, tsol_ip_label_t *label_type, 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 13710181SKen.Powell@Sun.COM *label_type = OPT_NONE; 1381676Sjpk 1391676Sjpk /* 1401676Sjpk * Get length (in 4 byte octets) of IP header options. 14110181SKen.Powell@Sun.COM * If header doesn't contain options, then return a label_type 14210181SKen.Powell@Sun.COM * of OPT_NONE. 1431676Sjpk */ 14410181SKen.Powell@Sun.COM ipha = (ipha_t *)mp->b_rptr; 1451676Sjpk totallen = ipha->ipha_version_and_hdr_length - 14610181SKen.Powell@Sun.COM (uint8_t)((IP_VERSION << 4)); 14710181SKen.Powell@Sun.COM totallen <<= 2; 14810181SKen.Powell@Sun.COM if (totallen < IP_SIMPLE_HDR_LENGTH || totallen > MBLKL(mp)) 14910181SKen.Powell@Sun.COM return (B_FALSE); 15010181SKen.Powell@Sun.COM totallen -= IP_SIMPLE_HDR_LENGTH; 1511676Sjpk if (totallen == 0) 15210181SKen.Powell@Sun.COM return (B_TRUE); 1531676Sjpk 1541676Sjpk /* 1551676Sjpk * Search for CIPSO option. 1561676Sjpk * If no such option is present, then return OPT_NONE. 1571676Sjpk */ 1581676Sjpk opt = (uchar_t *)&ipha[1]; 1591676Sjpk while (totallen != 0) { 1601676Sjpk switch (optval = opt[IPOPT_OPTVAL]) { 1611676Sjpk case IPOPT_EOL: 16210181SKen.Powell@Sun.COM return (B_TRUE); 1631676Sjpk case IPOPT_NOP: 1641676Sjpk optlen = 1; 1651676Sjpk break; 1661676Sjpk default: 1671676Sjpk if (totallen <= IPOPT_OLEN) 16810181SKen.Powell@Sun.COM return (B_FALSE); 1691676Sjpk optlen = opt[IPOPT_OLEN]; 1701676Sjpk if (optlen < 2) 17110181SKen.Powell@Sun.COM return (B_FALSE); 1721676Sjpk } 1731676Sjpk if (optlen > totallen) 17410181SKen.Powell@Sun.COM return (B_FALSE); 1751676Sjpk /* 1761676Sjpk * Copy pointer to option into '*buffer' and 1771676Sjpk * return the option type. 1781676Sjpk */ 1791676Sjpk switch (optval) { 1801676Sjpk case IPOPT_COMSEC: 1811676Sjpk if (TSOL_CIPSO_TAG_OFFSET < optlen && 18210181SKen.Powell@Sun.COM opt[TSOL_CIPSO_TAG_OFFSET] == 1) { 18310181SKen.Powell@Sun.COM *label_type = OPT_CIPSO; 18410181SKen.Powell@Sun.COM *buffer = opt; 18510181SKen.Powell@Sun.COM return (B_TRUE); 18610181SKen.Powell@Sun.COM } 18710181SKen.Powell@Sun.COM return (B_FALSE); 1881676Sjpk } 1891676Sjpk totallen -= optlen; 1901676Sjpk opt += optlen; 1911676Sjpk } 19210181SKen.Powell@Sun.COM return (B_TRUE); 19310181SKen.Powell@Sun.COM } 19410181SKen.Powell@Sun.COM 19510181SKen.Powell@Sun.COM /* 19610181SKen.Powell@Sun.COM * The following routine searches for a security label in an IPv6 datagram. 19710181SKen.Powell@Sun.COM * It returns label_type of: 19810181SKen.Powell@Sun.COM * OPT_CIPSO if a CIPSO IP option is found. 19910181SKen.Powell@Sun.COM * OPT_NONE if no security label is found. 20010181SKen.Powell@Sun.COM * 20110181SKen.Powell@Sun.COM * If OPT_CIPSO, a pointer to the IPv4 portion of the CIPSO IP option will 20210181SKen.Powell@Sun.COM * be returned in the buffer parameter. 20310181SKen.Powell@Sun.COM * 20410181SKen.Powell@Sun.COM * The function will return with B_FALSE if an IP format error 20510181SKen.Powell@Sun.COM * or an unexpected label content error is encountered. 20610181SKen.Powell@Sun.COM */ 20710181SKen.Powell@Sun.COM 20810181SKen.Powell@Sun.COM boolean_t 20910181SKen.Powell@Sun.COM tsol_get_option_v6(mblk_t *mp, tsol_ip_label_t *label_type, uchar_t **buffer) 21010181SKen.Powell@Sun.COM { 21110181SKen.Powell@Sun.COM uchar_t *opt_ptr = NULL; 21210181SKen.Powell@Sun.COM uchar_t *after_secopt; 21310181SKen.Powell@Sun.COM boolean_t hbh_needed; 21410181SKen.Powell@Sun.COM const uchar_t *ip6hbh; 21510181SKen.Powell@Sun.COM size_t optlen; 21610181SKen.Powell@Sun.COM uint32_t doi; 21710181SKen.Powell@Sun.COM const ip6_t *ip6h; 21810181SKen.Powell@Sun.COM 21910181SKen.Powell@Sun.COM *label_type = OPT_NONE; 22010181SKen.Powell@Sun.COM *buffer = NULL; 22110181SKen.Powell@Sun.COM ip6h = (const ip6_t *)mp->b_rptr; 22210181SKen.Powell@Sun.COM if (ip6h->ip6_nxt != IPPROTO_HOPOPTS) 22310181SKen.Powell@Sun.COM return (B_TRUE); 22410181SKen.Powell@Sun.COM ip6hbh = (const uchar_t *)&ip6h[1]; 22510181SKen.Powell@Sun.COM if (ip6hbh + MIN_EHDR_LEN > mp->b_wptr) 22610181SKen.Powell@Sun.COM return (B_FALSE); 22710181SKen.Powell@Sun.COM optlen = (ip6hbh[1] + 1) << 3; 22810181SKen.Powell@Sun.COM if (ip6hbh + optlen > mp->b_wptr) 22910181SKen.Powell@Sun.COM return (B_FALSE); 23010181SKen.Powell@Sun.COM if (!tsol_find_secopt_v6(ip6hbh, optlen, 23110181SKen.Powell@Sun.COM &opt_ptr, &after_secopt, &hbh_needed)) 23210181SKen.Powell@Sun.COM return (B_FALSE); 23310181SKen.Powell@Sun.COM /* tsol_find_secopt_v6 guarantees some sanity */ 23410181SKen.Powell@Sun.COM if (opt_ptr != NULL) { 23510181SKen.Powell@Sun.COM /* 23610181SKen.Powell@Sun.COM * IPv6 Option 23710181SKen.Powell@Sun.COM * opt_ptr[0]: Option type 23810181SKen.Powell@Sun.COM * opt_ptr[1]: Length of option data in bytes 23910181SKen.Powell@Sun.COM * opt_ptr[2]: First byte of option data 24010181SKen.Powell@Sun.COM */ 24110181SKen.Powell@Sun.COM if ((optlen = opt_ptr[1]) < 8) 24210181SKen.Powell@Sun.COM return (B_FALSE); 24310181SKen.Powell@Sun.COM opt_ptr += 2; 24410181SKen.Powell@Sun.COM /* 24510181SKen.Powell@Sun.COM * From "Generalized Labeled Security Option for IPv6" draft 24610181SKen.Powell@Sun.COM * opt_ptr[0] - opt_ptr[4]: DOI = IP6LS_DOI_V4 24710181SKen.Powell@Sun.COM * opt_ptr[4]: Tag type = IP6LS_TT_V4 24810181SKen.Powell@Sun.COM * opt_ptr[5]: Tag length in bytes starting at Tag type field 24910181SKen.Powell@Sun.COM * IPv4 CIPSO Option 25010181SKen.Powell@Sun.COM * opt_ptr[6]: option type 25110181SKen.Powell@Sun.COM * opt_ptr[7]: option length in bytes starting at type field 25210181SKen.Powell@Sun.COM */ 25310181SKen.Powell@Sun.COM bcopy(opt_ptr, &doi, sizeof (doi)); 25410181SKen.Powell@Sun.COM doi = ntohl(doi); 25510181SKen.Powell@Sun.COM if (doi == IP6LS_DOI_V4 && 25610181SKen.Powell@Sun.COM opt_ptr[4] == IP6LS_TT_V4 && 25710181SKen.Powell@Sun.COM opt_ptr[5] <= optlen - 4 && 25810181SKen.Powell@Sun.COM opt_ptr[7] <= optlen - 6 && 25910181SKen.Powell@Sun.COM opt_ptr[7] <= opt_ptr[5] - 2) { 26010181SKen.Powell@Sun.COM opt_ptr += sizeof (doi) + 2; 26110181SKen.Powell@Sun.COM *label_type = OPT_CIPSO; 26210181SKen.Powell@Sun.COM *buffer = opt_ptr; 26310181SKen.Powell@Sun.COM return (B_TRUE); 26410181SKen.Powell@Sun.COM } 26510181SKen.Powell@Sun.COM return (B_FALSE); 26610181SKen.Powell@Sun.COM } 26710181SKen.Powell@Sun.COM return (B_TRUE); 2681676Sjpk } 2691676Sjpk 2701676Sjpk /* 2719710SKen.Powell@Sun.COM * tsol_check_dest() 2729710SKen.Powell@Sun.COM * 2739710SKen.Powell@Sun.COM * This routine verifies if a destination is allowed to recieve messages 274*11042SErik.Nordmark@Sun.COM * based on the security label. If any adjustments to the label are needed 275*11042SErik.Nordmark@Sun.COM * due to the connection's MAC mode or the destination's ability 276*11042SErik.Nordmark@Sun.COM * to receive labels, an "effective label" will be returned. 2779710SKen.Powell@Sun.COM * 278*11042SErik.Nordmark@Sun.COM * zone_is_global is set if the actual zoneid is global. That is, it is 279*11042SErik.Nordmark@Sun.COM * not set for an exclusive-IP zone. 280*11042SErik.Nordmark@Sun.COM * 281*11042SErik.Nordmark@Sun.COM * On successful return, effective_tsl will point to the new label needed 282*11042SErik.Nordmark@Sun.COM * or will be NULL if a new label isn't needed. On error, effective_tsl will 283*11042SErik.Nordmark@Sun.COM * point to NULL. 2849710SKen.Powell@Sun.COM * 2859710SKen.Powell@Sun.COM * Returns: 286*11042SErik.Nordmark@Sun.COM * 0 Label (was|is now) correct 287*11042SErik.Nordmark@Sun.COM * EHOSTUNREACH The label failed the remote host accreditation 2889710SKen.Powell@Sun.COM * ENOMEM Memory allocation failure 2899710SKen.Powell@Sun.COM */ 2909710SKen.Powell@Sun.COM int 291*11042SErik.Nordmark@Sun.COM tsol_check_dest(const ts_label_t *tsl, const void *dst, 292*11042SErik.Nordmark@Sun.COM uchar_t version, uint_t mac_mode, boolean_t zone_is_global, 293*11042SErik.Nordmark@Sun.COM ts_label_t **effective_tsl) 2949710SKen.Powell@Sun.COM { 295*11042SErik.Nordmark@Sun.COM ts_label_t *newtsl = NULL; 2969710SKen.Powell@Sun.COM tsol_tpc_t *dst_rhtp; 2979710SKen.Powell@Sun.COM 298*11042SErik.Nordmark@Sun.COM if (effective_tsl != NULL) 299*11042SErik.Nordmark@Sun.COM *effective_tsl = NULL; 3009710SKen.Powell@Sun.COM ASSERT(version == IPV4_VERSION || 3019710SKen.Powell@Sun.COM (version == IPV6_VERSION && 3029710SKen.Powell@Sun.COM !IN6_IS_ADDR_V4MAPPED((in6_addr_t *)dst))); 3039710SKen.Powell@Sun.COM 3049710SKen.Powell@Sun.COM /* Always pass kernel level communication (NULL label) */ 305*11042SErik.Nordmark@Sun.COM if (tsl == NULL) { 3069710SKen.Powell@Sun.COM DTRACE_PROBE2(tx__tnopt__log__info__labeling__mac__allownull, 307*11042SErik.Nordmark@Sun.COM char *, "destination ip(1) with null label was passed", 3089710SKen.Powell@Sun.COM ipaddr_t, dst); 3099710SKen.Powell@Sun.COM return (0); 3109710SKen.Powell@Sun.COM } 3119710SKen.Powell@Sun.COM 31210934Ssommerfeld@sun.com if (tsl->tsl_flags & TSLF_IMPLICIT_IN) { 31310934Ssommerfeld@sun.com DTRACE_PROBE3(tx__tnopt__log__info__labeling__unresolved__label, 31410934Ssommerfeld@sun.com char *, 31510934Ssommerfeld@sun.com "implicit-in packet to ip(1) reached tsol_check_dest " 31610934Ssommerfeld@sun.com "with implied security label sl(2)", 31710934Ssommerfeld@sun.com ipaddr_t, dst, ts_label_t *, tsl); 31810934Ssommerfeld@sun.com } 31910934Ssommerfeld@sun.com 3209710SKen.Powell@Sun.COM /* Always pass multicast */ 3219710SKen.Powell@Sun.COM if (version == IPV4_VERSION && 3229710SKen.Powell@Sun.COM CLASSD(*(ipaddr_t *)dst)) { 3239710SKen.Powell@Sun.COM DTRACE_PROBE2(tx__tnopt__log__info__labeling__mac__allowmult, 3249710SKen.Powell@Sun.COM char *, "destination ip(1) with multicast dest was passed", 3259710SKen.Powell@Sun.COM ipaddr_t, dst); 3269710SKen.Powell@Sun.COM return (0); 3279710SKen.Powell@Sun.COM } else if (version == IPV6_VERSION && 3289710SKen.Powell@Sun.COM IN6_IS_ADDR_MULTICAST((in6_addr_t *)dst)) { 3299710SKen.Powell@Sun.COM DTRACE_PROBE2(tx__tnopt__log__info__labeling__mac__allowmult_v6, 3309710SKen.Powell@Sun.COM char *, "destination ip(1) with multicast dest was passed", 3319710SKen.Powell@Sun.COM in6_addr_t *, dst); 3329710SKen.Powell@Sun.COM return (0); 3339710SKen.Powell@Sun.COM } 3349710SKen.Powell@Sun.COM 3359710SKen.Powell@Sun.COM /* Never pass an undefined destination */ 3369710SKen.Powell@Sun.COM if ((dst_rhtp = find_tpc(dst, version, B_FALSE)) == NULL) { 3379710SKen.Powell@Sun.COM DTRACE_PROBE2(tx__tnopt__log__info__labeling__lookupdst, 3389710SKen.Powell@Sun.COM char *, "destination ip(1) not in tn database.", 3399710SKen.Powell@Sun.COM void *, dst); 3409710SKen.Powell@Sun.COM return (EHOSTUNREACH); 3419710SKen.Powell@Sun.COM } 3429710SKen.Powell@Sun.COM 3439710SKen.Powell@Sun.COM switch (dst_rhtp->tpc_tp.host_type) { 3449710SKen.Powell@Sun.COM case UNLABELED: 3459710SKen.Powell@Sun.COM /* 3469710SKen.Powell@Sun.COM * Can talk to unlabeled hosts if 3479710SKen.Powell@Sun.COM * (1) zone's label matches the default label, or 34810934Ssommerfeld@sun.com * (2) SO_MAC_EXEMPT is on and we 34910934Ssommerfeld@sun.com * dominate the peer's label, or 35010934Ssommerfeld@sun.com * (3) SO_MAC_EXEMPT is on and 35110934Ssommerfeld@sun.com * this is the global zone 3529710SKen.Powell@Sun.COM */ 3539710SKen.Powell@Sun.COM if (dst_rhtp->tpc_tp.tp_doi != tsl->tsl_doi) { 35410181SKen.Powell@Sun.COM DTRACE_PROBE4(tx__tnopt__log__info__labeling__doi, 3559710SKen.Powell@Sun.COM char *, "unlabeled dest ip(1)/tpc(2) doi does " 3569710SKen.Powell@Sun.COM "not match msg label(3) doi.", void *, dst, 3579710SKen.Powell@Sun.COM tsol_tpc_t *, dst_rhtp, ts_label_t *, tsl); 3589710SKen.Powell@Sun.COM TPC_RELE(dst_rhtp); 3599710SKen.Powell@Sun.COM return (EHOSTUNREACH); 3609710SKen.Powell@Sun.COM } 3619710SKen.Powell@Sun.COM if (!blequal(&dst_rhtp->tpc_tp.tp_def_label, 3629710SKen.Powell@Sun.COM &tsl->tsl_label)) { 36310934Ssommerfeld@sun.com if (mac_mode != CONN_MAC_AWARE || 364*11042SErik.Nordmark@Sun.COM !(zone_is_global || 3659710SKen.Powell@Sun.COM bldominates(&tsl->tsl_label, 3669710SKen.Powell@Sun.COM &dst_rhtp->tpc_tp.tp_def_label))) { 3679710SKen.Powell@Sun.COM DTRACE_PROBE4( 3689710SKen.Powell@Sun.COM tx__tnopt__log__info__labeling__mac, 3699710SKen.Powell@Sun.COM char *, "unlabeled dest ip(1)/tpc(2) does " 3709710SKen.Powell@Sun.COM "not match msg label(3).", void *, dst, 3719710SKen.Powell@Sun.COM tsol_tpc_t *, dst_rhtp, ts_label_t *, tsl); 3729710SKen.Powell@Sun.COM TPC_RELE(dst_rhtp); 3739710SKen.Powell@Sun.COM return (EHOSTUNREACH); 3749710SKen.Powell@Sun.COM } 3759710SKen.Powell@Sun.COM /* 3769710SKen.Powell@Sun.COM * This is a downlabel MAC-exempt exchange. 3779710SKen.Powell@Sun.COM * Use the remote destination's default label 3789710SKen.Powell@Sun.COM * as the label of the message data. 3799710SKen.Powell@Sun.COM */ 3809710SKen.Powell@Sun.COM if ((newtsl = labelalloc(&dst_rhtp->tpc_tp.tp_def_label, 3819710SKen.Powell@Sun.COM dst_rhtp->tpc_tp.tp_doi, KM_NOSLEEP)) == NULL) { 3829710SKen.Powell@Sun.COM TPC_RELE(dst_rhtp); 3839710SKen.Powell@Sun.COM return (ENOMEM); 3849710SKen.Powell@Sun.COM } 3859710SKen.Powell@Sun.COM newtsl->tsl_flags |= TSLF_UNLABELED; 3869710SKen.Powell@Sun.COM 3879710SKen.Powell@Sun.COM } else if (!(tsl->tsl_flags & TSLF_UNLABELED)) { 3889710SKen.Powell@Sun.COM /* 3899710SKen.Powell@Sun.COM * The security labels are the same but we need 3909710SKen.Powell@Sun.COM * to flag that the remote node is unlabeled. 3919710SKen.Powell@Sun.COM */ 3929710SKen.Powell@Sun.COM if ((newtsl = labeldup(tsl, KM_NOSLEEP)) == NULL) { 3939710SKen.Powell@Sun.COM TPC_RELE(dst_rhtp); 3949710SKen.Powell@Sun.COM return (ENOMEM); 3959710SKen.Powell@Sun.COM } 3969710SKen.Powell@Sun.COM newtsl->tsl_flags |= TSLF_UNLABELED; 3979710SKen.Powell@Sun.COM } 3989710SKen.Powell@Sun.COM break; 3999710SKen.Powell@Sun.COM 4009710SKen.Powell@Sun.COM case SUN_CIPSO: 4019710SKen.Powell@Sun.COM /* 4029710SKen.Powell@Sun.COM * Can talk to labeled hosts if zone's label is within target's 4039710SKen.Powell@Sun.COM * label range or set. 4049710SKen.Powell@Sun.COM */ 4059710SKen.Powell@Sun.COM if (dst_rhtp->tpc_tp.tp_cipso_doi_cipso != tsl->tsl_doi || 4069710SKen.Powell@Sun.COM (!_blinrange(&tsl->tsl_label, 4079710SKen.Powell@Sun.COM &dst_rhtp->tpc_tp.tp_sl_range_cipso) && 4089710SKen.Powell@Sun.COM !blinlset(&tsl->tsl_label, 4099710SKen.Powell@Sun.COM dst_rhtp->tpc_tp.tp_sl_set_cipso))) { 4109710SKen.Powell@Sun.COM DTRACE_PROBE4(tx__tnopt__log__info__labeling__mac, 4119710SKen.Powell@Sun.COM char *, "labeled dest ip(1)/tpc(2) does not " 4129710SKen.Powell@Sun.COM "match msg label(3).", void *, dst, 4139710SKen.Powell@Sun.COM tsol_tpc_t *, dst_rhtp, ts_label_t *, tsl); 4149710SKen.Powell@Sun.COM TPC_RELE(dst_rhtp); 4159710SKen.Powell@Sun.COM return (EHOSTUNREACH); 4169710SKen.Powell@Sun.COM } 41710934Ssommerfeld@sun.com if ((tsl->tsl_flags & TSLF_UNLABELED) || 41810934Ssommerfeld@sun.com (mac_mode == CONN_MAC_IMPLICIT)) { 4199710SKen.Powell@Sun.COM /* 42010934Ssommerfeld@sun.com * Copy label so we can modify the flags 4219710SKen.Powell@Sun.COM */ 4229710SKen.Powell@Sun.COM if ((newtsl = labeldup(tsl, KM_NOSLEEP)) == NULL) { 4239710SKen.Powell@Sun.COM TPC_RELE(dst_rhtp); 4249710SKen.Powell@Sun.COM return (ENOMEM); 4259710SKen.Powell@Sun.COM } 42610934Ssommerfeld@sun.com /* 42710934Ssommerfeld@sun.com * The security label is a match but we need to 42810934Ssommerfeld@sun.com * clear the unlabeled flag for this remote node. 42910934Ssommerfeld@sun.com */ 43010934Ssommerfeld@sun.com newtsl->tsl_flags &= ~TSLF_UNLABELED; 43110934Ssommerfeld@sun.com if (mac_mode == CONN_MAC_IMPLICIT) 43210934Ssommerfeld@sun.com newtsl->tsl_flags |= TSLF_IMPLICIT_OUT; 4339710SKen.Powell@Sun.COM } 4349710SKen.Powell@Sun.COM break; 4359710SKen.Powell@Sun.COM 4369710SKen.Powell@Sun.COM default: 4379710SKen.Powell@Sun.COM TPC_RELE(dst_rhtp); 4389710SKen.Powell@Sun.COM return (EHOSTUNREACH); 4399710SKen.Powell@Sun.COM } 4409710SKen.Powell@Sun.COM 4419710SKen.Powell@Sun.COM /* 442*11042SErik.Nordmark@Sun.COM * Return the new label. 4439710SKen.Powell@Sun.COM */ 4449710SKen.Powell@Sun.COM if (newtsl != NULL) { 445*11042SErik.Nordmark@Sun.COM if (effective_tsl != NULL) 446*11042SErik.Nordmark@Sun.COM *effective_tsl = newtsl; 447*11042SErik.Nordmark@Sun.COM else 448*11042SErik.Nordmark@Sun.COM label_rele(newtsl); 4499710SKen.Powell@Sun.COM } 4509710SKen.Powell@Sun.COM TPC_RELE(dst_rhtp); 4519710SKen.Powell@Sun.COM return (0); 4529710SKen.Powell@Sun.COM } 4539710SKen.Powell@Sun.COM 4549710SKen.Powell@Sun.COM /* 455*11042SErik.Nordmark@Sun.COM * tsol_compute_label_v4() 4561676Sjpk * 4571676Sjpk * This routine computes the IP label that should be on a packet based on the 4581676Sjpk * connection and destination information. 4591676Sjpk * 460*11042SErik.Nordmark@Sun.COM * The zoneid is the IP zoneid (i.e., GLOBAL_ZONEID for exlusive-IP zones). 461*11042SErik.Nordmark@Sun.COM * 4621676Sjpk * Returns: 4631676Sjpk * 0 Fetched label 4649710SKen.Powell@Sun.COM * EHOSTUNREACH No route to destination 4651676Sjpk * EINVAL Label cannot be computed 4661676Sjpk */ 4671676Sjpk int 468*11042SErik.Nordmark@Sun.COM tsol_compute_label_v4(const ts_label_t *tsl, zoneid_t zoneid, ipaddr_t dst, 469*11042SErik.Nordmark@Sun.COM uchar_t *opt_storage, ip_stack_t *ipst) 4701676Sjpk { 4711676Sjpk uint_t sec_opt_len; 472*11042SErik.Nordmark@Sun.COM ire_t *ire; 473*11042SErik.Nordmark@Sun.COM tsol_ire_gw_secattr_t *attrp = NULL; 4748778SErik.Nordmark@Sun.COM 4751676Sjpk if (opt_storage != NULL) 4761676Sjpk opt_storage[IPOPT_OLEN] = 0; 4771676Sjpk 478*11042SErik.Nordmark@Sun.COM if (tsl == NULL) 4791676Sjpk return (0); 4801676Sjpk 4811676Sjpk /* always pass multicast */ 4821676Sjpk if (CLASSD(dst)) 4831676Sjpk return (0); 4841676Sjpk 48510934Ssommerfeld@sun.com if (tsl->tsl_flags & TSLF_IMPLICIT_OUT) 48610934Ssommerfeld@sun.com return (0); 48710934Ssommerfeld@sun.com 4889710SKen.Powell@Sun.COM if (tsl->tsl_flags & TSLF_UNLABELED) { 4899710SKen.Powell@Sun.COM /* 4909710SKen.Powell@Sun.COM * The destination is unlabeled. Only add a label if the 4919710SKen.Powell@Sun.COM * destination is not a broadcast/local/loopback address, 4929710SKen.Powell@Sun.COM * the destination is not on the same subnet, and the 4939710SKen.Powell@Sun.COM * next-hop gateway is labeled. 4949710SKen.Powell@Sun.COM */ 495*11042SErik.Nordmark@Sun.COM ire = ire_route_recursive_v4(dst, 0, NULL, zoneid, tsl, 496*11042SErik.Nordmark@Sun.COM MATCH_IRE_SECATTR, B_TRUE, 0, ipst, NULL, &attrp, NULL); 497*11042SErik.Nordmark@Sun.COM ASSERT(ire != NULL); 498*11042SErik.Nordmark@Sun.COM if (ire->ire_flags & (RTF_REJECT|RTF_BLACKHOLE)) { 499*11042SErik.Nordmark@Sun.COM /* no route to destination */ 500*11042SErik.Nordmark@Sun.COM ire_refrele(ire); 5019710SKen.Powell@Sun.COM DTRACE_PROBE3( 5021676Sjpk tx__tnopt__log__info__labeling__routedst__v4, 5039710SKen.Powell@Sun.COM char *, "No route to unlabeled dest ip(1) with " 504*11042SErik.Nordmark@Sun.COM "with label(2).", ipaddr_t, dst, ts_label_t *, tsl); 5059710SKen.Powell@Sun.COM return (EHOSTUNREACH); 5061676Sjpk } 507*11042SErik.Nordmark@Sun.COM if (ire->ire_type & (IRE_BROADCAST | IRE_LOCAL | IRE_LOOPBACK | 508*11042SErik.Nordmark@Sun.COM IRE_INTERFACE)) { 509*11042SErik.Nordmark@Sun.COM ire_refrele(ire); 510*11042SErik.Nordmark@Sun.COM return (0); 511*11042SErik.Nordmark@Sun.COM } 5121676Sjpk 5131676Sjpk /* 514*11042SErik.Nordmark@Sun.COM * ire_route_recursive gives us the first attrp it finds 515*11042SErik.Nordmark@Sun.COM * in the recursive lookup. 5161676Sjpk */ 5171676Sjpk /* 5189710SKen.Powell@Sun.COM * Return now if next hop gateway is unlabeled. There is 5199710SKen.Powell@Sun.COM * no need to generate a CIPSO option for this message. 5201676Sjpk */ 5219710SKen.Powell@Sun.COM if (attrp == NULL || attrp->igsa_rhc == NULL || 5229710SKen.Powell@Sun.COM attrp->igsa_rhc->rhc_tpc->tpc_tp.host_type == UNLABELED) { 523*11042SErik.Nordmark@Sun.COM ire_refrele(ire); 5249710SKen.Powell@Sun.COM return (0); 5251676Sjpk } 526*11042SErik.Nordmark@Sun.COM ire_refrele(ire); 5271676Sjpk } 5281676Sjpk 5291676Sjpk /* compute the CIPSO option */ 5309710SKen.Powell@Sun.COM sec_opt_len = tsol2cipso_tt1(&tsl->tsl_label, opt_storage, 5319710SKen.Powell@Sun.COM tsl->tsl_doi); 5321676Sjpk 5331676Sjpk if (sec_opt_len == 0) { 5349710SKen.Powell@Sun.COM DTRACE_PROBE3(tx__tnopt__log__error__labeling__lostops__v4, 535*11042SErik.Nordmark@Sun.COM char *, "options lack length for dest ip(1) with label(2).", 536*11042SErik.Nordmark@Sun.COM ipaddr_t, dst, ts_label_t *, tsl); 5371676Sjpk return (EINVAL); 5381676Sjpk } 5391676Sjpk 5401676Sjpk return (0); 5411676Sjpk } 5421676Sjpk 5431676Sjpk /* 5441676Sjpk * Remove any existing security option (CIPSO) from the given IP 5451676Sjpk * header, move the 'buflen' bytes back to fill the gap, and return the number 5461676Sjpk * of bytes removed (as zero or negative number). Assumes that the headers are 5471676Sjpk * sane. 548*11042SErik.Nordmark@Sun.COM * 549*11042SErik.Nordmark@Sun.COM * Note that tsol_remove_secopt does not adjust ipha_length but 550*11042SErik.Nordmark@Sun.COM * tsol_remove_secopt_v6 does adjust ip6_plen. 5511676Sjpk */ 5521676Sjpk int 5531676Sjpk tsol_remove_secopt(ipha_t *ipha, int buflen) 5541676Sjpk { 5551676Sjpk int remlen, olen, oval, delta; 5561676Sjpk uchar_t *fptr, *tptr; 5571676Sjpk boolean_t noop_keep; 5581676Sjpk 5591676Sjpk remlen = IPH_HDR_LENGTH(ipha) - IP_SIMPLE_HDR_LENGTH; 5601676Sjpk fptr = tptr = (uchar_t *)(ipha + 1); 5611676Sjpk noop_keep = B_TRUE; 5621676Sjpk while (remlen > 0) { 5631676Sjpk oval = fptr[IPOPT_OPTVAL]; 5641676Sjpk 5651676Sjpk /* terminate on end of list */ 5661676Sjpk if (oval == IPOPT_EOL) 5671676Sjpk break; 5681676Sjpk 5691676Sjpk /* 5701676Sjpk * Delete any no-ops following a deleted option, at least up 5711676Sjpk * to a 4 octet alignment; copy others. 5721676Sjpk */ 5731676Sjpk if (oval == IPOPT_NOP) { 5741676Sjpk if (((fptr - (uchar_t *)ipha) & 3) == 0) 5751676Sjpk noop_keep = B_TRUE; 5761676Sjpk if (noop_keep) 5771676Sjpk *tptr++ = oval; 5781676Sjpk fptr++; 5791676Sjpk remlen--; 5801676Sjpk continue; 5811676Sjpk } 5821676Sjpk 5831676Sjpk /* stop on corrupted list; just do nothing. */ 5841676Sjpk if (remlen < 2) 5851676Sjpk return (0); 5861676Sjpk olen = fptr[IPOPT_OLEN]; 5871676Sjpk if (olen < 2 || olen > remlen) 5881676Sjpk return (0); 5891676Sjpk 5901676Sjpk /* skip over security options to delete them */ 5911676Sjpk if (oval == IPOPT_COMSEC || oval == IPOPT_SECURITY) { 5921676Sjpk noop_keep = B_FALSE; 5931676Sjpk fptr += olen; 5941676Sjpk remlen -= olen; 5951676Sjpk continue; 5961676Sjpk } 5971676Sjpk 5981676Sjpk /* copy the rest */ 5991676Sjpk noop_keep = B_TRUE; 6001676Sjpk if (tptr != fptr) 6011676Sjpk ovbcopy(fptr, tptr, olen); 6021676Sjpk fptr += olen; 6031676Sjpk tptr += olen; 6041676Sjpk remlen -= olen; 6051676Sjpk } 6061676Sjpk 6071676Sjpk fptr += remlen; 6081676Sjpk 6091676Sjpk /* figure how much padding we'll need for header alignment */ 6101676Sjpk olen = (tptr - (uchar_t *)ipha) & 3; 6111676Sjpk if (olen > 0) { 6121676Sjpk olen = 4 - olen; 6131676Sjpk /* pad with end-of-list */ 6141676Sjpk bzero(tptr, olen); 6151676Sjpk tptr += olen; 6161676Sjpk } 6171676Sjpk 6181676Sjpk /* slide back the headers that follow and update the IP header */ 6191676Sjpk delta = fptr - tptr; 6201676Sjpk if (delta != 0) { 6211676Sjpk ovbcopy(fptr, tptr, ((uchar_t *)ipha + buflen) - fptr); 6221676Sjpk ipha->ipha_version_and_hdr_length -= delta / 4; 6231676Sjpk } 6241676Sjpk return (-delta); 6251676Sjpk } 6261676Sjpk 6271676Sjpk /* 6281676Sjpk * Insert the option in 'optbuf' into the IP header pointed to by 'ipha', and 6291676Sjpk * move the data following the IP header (up to buflen) to accomodate the new 6301676Sjpk * option. Assumes that up to IP_MAX_OPT_LENGTH bytes are available (in total) 6311676Sjpk * for IP options. Returns the number of bytes actually inserted, or -1 if the 6321676Sjpk * option cannot be inserted. (Note that negative return values are possible 6331676Sjpk * when noops must be compressed, and that only -1 indicates error. Successful 6341676Sjpk * return value is always evenly divisible by 4, by definition.) 635*11042SErik.Nordmark@Sun.COM * 636*11042SErik.Nordmark@Sun.COM * Note that tsol_prepend_option does not adjust ipha_length but 637*11042SErik.Nordmark@Sun.COM * tsol_prepend_option_v6 does adjust ip6_plen. 6381676Sjpk */ 6391676Sjpk int 6401676Sjpk tsol_prepend_option(uchar_t *optbuf, ipha_t *ipha, int buflen) 6411676Sjpk { 6421676Sjpk int remlen, padding, lastpad, totlen; 6431676Sjpk int oval, olen; 6441676Sjpk int delta; 6451676Sjpk uchar_t *optr; 6461676Sjpk uchar_t tempopt[IP_MAX_OPT_LENGTH], *toptr; 6471676Sjpk 6481676Sjpk if (optbuf[IPOPT_OPTVAL] == IPOPT_EOL || 6491676Sjpk optbuf[IPOPT_OPTVAL] == IPOPT_NOP || 6501676Sjpk optbuf[IPOPT_OLEN] == 0) 6511676Sjpk return (0); 6521676Sjpk 6531676Sjpk ASSERT(optbuf[IPOPT_OLEN] >= 2 && 6541676Sjpk optbuf[IPOPT_OLEN] <= IP_MAX_OPT_LENGTH); 6551676Sjpk 6561676Sjpk /* first find the real (unpadded) length of the existing options */ 6571676Sjpk remlen = IPH_HDR_LENGTH(ipha) - IP_SIMPLE_HDR_LENGTH; 6581676Sjpk padding = totlen = lastpad = 0; 6591676Sjpk optr = (uchar_t *)(ipha + 1); 6601676Sjpk while (remlen > 0) { 6611676Sjpk oval = optr[IPOPT_OPTVAL]; 6621676Sjpk 6631676Sjpk /* stop at end of list */ 6641676Sjpk if (oval == IPOPT_EOL) 6651676Sjpk break; 6661676Sjpk 6671676Sjpk /* skip no-ops, noting that length byte isn't present */ 6681676Sjpk if (oval == IPOPT_NOP) { 6691676Sjpk optr++; 6701676Sjpk padding++; 6711676Sjpk lastpad++; 6721676Sjpk totlen++; 6731676Sjpk remlen--; 6741676Sjpk continue; 6751676Sjpk } 6761676Sjpk 6771676Sjpk /* give up on a corrupted list; report failure */ 6781676Sjpk if (remlen < 2) 6791676Sjpk return (-1); 6801676Sjpk olen = optr[IPOPT_OLEN]; 6811676Sjpk if (olen < 2 || olen > remlen) 6821676Sjpk return (-1); 6831676Sjpk 6841676Sjpk lastpad = 0; 6851676Sjpk optr += olen; 6861676Sjpk totlen += olen; 6871676Sjpk remlen -= olen; 6881676Sjpk } 6891676Sjpk 6901676Sjpk /* completely ignore any trailing padding */ 6911676Sjpk totlen -= lastpad; 6921676Sjpk padding -= lastpad; 6931676Sjpk 6941676Sjpk /* 6951676Sjpk * If some sort of inter-option alignment was present, try to preserve 6961676Sjpk * that alignment. If alignment pushes us out past the maximum, then 6971676Sjpk * discard it and try to compress to fit. (We just "assume" that any 6981676Sjpk * padding added was attempting to get 32 bit alignment. If that's 6991676Sjpk * wrong, that's just too bad.) 7001676Sjpk */ 7011676Sjpk if (padding > 0) { 7021676Sjpk olen = (optbuf[IPOPT_OLEN] + 3) & ~3; 7031676Sjpk if (olen + totlen > IP_MAX_OPT_LENGTH) { 7041676Sjpk totlen -= padding; 7051676Sjpk if (olen + totlen > IP_MAX_OPT_LENGTH) 7061676Sjpk return (-1); 7071676Sjpk padding = 0; 7081676Sjpk } 7091676Sjpk } 7101676Sjpk 7111676Sjpk /* 7121676Sjpk * Since we may need to compress or expand the option list, we write to 7131676Sjpk * a temporary buffer and then copy the results back to the IP header. 7141676Sjpk */ 7151676Sjpk toptr = tempopt; 7161676Sjpk 7171676Sjpk /* compute actual option to insert */ 7181676Sjpk olen = optbuf[IPOPT_OLEN]; 7191676Sjpk bcopy(optbuf, toptr, olen); 7201676Sjpk toptr += olen; 7211676Sjpk if (padding > 0) { 7221676Sjpk while ((olen & 3) != 0) { 7231676Sjpk *toptr++ = IPOPT_NOP; 7241676Sjpk olen++; 7251676Sjpk } 7261676Sjpk } 7271676Sjpk 7281676Sjpk /* copy over the existing options */ 7291676Sjpk optr = (uchar_t *)(ipha + 1); 7301676Sjpk while (totlen > 0) { 7311676Sjpk oval = optr[IPOPT_OPTVAL]; 7321676Sjpk 7331676Sjpk /* totlen doesn't include end-of-list marker */ 7341676Sjpk ASSERT(oval != IPOPT_EOL); 7351676Sjpk 7361676Sjpk /* handle no-ops; copy if desired, ignore otherwise */ 7371676Sjpk if (oval == IPOPT_NOP) { 7381676Sjpk if (padding > 0) { 7391676Sjpk /* note: cannot overflow due to checks above */ 7401676Sjpk ASSERT(toptr < tempopt + IP_MAX_OPT_LENGTH); 7411676Sjpk *toptr++ = oval; 7421676Sjpk } 7431676Sjpk optr++; 7441676Sjpk totlen--; 7451676Sjpk continue; 7461676Sjpk } 7471676Sjpk 7481676Sjpk /* list cannot be corrupt at this point */ 7491676Sjpk ASSERT(totlen >= 2); 7501676Sjpk olen = optr[IPOPT_OLEN]; 7511676Sjpk ASSERT(olen >= 2 && olen <= totlen); 7521676Sjpk 7531676Sjpk /* cannot run out of room due to tests above */ 7541676Sjpk ASSERT(toptr + olen <= tempopt + IP_MAX_OPT_LENGTH); 7551676Sjpk 7561676Sjpk bcopy(optr, toptr, olen); 7571676Sjpk optr += olen; 7581676Sjpk toptr += olen; 7591676Sjpk totlen -= olen; 7601676Sjpk } 7611676Sjpk 7621676Sjpk /* figure how much padding we'll need for header alignment */ 7631676Sjpk olen = (toptr - tempopt) & 3; 7641676Sjpk if (olen > 0) { 7651676Sjpk olen = 4 - olen; 7661676Sjpk ASSERT(toptr + olen <= tempopt + IP_MAX_OPT_LENGTH); 7671676Sjpk /* pad with end-of-list value */ 7681676Sjpk bzero(toptr, olen); 7691676Sjpk toptr += olen; 7701676Sjpk } 7711676Sjpk 7721676Sjpk /* move the headers as needed and update IP header */ 7731676Sjpk olen = (toptr - tempopt) + IP_SIMPLE_HDR_LENGTH; 7741676Sjpk remlen = IPH_HDR_LENGTH(ipha); 7751676Sjpk delta = olen - remlen; 7761676Sjpk if (delta != 0) { 7771676Sjpk ovbcopy((uchar_t *)ipha + remlen, (uchar_t *)ipha + olen, 7781676Sjpk buflen - remlen); 7791676Sjpk ipha->ipha_version_and_hdr_length += delta / 4; 7801676Sjpk } 7811676Sjpk 7821676Sjpk /* slap in the new options */ 7831676Sjpk bcopy(tempopt, ipha + 1, olen - IP_SIMPLE_HDR_LENGTH); 7841676Sjpk 7851676Sjpk return (delta); 7861676Sjpk } 7871676Sjpk 7881676Sjpk /* 789*11042SErik.Nordmark@Sun.COM * tsol_check_label_v4() 7901676Sjpk * 7911676Sjpk * This routine computes the IP label that should be on the packet based on the 792*11042SErik.Nordmark@Sun.COM * connection and destination information. It's called by the IP forwarding 793*11042SErik.Nordmark@Sun.COM * logic and by ip_output_simple. The ULPs generate the labels before calling 794*11042SErik.Nordmark@Sun.COM * conn_ip_output. If any adjustments to 795*11042SErik.Nordmark@Sun.COM * the label are needed due to the connection's MAC-exempt status or 796*11042SErik.Nordmark@Sun.COM * the destination's ability to receive labels, an "effective label" 797*11042SErik.Nordmark@Sun.COM * will be returned. 7981676Sjpk * 7996596Skp158701 * The packet's header is clear before entering IPsec's engine. 8001676Sjpk * 801*11042SErik.Nordmark@Sun.COM * The zoneid is the IP zoneid (i.e., GLOBAL_ZONEID for exlusive-IP zones). 802*11042SErik.Nordmark@Sun.COM * zone_is_global is set if the actual zoneid is global. 803*11042SErik.Nordmark@Sun.COM * 804*11042SErik.Nordmark@Sun.COM * On successful return, effective_tslp will point to the new label needed 805*11042SErik.Nordmark@Sun.COM * or will be NULL if a new label isn't needed. On error, effective_tsl will 806*11042SErik.Nordmark@Sun.COM * point to NULL. 807*11042SErik.Nordmark@Sun.COM * 8081676Sjpk * Returns: 809*11042SErik.Nordmark@Sun.COM * 0 Label on (was|is now) correct 8101676Sjpk * EACCES The packet failed the remote host accreditation. 8111676Sjpk * ENOMEM Memory allocation failure. 8121676Sjpk * EINVAL Label cannot be computed 8131676Sjpk */ 8141676Sjpk int 815*11042SErik.Nordmark@Sun.COM tsol_check_label_v4(const ts_label_t *tsl, zoneid_t zoneid, mblk_t **mpp, 816*11042SErik.Nordmark@Sun.COM uint_t mac_mode, boolean_t zone_is_global, ip_stack_t *ipst, 817*11042SErik.Nordmark@Sun.COM ts_label_t **effective_tslp) 8181676Sjpk { 8191676Sjpk mblk_t *mp = *mpp; 8201676Sjpk ipha_t *ipha; 821*11042SErik.Nordmark@Sun.COM ts_label_t *effective_tsl = NULL; 8221676Sjpk uchar_t opt_storage[IP_MAX_OPT_LENGTH]; 8231676Sjpk uint_t hlen; 8241676Sjpk uint_t sec_opt_len; 8251676Sjpk uchar_t *optr; 8266596Skp158701 int delta_remove = 0, delta_add, adjust; 8271676Sjpk int retv; 8281676Sjpk 829*11042SErik.Nordmark@Sun.COM *effective_tslp = NULL; 8301676Sjpk opt_storage[IPOPT_OPTVAL] = 0; 8311676Sjpk 8321676Sjpk ipha = (ipha_t *)mp->b_rptr; 8331676Sjpk 8349710SKen.Powell@Sun.COM /* 8359710SKen.Powell@Sun.COM * Verify the destination is allowed to receive packets at 836*11042SErik.Nordmark@Sun.COM * the security label of the message data. tsol_check_dest() 837*11042SErik.Nordmark@Sun.COM * may create a new effective label or label flags. 8389710SKen.Powell@Sun.COM */ 839*11042SErik.Nordmark@Sun.COM retv = tsol_check_dest(tsl, &ipha->ipha_dst, IPV4_VERSION, 840*11042SErik.Nordmark@Sun.COM mac_mode, zone_is_global, &effective_tsl); 8411676Sjpk if (retv != 0) 8421676Sjpk return (retv); 8431676Sjpk 8449710SKen.Powell@Sun.COM /* 8459710SKen.Powell@Sun.COM * Calculate the security label to be placed in the text 8469710SKen.Powell@Sun.COM * of the message (if any). 8479710SKen.Powell@Sun.COM */ 848*11042SErik.Nordmark@Sun.COM if (effective_tsl != NULL) { 849*11042SErik.Nordmark@Sun.COM if ((retv = tsol_compute_label_v4(effective_tsl, zoneid, 8509710SKen.Powell@Sun.COM ipha->ipha_dst, opt_storage, ipst)) != 0) { 851*11042SErik.Nordmark@Sun.COM label_rele(effective_tsl); 8529710SKen.Powell@Sun.COM return (retv); 8539710SKen.Powell@Sun.COM } 854*11042SErik.Nordmark@Sun.COM *effective_tslp = effective_tsl; 8559710SKen.Powell@Sun.COM } else { 856*11042SErik.Nordmark@Sun.COM if ((retv = tsol_compute_label_v4(tsl, zoneid, 8579710SKen.Powell@Sun.COM ipha->ipha_dst, opt_storage, ipst)) != 0) { 8589710SKen.Powell@Sun.COM return (retv); 8599710SKen.Powell@Sun.COM } 8609710SKen.Powell@Sun.COM } 8619710SKen.Powell@Sun.COM 8621676Sjpk optr = (uchar_t *)(ipha + 1); 8631676Sjpk hlen = IPH_HDR_LENGTH(ipha) - IP_SIMPLE_HDR_LENGTH; 8641676Sjpk sec_opt_len = opt_storage[IPOPT_OLEN]; 8651676Sjpk 8661676Sjpk if (hlen >= sec_opt_len) { 8671676Sjpk /* If no option is supposed to be there, make sure it's not */ 8681676Sjpk if (sec_opt_len == 0 && hlen > 0 && 8691676Sjpk optr[IPOPT_OPTVAL] != IPOPT_COMSEC && 8701676Sjpk optr[IPOPT_OPTVAL] != IPOPT_SECURITY) 8711676Sjpk return (0); 8721676Sjpk /* if the option is there, it's always first */ 8731676Sjpk if (sec_opt_len != 0 && 8741676Sjpk bcmp(opt_storage, optr, sec_opt_len) == 0) 8751676Sjpk return (0); 8761676Sjpk } 8771676Sjpk 8781676Sjpk /* 8791676Sjpk * If there is an option there, then it must be the wrong one; delete. 8801676Sjpk */ 8816596Skp158701 if (hlen > 0) { 8826596Skp158701 delta_remove = tsol_remove_secopt(ipha, MBLKL(mp)); 8836596Skp158701 mp->b_wptr += delta_remove; 8846596Skp158701 } 8851676Sjpk 8861676Sjpk /* Make sure we have room for the worst-case addition */ 8871676Sjpk hlen = IPH_HDR_LENGTH(ipha) + opt_storage[IPOPT_OLEN]; 8881676Sjpk hlen = (hlen + 3) & ~3; 8891676Sjpk if (hlen > IP_MAX_HDR_LENGTH) 8901676Sjpk hlen = IP_MAX_HDR_LENGTH; 8911676Sjpk hlen -= IPH_HDR_LENGTH(ipha); 8921676Sjpk if (mp->b_wptr + hlen > mp->b_datap->db_lim) { 8931676Sjpk int copylen; 8941676Sjpk mblk_t *new_mp; 8951676Sjpk 8961676Sjpk /* allocate enough to be meaningful, but not *too* much */ 8971676Sjpk copylen = MBLKL(mp); 8981676Sjpk if (copylen > 256) 8991676Sjpk copylen = 256; 9008778SErik.Nordmark@Sun.COM new_mp = allocb_tmpl(hlen + copylen + 9018778SErik.Nordmark@Sun.COM (mp->b_rptr - mp->b_datap->db_base), mp); 902*11042SErik.Nordmark@Sun.COM if (new_mp == NULL) { 903*11042SErik.Nordmark@Sun.COM if (effective_tsl != NULL) { 904*11042SErik.Nordmark@Sun.COM label_rele(effective_tsl); 905*11042SErik.Nordmark@Sun.COM *effective_tslp = NULL; 906*11042SErik.Nordmark@Sun.COM } 9071676Sjpk return (ENOMEM); 908*11042SErik.Nordmark@Sun.COM } 9091676Sjpk 9101676Sjpk /* keep the bias */ 9111676Sjpk new_mp->b_rptr += mp->b_rptr - mp->b_datap->db_base; 9121676Sjpk new_mp->b_wptr = new_mp->b_rptr + copylen; 9131676Sjpk bcopy(mp->b_rptr, new_mp->b_rptr, copylen); 9141676Sjpk new_mp->b_cont = mp; 9151676Sjpk if ((mp->b_rptr += copylen) >= mp->b_wptr) { 9161676Sjpk new_mp->b_cont = mp->b_cont; 9171676Sjpk freeb(mp); 9181676Sjpk } 9191676Sjpk *mpp = mp = new_mp; 9201676Sjpk ipha = (ipha_t *)mp->b_rptr; 9211676Sjpk } 9221676Sjpk 9236596Skp158701 delta_add = tsol_prepend_option(opt_storage, ipha, MBLKL(mp)); 9246596Skp158701 if (delta_add == -1) 9251676Sjpk goto param_prob; 9261676Sjpk 9276596Skp158701 ASSERT((mp->b_wptr + delta_add) <= DB_LIM(mp)); 9286596Skp158701 mp->b_wptr += delta_add; 9291676Sjpk 9306596Skp158701 adjust = delta_remove + delta_add; 9316596Skp158701 adjust += ntohs(ipha->ipha_length); 9326596Skp158701 ipha->ipha_length = htons(adjust); 9331676Sjpk 9341676Sjpk return (0); 9351676Sjpk 9361676Sjpk param_prob: 937*11042SErik.Nordmark@Sun.COM if (effective_tsl != NULL) { 938*11042SErik.Nordmark@Sun.COM label_rele(effective_tsl); 939*11042SErik.Nordmark@Sun.COM *effective_tslp = NULL; 940*11042SErik.Nordmark@Sun.COM } 9411676Sjpk return (EINVAL); 9421676Sjpk } 9431676Sjpk 9441676Sjpk /* 9451676Sjpk * IPv6 HopOpt extension header for the label option layout: 9461676Sjpk * - One octet giving the type of the 'next extension header' 9471676Sjpk * - Header extension length in 8-byte words, not including the 9481676Sjpk * 1st 8 bytes, but including any pad bytes at the end. 9491676Sjpk * Eg. A value of 2 means 16 bytes not including the 1st 8 bytes. 9501676Sjpk * - Followed by TLV encoded IPv6 label option. Option layout is 9511676Sjpk * * One octet, IP6OPT_LS 9521676Sjpk * * One octet option length in bytes of the option data following 9531676Sjpk * the length, but not including any pad bytes at the end. 9541676Sjpk * * Four-octet DOI (IP6LS_DOI_V4) 9551676Sjpk * * One octet suboption, IP6LS_TT_V4 9561676Sjpk * * One octet suboption length in bytes of the suboption 9571676Sjpk * following the suboption length, including the suboption 9581676Sjpk * header length, but not including any pad bytes at the end. 9591676Sjpk * - Pad to make the extension header a multiple of 8 bytes. 9601676Sjpk * 9611676Sjpk * This function returns the contents of 'IPv6 option structure' in the above. 9621676Sjpk * i.e starting from the IP6OPT_LS but not including the pad at the end. 9631676Sjpk * The user must prepend two octets (either padding or next header / length) 9641676Sjpk * and append padding out to the next 8 octet boundary. 965*11042SErik.Nordmark@Sun.COM * 966*11042SErik.Nordmark@Sun.COM * The zoneid is the IP zoneid (i.e., GLOBAL_ZONEID for exlusive-IP zones). 9671676Sjpk */ 9681676Sjpk int 969*11042SErik.Nordmark@Sun.COM tsol_compute_label_v6(const ts_label_t *tsl, zoneid_t zoneid, 970*11042SErik.Nordmark@Sun.COM const in6_addr_t *dst, uchar_t *opt_storage, ip_stack_t *ipst) 9711676Sjpk { 9721676Sjpk uint_t sec_opt_len; 9731676Sjpk uint32_t doi; 974*11042SErik.Nordmark@Sun.COM ire_t *ire; 975*11042SErik.Nordmark@Sun.COM tsol_ire_gw_secattr_t *attrp = NULL; 9768778SErik.Nordmark@Sun.COM 9771676Sjpk if (ip6opt_ls == 0) 9781676Sjpk return (EINVAL); 9791676Sjpk 9801676Sjpk if (opt_storage != NULL) 9811676Sjpk opt_storage[IPOPT_OLEN] = 0; 9821676Sjpk 983*11042SErik.Nordmark@Sun.COM if (tsl == NULL) 9841676Sjpk return (0); 9851676Sjpk 9861676Sjpk /* Always pass multicast */ 9871676Sjpk if (IN6_IS_ADDR_MULTICAST(dst)) 9881676Sjpk return (0); 9891676Sjpk 9901676Sjpk /* 9911676Sjpk * Fill in a V6 label. If a new format is added here, make certain 9921676Sjpk * that the maximum size of this label is reflected in sys/tsol/tnet.h 9931676Sjpk * as TSOL_MAX_IPV6_OPTION. 9941676Sjpk */ 99510934Ssommerfeld@sun.com if (tsl->tsl_flags & TSLF_IMPLICIT_OUT) 99610934Ssommerfeld@sun.com return (0); 99710934Ssommerfeld@sun.com 9989710SKen.Powell@Sun.COM if (tsl->tsl_flags & TSLF_UNLABELED) { 9991676Sjpk /* 10009710SKen.Powell@Sun.COM * The destination is unlabeled. Only add a label if the 1001*11042SErik.Nordmark@Sun.COM * destination is not a broadcast/local/loopback address, 10029710SKen.Powell@Sun.COM * the destination is not on the same subnet, and the 10039710SKen.Powell@Sun.COM * next-hop gateway is labeled. 10041676Sjpk */ 1005*11042SErik.Nordmark@Sun.COM ire = ire_route_recursive_v6(dst, 0, NULL, zoneid, tsl, 1006*11042SErik.Nordmark@Sun.COM MATCH_IRE_SECATTR, B_TRUE, 0, ipst, NULL, &attrp, NULL); 1007*11042SErik.Nordmark@Sun.COM ASSERT(ire != NULL); 1008*11042SErik.Nordmark@Sun.COM if (ire->ire_flags & (RTF_REJECT|RTF_BLACKHOLE)) { 1009*11042SErik.Nordmark@Sun.COM /* no route to destination */ 1010*11042SErik.Nordmark@Sun.COM ire_refrele(ire); 10119710SKen.Powell@Sun.COM DTRACE_PROBE3( 10121676Sjpk tx__tnopt__log__info__labeling__routedst__v6, 10139710SKen.Powell@Sun.COM char *, "No route to unlabeled dest ip6(1) with " 1014*11042SErik.Nordmark@Sun.COM "label(2).", in6_addr_t *, dst, ts_label_t *, tsl); 10159710SKen.Powell@Sun.COM return (EHOSTUNREACH); 10161676Sjpk } 1017*11042SErik.Nordmark@Sun.COM if (ire->ire_type & (IRE_LOCAL | IRE_LOOPBACK | 1018*11042SErik.Nordmark@Sun.COM IRE_INTERFACE)) { 1019*11042SErik.Nordmark@Sun.COM ire_refrele(ire); 1020*11042SErik.Nordmark@Sun.COM return (0); 1021*11042SErik.Nordmark@Sun.COM } 10221676Sjpk /* 1023*11042SErik.Nordmark@Sun.COM * ire_route_recursive gives us the first attrp it finds 1024*11042SErik.Nordmark@Sun.COM * in the recursive lookup. 10251676Sjpk */ 10269710SKen.Powell@Sun.COM /* 10279710SKen.Powell@Sun.COM * Return now if next hop gateway is unlabeled. There is 10289710SKen.Powell@Sun.COM * no need to generate a CIPSO option for this message. 10299710SKen.Powell@Sun.COM */ 10309710SKen.Powell@Sun.COM if (attrp == NULL || attrp->igsa_rhc == NULL || 10319710SKen.Powell@Sun.COM attrp->igsa_rhc->rhc_tpc->tpc_tp.host_type == UNLABELED) { 1032*11042SErik.Nordmark@Sun.COM ire_refrele(ire); 10339710SKen.Powell@Sun.COM return (0); 10341676Sjpk } 1035*11042SErik.Nordmark@Sun.COM ire_refrele(ire); 10361676Sjpk } 10371676Sjpk 10381676Sjpk /* compute the CIPSO option */ 10391676Sjpk if (opt_storage != NULL) 10401676Sjpk opt_storage += 8; 10419710SKen.Powell@Sun.COM sec_opt_len = tsol2cipso_tt1(&tsl->tsl_label, opt_storage, 10429710SKen.Powell@Sun.COM tsl->tsl_doi); 10431676Sjpk 10441676Sjpk if (sec_opt_len == 0) { 10459710SKen.Powell@Sun.COM DTRACE_PROBE3(tx__tnopt__log__error__labeling__lostops__v6, 10469710SKen.Powell@Sun.COM char *, "options lack length for dest ip6(1) with " 1047*11042SErik.Nordmark@Sun.COM "label(2).", in6_addr_t *, dst, ts_label_t *, tsl); 10481676Sjpk return (EINVAL); 10491676Sjpk } 10501676Sjpk 10511676Sjpk if (opt_storage == NULL) 10521676Sjpk return (0); 10531676Sjpk 10541676Sjpk if (sec_opt_len < IP_MAX_OPT_LENGTH) 10551676Sjpk opt_storage[sec_opt_len] = IPOPT_EOL; 10561676Sjpk 10571676Sjpk /* 10581676Sjpk * Just in case the option length is odd, round it up to the next even 10591676Sjpk * multiple. The IPv6 option definition doesn't like odd numbers for 10601676Sjpk * some reason. 10611676Sjpk * 10621676Sjpk * Length in the overall option header (IP6OPT_LS) does not include the 10631676Sjpk * option header itself, but the length in the suboption does include 10641676Sjpk * the suboption header. Thus, when there's just one suboption, the 10651676Sjpk * length in the option header is the suboption length plus 4 (for the 10661676Sjpk * DOI value). 10671676Sjpk */ 10681676Sjpk opt_storage[-2] = IP6LS_TT_V4; 10691676Sjpk opt_storage[-1] = (sec_opt_len + 2 + 1) & ~1; 10701676Sjpk opt_storage[-8] = ip6opt_ls; 10711676Sjpk opt_storage[-7] = opt_storage[-1] + 4; 10721676Sjpk doi = htons(IP6LS_DOI_V4); 10731676Sjpk bcopy(&doi, opt_storage - 6, 4); 10741676Sjpk 10751676Sjpk return (0); 10761676Sjpk } 10771676Sjpk 10781676Sjpk /* 10791676Sjpk * Locate the start of the IP6OPT_LS label option and return it. 10801676Sjpk * Also return the start of the next non-pad option in after_secoptp. 10811676Sjpk * Usually the label option is the first option at least when packets 10821676Sjpk * are generated, but for generality we don't assume that on received packets. 108310181SKen.Powell@Sun.COM * 108410181SKen.Powell@Sun.COM * The function will return with B_FALSE if an IP format error 108510181SKen.Powell@Sun.COM * or an unexpected label content error is encountered. 10861676Sjpk */ 108710181SKen.Powell@Sun.COM boolean_t 10881676Sjpk tsol_find_secopt_v6( 10891676Sjpk const uchar_t *ip6hbh, /* Start of the hop-by-hop extension header */ 10901676Sjpk uint_t hbhlen, /* Length of the hop-by-hop extension header */ 109110181SKen.Powell@Sun.COM uchar_t **secoptp, /* Location of IP6OPT_LS label option */ 10921676Sjpk uchar_t **after_secoptp, /* Non-pad option following the label option */ 10931676Sjpk boolean_t *hbh_needed) /* Is hop-by-hop hdr needed w/o label */ 10941676Sjpk { 10951676Sjpk uint_t optlen; 10961676Sjpk uint_t optused; 10971676Sjpk const uchar_t *optptr; 10981676Sjpk uchar_t opt_type; 10991676Sjpk 110010181SKen.Powell@Sun.COM *secoptp = NULL; 11011676Sjpk *hbh_needed = B_FALSE; 11021676Sjpk *after_secoptp = NULL; 11031676Sjpk optlen = hbhlen - 2; 11041676Sjpk optptr = ip6hbh + 2; 11051676Sjpk while (optlen != 0) { 11061676Sjpk opt_type = *optptr; 11071676Sjpk if (opt_type == IP6OPT_PAD1) { 11081676Sjpk optptr++; 11091676Sjpk optlen--; 11101676Sjpk continue; 11111676Sjpk } 11121676Sjpk if (optlen == 1) 111310181SKen.Powell@Sun.COM return (B_FALSE); 11141676Sjpk optused = 2 + optptr[1]; 11151676Sjpk if (optused > optlen) 111610181SKen.Powell@Sun.COM return (B_FALSE); 11171676Sjpk /* 11181676Sjpk * if we get here, ip6opt_ls can 11191676Sjpk * not be 0 because it will always 11201676Sjpk * match the IP6OPT_PAD1 above. 11211676Sjpk * Therefore ip6opt_ls == 0 forces 11221676Sjpk * this test to always fail here. 11231676Sjpk */ 112410181SKen.Powell@Sun.COM if (opt_type == ip6opt_ls) { 112510181SKen.Powell@Sun.COM if (*secoptp != NULL) 112610181SKen.Powell@Sun.COM /* More than one security option found */ 112710181SKen.Powell@Sun.COM return (B_FALSE); 112810181SKen.Powell@Sun.COM *secoptp = (uchar_t *)optptr; 112910181SKen.Powell@Sun.COM } else switch (opt_type) { 11301676Sjpk case IP6OPT_PADN: 11311676Sjpk break; 11321676Sjpk default: 11331676Sjpk /* 11341676Sjpk * There is at least 1 option other than 11351676Sjpk * the label option. So the hop-by-hop header is needed 11361676Sjpk */ 11371676Sjpk *hbh_needed = B_TRUE; 113810181SKen.Powell@Sun.COM if (*secoptp != NULL) { 11391676Sjpk *after_secoptp = (uchar_t *)optptr; 114010181SKen.Powell@Sun.COM return (B_TRUE); 11411676Sjpk } 11421676Sjpk break; 11431676Sjpk } 11441676Sjpk optlen -= optused; 11451676Sjpk optptr += optused; 11461676Sjpk } 114710181SKen.Powell@Sun.COM return (B_TRUE); 11481676Sjpk } 11491676Sjpk 11501676Sjpk /* 11511676Sjpk * Remove the label option from the hop-by-hop options header if it exists. 11521676Sjpk * 'buflen' is the total length of the packet typically b_wptr - b_rptr. 11531676Sjpk * Header and data following the label option that is deleted are copied 11544564Swy83408 * (i.e. slid backward) to the right position, and returns the number 11554564Swy83408 * of bytes removed (as zero or negative number.) 1156*11042SErik.Nordmark@Sun.COM * 1157*11042SErik.Nordmark@Sun.COM * Note that tsol_remove_secopt does not adjust ipha_length but 1158*11042SErik.Nordmark@Sun.COM * tsol_remove_secopt_v6 does adjust ip6_plen. 11591676Sjpk */ 11601676Sjpk int 11611676Sjpk tsol_remove_secopt_v6(ip6_t *ip6h, int buflen) 11621676Sjpk { 11631676Sjpk uchar_t *ip6hbh; /* hop-by-hop header */ 11641676Sjpk uint_t hbhlen; /* hop-by-hop extension header length */ 11651676Sjpk uchar_t *secopt = NULL; 11661676Sjpk uchar_t *after_secopt; 11671676Sjpk uint_t pad; 11681676Sjpk uint_t delta; 11691676Sjpk boolean_t hbh_needed; 11701676Sjpk 11711676Sjpk /* 11721676Sjpk * hop-by-hop extension header must appear first, if it does not 11731676Sjpk * exist, there is no label option. 11741676Sjpk */ 11751676Sjpk if (ip6h->ip6_nxt != IPPROTO_HOPOPTS) 11761676Sjpk return (0); 11771676Sjpk 11781676Sjpk ip6hbh = (uchar_t *)&ip6h[1]; 11791676Sjpk hbhlen = (ip6hbh[1] + 1) << 3; 11801676Sjpk /* 11811676Sjpk * Locate the start of the label option if it exists and the end 11821676Sjpk * of the label option including pads if any. 11831676Sjpk */ 118410181SKen.Powell@Sun.COM if (!tsol_find_secopt_v6(ip6hbh, hbhlen, &secopt, &after_secopt, 118510181SKen.Powell@Sun.COM &hbh_needed)) { 118610181SKen.Powell@Sun.COM /* 118710181SKen.Powell@Sun.COM * This function should not see invalid messages. 118810181SKen.Powell@Sun.COM * If one occurs, it would indicate either an 118910181SKen.Powell@Sun.COM * option previously verified in the forwarding 119010181SKen.Powell@Sun.COM * path has been corrupted or an option was 119110181SKen.Powell@Sun.COM * incorrectly generated locally. 119210181SKen.Powell@Sun.COM */ 119310181SKen.Powell@Sun.COM ASSERT(0); 119410181SKen.Powell@Sun.COM return (0); 119510181SKen.Powell@Sun.COM } 11961676Sjpk if (secopt == NULL) 11971676Sjpk return (0); 11981676Sjpk if (!hbh_needed) { 11991676Sjpk uchar_t next_hdr; 12001676Sjpk /* 12011676Sjpk * The label option was the only option in the hop-by-hop 12021676Sjpk * header. We don't need the hop-by-hop header itself any 12031676Sjpk * longer. 12041676Sjpk */ 12051676Sjpk next_hdr = ip6hbh[0]; 12061676Sjpk ovbcopy(ip6hbh + hbhlen, ip6hbh, 12071676Sjpk buflen - (IPV6_HDR_LEN + hbhlen)); 12082776Skp158701 ip6h->ip6_plen = htons(ntohs(ip6h->ip6_plen) - hbhlen); 12091676Sjpk ip6h->ip6_nxt = next_hdr; 12104564Swy83408 return (-hbhlen); 12111676Sjpk } 12121676Sjpk 12131676Sjpk if (after_secopt == NULL) { 12141676Sjpk /* There is no option following the label option */ 12151676Sjpk after_secopt = ip6hbh + hbhlen; 12161676Sjpk } 12171676Sjpk 12181676Sjpk /* 12191676Sjpk * After deleting the label option, we need to slide the headers 12201676Sjpk * and data back, while still maintaining the same alignment (module 8) 12211676Sjpk * for the other options. So we slide the headers and data back only 12221676Sjpk * by an integral multiple of 8 bytes, and fill the remaining bytes 12231676Sjpk * with pads. 12241676Sjpk */ 12251676Sjpk delta = after_secopt - secopt; 12261676Sjpk pad = delta % 8; 12271676Sjpk if (pad == 1) { 12281676Sjpk secopt[0] = IP6OPT_PAD1; 12291676Sjpk } else if (pad > 1) { 12301676Sjpk secopt[0] = IP6OPT_PADN; 12311676Sjpk secopt[1] = pad - 2; 12321676Sjpk if (pad > 2) 12331676Sjpk bzero(&secopt[2], pad - 2); 12341676Sjpk } 12351676Sjpk secopt += pad; 12361676Sjpk delta -= pad; 12371676Sjpk ovbcopy(after_secopt, secopt, 12381676Sjpk (uchar_t *)ip6h + buflen - after_secopt); 12391676Sjpk ip6hbh[1] -= delta/8; 12402776Skp158701 ip6h->ip6_plen = htons(ntohs(ip6h->ip6_plen) - delta); 12411676Sjpk 12424564Swy83408 return (-delta); 12431676Sjpk } 12441676Sjpk 12451676Sjpk /* 12461676Sjpk * 'optbuf' contains a CIPSO label embedded in an IPv6 hop-by-hop option, 12471676Sjpk * starting with the IP6OPT_LS option type. The format of this hop-by-hop 12481676Sjpk * option is described in the block comment above tsol_compute_label_v6. 12491676Sjpk * This function prepends this hop-by-hop option before any other hop-by-hop 12501676Sjpk * options in the hop-by-hop header if one already exists, else a new 12511676Sjpk * hop-by-hop header is created and stuffed into the packet following 12521676Sjpk * the IPv6 header. 'buflen' is the total length of the packet i.e. 12531676Sjpk * b_wptr - b_rptr. The caller ensures that there is enough space for the 12541676Sjpk * extra option being added. Header and data following the position where 12551676Sjpk * the label option is inserted are copied (i.e. slid forward) to the right 12561676Sjpk * position. 1257*11042SErik.Nordmark@Sun.COM * 1258*11042SErik.Nordmark@Sun.COM * Note that tsol_prepend_option does not adjust ipha_length but 1259*11042SErik.Nordmark@Sun.COM * tsol_prepend_option_v6 does adjust ip6_plen. 12601676Sjpk */ 12611676Sjpk int 12621676Sjpk tsol_prepend_option_v6(uchar_t *optbuf, ip6_t *ip6h, int buflen) 12631676Sjpk { 12641676Sjpk /* 12651676Sjpk * rawlen is the length of the label option in bytes, not including 12661676Sjpk * any pads, starting from the IP6OPT_LS (option type) byte. 12671676Sjpk */ 12681676Sjpk uint_t rawlen; 12691676Sjpk 12701676Sjpk uint_t optlen; /* rawlen rounded to an 8 byte multiple */ 12711676Sjpk uchar_t *ip6hbh; /* start of the hop-by-hop extension header */ 12721676Sjpk uint_t hbhlen; /* Length of the hop-by-hop extension header */ 12731676Sjpk uint_t pad_len; 12741676Sjpk uchar_t *pad_position; 12751676Sjpk int delta; /* Actual number of bytes inserted */ 12761676Sjpk 12771676Sjpk rawlen = optbuf[1] + 2; /* Add 2 for the option type, option length */ 12781676Sjpk ip6hbh = (uchar_t *)&ip6h[1]; 12791676Sjpk if (ip6h->ip6_nxt == IPPROTO_HOPOPTS) { 12801676Sjpk /* 12811676Sjpk * There is a hop-by-hop header present already. In order to 12821676Sjpk * preserve the alignment of the other options at the existing 12831676Sjpk * value (modulo 8) we need to pad the label option to a 12841676Sjpk * multiple of 8 bytes before prepending it to the other 12851676Sjpk * options. Slide the extension headers and data forward to 12861676Sjpk * accomodate the label option at the start of the hop-by-hop 12871676Sjpk * header 12881676Sjpk */ 12891676Sjpk delta = optlen = (rawlen + 7) & ~7; 12901676Sjpk pad_len = optlen - rawlen; 12911676Sjpk pad_position = ip6hbh + 2 + rawlen; 12921676Sjpk ovbcopy(ip6hbh + 2, ip6hbh + 2 + optlen, 12931676Sjpk buflen - (IPV6_HDR_LEN + 2)); 12941676Sjpk /* 12951676Sjpk * Bump up the hop-by-hop extension header length by 12961676Sjpk * the number of 8-byte words added 12971676Sjpk */ 12981676Sjpk optlen >>= 3; 12991676Sjpk if (ip6hbh[1] + optlen > 255) 13001676Sjpk return (-1); 13011676Sjpk ip6hbh[1] += optlen; 13021676Sjpk } else { 13031676Sjpk /* 13041676Sjpk * There is no hop-by-hop header in the packet. Construct a 13051676Sjpk * new Hop-by-hop extension header (a multiple of 8 bytes). 13061676Sjpk * Slide any other extension headers and data forward to 13071676Sjpk * accomodate this hop-by-hop header 13081676Sjpk */ 13091676Sjpk delta = hbhlen = (2 + rawlen + 7) & ~7; /* +2 for nxthdr, len */ 13101676Sjpk pad_len = hbhlen - (2 + rawlen); 13111676Sjpk pad_position = ip6hbh + 2 + rawlen; 13121676Sjpk ovbcopy(ip6hbh, ip6hbh + hbhlen, buflen - IPV6_HDR_LEN); 13131676Sjpk ip6hbh[0] = ip6h->ip6_nxt; 13141676Sjpk /* 13151676Sjpk * hop-by-hop extension header length in 8-byte words, not 13161676Sjpk * including the 1st 8 bytes of the hop-by-hop header. 13171676Sjpk */ 13181676Sjpk ip6hbh[1] = (hbhlen >> 3) - 1; 13191676Sjpk ip6h->ip6_nxt = IPPROTO_HOPOPTS; 13201676Sjpk } 13211676Sjpk /* 13221676Sjpk * Copy the label option into the hop-by-hop header and insert any 13231676Sjpk * needed pads 13241676Sjpk */ 13251676Sjpk bcopy(optbuf, ip6hbh + 2, rawlen); 13261676Sjpk if (pad_len == 1) { 13271676Sjpk pad_position[0] = IP6OPT_PAD1; 13281676Sjpk } else if (pad_len > 1) { 13291676Sjpk pad_position[0] = IP6OPT_PADN; 13301676Sjpk pad_position[1] = pad_len - 2; 13311676Sjpk if (pad_len > 2) 13321676Sjpk bzero(pad_position + 2, pad_len - 2); 13331676Sjpk } 13342776Skp158701 ip6h->ip6_plen = htons(ntohs(ip6h->ip6_plen) + delta); 13351676Sjpk return (delta); 13361676Sjpk } 13371676Sjpk 13381676Sjpk /* 13391676Sjpk * tsol_check_label_v6() 13401676Sjpk * 13411676Sjpk * This routine computes the IP label that should be on the packet based on the 1342*11042SErik.Nordmark@Sun.COM * connection and destination information. It's called by the IP forwarding 1343*11042SErik.Nordmark@Sun.COM * logic and by ip_output_simple. The ULPs generate the labels before calling 1344*11042SErik.Nordmark@Sun.COM * conn_ip_output. If any adjustments to 1345*11042SErik.Nordmark@Sun.COM * the label are needed due to the connection's MAC-exempt status or 1346*11042SErik.Nordmark@Sun.COM * the destination's ability to receive labels, an "effective label" 1347*11042SErik.Nordmark@Sun.COM * will be returned. 1348*11042SErik.Nordmark@Sun.COM * 1349*11042SErik.Nordmark@Sun.COM * The packet's header is clear before entering IPsec's engine. 1350*11042SErik.Nordmark@Sun.COM * 1351*11042SErik.Nordmark@Sun.COM * The zoneid is the IP zoneid (i.e., GLOBAL_ZONEID for exlusive-IP zones). 1352*11042SErik.Nordmark@Sun.COM * zone_is_global is set if the actual zoneid is global. 1353*11042SErik.Nordmark@Sun.COM * 1354*11042SErik.Nordmark@Sun.COM * On successful return, effective_tslp will point to the new label needed 1355*11042SErik.Nordmark@Sun.COM * or will be NULL if a new label isn't needed. On error, effective_tsl will 1356*11042SErik.Nordmark@Sun.COM * point to NULL. 13571676Sjpk * 13581676Sjpk * Returns: 1359*11042SErik.Nordmark@Sun.COM * 0 Label on (was|is now) correct 13609710SKen.Powell@Sun.COM * EACCES The packet failed the remote host accreditation. 13611676Sjpk * ENOMEM Memory allocation failure. 1362*11042SErik.Nordmark@Sun.COM * EINVAL Label cannot be computed 13631676Sjpk */ 13641676Sjpk int 1365*11042SErik.Nordmark@Sun.COM tsol_check_label_v6(const ts_label_t *tsl, zoneid_t zoneid, mblk_t **mpp, 1366*11042SErik.Nordmark@Sun.COM uint_t mac_mode, boolean_t zone_is_global, ip_stack_t *ipst, 1367*11042SErik.Nordmark@Sun.COM ts_label_t **effective_tslp) 13681676Sjpk { 13691676Sjpk mblk_t *mp = *mpp; 13701676Sjpk ip6_t *ip6h; 1371*11042SErik.Nordmark@Sun.COM ts_label_t *effective_tsl = NULL; 13721676Sjpk /* 13731676Sjpk * Label option length is limited to IP_MAX_OPT_LENGTH for 13741676Sjpk * symmetry with IPv4. Can be relaxed if needed 13751676Sjpk */ 13761676Sjpk uchar_t opt_storage[TSOL_MAX_IPV6_OPTION]; 13771676Sjpk uint_t hlen; 13781676Sjpk uint_t sec_opt_len; /* label option length not including type, len */ 13796596Skp158701 int delta_remove = 0, delta_add; 13801676Sjpk int retv; 13811676Sjpk uchar_t *after_secopt; 13821676Sjpk uchar_t *secopt = NULL; 13831676Sjpk uchar_t *ip6hbh; 13841676Sjpk uint_t hbhlen; 13851676Sjpk boolean_t hbh_needed; 13861676Sjpk 1387*11042SErik.Nordmark@Sun.COM *effective_tslp = NULL; 1388*11042SErik.Nordmark@Sun.COM 13899710SKen.Powell@Sun.COM /* 13909710SKen.Powell@Sun.COM * Verify the destination is allowed to receive packets at 1391*11042SErik.Nordmark@Sun.COM * the security label of the message data. tsol_check_dest() 1392*11042SErik.Nordmark@Sun.COM * may create a new effective label or label flags. 13939710SKen.Powell@Sun.COM */ 13941676Sjpk ip6h = (ip6_t *)mp->b_rptr; 1395*11042SErik.Nordmark@Sun.COM retv = tsol_check_dest(tsl, &ip6h->ip6_dst, IPV6_VERSION, 1396*11042SErik.Nordmark@Sun.COM mac_mode, zone_is_global, &effective_tsl); 13971676Sjpk if (retv != 0) 13981676Sjpk return (retv); 13991676Sjpk 14009710SKen.Powell@Sun.COM /* 14019710SKen.Powell@Sun.COM * Calculate the security label to be placed in the text 14029710SKen.Powell@Sun.COM * of the message (if any). 14039710SKen.Powell@Sun.COM */ 1404*11042SErik.Nordmark@Sun.COM if (effective_tsl != NULL) { 1405*11042SErik.Nordmark@Sun.COM if ((retv = tsol_compute_label_v6(effective_tsl, zoneid, 14069710SKen.Powell@Sun.COM &ip6h->ip6_dst, opt_storage, ipst)) != 0) { 1407*11042SErik.Nordmark@Sun.COM label_rele(effective_tsl); 14089710SKen.Powell@Sun.COM return (retv); 14099710SKen.Powell@Sun.COM } 1410*11042SErik.Nordmark@Sun.COM *effective_tslp = effective_tsl; 14119710SKen.Powell@Sun.COM } else { 1412*11042SErik.Nordmark@Sun.COM if ((retv = tsol_compute_label_v6(tsl, zoneid, 14139710SKen.Powell@Sun.COM &ip6h->ip6_dst, opt_storage, ipst)) != 0) 14149710SKen.Powell@Sun.COM return (retv); 14159710SKen.Powell@Sun.COM } 14169710SKen.Powell@Sun.COM 14171676Sjpk sec_opt_len = opt_storage[1]; 14181676Sjpk 14191676Sjpk if (ip6h->ip6_nxt == IPPROTO_HOPOPTS) { 14201676Sjpk ip6hbh = (uchar_t *)&ip6h[1]; 14211676Sjpk hbhlen = (ip6hbh[1] + 1) << 3; 142210181SKen.Powell@Sun.COM if (!tsol_find_secopt_v6(ip6hbh, hbhlen, &secopt, 142310181SKen.Powell@Sun.COM &after_secopt, &hbh_needed)) { 142410181SKen.Powell@Sun.COM /* 142510181SKen.Powell@Sun.COM * This function should not see invalid messages. 142610181SKen.Powell@Sun.COM * If one occurs, it would indicate either an 142710181SKen.Powell@Sun.COM * option previously verified in the forwarding 142810181SKen.Powell@Sun.COM * path has been corrupted or an option was 142910181SKen.Powell@Sun.COM * incorrectly generated locally. 143010181SKen.Powell@Sun.COM */ 143110181SKen.Powell@Sun.COM ASSERT(0); 143210181SKen.Powell@Sun.COM return (EACCES); 143310181SKen.Powell@Sun.COM } 14341676Sjpk } 14351676Sjpk 14361676Sjpk if (sec_opt_len == 0 && secopt == NULL) { 14371676Sjpk /* 14381676Sjpk * The packet is not supposed to have a label, and it 14391676Sjpk * does not have one currently 14401676Sjpk */ 14411676Sjpk return (0); 14421676Sjpk } 144310934Ssommerfeld@sun.com 14441676Sjpk if (secopt != NULL && sec_opt_len != 0 && 14451676Sjpk (bcmp(opt_storage, secopt, sec_opt_len + 2) == 0)) { 14461676Sjpk /* The packet has the correct label already */ 14471676Sjpk return (0); 14481676Sjpk } 14491676Sjpk 14501676Sjpk /* 14511676Sjpk * If there is an option there, then it must be the wrong one; delete. 14521676Sjpk */ 14536596Skp158701 if (secopt != NULL) { 14546596Skp158701 delta_remove = tsol_remove_secopt_v6(ip6h, MBLKL(mp)); 14556596Skp158701 mp->b_wptr += delta_remove; 14566596Skp158701 } 14571676Sjpk 14581676Sjpk /* 14591676Sjpk * Make sure we have room for the worst-case addition. Add 2 bytes for 14601676Sjpk * the hop-by-hop ext header's next header and length fields. Add 14611676Sjpk * another 2 bytes for the label option type, len and then round 14621676Sjpk * up to the next 8-byte multiple. 14631676Sjpk */ 14641676Sjpk hlen = (4 + sec_opt_len + 7) & ~7; 14651676Sjpk if (mp->b_wptr + hlen > mp->b_datap->db_lim) { 14661676Sjpk int copylen; 14671676Sjpk mblk_t *new_mp; 14681676Sjpk uint16_t hdr_len; 14691676Sjpk 14701676Sjpk hdr_len = ip_hdr_length_v6(mp, ip6h); 14711676Sjpk /* 14721676Sjpk * Allocate enough to be meaningful, but not *too* much. 14731676Sjpk * Also all the IPv6 extension headers must be in the same mblk 14741676Sjpk */ 14751676Sjpk copylen = MBLKL(mp); 14761676Sjpk if (copylen > 256) 14771676Sjpk copylen = 256; 14781676Sjpk if (copylen < hdr_len) 14791676Sjpk copylen = hdr_len; 14808778SErik.Nordmark@Sun.COM new_mp = allocb_tmpl(hlen + copylen + 14818778SErik.Nordmark@Sun.COM (mp->b_rptr - mp->b_datap->db_base), mp); 1482*11042SErik.Nordmark@Sun.COM if (new_mp == NULL) { 1483*11042SErik.Nordmark@Sun.COM if (effective_tsl != NULL) { 1484*11042SErik.Nordmark@Sun.COM label_rele(effective_tsl); 1485*11042SErik.Nordmark@Sun.COM *effective_tslp = NULL; 1486*11042SErik.Nordmark@Sun.COM } 14871676Sjpk return (ENOMEM); 1488*11042SErik.Nordmark@Sun.COM } 14891676Sjpk 14901676Sjpk /* keep the bias */ 14911676Sjpk new_mp->b_rptr += mp->b_rptr - mp->b_datap->db_base; 14921676Sjpk new_mp->b_wptr = new_mp->b_rptr + copylen; 14931676Sjpk bcopy(mp->b_rptr, new_mp->b_rptr, copylen); 14941676Sjpk new_mp->b_cont = mp; 14951676Sjpk if ((mp->b_rptr += copylen) >= mp->b_wptr) { 14961676Sjpk new_mp->b_cont = mp->b_cont; 14971676Sjpk freeb(mp); 14981676Sjpk } 14991676Sjpk *mpp = mp = new_mp; 15001676Sjpk ip6h = (ip6_t *)mp->b_rptr; 15011676Sjpk } 15021676Sjpk 15036596Skp158701 delta_add = tsol_prepend_option_v6(opt_storage, ip6h, MBLKL(mp)); 15046596Skp158701 if (delta_add == -1) 15051676Sjpk goto param_prob; 15061676Sjpk 15076596Skp158701 ASSERT(mp->b_wptr + delta_add <= DB_LIM(mp)); 15086596Skp158701 mp->b_wptr += delta_add; 15091676Sjpk 1510*11042SErik.Nordmark@Sun.COM /* tsol_prepend_option_v6 has adjusted ip6_plen */ 15111676Sjpk return (0); 15121676Sjpk 15131676Sjpk param_prob: 1514*11042SErik.Nordmark@Sun.COM if (effective_tsl != NULL) { 1515*11042SErik.Nordmark@Sun.COM label_rele(effective_tsl); 1516*11042SErik.Nordmark@Sun.COM *effective_tslp = NULL; 1517*11042SErik.Nordmark@Sun.COM } 15181676Sjpk return (EINVAL); 15191676Sjpk } 1520