145916cd2Sjpk /* 245916cd2Sjpk * CDDL HEADER START 345916cd2Sjpk * 445916cd2Sjpk * The contents of this file are subject to the terms of the 545916cd2Sjpk * Common Development and Distribution License (the "License"). 645916cd2Sjpk * You may not use this file except in compliance with the License. 745916cd2Sjpk * 845916cd2Sjpk * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 945916cd2Sjpk * or http://www.opensolaris.org/os/licensing. 1045916cd2Sjpk * See the License for the specific language governing permissions 1145916cd2Sjpk * and limitations under the License. 1245916cd2Sjpk * 1345916cd2Sjpk * When distributing Covered Code, include this CDDL HEADER in each 1445916cd2Sjpk * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 1545916cd2Sjpk * If applicable, add the following below this CDDL HEADER, with the 1645916cd2Sjpk * fields enclosed by brackets "[]" replaced with your own identifying 1745916cd2Sjpk * information: Portions Copyright [yyyy] [name of copyright owner] 1845916cd2Sjpk * 1945916cd2Sjpk * CDDL HEADER END 2045916cd2Sjpk */ 2145916cd2Sjpk /* 22*9e3469d3SErik Nordmark * Copyright 2010 Sun Microsystems, Inc. All rights reserved. 2345916cd2Sjpk * Use is subject to license terms. 2445916cd2Sjpk */ 2545916cd2Sjpk 2645916cd2Sjpk #include <sys/types.h> 2745916cd2Sjpk #include <sys/systm.h> 2845916cd2Sjpk #include <sys/kmem.h> 2945916cd2Sjpk #include <sys/disp.h> 3045916cd2Sjpk #include <sys/stream.h> 3145916cd2Sjpk #include <sys/strsubr.h> 3245916cd2Sjpk #include <sys/strsun.h> 3345916cd2Sjpk #include <sys/policy.h> 3445916cd2Sjpk #include <sys/tsol/label_macro.h> 3545916cd2Sjpk #include <sys/tsol/tndb.h> 3645916cd2Sjpk #include <sys/tsol/tnet.h> 3745916cd2Sjpk #include <inet/ip.h> 3845916cd2Sjpk #include <inet/ip6.h> 3945916cd2Sjpk #include <inet/tcp.h> 4045916cd2Sjpk #include <inet/ipclassifier.h> 4145916cd2Sjpk #include <inet/ip_ire.h> 42c793af95Ssangeeta #include <inet/ip_ftable.h> 4345916cd2Sjpk 4445916cd2Sjpk /* 4545916cd2Sjpk * This routine takes a sensitivity label as input and creates a CIPSO 4645916cd2Sjpk * option in the specified buffer. It returns the size of the CIPSO option. 4745916cd2Sjpk * If the sensitivity label is too large for the CIPSO option, then 0 4845916cd2Sjpk * is returned. 4945916cd2Sjpk * 5045916cd2Sjpk * tsol2cipso_tt1 returns 0 for failure and greater than 0 for success 5145916cd2Sjpk * (more accurately, success means a return value between 10 and 40). 5245916cd2Sjpk */ 5345916cd2Sjpk 5445916cd2Sjpk static int 5545916cd2Sjpk tsol2cipso_tt1(const bslabel_t *sl, unsigned char *cop, uint32_t doi) 5645916cd2Sjpk { 5745916cd2Sjpk struct cipso_tag_type_1 *tt1; 5845916cd2Sjpk const _bslabel_impl_t *bsl; 5945916cd2Sjpk const uchar_t *ucp; 6045916cd2Sjpk int i; 6145916cd2Sjpk 6245916cd2Sjpk if (doi == 0) 6345916cd2Sjpk return (0); 6445916cd2Sjpk 6545916cd2Sjpk /* check for Admin High sensitivity label */ 6645916cd2Sjpk if (blequal(sl, label2bslabel(l_admin_high))) 6745916cd2Sjpk return (0); 6845916cd2Sjpk 6945916cd2Sjpk /* check whether classification will fit in one octet */ 7045916cd2Sjpk bsl = (const _bslabel_impl_t *)sl; 7145916cd2Sjpk if (LCLASS(bsl) & 0xFF00) 7245916cd2Sjpk return (0); 7345916cd2Sjpk 7445916cd2Sjpk /* 7545916cd2Sjpk * Check whether compartments will fit in 30 octets. 7645916cd2Sjpk * Compartments 241 - 256 are not allowed. 7745916cd2Sjpk */ 7845916cd2Sjpk if (ntohl(bsl->compartments.c8) & 0x0000FFFF) 7945916cd2Sjpk return (0); 8045916cd2Sjpk 8145916cd2Sjpk /* 8245916cd2Sjpk * Compute option length and tag length. 8345916cd2Sjpk * 'p' points to the last two bytes in the Sensitivity Label's 8445916cd2Sjpk * compartments; these cannot be mapped into CIPSO compartments. 8545916cd2Sjpk */ 8645916cd2Sjpk ucp = (const uchar_t *)&bsl->compartments.c8 + 2; 8745916cd2Sjpk while (--ucp >= (const uchar_t *)&bsl->compartments.c1) 8845916cd2Sjpk if (*ucp != 0) 8945916cd2Sjpk break; 9045916cd2Sjpk 9145916cd2Sjpk i = ucp - (const uchar_t *)&bsl->compartments.c1 + 1; 9245916cd2Sjpk 9345916cd2Sjpk if (cop == NULL) 9445916cd2Sjpk return (10 + i); 9545916cd2Sjpk 9645916cd2Sjpk doi = htonl(doi); 9745916cd2Sjpk ucp = (const uchar_t *)&doi; 9845916cd2Sjpk cop[IPOPT_OPTVAL] = IPOPT_COMSEC; 9945916cd2Sjpk cop[IPOPT_OLEN] = 10 + i; 10045916cd2Sjpk cop[IPOPT_OLEN+1] = ucp[0]; 10145916cd2Sjpk cop[IPOPT_OLEN+2] = ucp[1]; 10245916cd2Sjpk cop[IPOPT_OLEN+3] = ucp[2]; 10345916cd2Sjpk cop[IPOPT_OLEN+4] = ucp[3]; 10445916cd2Sjpk tt1 = (struct cipso_tag_type_1 *)&cop[IPOPT_OLEN + 5]; 10545916cd2Sjpk tt1->tag_type = 1; 10645916cd2Sjpk tt1->tag_align = 0; 10745916cd2Sjpk tt1->tag_sl = LCLASS(bsl); 10845916cd2Sjpk tt1->tag_length = 4 + i; 10945916cd2Sjpk 11045916cd2Sjpk bcopy(&bsl->compartments.c1, tt1->tag_cat, i); 11145916cd2Sjpk 11245916cd2Sjpk return (cop[IPOPT_OLEN]); 11345916cd2Sjpk } 11445916cd2Sjpk 11545916cd2Sjpk /* 116c4e55c13Sken Powell - Sun Microsystem * The following routine searches for a security label in an IPv4 datagram. 117c4e55c13Sken Powell - Sun Microsystem * It returns label_type of: 118c4e55c13Sken Powell - Sun Microsystem * OPT_CIPSO if a CIPSO IP option is found. 119c4e55c13Sken Powell - Sun Microsystem * OPT_NONE if no security label is found. 12045916cd2Sjpk * 121c4e55c13Sken Powell - Sun Microsystem * If OPT_CIPSO, a pointer to the CIPSO IP option will be returned in 122c4e55c13Sken Powell - Sun Microsystem * the buffer parameter. 123c4e55c13Sken Powell - Sun Microsystem * 124c4e55c13Sken Powell - Sun Microsystem * The function will return with B_FALSE if an IP format error 125c4e55c13Sken Powell - Sun Microsystem * is encountered. 12645916cd2Sjpk */ 12745916cd2Sjpk 128c4e55c13Sken Powell - Sun Microsystem boolean_t 129c4e55c13Sken Powell - Sun Microsystem tsol_get_option_v4(mblk_t *mp, tsol_ip_label_t *label_type, uchar_t **buffer) 13045916cd2Sjpk { 13145916cd2Sjpk ipha_t *ipha; 13245916cd2Sjpk uchar_t *opt; 13345916cd2Sjpk uint32_t totallen; 13445916cd2Sjpk uint32_t optval; 13545916cd2Sjpk uint32_t optlen; 13645916cd2Sjpk 137c4e55c13Sken Powell - Sun Microsystem *label_type = OPT_NONE; 13845916cd2Sjpk 13945916cd2Sjpk /* 14045916cd2Sjpk * Get length (in 4 byte octets) of IP header options. 141c4e55c13Sken Powell - Sun Microsystem * If header doesn't contain options, then return a label_type 142c4e55c13Sken Powell - Sun Microsystem * of OPT_NONE. 14345916cd2Sjpk */ 144c4e55c13Sken Powell - Sun Microsystem ipha = (ipha_t *)mp->b_rptr; 14545916cd2Sjpk totallen = ipha->ipha_version_and_hdr_length - 146c4e55c13Sken Powell - Sun Microsystem (uint8_t)((IP_VERSION << 4)); 14745916cd2Sjpk totallen <<= 2; 148c4e55c13Sken Powell - Sun Microsystem if (totallen < IP_SIMPLE_HDR_LENGTH || totallen > MBLKL(mp)) 149c4e55c13Sken Powell - Sun Microsystem return (B_FALSE); 150c4e55c13Sken Powell - Sun Microsystem totallen -= IP_SIMPLE_HDR_LENGTH; 151c4e55c13Sken Powell - Sun Microsystem if (totallen == 0) 152c4e55c13Sken Powell - Sun Microsystem return (B_TRUE); 15345916cd2Sjpk 15445916cd2Sjpk /* 15545916cd2Sjpk * Search for CIPSO option. 15645916cd2Sjpk * If no such option is present, then return OPT_NONE. 15745916cd2Sjpk */ 15845916cd2Sjpk opt = (uchar_t *)&ipha[1]; 15945916cd2Sjpk while (totallen != 0) { 16045916cd2Sjpk switch (optval = opt[IPOPT_OPTVAL]) { 16145916cd2Sjpk case IPOPT_EOL: 162c4e55c13Sken Powell - Sun Microsystem return (B_TRUE); 16345916cd2Sjpk case IPOPT_NOP: 16445916cd2Sjpk optlen = 1; 16545916cd2Sjpk break; 16645916cd2Sjpk default: 16745916cd2Sjpk if (totallen <= IPOPT_OLEN) 168c4e55c13Sken Powell - Sun Microsystem return (B_FALSE); 16945916cd2Sjpk optlen = opt[IPOPT_OLEN]; 17045916cd2Sjpk if (optlen < 2) 171c4e55c13Sken Powell - Sun Microsystem return (B_FALSE); 17245916cd2Sjpk } 17345916cd2Sjpk if (optlen > totallen) 174c4e55c13Sken Powell - Sun Microsystem return (B_FALSE); 17545916cd2Sjpk /* 17645916cd2Sjpk * Copy pointer to option into '*buffer' and 17745916cd2Sjpk * return the option type. 17845916cd2Sjpk */ 17945916cd2Sjpk switch (optval) { 18045916cd2Sjpk case IPOPT_COMSEC: 18145916cd2Sjpk if (TSOL_CIPSO_TAG_OFFSET < optlen && 182c4e55c13Sken Powell - Sun Microsystem opt[TSOL_CIPSO_TAG_OFFSET] == 1) { 183c4e55c13Sken Powell - Sun Microsystem *label_type = OPT_CIPSO; 184c4e55c13Sken Powell - Sun Microsystem *buffer = opt; 185c4e55c13Sken Powell - Sun Microsystem return (B_TRUE); 186c4e55c13Sken Powell - Sun Microsystem } 187c4e55c13Sken Powell - Sun Microsystem return (B_FALSE); 18845916cd2Sjpk } 18945916cd2Sjpk totallen -= optlen; 19045916cd2Sjpk opt += optlen; 19145916cd2Sjpk } 192c4e55c13Sken Powell - Sun Microsystem return (B_TRUE); 193c4e55c13Sken Powell - Sun Microsystem } 194c4e55c13Sken Powell - Sun Microsystem 195c4e55c13Sken Powell - Sun Microsystem /* 196c4e55c13Sken Powell - Sun Microsystem * The following routine searches for a security label in an IPv6 datagram. 197c4e55c13Sken Powell - Sun Microsystem * It returns label_type of: 198c4e55c13Sken Powell - Sun Microsystem * OPT_CIPSO if a CIPSO IP option is found. 199c4e55c13Sken Powell - Sun Microsystem * OPT_NONE if no security label is found. 200c4e55c13Sken Powell - Sun Microsystem * 201c4e55c13Sken Powell - Sun Microsystem * If OPT_CIPSO, a pointer to the IPv4 portion of the CIPSO IP option will 202c4e55c13Sken Powell - Sun Microsystem * be returned in the buffer parameter. 203c4e55c13Sken Powell - Sun Microsystem * 204c4e55c13Sken Powell - Sun Microsystem * The function will return with B_FALSE if an IP format error 205c4e55c13Sken Powell - Sun Microsystem * or an unexpected label content error is encountered. 206c4e55c13Sken Powell - Sun Microsystem */ 207c4e55c13Sken Powell - Sun Microsystem 208c4e55c13Sken Powell - Sun Microsystem boolean_t 209c4e55c13Sken Powell - Sun Microsystem tsol_get_option_v6(mblk_t *mp, tsol_ip_label_t *label_type, uchar_t **buffer) 210c4e55c13Sken Powell - Sun Microsystem { 211c4e55c13Sken Powell - Sun Microsystem uchar_t *opt_ptr = NULL; 212c4e55c13Sken Powell - Sun Microsystem uchar_t *after_secopt; 213c4e55c13Sken Powell - Sun Microsystem boolean_t hbh_needed; 214c4e55c13Sken Powell - Sun Microsystem const uchar_t *ip6hbh; 215c4e55c13Sken Powell - Sun Microsystem size_t optlen; 216c4e55c13Sken Powell - Sun Microsystem uint32_t doi; 217c4e55c13Sken Powell - Sun Microsystem const ip6_t *ip6h; 218c4e55c13Sken Powell - Sun Microsystem 219c4e55c13Sken Powell - Sun Microsystem *label_type = OPT_NONE; 220c4e55c13Sken Powell - Sun Microsystem *buffer = NULL; 221c4e55c13Sken Powell - Sun Microsystem ip6h = (const ip6_t *)mp->b_rptr; 222c4e55c13Sken Powell - Sun Microsystem if (ip6h->ip6_nxt != IPPROTO_HOPOPTS) 223c4e55c13Sken Powell - Sun Microsystem return (B_TRUE); 224c4e55c13Sken Powell - Sun Microsystem ip6hbh = (const uchar_t *)&ip6h[1]; 225c4e55c13Sken Powell - Sun Microsystem if (ip6hbh + MIN_EHDR_LEN > mp->b_wptr) 226c4e55c13Sken Powell - Sun Microsystem return (B_FALSE); 227c4e55c13Sken Powell - Sun Microsystem optlen = (ip6hbh[1] + 1) << 3; 228c4e55c13Sken Powell - Sun Microsystem if (ip6hbh + optlen > mp->b_wptr) 229c4e55c13Sken Powell - Sun Microsystem return (B_FALSE); 230c4e55c13Sken Powell - Sun Microsystem if (!tsol_find_secopt_v6(ip6hbh, optlen, 231c4e55c13Sken Powell - Sun Microsystem &opt_ptr, &after_secopt, &hbh_needed)) 232c4e55c13Sken Powell - Sun Microsystem return (B_FALSE); 233c4e55c13Sken Powell - Sun Microsystem /* tsol_find_secopt_v6 guarantees some sanity */ 234c4e55c13Sken Powell - Sun Microsystem if (opt_ptr != NULL) { 235c4e55c13Sken Powell - Sun Microsystem /* 236c4e55c13Sken Powell - Sun Microsystem * IPv6 Option 237c4e55c13Sken Powell - Sun Microsystem * opt_ptr[0]: Option type 238c4e55c13Sken Powell - Sun Microsystem * opt_ptr[1]: Length of option data in bytes 239c4e55c13Sken Powell - Sun Microsystem * opt_ptr[2]: First byte of option data 240c4e55c13Sken Powell - Sun Microsystem */ 241c4e55c13Sken Powell - Sun Microsystem if ((optlen = opt_ptr[1]) < 8) 242c4e55c13Sken Powell - Sun Microsystem return (B_FALSE); 243c4e55c13Sken Powell - Sun Microsystem opt_ptr += 2; 244c4e55c13Sken Powell - Sun Microsystem /* 245c4e55c13Sken Powell - Sun Microsystem * From "Generalized Labeled Security Option for IPv6" draft 246c4e55c13Sken Powell - Sun Microsystem * opt_ptr[0] - opt_ptr[4]: DOI = IP6LS_DOI_V4 247c4e55c13Sken Powell - Sun Microsystem * opt_ptr[4]: Tag type = IP6LS_TT_V4 248c4e55c13Sken Powell - Sun Microsystem * opt_ptr[5]: Tag length in bytes starting at Tag type field 249c4e55c13Sken Powell - Sun Microsystem * IPv4 CIPSO Option 250c4e55c13Sken Powell - Sun Microsystem * opt_ptr[6]: option type 251c4e55c13Sken Powell - Sun Microsystem * opt_ptr[7]: option length in bytes starting at type field 252c4e55c13Sken Powell - Sun Microsystem */ 253c4e55c13Sken Powell - Sun Microsystem bcopy(opt_ptr, &doi, sizeof (doi)); 254c4e55c13Sken Powell - Sun Microsystem doi = ntohl(doi); 255c4e55c13Sken Powell - Sun Microsystem if (doi == IP6LS_DOI_V4 && 256c4e55c13Sken Powell - Sun Microsystem opt_ptr[4] == IP6LS_TT_V4 && 257c4e55c13Sken Powell - Sun Microsystem opt_ptr[5] <= optlen - 4 && 258c4e55c13Sken Powell - Sun Microsystem opt_ptr[7] <= optlen - 6 && 259c4e55c13Sken Powell - Sun Microsystem opt_ptr[7] <= opt_ptr[5] - 2) { 260c4e55c13Sken Powell - Sun Microsystem opt_ptr += sizeof (doi) + 2; 261c4e55c13Sken Powell - Sun Microsystem *label_type = OPT_CIPSO; 262c4e55c13Sken Powell - Sun Microsystem *buffer = opt_ptr; 263c4e55c13Sken Powell - Sun Microsystem return (B_TRUE); 264c4e55c13Sken Powell - Sun Microsystem } 265c4e55c13Sken Powell - Sun Microsystem return (B_FALSE); 266c4e55c13Sken Powell - Sun Microsystem } 267c4e55c13Sken Powell - Sun Microsystem return (B_TRUE); 26845916cd2Sjpk } 26945916cd2Sjpk 27045916cd2Sjpk /* 2715f9878b0Sken Powell - Sun Microsystem * tsol_check_dest() 2725f9878b0Sken Powell - Sun Microsystem * 2735f9878b0Sken Powell - Sun Microsystem * This routine verifies if a destination is allowed to recieve messages 274bd670b35SErik Nordmark * based on the security label. If any adjustments to the label are needed 275bd670b35SErik Nordmark * due to the connection's MAC mode or the destination's ability 276bd670b35SErik Nordmark * to receive labels, an "effective label" will be returned. 2775f9878b0Sken Powell - Sun Microsystem * 278bd670b35SErik Nordmark * zone_is_global is set if the actual zoneid is global. That is, it is 279bd670b35SErik Nordmark * not set for an exclusive-IP zone. 280bd670b35SErik Nordmark * 281bd670b35SErik Nordmark * On successful return, effective_tsl will point to the new label needed 282bd670b35SErik Nordmark * or will be NULL if a new label isn't needed. On error, effective_tsl will 283bd670b35SErik Nordmark * point to NULL. 2845f9878b0Sken Powell - Sun Microsystem * 2855f9878b0Sken Powell - Sun Microsystem * Returns: 286bd670b35SErik Nordmark * 0 Label (was|is now) correct 287bd670b35SErik Nordmark * EHOSTUNREACH The label failed the remote host accreditation 2885f9878b0Sken Powell - Sun Microsystem * ENOMEM Memory allocation failure 2895f9878b0Sken Powell - Sun Microsystem */ 2905f9878b0Sken Powell - Sun Microsystem int 291bd670b35SErik Nordmark tsol_check_dest(const ts_label_t *tsl, const void *dst, 292bd670b35SErik Nordmark uchar_t version, uint_t mac_mode, boolean_t zone_is_global, 293bd670b35SErik Nordmark ts_label_t **effective_tsl) 2945f9878b0Sken Powell - Sun Microsystem { 295bd670b35SErik Nordmark ts_label_t *newtsl = NULL; 2965f9878b0Sken Powell - Sun Microsystem tsol_tpc_t *dst_rhtp; 2975f9878b0Sken Powell - Sun Microsystem 298bd670b35SErik Nordmark if (effective_tsl != NULL) 299bd670b35SErik Nordmark *effective_tsl = NULL; 3005f9878b0Sken Powell - Sun Microsystem ASSERT(version == IPV4_VERSION || 3015f9878b0Sken Powell - Sun Microsystem (version == IPV6_VERSION && 3025f9878b0Sken Powell - Sun Microsystem !IN6_IS_ADDR_V4MAPPED((in6_addr_t *)dst))); 3035f9878b0Sken Powell - Sun Microsystem 3045f9878b0Sken Powell - Sun Microsystem /* Always pass kernel level communication (NULL label) */ 305bd670b35SErik Nordmark if (tsl == NULL) { 3065f9878b0Sken Powell - Sun Microsystem DTRACE_PROBE2(tx__tnopt__log__info__labeling__mac__allownull, 307bd670b35SErik Nordmark char *, "destination ip(1) with null label was passed", 3085f9878b0Sken Powell - Sun Microsystem ipaddr_t, dst); 3095f9878b0Sken Powell - Sun Microsystem return (0); 3105f9878b0Sken Powell - Sun Microsystem } 3115f9878b0Sken Powell - Sun Microsystem 3125d3b8cb7SBill Sommerfeld if (tsl->tsl_flags & TSLF_IMPLICIT_IN) { 3135d3b8cb7SBill Sommerfeld DTRACE_PROBE3(tx__tnopt__log__info__labeling__unresolved__label, 3145d3b8cb7SBill Sommerfeld char *, 3155d3b8cb7SBill Sommerfeld "implicit-in packet to ip(1) reached tsol_check_dest " 3165d3b8cb7SBill Sommerfeld "with implied security label sl(2)", 3175d3b8cb7SBill Sommerfeld ipaddr_t, dst, ts_label_t *, tsl); 3185d3b8cb7SBill Sommerfeld } 3195d3b8cb7SBill Sommerfeld 3205f9878b0Sken Powell - Sun Microsystem /* Always pass multicast */ 3215f9878b0Sken Powell - Sun Microsystem if (version == IPV4_VERSION && 3225f9878b0Sken Powell - Sun Microsystem CLASSD(*(ipaddr_t *)dst)) { 3235f9878b0Sken Powell - Sun Microsystem DTRACE_PROBE2(tx__tnopt__log__info__labeling__mac__allowmult, 3245f9878b0Sken Powell - Sun Microsystem char *, "destination ip(1) with multicast dest was passed", 3255f9878b0Sken Powell - Sun Microsystem ipaddr_t, dst); 3265f9878b0Sken Powell - Sun Microsystem return (0); 3275f9878b0Sken Powell - Sun Microsystem } else if (version == IPV6_VERSION && 3285f9878b0Sken Powell - Sun Microsystem IN6_IS_ADDR_MULTICAST((in6_addr_t *)dst)) { 3295f9878b0Sken Powell - Sun Microsystem DTRACE_PROBE2(tx__tnopt__log__info__labeling__mac__allowmult_v6, 3305f9878b0Sken Powell - Sun Microsystem char *, "destination ip(1) with multicast dest was passed", 3315f9878b0Sken Powell - Sun Microsystem in6_addr_t *, dst); 3325f9878b0Sken Powell - Sun Microsystem return (0); 3335f9878b0Sken Powell - Sun Microsystem } 3345f9878b0Sken Powell - Sun Microsystem 3355f9878b0Sken Powell - Sun Microsystem /* Never pass an undefined destination */ 3365f9878b0Sken Powell - Sun Microsystem if ((dst_rhtp = find_tpc(dst, version, B_FALSE)) == NULL) { 3375f9878b0Sken Powell - Sun Microsystem DTRACE_PROBE2(tx__tnopt__log__info__labeling__lookupdst, 3385f9878b0Sken Powell - Sun Microsystem char *, "destination ip(1) not in tn database.", 3395f9878b0Sken Powell - Sun Microsystem void *, dst); 3405f9878b0Sken Powell - Sun Microsystem return (EHOSTUNREACH); 3415f9878b0Sken Powell - Sun Microsystem } 3425f9878b0Sken Powell - Sun Microsystem 3435f9878b0Sken Powell - Sun Microsystem switch (dst_rhtp->tpc_tp.host_type) { 3445f9878b0Sken Powell - Sun Microsystem case UNLABELED: 3455f9878b0Sken Powell - Sun Microsystem /* 3465f9878b0Sken Powell - Sun Microsystem * Can talk to unlabeled hosts if 3475f9878b0Sken Powell - Sun Microsystem * (1) zone's label matches the default label, or 3485d3b8cb7SBill Sommerfeld * (2) SO_MAC_EXEMPT is on and we 3495d3b8cb7SBill Sommerfeld * dominate the peer's label, or 3505d3b8cb7SBill Sommerfeld * (3) SO_MAC_EXEMPT is on and 3515d3b8cb7SBill Sommerfeld * this is the global zone 3525f9878b0Sken Powell - Sun Microsystem */ 3535f9878b0Sken Powell - Sun Microsystem if (dst_rhtp->tpc_tp.tp_doi != tsl->tsl_doi) { 3545f9878b0Sken Powell - Sun Microsystem DTRACE_PROBE4(tx__tnopt__log__info__labeling__doi, 3555f9878b0Sken Powell - Sun Microsystem char *, "unlabeled dest ip(1)/tpc(2) doi does " 3565f9878b0Sken Powell - Sun Microsystem "not match msg label(3) doi.", void *, dst, 3575f9878b0Sken Powell - Sun Microsystem tsol_tpc_t *, dst_rhtp, ts_label_t *, tsl); 3585f9878b0Sken Powell - Sun Microsystem TPC_RELE(dst_rhtp); 3595f9878b0Sken Powell - Sun Microsystem return (EHOSTUNREACH); 3605f9878b0Sken Powell - Sun Microsystem } 3615f9878b0Sken Powell - Sun Microsystem if (!blequal(&dst_rhtp->tpc_tp.tp_def_label, 3625f9878b0Sken Powell - Sun Microsystem &tsl->tsl_label)) { 3635d3b8cb7SBill Sommerfeld if (mac_mode != CONN_MAC_AWARE || 364bd670b35SErik Nordmark !(zone_is_global || 3655f9878b0Sken Powell - Sun Microsystem bldominates(&tsl->tsl_label, 3665f9878b0Sken Powell - Sun Microsystem &dst_rhtp->tpc_tp.tp_def_label))) { 3675f9878b0Sken Powell - Sun Microsystem DTRACE_PROBE4( 3685f9878b0Sken Powell - Sun Microsystem tx__tnopt__log__info__labeling__mac, 3695f9878b0Sken Powell - Sun Microsystem char *, "unlabeled dest ip(1)/tpc(2) does " 3705f9878b0Sken Powell - Sun Microsystem "not match msg label(3).", void *, dst, 3715f9878b0Sken Powell - Sun Microsystem tsol_tpc_t *, dst_rhtp, ts_label_t *, tsl); 3725f9878b0Sken Powell - Sun Microsystem TPC_RELE(dst_rhtp); 3735f9878b0Sken Powell - Sun Microsystem return (EHOSTUNREACH); 3745f9878b0Sken Powell - Sun Microsystem } 3755f9878b0Sken Powell - Sun Microsystem /* 3765f9878b0Sken Powell - Sun Microsystem * This is a downlabel MAC-exempt exchange. 3775f9878b0Sken Powell - Sun Microsystem * Use the remote destination's default label 3785f9878b0Sken Powell - Sun Microsystem * as the label of the message data. 3795f9878b0Sken Powell - Sun Microsystem */ 3805f9878b0Sken Powell - Sun Microsystem if ((newtsl = labelalloc(&dst_rhtp->tpc_tp.tp_def_label, 3815f9878b0Sken Powell - Sun Microsystem dst_rhtp->tpc_tp.tp_doi, KM_NOSLEEP)) == NULL) { 3825f9878b0Sken Powell - Sun Microsystem TPC_RELE(dst_rhtp); 3835f9878b0Sken Powell - Sun Microsystem return (ENOMEM); 3845f9878b0Sken Powell - Sun Microsystem } 3855f9878b0Sken Powell - Sun Microsystem newtsl->tsl_flags |= TSLF_UNLABELED; 3865f9878b0Sken Powell - Sun Microsystem 3875f9878b0Sken Powell - Sun Microsystem } else if (!(tsl->tsl_flags & TSLF_UNLABELED)) { 3885f9878b0Sken Powell - Sun Microsystem /* 3895f9878b0Sken Powell - Sun Microsystem * The security labels are the same but we need 3905f9878b0Sken Powell - Sun Microsystem * to flag that the remote node is unlabeled. 3915f9878b0Sken Powell - Sun Microsystem */ 3925f9878b0Sken Powell - Sun Microsystem if ((newtsl = labeldup(tsl, KM_NOSLEEP)) == NULL) { 3935f9878b0Sken Powell - Sun Microsystem TPC_RELE(dst_rhtp); 3945f9878b0Sken Powell - Sun Microsystem return (ENOMEM); 3955f9878b0Sken Powell - Sun Microsystem } 3965f9878b0Sken Powell - Sun Microsystem newtsl->tsl_flags |= TSLF_UNLABELED; 3975f9878b0Sken Powell - Sun Microsystem } 3985f9878b0Sken Powell - Sun Microsystem break; 3995f9878b0Sken Powell - Sun Microsystem 4005f9878b0Sken Powell - Sun Microsystem case SUN_CIPSO: 4015f9878b0Sken Powell - Sun Microsystem /* 4025f9878b0Sken Powell - Sun Microsystem * Can talk to labeled hosts if zone's label is within target's 4035f9878b0Sken Powell - Sun Microsystem * label range or set. 4045f9878b0Sken Powell - Sun Microsystem */ 4055f9878b0Sken Powell - Sun Microsystem if (dst_rhtp->tpc_tp.tp_cipso_doi_cipso != tsl->tsl_doi || 4065f9878b0Sken Powell - Sun Microsystem (!_blinrange(&tsl->tsl_label, 4075f9878b0Sken Powell - Sun Microsystem &dst_rhtp->tpc_tp.tp_sl_range_cipso) && 4085f9878b0Sken Powell - Sun Microsystem !blinlset(&tsl->tsl_label, 4095f9878b0Sken Powell - Sun Microsystem dst_rhtp->tpc_tp.tp_sl_set_cipso))) { 4105f9878b0Sken Powell - Sun Microsystem DTRACE_PROBE4(tx__tnopt__log__info__labeling__mac, 4115f9878b0Sken Powell - Sun Microsystem char *, "labeled dest ip(1)/tpc(2) does not " 4125f9878b0Sken Powell - Sun Microsystem "match msg label(3).", void *, dst, 4135f9878b0Sken Powell - Sun Microsystem tsol_tpc_t *, dst_rhtp, ts_label_t *, tsl); 4145f9878b0Sken Powell - Sun Microsystem TPC_RELE(dst_rhtp); 4155f9878b0Sken Powell - Sun Microsystem return (EHOSTUNREACH); 4165f9878b0Sken Powell - Sun Microsystem } 4175d3b8cb7SBill Sommerfeld if ((tsl->tsl_flags & TSLF_UNLABELED) || 4185d3b8cb7SBill Sommerfeld (mac_mode == CONN_MAC_IMPLICIT)) { 4195f9878b0Sken Powell - Sun Microsystem /* 4205d3b8cb7SBill Sommerfeld * Copy label so we can modify the flags 4215f9878b0Sken Powell - Sun Microsystem */ 4225f9878b0Sken Powell - Sun Microsystem if ((newtsl = labeldup(tsl, KM_NOSLEEP)) == NULL) { 4235f9878b0Sken Powell - Sun Microsystem TPC_RELE(dst_rhtp); 4245f9878b0Sken Powell - Sun Microsystem return (ENOMEM); 4255f9878b0Sken Powell - Sun Microsystem } 4265d3b8cb7SBill Sommerfeld /* 4275d3b8cb7SBill Sommerfeld * The security label is a match but we need to 4285d3b8cb7SBill Sommerfeld * clear the unlabeled flag for this remote node. 4295d3b8cb7SBill Sommerfeld */ 4305d3b8cb7SBill Sommerfeld newtsl->tsl_flags &= ~TSLF_UNLABELED; 4315d3b8cb7SBill Sommerfeld if (mac_mode == CONN_MAC_IMPLICIT) 4325d3b8cb7SBill Sommerfeld newtsl->tsl_flags |= TSLF_IMPLICIT_OUT; 4335f9878b0Sken Powell - Sun Microsystem } 4345f9878b0Sken Powell - Sun Microsystem break; 4355f9878b0Sken Powell - Sun Microsystem 4365f9878b0Sken Powell - Sun Microsystem default: 4375f9878b0Sken Powell - Sun Microsystem TPC_RELE(dst_rhtp); 4385f9878b0Sken Powell - Sun Microsystem return (EHOSTUNREACH); 4395f9878b0Sken Powell - Sun Microsystem } 4405f9878b0Sken Powell - Sun Microsystem 4415f9878b0Sken Powell - Sun Microsystem /* 442bd670b35SErik Nordmark * Return the new label. 4435f9878b0Sken Powell - Sun Microsystem */ 4445f9878b0Sken Powell - Sun Microsystem if (newtsl != NULL) { 445bd670b35SErik Nordmark if (effective_tsl != NULL) 446bd670b35SErik Nordmark *effective_tsl = newtsl; 447bd670b35SErik Nordmark else 4485f9878b0Sken Powell - Sun Microsystem label_rele(newtsl); 4495f9878b0Sken Powell - Sun Microsystem } 4505f9878b0Sken Powell - Sun Microsystem TPC_RELE(dst_rhtp); 4515f9878b0Sken Powell - Sun Microsystem return (0); 4525f9878b0Sken Powell - Sun Microsystem } 4535f9878b0Sken Powell - Sun Microsystem 4545f9878b0Sken Powell - Sun Microsystem /* 455bd670b35SErik Nordmark * tsol_compute_label_v4() 45645916cd2Sjpk * 45745916cd2Sjpk * This routine computes the IP label that should be on a packet based on the 45845916cd2Sjpk * connection and destination information. 45945916cd2Sjpk * 460bd670b35SErik Nordmark * The zoneid is the IP zoneid (i.e., GLOBAL_ZONEID for exlusive-IP zones). 461bd670b35SErik Nordmark * 46245916cd2Sjpk * Returns: 46345916cd2Sjpk * 0 Fetched label 4645f9878b0Sken Powell - Sun Microsystem * EHOSTUNREACH No route to destination 46545916cd2Sjpk * EINVAL Label cannot be computed 46645916cd2Sjpk */ 46745916cd2Sjpk int 468bd670b35SErik Nordmark tsol_compute_label_v4(const ts_label_t *tsl, zoneid_t zoneid, ipaddr_t dst, 469bd670b35SErik Nordmark uchar_t *opt_storage, ip_stack_t *ipst) 47045916cd2Sjpk { 47145916cd2Sjpk uint_t sec_opt_len; 472bd670b35SErik Nordmark ire_t *ire; 473bd670b35SErik Nordmark tsol_ire_gw_secattr_t *attrp = NULL; 474de8c4a14SErik Nordmark 47545916cd2Sjpk if (opt_storage != NULL) 47645916cd2Sjpk opt_storage[IPOPT_OLEN] = 0; 47745916cd2Sjpk 478bd670b35SErik Nordmark if (tsl == NULL) 47945916cd2Sjpk return (0); 48045916cd2Sjpk 48145916cd2Sjpk /* always pass multicast */ 48245916cd2Sjpk if (CLASSD(dst)) 48345916cd2Sjpk return (0); 48445916cd2Sjpk 4855d3b8cb7SBill Sommerfeld if (tsl->tsl_flags & TSLF_IMPLICIT_OUT) 4865d3b8cb7SBill Sommerfeld return (0); 4875d3b8cb7SBill Sommerfeld 4885f9878b0Sken Powell - Sun Microsystem if (tsl->tsl_flags & TSLF_UNLABELED) { 489f4b3ec61Sdh155122 /* 4905f9878b0Sken Powell - Sun Microsystem * The destination is unlabeled. Only add a label if the 4915f9878b0Sken Powell - Sun Microsystem * destination is not a broadcast/local/loopback address, 4925f9878b0Sken Powell - Sun Microsystem * the destination is not on the same subnet, and the 4935f9878b0Sken Powell - Sun Microsystem * next-hop gateway is labeled. 494f4b3ec61Sdh155122 */ 495bd670b35SErik Nordmark ire = ire_route_recursive_v4(dst, 0, NULL, zoneid, tsl, 496*9e3469d3SErik Nordmark MATCH_IRE_SECATTR, IRR_ALLOCATE, 0, ipst, NULL, &attrp, 497*9e3469d3SErik Nordmark NULL); 498bd670b35SErik Nordmark ASSERT(ire != NULL); 499bd670b35SErik Nordmark if (ire->ire_flags & (RTF_REJECT|RTF_BLACKHOLE)) { 50045916cd2Sjpk /* no route to destination */ 501bd670b35SErik Nordmark ire_refrele(ire); 5025f9878b0Sken Powell - Sun Microsystem DTRACE_PROBE3( 50345916cd2Sjpk tx__tnopt__log__info__labeling__routedst__v4, 5045f9878b0Sken Powell - Sun Microsystem char *, "No route to unlabeled dest ip(1) with " 505bd670b35SErik Nordmark "with label(2).", ipaddr_t, dst, ts_label_t *, tsl); 5065f9878b0Sken Powell - Sun Microsystem return (EHOSTUNREACH); 50745916cd2Sjpk } 508bd670b35SErik Nordmark if (ire->ire_type & (IRE_BROADCAST | IRE_LOCAL | IRE_LOOPBACK | 509bd670b35SErik Nordmark IRE_INTERFACE)) { 510bd670b35SErik Nordmark ire_refrele(ire); 511bd670b35SErik Nordmark return (0); 512bd670b35SErik Nordmark } 51345916cd2Sjpk 51445916cd2Sjpk /* 515bd670b35SErik Nordmark * ire_route_recursive gives us the first attrp it finds 516bd670b35SErik Nordmark * in the recursive lookup. 51745916cd2Sjpk */ 5185f9878b0Sken Powell - Sun Microsystem /* 5195f9878b0Sken Powell - Sun Microsystem * Return now if next hop gateway is unlabeled. There is 5205f9878b0Sken Powell - Sun Microsystem * no need to generate a CIPSO option for this message. 5215f9878b0Sken Powell - Sun Microsystem */ 5225f9878b0Sken Powell - Sun Microsystem if (attrp == NULL || attrp->igsa_rhc == NULL || 5235f9878b0Sken Powell - Sun Microsystem attrp->igsa_rhc->rhc_tpc->tpc_tp.host_type == UNLABELED) { 524bd670b35SErik Nordmark ire_refrele(ire); 52545916cd2Sjpk return (0); 52645916cd2Sjpk } 527bd670b35SErik Nordmark ire_refrele(ire); 5285f9878b0Sken Powell - Sun Microsystem } 5295f9878b0Sken Powell - Sun Microsystem 53045916cd2Sjpk /* compute the CIPSO option */ 53145916cd2Sjpk sec_opt_len = tsol2cipso_tt1(&tsl->tsl_label, opt_storage, 53245916cd2Sjpk tsl->tsl_doi); 53345916cd2Sjpk 53445916cd2Sjpk if (sec_opt_len == 0) { 5355f9878b0Sken Powell - Sun Microsystem DTRACE_PROBE3(tx__tnopt__log__error__labeling__lostops__v4, 536bd670b35SErik Nordmark char *, "options lack length for dest ip(1) with label(2).", 537bd670b35SErik Nordmark ipaddr_t, dst, ts_label_t *, tsl); 53845916cd2Sjpk return (EINVAL); 53945916cd2Sjpk } 54045916cd2Sjpk 54145916cd2Sjpk return (0); 54245916cd2Sjpk } 54345916cd2Sjpk 54445916cd2Sjpk /* 54545916cd2Sjpk * Remove any existing security option (CIPSO) from the given IP 54645916cd2Sjpk * header, move the 'buflen' bytes back to fill the gap, and return the number 54745916cd2Sjpk * of bytes removed (as zero or negative number). Assumes that the headers are 54845916cd2Sjpk * sane. 549bd670b35SErik Nordmark * 550bd670b35SErik Nordmark * Note that tsol_remove_secopt does not adjust ipha_length but 551bd670b35SErik Nordmark * tsol_remove_secopt_v6 does adjust ip6_plen. 55245916cd2Sjpk */ 55345916cd2Sjpk int 55445916cd2Sjpk tsol_remove_secopt(ipha_t *ipha, int buflen) 55545916cd2Sjpk { 55645916cd2Sjpk int remlen, olen, oval, delta; 55745916cd2Sjpk uchar_t *fptr, *tptr; 55845916cd2Sjpk boolean_t noop_keep; 55945916cd2Sjpk 56045916cd2Sjpk remlen = IPH_HDR_LENGTH(ipha) - IP_SIMPLE_HDR_LENGTH; 56145916cd2Sjpk fptr = tptr = (uchar_t *)(ipha + 1); 56245916cd2Sjpk noop_keep = B_TRUE; 56345916cd2Sjpk while (remlen > 0) { 56445916cd2Sjpk oval = fptr[IPOPT_OPTVAL]; 56545916cd2Sjpk 56645916cd2Sjpk /* terminate on end of list */ 56745916cd2Sjpk if (oval == IPOPT_EOL) 56845916cd2Sjpk break; 56945916cd2Sjpk 57045916cd2Sjpk /* 57145916cd2Sjpk * Delete any no-ops following a deleted option, at least up 57245916cd2Sjpk * to a 4 octet alignment; copy others. 57345916cd2Sjpk */ 57445916cd2Sjpk if (oval == IPOPT_NOP) { 57545916cd2Sjpk if (((fptr - (uchar_t *)ipha) & 3) == 0) 57645916cd2Sjpk noop_keep = B_TRUE; 57745916cd2Sjpk if (noop_keep) 57845916cd2Sjpk *tptr++ = oval; 57945916cd2Sjpk fptr++; 58045916cd2Sjpk remlen--; 58145916cd2Sjpk continue; 58245916cd2Sjpk } 58345916cd2Sjpk 58445916cd2Sjpk /* stop on corrupted list; just do nothing. */ 58545916cd2Sjpk if (remlen < 2) 58645916cd2Sjpk return (0); 58745916cd2Sjpk olen = fptr[IPOPT_OLEN]; 58845916cd2Sjpk if (olen < 2 || olen > remlen) 58945916cd2Sjpk return (0); 59045916cd2Sjpk 59145916cd2Sjpk /* skip over security options to delete them */ 59245916cd2Sjpk if (oval == IPOPT_COMSEC || oval == IPOPT_SECURITY) { 59345916cd2Sjpk noop_keep = B_FALSE; 59445916cd2Sjpk fptr += olen; 59545916cd2Sjpk remlen -= olen; 59645916cd2Sjpk continue; 59745916cd2Sjpk } 59845916cd2Sjpk 59945916cd2Sjpk /* copy the rest */ 60045916cd2Sjpk noop_keep = B_TRUE; 60145916cd2Sjpk if (tptr != fptr) 60245916cd2Sjpk ovbcopy(fptr, tptr, olen); 60345916cd2Sjpk fptr += olen; 60445916cd2Sjpk tptr += olen; 60545916cd2Sjpk remlen -= olen; 60645916cd2Sjpk } 60745916cd2Sjpk 60845916cd2Sjpk fptr += remlen; 60945916cd2Sjpk 61045916cd2Sjpk /* figure how much padding we'll need for header alignment */ 61145916cd2Sjpk olen = (tptr - (uchar_t *)ipha) & 3; 61245916cd2Sjpk if (olen > 0) { 61345916cd2Sjpk olen = 4 - olen; 61445916cd2Sjpk /* pad with end-of-list */ 61545916cd2Sjpk bzero(tptr, olen); 61645916cd2Sjpk tptr += olen; 61745916cd2Sjpk } 61845916cd2Sjpk 61945916cd2Sjpk /* slide back the headers that follow and update the IP header */ 62045916cd2Sjpk delta = fptr - tptr; 62145916cd2Sjpk if (delta != 0) { 62245916cd2Sjpk ovbcopy(fptr, tptr, ((uchar_t *)ipha + buflen) - fptr); 62345916cd2Sjpk ipha->ipha_version_and_hdr_length -= delta / 4; 62445916cd2Sjpk } 62545916cd2Sjpk return (-delta); 62645916cd2Sjpk } 62745916cd2Sjpk 62845916cd2Sjpk /* 62945916cd2Sjpk * Insert the option in 'optbuf' into the IP header pointed to by 'ipha', and 63045916cd2Sjpk * move the data following the IP header (up to buflen) to accomodate the new 63145916cd2Sjpk * option. Assumes that up to IP_MAX_OPT_LENGTH bytes are available (in total) 63245916cd2Sjpk * for IP options. Returns the number of bytes actually inserted, or -1 if the 63345916cd2Sjpk * option cannot be inserted. (Note that negative return values are possible 63445916cd2Sjpk * when noops must be compressed, and that only -1 indicates error. Successful 63545916cd2Sjpk * return value is always evenly divisible by 4, by definition.) 636bd670b35SErik Nordmark * 637bd670b35SErik Nordmark * Note that tsol_prepend_option does not adjust ipha_length but 638bd670b35SErik Nordmark * tsol_prepend_option_v6 does adjust ip6_plen. 63945916cd2Sjpk */ 64045916cd2Sjpk int 64145916cd2Sjpk tsol_prepend_option(uchar_t *optbuf, ipha_t *ipha, int buflen) 64245916cd2Sjpk { 64345916cd2Sjpk int remlen, padding, lastpad, totlen; 64445916cd2Sjpk int oval, olen; 64545916cd2Sjpk int delta; 64645916cd2Sjpk uchar_t *optr; 64745916cd2Sjpk uchar_t tempopt[IP_MAX_OPT_LENGTH], *toptr; 64845916cd2Sjpk 64945916cd2Sjpk if (optbuf[IPOPT_OPTVAL] == IPOPT_EOL || 65045916cd2Sjpk optbuf[IPOPT_OPTVAL] == IPOPT_NOP || 65145916cd2Sjpk optbuf[IPOPT_OLEN] == 0) 65245916cd2Sjpk return (0); 65345916cd2Sjpk 65445916cd2Sjpk ASSERT(optbuf[IPOPT_OLEN] >= 2 && 65545916cd2Sjpk optbuf[IPOPT_OLEN] <= IP_MAX_OPT_LENGTH); 65645916cd2Sjpk 65745916cd2Sjpk /* first find the real (unpadded) length of the existing options */ 65845916cd2Sjpk remlen = IPH_HDR_LENGTH(ipha) - IP_SIMPLE_HDR_LENGTH; 65945916cd2Sjpk padding = totlen = lastpad = 0; 66045916cd2Sjpk optr = (uchar_t *)(ipha + 1); 66145916cd2Sjpk while (remlen > 0) { 66245916cd2Sjpk oval = optr[IPOPT_OPTVAL]; 66345916cd2Sjpk 66445916cd2Sjpk /* stop at end of list */ 66545916cd2Sjpk if (oval == IPOPT_EOL) 66645916cd2Sjpk break; 66745916cd2Sjpk 66845916cd2Sjpk /* skip no-ops, noting that length byte isn't present */ 66945916cd2Sjpk if (oval == IPOPT_NOP) { 67045916cd2Sjpk optr++; 67145916cd2Sjpk padding++; 67245916cd2Sjpk lastpad++; 67345916cd2Sjpk totlen++; 67445916cd2Sjpk remlen--; 67545916cd2Sjpk continue; 67645916cd2Sjpk } 67745916cd2Sjpk 67845916cd2Sjpk /* give up on a corrupted list; report failure */ 67945916cd2Sjpk if (remlen < 2) 68045916cd2Sjpk return (-1); 68145916cd2Sjpk olen = optr[IPOPT_OLEN]; 68245916cd2Sjpk if (olen < 2 || olen > remlen) 68345916cd2Sjpk return (-1); 68445916cd2Sjpk 68545916cd2Sjpk lastpad = 0; 68645916cd2Sjpk optr += olen; 68745916cd2Sjpk totlen += olen; 68845916cd2Sjpk remlen -= olen; 68945916cd2Sjpk } 69045916cd2Sjpk 69145916cd2Sjpk /* completely ignore any trailing padding */ 69245916cd2Sjpk totlen -= lastpad; 69345916cd2Sjpk padding -= lastpad; 69445916cd2Sjpk 69545916cd2Sjpk /* 69645916cd2Sjpk * If some sort of inter-option alignment was present, try to preserve 69745916cd2Sjpk * that alignment. If alignment pushes us out past the maximum, then 69845916cd2Sjpk * discard it and try to compress to fit. (We just "assume" that any 69945916cd2Sjpk * padding added was attempting to get 32 bit alignment. If that's 70045916cd2Sjpk * wrong, that's just too bad.) 70145916cd2Sjpk */ 70245916cd2Sjpk if (padding > 0) { 70345916cd2Sjpk olen = (optbuf[IPOPT_OLEN] + 3) & ~3; 70445916cd2Sjpk if (olen + totlen > IP_MAX_OPT_LENGTH) { 70545916cd2Sjpk totlen -= padding; 70645916cd2Sjpk if (olen + totlen > IP_MAX_OPT_LENGTH) 70745916cd2Sjpk return (-1); 70845916cd2Sjpk padding = 0; 70945916cd2Sjpk } 71045916cd2Sjpk } 71145916cd2Sjpk 71245916cd2Sjpk /* 71345916cd2Sjpk * Since we may need to compress or expand the option list, we write to 71445916cd2Sjpk * a temporary buffer and then copy the results back to the IP header. 71545916cd2Sjpk */ 71645916cd2Sjpk toptr = tempopt; 71745916cd2Sjpk 71845916cd2Sjpk /* compute actual option to insert */ 71945916cd2Sjpk olen = optbuf[IPOPT_OLEN]; 72045916cd2Sjpk bcopy(optbuf, toptr, olen); 72145916cd2Sjpk toptr += olen; 72245916cd2Sjpk if (padding > 0) { 72345916cd2Sjpk while ((olen & 3) != 0) { 72445916cd2Sjpk *toptr++ = IPOPT_NOP; 72545916cd2Sjpk olen++; 72645916cd2Sjpk } 72745916cd2Sjpk } 72845916cd2Sjpk 72945916cd2Sjpk /* copy over the existing options */ 73045916cd2Sjpk optr = (uchar_t *)(ipha + 1); 73145916cd2Sjpk while (totlen > 0) { 73245916cd2Sjpk oval = optr[IPOPT_OPTVAL]; 73345916cd2Sjpk 73445916cd2Sjpk /* totlen doesn't include end-of-list marker */ 73545916cd2Sjpk ASSERT(oval != IPOPT_EOL); 73645916cd2Sjpk 73745916cd2Sjpk /* handle no-ops; copy if desired, ignore otherwise */ 73845916cd2Sjpk if (oval == IPOPT_NOP) { 73945916cd2Sjpk if (padding > 0) { 74045916cd2Sjpk /* note: cannot overflow due to checks above */ 74145916cd2Sjpk ASSERT(toptr < tempopt + IP_MAX_OPT_LENGTH); 74245916cd2Sjpk *toptr++ = oval; 74345916cd2Sjpk } 74445916cd2Sjpk optr++; 74545916cd2Sjpk totlen--; 74645916cd2Sjpk continue; 74745916cd2Sjpk } 74845916cd2Sjpk 74945916cd2Sjpk /* list cannot be corrupt at this point */ 75045916cd2Sjpk ASSERT(totlen >= 2); 75145916cd2Sjpk olen = optr[IPOPT_OLEN]; 75245916cd2Sjpk ASSERT(olen >= 2 && olen <= totlen); 75345916cd2Sjpk 75445916cd2Sjpk /* cannot run out of room due to tests above */ 75545916cd2Sjpk ASSERT(toptr + olen <= tempopt + IP_MAX_OPT_LENGTH); 75645916cd2Sjpk 75745916cd2Sjpk bcopy(optr, toptr, olen); 75845916cd2Sjpk optr += olen; 75945916cd2Sjpk toptr += olen; 76045916cd2Sjpk totlen -= olen; 76145916cd2Sjpk } 76245916cd2Sjpk 76345916cd2Sjpk /* figure how much padding we'll need for header alignment */ 76445916cd2Sjpk olen = (toptr - tempopt) & 3; 76545916cd2Sjpk if (olen > 0) { 76645916cd2Sjpk olen = 4 - olen; 76745916cd2Sjpk ASSERT(toptr + olen <= tempopt + IP_MAX_OPT_LENGTH); 76845916cd2Sjpk /* pad with end-of-list value */ 76945916cd2Sjpk bzero(toptr, olen); 77045916cd2Sjpk toptr += olen; 77145916cd2Sjpk } 77245916cd2Sjpk 77345916cd2Sjpk /* move the headers as needed and update IP header */ 77445916cd2Sjpk olen = (toptr - tempopt) + IP_SIMPLE_HDR_LENGTH; 77545916cd2Sjpk remlen = IPH_HDR_LENGTH(ipha); 77645916cd2Sjpk delta = olen - remlen; 77745916cd2Sjpk if (delta != 0) { 77845916cd2Sjpk ovbcopy((uchar_t *)ipha + remlen, (uchar_t *)ipha + olen, 77945916cd2Sjpk buflen - remlen); 78045916cd2Sjpk ipha->ipha_version_and_hdr_length += delta / 4; 78145916cd2Sjpk } 78245916cd2Sjpk 78345916cd2Sjpk /* slap in the new options */ 78445916cd2Sjpk bcopy(tempopt, ipha + 1, olen - IP_SIMPLE_HDR_LENGTH); 78545916cd2Sjpk 78645916cd2Sjpk return (delta); 78745916cd2Sjpk } 78845916cd2Sjpk 78945916cd2Sjpk /* 790bd670b35SErik Nordmark * tsol_check_label_v4() 79145916cd2Sjpk * 79245916cd2Sjpk * This routine computes the IP label that should be on the packet based on the 793bd670b35SErik Nordmark * connection and destination information. It's called by the IP forwarding 794bd670b35SErik Nordmark * logic and by ip_output_simple. The ULPs generate the labels before calling 795bd670b35SErik Nordmark * conn_ip_output. If any adjustments to 796bd670b35SErik Nordmark * the label are needed due to the connection's MAC-exempt status or 797bd670b35SErik Nordmark * the destination's ability to receive labels, an "effective label" 798bd670b35SErik Nordmark * will be returned. 79945916cd2Sjpk * 800222c5bceSkp158701 * The packet's header is clear before entering IPsec's engine. 80145916cd2Sjpk * 802bd670b35SErik Nordmark * The zoneid is the IP zoneid (i.e., GLOBAL_ZONEID for exlusive-IP zones). 803bd670b35SErik Nordmark * zone_is_global is set if the actual zoneid is global. 804bd670b35SErik Nordmark * 805bd670b35SErik Nordmark * On successful return, effective_tslp will point to the new label needed 806bd670b35SErik Nordmark * or will be NULL if a new label isn't needed. On error, effective_tsl will 807bd670b35SErik Nordmark * point to NULL. 808bd670b35SErik Nordmark * 80945916cd2Sjpk * Returns: 8100e0e37a8SErik Nordmark * 0 Label (was|is now) correct 81145916cd2Sjpk * EACCES The packet failed the remote host accreditation. 81245916cd2Sjpk * ENOMEM Memory allocation failure. 81345916cd2Sjpk * EINVAL Label cannot be computed 81445916cd2Sjpk */ 81545916cd2Sjpk int 816bd670b35SErik Nordmark tsol_check_label_v4(const ts_label_t *tsl, zoneid_t zoneid, mblk_t **mpp, 817bd670b35SErik Nordmark uint_t mac_mode, boolean_t zone_is_global, ip_stack_t *ipst, 818bd670b35SErik Nordmark ts_label_t **effective_tslp) 81945916cd2Sjpk { 82045916cd2Sjpk mblk_t *mp = *mpp; 82145916cd2Sjpk ipha_t *ipha; 822bd670b35SErik Nordmark ts_label_t *effective_tsl = NULL; 82345916cd2Sjpk uchar_t opt_storage[IP_MAX_OPT_LENGTH]; 82445916cd2Sjpk uint_t hlen; 82545916cd2Sjpk uint_t sec_opt_len; 82645916cd2Sjpk uchar_t *optr; 827222c5bceSkp158701 int delta_remove = 0, delta_add, adjust; 82845916cd2Sjpk int retv; 82945916cd2Sjpk 830bd670b35SErik Nordmark *effective_tslp = NULL; 83145916cd2Sjpk opt_storage[IPOPT_OPTVAL] = 0; 83245916cd2Sjpk 83345916cd2Sjpk ipha = (ipha_t *)mp->b_rptr; 83445916cd2Sjpk 8355f9878b0Sken Powell - Sun Microsystem /* 8365f9878b0Sken Powell - Sun Microsystem * Verify the destination is allowed to receive packets at 837bd670b35SErik Nordmark * the security label of the message data. tsol_check_dest() 838bd670b35SErik Nordmark * may create a new effective label or label flags. 8395f9878b0Sken Powell - Sun Microsystem */ 840bd670b35SErik Nordmark retv = tsol_check_dest(tsl, &ipha->ipha_dst, IPV4_VERSION, 841bd670b35SErik Nordmark mac_mode, zone_is_global, &effective_tsl); 84245916cd2Sjpk if (retv != 0) 84345916cd2Sjpk return (retv); 84445916cd2Sjpk 8455f9878b0Sken Powell - Sun Microsystem /* 8465f9878b0Sken Powell - Sun Microsystem * Calculate the security label to be placed in the text 8475f9878b0Sken Powell - Sun Microsystem * of the message (if any). 8485f9878b0Sken Powell - Sun Microsystem */ 849bd670b35SErik Nordmark if (effective_tsl != NULL) { 850bd670b35SErik Nordmark if ((retv = tsol_compute_label_v4(effective_tsl, zoneid, 8515f9878b0Sken Powell - Sun Microsystem ipha->ipha_dst, opt_storage, ipst)) != 0) { 852bd670b35SErik Nordmark label_rele(effective_tsl); 8535f9878b0Sken Powell - Sun Microsystem return (retv); 8545f9878b0Sken Powell - Sun Microsystem } 855bd670b35SErik Nordmark *effective_tslp = effective_tsl; 8565f9878b0Sken Powell - Sun Microsystem } else { 857bd670b35SErik Nordmark if ((retv = tsol_compute_label_v4(tsl, zoneid, 8585f9878b0Sken Powell - Sun Microsystem ipha->ipha_dst, opt_storage, ipst)) != 0) { 8595f9878b0Sken Powell - Sun Microsystem return (retv); 8605f9878b0Sken Powell - Sun Microsystem } 8615f9878b0Sken Powell - Sun Microsystem } 8625f9878b0Sken Powell - Sun Microsystem 86345916cd2Sjpk optr = (uchar_t *)(ipha + 1); 86445916cd2Sjpk hlen = IPH_HDR_LENGTH(ipha) - IP_SIMPLE_HDR_LENGTH; 86545916cd2Sjpk sec_opt_len = opt_storage[IPOPT_OLEN]; 86645916cd2Sjpk 86745916cd2Sjpk if (hlen >= sec_opt_len) { 86845916cd2Sjpk /* If no option is supposed to be there, make sure it's not */ 86945916cd2Sjpk if (sec_opt_len == 0 && hlen > 0 && 87045916cd2Sjpk optr[IPOPT_OPTVAL] != IPOPT_COMSEC && 87145916cd2Sjpk optr[IPOPT_OPTVAL] != IPOPT_SECURITY) 87245916cd2Sjpk return (0); 87345916cd2Sjpk /* if the option is there, it's always first */ 87445916cd2Sjpk if (sec_opt_len != 0 && 87545916cd2Sjpk bcmp(opt_storage, optr, sec_opt_len) == 0) 87645916cd2Sjpk return (0); 87745916cd2Sjpk } 87845916cd2Sjpk 87945916cd2Sjpk /* 88045916cd2Sjpk * If there is an option there, then it must be the wrong one; delete. 88145916cd2Sjpk */ 882222c5bceSkp158701 if (hlen > 0) { 883222c5bceSkp158701 delta_remove = tsol_remove_secopt(ipha, MBLKL(mp)); 884222c5bceSkp158701 mp->b_wptr += delta_remove; 885222c5bceSkp158701 } 88645916cd2Sjpk 88745916cd2Sjpk /* Make sure we have room for the worst-case addition */ 88845916cd2Sjpk hlen = IPH_HDR_LENGTH(ipha) + opt_storage[IPOPT_OLEN]; 88945916cd2Sjpk hlen = (hlen + 3) & ~3; 89045916cd2Sjpk if (hlen > IP_MAX_HDR_LENGTH) 89145916cd2Sjpk hlen = IP_MAX_HDR_LENGTH; 89245916cd2Sjpk hlen -= IPH_HDR_LENGTH(ipha); 89345916cd2Sjpk if (mp->b_wptr + hlen > mp->b_datap->db_lim) { 89445916cd2Sjpk int copylen; 89545916cd2Sjpk mblk_t *new_mp; 89645916cd2Sjpk 89745916cd2Sjpk /* allocate enough to be meaningful, but not *too* much */ 89845916cd2Sjpk copylen = MBLKL(mp); 89945916cd2Sjpk if (copylen > 256) 90045916cd2Sjpk copylen = 256; 901de8c4a14SErik Nordmark new_mp = allocb_tmpl(hlen + copylen + 902de8c4a14SErik Nordmark (mp->b_rptr - mp->b_datap->db_base), mp); 903bd670b35SErik Nordmark if (new_mp == NULL) { 904bd670b35SErik Nordmark if (effective_tsl != NULL) { 905bd670b35SErik Nordmark label_rele(effective_tsl); 906bd670b35SErik Nordmark *effective_tslp = NULL; 907bd670b35SErik Nordmark } 90845916cd2Sjpk return (ENOMEM); 909bd670b35SErik Nordmark } 91045916cd2Sjpk 91145916cd2Sjpk /* keep the bias */ 91245916cd2Sjpk new_mp->b_rptr += mp->b_rptr - mp->b_datap->db_base; 91345916cd2Sjpk new_mp->b_wptr = new_mp->b_rptr + copylen; 91445916cd2Sjpk bcopy(mp->b_rptr, new_mp->b_rptr, copylen); 91545916cd2Sjpk new_mp->b_cont = mp; 91645916cd2Sjpk if ((mp->b_rptr += copylen) >= mp->b_wptr) { 91745916cd2Sjpk new_mp->b_cont = mp->b_cont; 91845916cd2Sjpk freeb(mp); 91945916cd2Sjpk } 92045916cd2Sjpk *mpp = mp = new_mp; 92145916cd2Sjpk ipha = (ipha_t *)mp->b_rptr; 92245916cd2Sjpk } 92345916cd2Sjpk 924222c5bceSkp158701 delta_add = tsol_prepend_option(opt_storage, ipha, MBLKL(mp)); 925222c5bceSkp158701 if (delta_add == -1) 92645916cd2Sjpk goto param_prob; 92745916cd2Sjpk 928222c5bceSkp158701 ASSERT((mp->b_wptr + delta_add) <= DB_LIM(mp)); 929222c5bceSkp158701 mp->b_wptr += delta_add; 93045916cd2Sjpk 931222c5bceSkp158701 adjust = delta_remove + delta_add; 932222c5bceSkp158701 adjust += ntohs(ipha->ipha_length); 933222c5bceSkp158701 ipha->ipha_length = htons(adjust); 93445916cd2Sjpk 93545916cd2Sjpk return (0); 93645916cd2Sjpk 93745916cd2Sjpk param_prob: 938bd670b35SErik Nordmark if (effective_tsl != NULL) { 939bd670b35SErik Nordmark label_rele(effective_tsl); 940bd670b35SErik Nordmark *effective_tslp = NULL; 941bd670b35SErik Nordmark } 94245916cd2Sjpk return (EINVAL); 94345916cd2Sjpk } 94445916cd2Sjpk 94545916cd2Sjpk /* 94645916cd2Sjpk * IPv6 HopOpt extension header for the label option layout: 94745916cd2Sjpk * - One octet giving the type of the 'next extension header' 94845916cd2Sjpk * - Header extension length in 8-byte words, not including the 94945916cd2Sjpk * 1st 8 bytes, but including any pad bytes at the end. 95045916cd2Sjpk * Eg. A value of 2 means 16 bytes not including the 1st 8 bytes. 95145916cd2Sjpk * - Followed by TLV encoded IPv6 label option. Option layout is 95245916cd2Sjpk * * One octet, IP6OPT_LS 95345916cd2Sjpk * * One octet option length in bytes of the option data following 95445916cd2Sjpk * the length, but not including any pad bytes at the end. 95545916cd2Sjpk * * Four-octet DOI (IP6LS_DOI_V4) 95645916cd2Sjpk * * One octet suboption, IP6LS_TT_V4 95745916cd2Sjpk * * One octet suboption length in bytes of the suboption 95845916cd2Sjpk * following the suboption length, including the suboption 95945916cd2Sjpk * header length, but not including any pad bytes at the end. 96045916cd2Sjpk * - Pad to make the extension header a multiple of 8 bytes. 96145916cd2Sjpk * 96245916cd2Sjpk * This function returns the contents of 'IPv6 option structure' in the above. 96345916cd2Sjpk * i.e starting from the IP6OPT_LS but not including the pad at the end. 96445916cd2Sjpk * The user must prepend two octets (either padding or next header / length) 96545916cd2Sjpk * and append padding out to the next 8 octet boundary. 966bd670b35SErik Nordmark * 967bd670b35SErik Nordmark * The zoneid is the IP zoneid (i.e., GLOBAL_ZONEID for exlusive-IP zones). 96845916cd2Sjpk */ 96945916cd2Sjpk int 970bd670b35SErik Nordmark tsol_compute_label_v6(const ts_label_t *tsl, zoneid_t zoneid, 971bd670b35SErik Nordmark const in6_addr_t *dst, uchar_t *opt_storage, ip_stack_t *ipst) 97245916cd2Sjpk { 97345916cd2Sjpk uint_t sec_opt_len; 97445916cd2Sjpk uint32_t doi; 975bd670b35SErik Nordmark ire_t *ire; 976bd670b35SErik Nordmark tsol_ire_gw_secattr_t *attrp = NULL; 977de8c4a14SErik Nordmark 97845916cd2Sjpk if (ip6opt_ls == 0) 97945916cd2Sjpk return (EINVAL); 98045916cd2Sjpk 98145916cd2Sjpk if (opt_storage != NULL) 98245916cd2Sjpk opt_storage[IPOPT_OLEN] = 0; 98345916cd2Sjpk 984bd670b35SErik Nordmark if (tsl == NULL) 98545916cd2Sjpk return (0); 98645916cd2Sjpk 98745916cd2Sjpk /* Always pass multicast */ 98845916cd2Sjpk if (IN6_IS_ADDR_MULTICAST(dst)) 98945916cd2Sjpk return (0); 99045916cd2Sjpk 99145916cd2Sjpk /* 99245916cd2Sjpk * Fill in a V6 label. If a new format is added here, make certain 99345916cd2Sjpk * that the maximum size of this label is reflected in sys/tsol/tnet.h 99445916cd2Sjpk * as TSOL_MAX_IPV6_OPTION. 99545916cd2Sjpk */ 9965d3b8cb7SBill Sommerfeld if (tsl->tsl_flags & TSLF_IMPLICIT_OUT) 9975d3b8cb7SBill Sommerfeld return (0); 9985d3b8cb7SBill Sommerfeld 9995f9878b0Sken Powell - Sun Microsystem if (tsl->tsl_flags & TSLF_UNLABELED) { 100045916cd2Sjpk /* 10015f9878b0Sken Powell - Sun Microsystem * The destination is unlabeled. Only add a label if the 1002bd670b35SErik Nordmark * destination is not a broadcast/local/loopback address, 10035f9878b0Sken Powell - Sun Microsystem * the destination is not on the same subnet, and the 10045f9878b0Sken Powell - Sun Microsystem * next-hop gateway is labeled. 100545916cd2Sjpk */ 1006bd670b35SErik Nordmark ire = ire_route_recursive_v6(dst, 0, NULL, zoneid, tsl, 1007*9e3469d3SErik Nordmark MATCH_IRE_SECATTR, IRR_ALLOCATE, 0, ipst, NULL, &attrp, 1008*9e3469d3SErik Nordmark NULL); 1009bd670b35SErik Nordmark ASSERT(ire != NULL); 1010bd670b35SErik Nordmark if (ire->ire_flags & (RTF_REJECT|RTF_BLACKHOLE)) { 101145916cd2Sjpk /* no route to destination */ 1012bd670b35SErik Nordmark ire_refrele(ire); 10135f9878b0Sken Powell - Sun Microsystem DTRACE_PROBE3( 101445916cd2Sjpk tx__tnopt__log__info__labeling__routedst__v6, 10155f9878b0Sken Powell - Sun Microsystem char *, "No route to unlabeled dest ip6(1) with " 1016bd670b35SErik Nordmark "label(2).", in6_addr_t *, dst, ts_label_t *, tsl); 10175f9878b0Sken Powell - Sun Microsystem return (EHOSTUNREACH); 101845916cd2Sjpk } 1019bd670b35SErik Nordmark if (ire->ire_type & (IRE_LOCAL | IRE_LOOPBACK | 1020bd670b35SErik Nordmark IRE_INTERFACE)) { 1021bd670b35SErik Nordmark ire_refrele(ire); 1022bd670b35SErik Nordmark return (0); 102345916cd2Sjpk } 1024bd670b35SErik Nordmark /* 1025bd670b35SErik Nordmark * ire_route_recursive gives us the first attrp it finds 1026bd670b35SErik Nordmark * in the recursive lookup. 1027bd670b35SErik Nordmark */ 10285f9878b0Sken Powell - Sun Microsystem /* 10295f9878b0Sken Powell - Sun Microsystem * Return now if next hop gateway is unlabeled. There is 10305f9878b0Sken Powell - Sun Microsystem * no need to generate a CIPSO option for this message. 10315f9878b0Sken Powell - Sun Microsystem */ 10325f9878b0Sken Powell - Sun Microsystem if (attrp == NULL || attrp->igsa_rhc == NULL || 10335f9878b0Sken Powell - Sun Microsystem attrp->igsa_rhc->rhc_tpc->tpc_tp.host_type == UNLABELED) { 1034bd670b35SErik Nordmark ire_refrele(ire); 103545916cd2Sjpk return (0); 103645916cd2Sjpk } 1037bd670b35SErik Nordmark ire_refrele(ire); 10385f9878b0Sken Powell - Sun Microsystem } 103945916cd2Sjpk 104045916cd2Sjpk /* compute the CIPSO option */ 104145916cd2Sjpk if (opt_storage != NULL) 104245916cd2Sjpk opt_storage += 8; 104345916cd2Sjpk sec_opt_len = tsol2cipso_tt1(&tsl->tsl_label, opt_storage, 104445916cd2Sjpk tsl->tsl_doi); 104545916cd2Sjpk 104645916cd2Sjpk if (sec_opt_len == 0) { 10475f9878b0Sken Powell - Sun Microsystem DTRACE_PROBE3(tx__tnopt__log__error__labeling__lostops__v6, 10485f9878b0Sken Powell - Sun Microsystem char *, "options lack length for dest ip6(1) with " 1049bd670b35SErik Nordmark "label(2).", in6_addr_t *, dst, ts_label_t *, tsl); 105045916cd2Sjpk return (EINVAL); 105145916cd2Sjpk } 105245916cd2Sjpk 105345916cd2Sjpk if (opt_storage == NULL) 105445916cd2Sjpk return (0); 105545916cd2Sjpk 105645916cd2Sjpk if (sec_opt_len < IP_MAX_OPT_LENGTH) 105745916cd2Sjpk opt_storage[sec_opt_len] = IPOPT_EOL; 105845916cd2Sjpk 105945916cd2Sjpk /* 106045916cd2Sjpk * Just in case the option length is odd, round it up to the next even 106145916cd2Sjpk * multiple. The IPv6 option definition doesn't like odd numbers for 106245916cd2Sjpk * some reason. 106345916cd2Sjpk * 106445916cd2Sjpk * Length in the overall option header (IP6OPT_LS) does not include the 106545916cd2Sjpk * option header itself, but the length in the suboption does include 106645916cd2Sjpk * the suboption header. Thus, when there's just one suboption, the 106745916cd2Sjpk * length in the option header is the suboption length plus 4 (for the 106845916cd2Sjpk * DOI value). 106945916cd2Sjpk */ 107045916cd2Sjpk opt_storage[-2] = IP6LS_TT_V4; 107145916cd2Sjpk opt_storage[-1] = (sec_opt_len + 2 + 1) & ~1; 107245916cd2Sjpk opt_storage[-8] = ip6opt_ls; 107345916cd2Sjpk opt_storage[-7] = opt_storage[-1] + 4; 107445916cd2Sjpk doi = htons(IP6LS_DOI_V4); 107545916cd2Sjpk bcopy(&doi, opt_storage - 6, 4); 107645916cd2Sjpk 107745916cd2Sjpk return (0); 107845916cd2Sjpk } 107945916cd2Sjpk 108045916cd2Sjpk /* 108145916cd2Sjpk * Locate the start of the IP6OPT_LS label option and return it. 108245916cd2Sjpk * Also return the start of the next non-pad option in after_secoptp. 108345916cd2Sjpk * Usually the label option is the first option at least when packets 108445916cd2Sjpk * are generated, but for generality we don't assume that on received packets. 1085c4e55c13Sken Powell - Sun Microsystem * 1086c4e55c13Sken Powell - Sun Microsystem * The function will return with B_FALSE if an IP format error 1087c4e55c13Sken Powell - Sun Microsystem * or an unexpected label content error is encountered. 108845916cd2Sjpk */ 1089c4e55c13Sken Powell - Sun Microsystem boolean_t 109045916cd2Sjpk tsol_find_secopt_v6( 109145916cd2Sjpk const uchar_t *ip6hbh, /* Start of the hop-by-hop extension header */ 109245916cd2Sjpk uint_t hbhlen, /* Length of the hop-by-hop extension header */ 1093c4e55c13Sken Powell - Sun Microsystem uchar_t **secoptp, /* Location of IP6OPT_LS label option */ 109445916cd2Sjpk uchar_t **after_secoptp, /* Non-pad option following the label option */ 109545916cd2Sjpk boolean_t *hbh_needed) /* Is hop-by-hop hdr needed w/o label */ 109645916cd2Sjpk { 109745916cd2Sjpk uint_t optlen; 109845916cd2Sjpk uint_t optused; 109945916cd2Sjpk const uchar_t *optptr; 110045916cd2Sjpk uchar_t opt_type; 110145916cd2Sjpk 1102c4e55c13Sken Powell - Sun Microsystem *secoptp = NULL; 110345916cd2Sjpk *hbh_needed = B_FALSE; 110445916cd2Sjpk *after_secoptp = NULL; 110545916cd2Sjpk optlen = hbhlen - 2; 110645916cd2Sjpk optptr = ip6hbh + 2; 110745916cd2Sjpk while (optlen != 0) { 110845916cd2Sjpk opt_type = *optptr; 110945916cd2Sjpk if (opt_type == IP6OPT_PAD1) { 111045916cd2Sjpk optptr++; 111145916cd2Sjpk optlen--; 111245916cd2Sjpk continue; 111345916cd2Sjpk } 111445916cd2Sjpk if (optlen == 1) 1115c4e55c13Sken Powell - Sun Microsystem return (B_FALSE); 111645916cd2Sjpk optused = 2 + optptr[1]; 111745916cd2Sjpk if (optused > optlen) 1118c4e55c13Sken Powell - Sun Microsystem return (B_FALSE); 111945916cd2Sjpk /* 112045916cd2Sjpk * if we get here, ip6opt_ls can 112145916cd2Sjpk * not be 0 because it will always 112245916cd2Sjpk * match the IP6OPT_PAD1 above. 112345916cd2Sjpk * Therefore ip6opt_ls == 0 forces 112445916cd2Sjpk * this test to always fail here. 112545916cd2Sjpk */ 1126c4e55c13Sken Powell - Sun Microsystem if (opt_type == ip6opt_ls) { 1127c4e55c13Sken Powell - Sun Microsystem if (*secoptp != NULL) 1128c4e55c13Sken Powell - Sun Microsystem /* More than one security option found */ 1129c4e55c13Sken Powell - Sun Microsystem return (B_FALSE); 1130c4e55c13Sken Powell - Sun Microsystem *secoptp = (uchar_t *)optptr; 1131c4e55c13Sken Powell - Sun Microsystem } else switch (opt_type) { 113245916cd2Sjpk case IP6OPT_PADN: 113345916cd2Sjpk break; 113445916cd2Sjpk default: 113545916cd2Sjpk /* 113645916cd2Sjpk * There is at least 1 option other than 113745916cd2Sjpk * the label option. So the hop-by-hop header is needed 113845916cd2Sjpk */ 113945916cd2Sjpk *hbh_needed = B_TRUE; 1140c4e55c13Sken Powell - Sun Microsystem if (*secoptp != NULL) { 114145916cd2Sjpk *after_secoptp = (uchar_t *)optptr; 1142c4e55c13Sken Powell - Sun Microsystem return (B_TRUE); 114345916cd2Sjpk } 114445916cd2Sjpk break; 114545916cd2Sjpk } 114645916cd2Sjpk optlen -= optused; 114745916cd2Sjpk optptr += optused; 114845916cd2Sjpk } 1149c4e55c13Sken Powell - Sun Microsystem return (B_TRUE); 115045916cd2Sjpk } 115145916cd2Sjpk 115245916cd2Sjpk /* 115345916cd2Sjpk * Remove the label option from the hop-by-hop options header if it exists. 115445916cd2Sjpk * 'buflen' is the total length of the packet typically b_wptr - b_rptr. 115545916cd2Sjpk * Header and data following the label option that is deleted are copied 11560ec92a15Swy83408 * (i.e. slid backward) to the right position, and returns the number 11570ec92a15Swy83408 * of bytes removed (as zero or negative number.) 1158bd670b35SErik Nordmark * 1159bd670b35SErik Nordmark * Note that tsol_remove_secopt does not adjust ipha_length but 1160bd670b35SErik Nordmark * tsol_remove_secopt_v6 does adjust ip6_plen. 116145916cd2Sjpk */ 116245916cd2Sjpk int 116345916cd2Sjpk tsol_remove_secopt_v6(ip6_t *ip6h, int buflen) 116445916cd2Sjpk { 116545916cd2Sjpk uchar_t *ip6hbh; /* hop-by-hop header */ 116645916cd2Sjpk uint_t hbhlen; /* hop-by-hop extension header length */ 116745916cd2Sjpk uchar_t *secopt = NULL; 116845916cd2Sjpk uchar_t *after_secopt; 116945916cd2Sjpk uint_t pad; 117045916cd2Sjpk uint_t delta; 117145916cd2Sjpk boolean_t hbh_needed; 117245916cd2Sjpk 117345916cd2Sjpk /* 117445916cd2Sjpk * hop-by-hop extension header must appear first, if it does not 117545916cd2Sjpk * exist, there is no label option. 117645916cd2Sjpk */ 117745916cd2Sjpk if (ip6h->ip6_nxt != IPPROTO_HOPOPTS) 117845916cd2Sjpk return (0); 117945916cd2Sjpk 118045916cd2Sjpk ip6hbh = (uchar_t *)&ip6h[1]; 118145916cd2Sjpk hbhlen = (ip6hbh[1] + 1) << 3; 118245916cd2Sjpk /* 118345916cd2Sjpk * Locate the start of the label option if it exists and the end 118445916cd2Sjpk * of the label option including pads if any. 118545916cd2Sjpk */ 1186c4e55c13Sken Powell - Sun Microsystem if (!tsol_find_secopt_v6(ip6hbh, hbhlen, &secopt, &after_secopt, 1187c4e55c13Sken Powell - Sun Microsystem &hbh_needed)) { 1188c4e55c13Sken Powell - Sun Microsystem /* 1189c4e55c13Sken Powell - Sun Microsystem * This function should not see invalid messages. 1190c4e55c13Sken Powell - Sun Microsystem * If one occurs, it would indicate either an 1191c4e55c13Sken Powell - Sun Microsystem * option previously verified in the forwarding 1192c4e55c13Sken Powell - Sun Microsystem * path has been corrupted or an option was 1193c4e55c13Sken Powell - Sun Microsystem * incorrectly generated locally. 1194c4e55c13Sken Powell - Sun Microsystem */ 1195c4e55c13Sken Powell - Sun Microsystem ASSERT(0); 1196c4e55c13Sken Powell - Sun Microsystem return (0); 1197c4e55c13Sken Powell - Sun Microsystem } 119845916cd2Sjpk if (secopt == NULL) 119945916cd2Sjpk return (0); 120045916cd2Sjpk if (!hbh_needed) { 120145916cd2Sjpk uchar_t next_hdr; 120245916cd2Sjpk /* 120345916cd2Sjpk * The label option was the only option in the hop-by-hop 120445916cd2Sjpk * header. We don't need the hop-by-hop header itself any 120545916cd2Sjpk * longer. 120645916cd2Sjpk */ 120745916cd2Sjpk next_hdr = ip6hbh[0]; 120845916cd2Sjpk ovbcopy(ip6hbh + hbhlen, ip6hbh, 120945916cd2Sjpk buflen - (IPV6_HDR_LEN + hbhlen)); 1210d7ab25acSkp158701 ip6h->ip6_plen = htons(ntohs(ip6h->ip6_plen) - hbhlen); 121145916cd2Sjpk ip6h->ip6_nxt = next_hdr; 12120ec92a15Swy83408 return (-hbhlen); 121345916cd2Sjpk } 121445916cd2Sjpk 121545916cd2Sjpk if (after_secopt == NULL) { 121645916cd2Sjpk /* There is no option following the label option */ 121745916cd2Sjpk after_secopt = ip6hbh + hbhlen; 121845916cd2Sjpk } 121945916cd2Sjpk 122045916cd2Sjpk /* 122145916cd2Sjpk * After deleting the label option, we need to slide the headers 122245916cd2Sjpk * and data back, while still maintaining the same alignment (module 8) 122345916cd2Sjpk * for the other options. So we slide the headers and data back only 122445916cd2Sjpk * by an integral multiple of 8 bytes, and fill the remaining bytes 122545916cd2Sjpk * with pads. 122645916cd2Sjpk */ 122745916cd2Sjpk delta = after_secopt - secopt; 122845916cd2Sjpk pad = delta % 8; 122945916cd2Sjpk if (pad == 1) { 123045916cd2Sjpk secopt[0] = IP6OPT_PAD1; 123145916cd2Sjpk } else if (pad > 1) { 123245916cd2Sjpk secopt[0] = IP6OPT_PADN; 123345916cd2Sjpk secopt[1] = pad - 2; 123445916cd2Sjpk if (pad > 2) 123545916cd2Sjpk bzero(&secopt[2], pad - 2); 123645916cd2Sjpk } 123745916cd2Sjpk secopt += pad; 123845916cd2Sjpk delta -= pad; 123945916cd2Sjpk ovbcopy(after_secopt, secopt, 124045916cd2Sjpk (uchar_t *)ip6h + buflen - after_secopt); 124145916cd2Sjpk ip6hbh[1] -= delta/8; 1242d7ab25acSkp158701 ip6h->ip6_plen = htons(ntohs(ip6h->ip6_plen) - delta); 124345916cd2Sjpk 12440ec92a15Swy83408 return (-delta); 124545916cd2Sjpk } 124645916cd2Sjpk 124745916cd2Sjpk /* 124845916cd2Sjpk * 'optbuf' contains a CIPSO label embedded in an IPv6 hop-by-hop option, 124945916cd2Sjpk * starting with the IP6OPT_LS option type. The format of this hop-by-hop 125045916cd2Sjpk * option is described in the block comment above tsol_compute_label_v6. 125145916cd2Sjpk * This function prepends this hop-by-hop option before any other hop-by-hop 125245916cd2Sjpk * options in the hop-by-hop header if one already exists, else a new 125345916cd2Sjpk * hop-by-hop header is created and stuffed into the packet following 125445916cd2Sjpk * the IPv6 header. 'buflen' is the total length of the packet i.e. 125545916cd2Sjpk * b_wptr - b_rptr. The caller ensures that there is enough space for the 125645916cd2Sjpk * extra option being added. Header and data following the position where 125745916cd2Sjpk * the label option is inserted are copied (i.e. slid forward) to the right 125845916cd2Sjpk * position. 1259bd670b35SErik Nordmark * 1260bd670b35SErik Nordmark * Note that tsol_prepend_option does not adjust ipha_length but 1261bd670b35SErik Nordmark * tsol_prepend_option_v6 does adjust ip6_plen. 126245916cd2Sjpk */ 126345916cd2Sjpk int 126445916cd2Sjpk tsol_prepend_option_v6(uchar_t *optbuf, ip6_t *ip6h, int buflen) 126545916cd2Sjpk { 126645916cd2Sjpk /* 126745916cd2Sjpk * rawlen is the length of the label option in bytes, not including 126845916cd2Sjpk * any pads, starting from the IP6OPT_LS (option type) byte. 126945916cd2Sjpk */ 127045916cd2Sjpk uint_t rawlen; 127145916cd2Sjpk 127245916cd2Sjpk uint_t optlen; /* rawlen rounded to an 8 byte multiple */ 127345916cd2Sjpk uchar_t *ip6hbh; /* start of the hop-by-hop extension header */ 127445916cd2Sjpk uint_t hbhlen; /* Length of the hop-by-hop extension header */ 127545916cd2Sjpk uint_t pad_len; 127645916cd2Sjpk uchar_t *pad_position; 127745916cd2Sjpk int delta; /* Actual number of bytes inserted */ 127845916cd2Sjpk 127945916cd2Sjpk rawlen = optbuf[1] + 2; /* Add 2 for the option type, option length */ 128045916cd2Sjpk ip6hbh = (uchar_t *)&ip6h[1]; 128145916cd2Sjpk if (ip6h->ip6_nxt == IPPROTO_HOPOPTS) { 128245916cd2Sjpk /* 128345916cd2Sjpk * There is a hop-by-hop header present already. In order to 128445916cd2Sjpk * preserve the alignment of the other options at the existing 128545916cd2Sjpk * value (modulo 8) we need to pad the label option to a 128645916cd2Sjpk * multiple of 8 bytes before prepending it to the other 128745916cd2Sjpk * options. Slide the extension headers and data forward to 128845916cd2Sjpk * accomodate the label option at the start of the hop-by-hop 128945916cd2Sjpk * header 129045916cd2Sjpk */ 129145916cd2Sjpk delta = optlen = (rawlen + 7) & ~7; 129245916cd2Sjpk pad_len = optlen - rawlen; 129345916cd2Sjpk pad_position = ip6hbh + 2 + rawlen; 129445916cd2Sjpk ovbcopy(ip6hbh + 2, ip6hbh + 2 + optlen, 129545916cd2Sjpk buflen - (IPV6_HDR_LEN + 2)); 129645916cd2Sjpk /* 129745916cd2Sjpk * Bump up the hop-by-hop extension header length by 129845916cd2Sjpk * the number of 8-byte words added 129945916cd2Sjpk */ 130045916cd2Sjpk optlen >>= 3; 130145916cd2Sjpk if (ip6hbh[1] + optlen > 255) 130245916cd2Sjpk return (-1); 130345916cd2Sjpk ip6hbh[1] += optlen; 130445916cd2Sjpk } else { 130545916cd2Sjpk /* 130645916cd2Sjpk * There is no hop-by-hop header in the packet. Construct a 130745916cd2Sjpk * new Hop-by-hop extension header (a multiple of 8 bytes). 130845916cd2Sjpk * Slide any other extension headers and data forward to 130945916cd2Sjpk * accomodate this hop-by-hop header 131045916cd2Sjpk */ 131145916cd2Sjpk delta = hbhlen = (2 + rawlen + 7) & ~7; /* +2 for nxthdr, len */ 131245916cd2Sjpk pad_len = hbhlen - (2 + rawlen); 131345916cd2Sjpk pad_position = ip6hbh + 2 + rawlen; 131445916cd2Sjpk ovbcopy(ip6hbh, ip6hbh + hbhlen, buflen - IPV6_HDR_LEN); 131545916cd2Sjpk ip6hbh[0] = ip6h->ip6_nxt; 131645916cd2Sjpk /* 131745916cd2Sjpk * hop-by-hop extension header length in 8-byte words, not 131845916cd2Sjpk * including the 1st 8 bytes of the hop-by-hop header. 131945916cd2Sjpk */ 132045916cd2Sjpk ip6hbh[1] = (hbhlen >> 3) - 1; 132145916cd2Sjpk ip6h->ip6_nxt = IPPROTO_HOPOPTS; 132245916cd2Sjpk } 132345916cd2Sjpk /* 132445916cd2Sjpk * Copy the label option into the hop-by-hop header and insert any 132545916cd2Sjpk * needed pads 132645916cd2Sjpk */ 132745916cd2Sjpk bcopy(optbuf, ip6hbh + 2, rawlen); 132845916cd2Sjpk if (pad_len == 1) { 132945916cd2Sjpk pad_position[0] = IP6OPT_PAD1; 133045916cd2Sjpk } else if (pad_len > 1) { 133145916cd2Sjpk pad_position[0] = IP6OPT_PADN; 133245916cd2Sjpk pad_position[1] = pad_len - 2; 133345916cd2Sjpk if (pad_len > 2) 133445916cd2Sjpk bzero(pad_position + 2, pad_len - 2); 133545916cd2Sjpk } 1336d7ab25acSkp158701 ip6h->ip6_plen = htons(ntohs(ip6h->ip6_plen) + delta); 133745916cd2Sjpk return (delta); 133845916cd2Sjpk } 133945916cd2Sjpk 134045916cd2Sjpk /* 134145916cd2Sjpk * tsol_check_label_v6() 134245916cd2Sjpk * 134345916cd2Sjpk * This routine computes the IP label that should be on the packet based on the 1344bd670b35SErik Nordmark * connection and destination information. It's called by the IP forwarding 1345bd670b35SErik Nordmark * logic and by ip_output_simple. The ULPs generate the labels before calling 1346bd670b35SErik Nordmark * conn_ip_output. If any adjustments to 1347bd670b35SErik Nordmark * the label are needed due to the connection's MAC-exempt status or 1348bd670b35SErik Nordmark * the destination's ability to receive labels, an "effective label" 1349bd670b35SErik Nordmark * will be returned. 1350bd670b35SErik Nordmark * 1351bd670b35SErik Nordmark * The packet's header is clear before entering IPsec's engine. 1352bd670b35SErik Nordmark * 1353bd670b35SErik Nordmark * The zoneid is the IP zoneid (i.e., GLOBAL_ZONEID for exlusive-IP zones). 1354bd670b35SErik Nordmark * zone_is_global is set if the actual zoneid is global. 1355bd670b35SErik Nordmark * 1356bd670b35SErik Nordmark * On successful return, effective_tslp will point to the new label needed 1357bd670b35SErik Nordmark * or will be NULL if a new label isn't needed. On error, effective_tsl will 1358bd670b35SErik Nordmark * point to NULL. 135945916cd2Sjpk * 136045916cd2Sjpk * Returns: 13610e0e37a8SErik Nordmark * 0 Label (was|is now) correct 13625f9878b0Sken Powell - Sun Microsystem * EACCES The packet failed the remote host accreditation. 136345916cd2Sjpk * ENOMEM Memory allocation failure. 1364bd670b35SErik Nordmark * EINVAL Label cannot be computed 136545916cd2Sjpk */ 136645916cd2Sjpk int 1367bd670b35SErik Nordmark tsol_check_label_v6(const ts_label_t *tsl, zoneid_t zoneid, mblk_t **mpp, 1368bd670b35SErik Nordmark uint_t mac_mode, boolean_t zone_is_global, ip_stack_t *ipst, 1369bd670b35SErik Nordmark ts_label_t **effective_tslp) 137045916cd2Sjpk { 137145916cd2Sjpk mblk_t *mp = *mpp; 137245916cd2Sjpk ip6_t *ip6h; 1373bd670b35SErik Nordmark ts_label_t *effective_tsl = NULL; 137445916cd2Sjpk /* 137545916cd2Sjpk * Label option length is limited to IP_MAX_OPT_LENGTH for 137645916cd2Sjpk * symmetry with IPv4. Can be relaxed if needed 137745916cd2Sjpk */ 137845916cd2Sjpk uchar_t opt_storage[TSOL_MAX_IPV6_OPTION]; 137945916cd2Sjpk uint_t hlen; 138045916cd2Sjpk uint_t sec_opt_len; /* label option length not including type, len */ 1381222c5bceSkp158701 int delta_remove = 0, delta_add; 138245916cd2Sjpk int retv; 138345916cd2Sjpk uchar_t *after_secopt; 138445916cd2Sjpk uchar_t *secopt = NULL; 138545916cd2Sjpk uchar_t *ip6hbh; 138645916cd2Sjpk uint_t hbhlen; 138745916cd2Sjpk boolean_t hbh_needed; 138845916cd2Sjpk 1389bd670b35SErik Nordmark *effective_tslp = NULL; 1390bd670b35SErik Nordmark 13915f9878b0Sken Powell - Sun Microsystem /* 13925f9878b0Sken Powell - Sun Microsystem * Verify the destination is allowed to receive packets at 1393bd670b35SErik Nordmark * the security label of the message data. tsol_check_dest() 1394bd670b35SErik Nordmark * may create a new effective label or label flags. 13955f9878b0Sken Powell - Sun Microsystem */ 139645916cd2Sjpk ip6h = (ip6_t *)mp->b_rptr; 1397bd670b35SErik Nordmark retv = tsol_check_dest(tsl, &ip6h->ip6_dst, IPV6_VERSION, 1398bd670b35SErik Nordmark mac_mode, zone_is_global, &effective_tsl); 139945916cd2Sjpk if (retv != 0) 140045916cd2Sjpk return (retv); 140145916cd2Sjpk 14025f9878b0Sken Powell - Sun Microsystem /* 14035f9878b0Sken Powell - Sun Microsystem * Calculate the security label to be placed in the text 14045f9878b0Sken Powell - Sun Microsystem * of the message (if any). 14055f9878b0Sken Powell - Sun Microsystem */ 1406bd670b35SErik Nordmark if (effective_tsl != NULL) { 1407bd670b35SErik Nordmark if ((retv = tsol_compute_label_v6(effective_tsl, zoneid, 14085f9878b0Sken Powell - Sun Microsystem &ip6h->ip6_dst, opt_storage, ipst)) != 0) { 1409bd670b35SErik Nordmark label_rele(effective_tsl); 14105f9878b0Sken Powell - Sun Microsystem return (retv); 14115f9878b0Sken Powell - Sun Microsystem } 1412bd670b35SErik Nordmark *effective_tslp = effective_tsl; 14135f9878b0Sken Powell - Sun Microsystem } else { 1414bd670b35SErik Nordmark if ((retv = tsol_compute_label_v6(tsl, zoneid, 14155f9878b0Sken Powell - Sun Microsystem &ip6h->ip6_dst, opt_storage, ipst)) != 0) 14165f9878b0Sken Powell - Sun Microsystem return (retv); 14175f9878b0Sken Powell - Sun Microsystem } 14185f9878b0Sken Powell - Sun Microsystem 141945916cd2Sjpk sec_opt_len = opt_storage[1]; 142045916cd2Sjpk 142145916cd2Sjpk if (ip6h->ip6_nxt == IPPROTO_HOPOPTS) { 142245916cd2Sjpk ip6hbh = (uchar_t *)&ip6h[1]; 142345916cd2Sjpk hbhlen = (ip6hbh[1] + 1) << 3; 1424c4e55c13Sken Powell - Sun Microsystem if (!tsol_find_secopt_v6(ip6hbh, hbhlen, &secopt, 1425c4e55c13Sken Powell - Sun Microsystem &after_secopt, &hbh_needed)) { 1426c4e55c13Sken Powell - Sun Microsystem /* 1427c4e55c13Sken Powell - Sun Microsystem * This function should not see invalid messages. 1428c4e55c13Sken Powell - Sun Microsystem * If one occurs, it would indicate either an 1429c4e55c13Sken Powell - Sun Microsystem * option previously verified in the forwarding 1430c4e55c13Sken Powell - Sun Microsystem * path has been corrupted or an option was 1431c4e55c13Sken Powell - Sun Microsystem * incorrectly generated locally. 1432c4e55c13Sken Powell - Sun Microsystem */ 1433c4e55c13Sken Powell - Sun Microsystem ASSERT(0); 1434c4e55c13Sken Powell - Sun Microsystem return (EACCES); 1435c4e55c13Sken Powell - Sun Microsystem } 143645916cd2Sjpk } 143745916cd2Sjpk 143845916cd2Sjpk if (sec_opt_len == 0 && secopt == NULL) { 143945916cd2Sjpk /* 144045916cd2Sjpk * The packet is not supposed to have a label, and it 144145916cd2Sjpk * does not have one currently 144245916cd2Sjpk */ 144345916cd2Sjpk return (0); 144445916cd2Sjpk } 14455d3b8cb7SBill Sommerfeld 144645916cd2Sjpk if (secopt != NULL && sec_opt_len != 0 && 144745916cd2Sjpk (bcmp(opt_storage, secopt, sec_opt_len + 2) == 0)) { 144845916cd2Sjpk /* The packet has the correct label already */ 144945916cd2Sjpk return (0); 145045916cd2Sjpk } 145145916cd2Sjpk 145245916cd2Sjpk /* 145345916cd2Sjpk * If there is an option there, then it must be the wrong one; delete. 145445916cd2Sjpk */ 1455222c5bceSkp158701 if (secopt != NULL) { 1456222c5bceSkp158701 delta_remove = tsol_remove_secopt_v6(ip6h, MBLKL(mp)); 1457222c5bceSkp158701 mp->b_wptr += delta_remove; 1458222c5bceSkp158701 } 145945916cd2Sjpk 146045916cd2Sjpk /* 146145916cd2Sjpk * Make sure we have room for the worst-case addition. Add 2 bytes for 146245916cd2Sjpk * the hop-by-hop ext header's next header and length fields. Add 146345916cd2Sjpk * another 2 bytes for the label option type, len and then round 146445916cd2Sjpk * up to the next 8-byte multiple. 146545916cd2Sjpk */ 146645916cd2Sjpk hlen = (4 + sec_opt_len + 7) & ~7; 146745916cd2Sjpk if (mp->b_wptr + hlen > mp->b_datap->db_lim) { 146845916cd2Sjpk int copylen; 146945916cd2Sjpk mblk_t *new_mp; 147045916cd2Sjpk uint16_t hdr_len; 147145916cd2Sjpk 147245916cd2Sjpk hdr_len = ip_hdr_length_v6(mp, ip6h); 147345916cd2Sjpk /* 147445916cd2Sjpk * Allocate enough to be meaningful, but not *too* much. 147545916cd2Sjpk * Also all the IPv6 extension headers must be in the same mblk 147645916cd2Sjpk */ 147745916cd2Sjpk copylen = MBLKL(mp); 147845916cd2Sjpk if (copylen > 256) 147945916cd2Sjpk copylen = 256; 148045916cd2Sjpk if (copylen < hdr_len) 148145916cd2Sjpk copylen = hdr_len; 1482de8c4a14SErik Nordmark new_mp = allocb_tmpl(hlen + copylen + 1483de8c4a14SErik Nordmark (mp->b_rptr - mp->b_datap->db_base), mp); 1484bd670b35SErik Nordmark if (new_mp == NULL) { 1485bd670b35SErik Nordmark if (effective_tsl != NULL) { 1486bd670b35SErik Nordmark label_rele(effective_tsl); 1487bd670b35SErik Nordmark *effective_tslp = NULL; 1488bd670b35SErik Nordmark } 148945916cd2Sjpk return (ENOMEM); 1490bd670b35SErik Nordmark } 149145916cd2Sjpk 149245916cd2Sjpk /* keep the bias */ 149345916cd2Sjpk new_mp->b_rptr += mp->b_rptr - mp->b_datap->db_base; 149445916cd2Sjpk new_mp->b_wptr = new_mp->b_rptr + copylen; 149545916cd2Sjpk bcopy(mp->b_rptr, new_mp->b_rptr, copylen); 149645916cd2Sjpk new_mp->b_cont = mp; 149745916cd2Sjpk if ((mp->b_rptr += copylen) >= mp->b_wptr) { 149845916cd2Sjpk new_mp->b_cont = mp->b_cont; 149945916cd2Sjpk freeb(mp); 150045916cd2Sjpk } 150145916cd2Sjpk *mpp = mp = new_mp; 150245916cd2Sjpk ip6h = (ip6_t *)mp->b_rptr; 150345916cd2Sjpk } 150445916cd2Sjpk 1505222c5bceSkp158701 delta_add = tsol_prepend_option_v6(opt_storage, ip6h, MBLKL(mp)); 1506222c5bceSkp158701 if (delta_add == -1) 150745916cd2Sjpk goto param_prob; 150845916cd2Sjpk 1509222c5bceSkp158701 ASSERT(mp->b_wptr + delta_add <= DB_LIM(mp)); 1510222c5bceSkp158701 mp->b_wptr += delta_add; 151145916cd2Sjpk 1512bd670b35SErik Nordmark /* tsol_prepend_option_v6 has adjusted ip6_plen */ 151345916cd2Sjpk return (0); 151445916cd2Sjpk 151545916cd2Sjpk param_prob: 1516bd670b35SErik Nordmark if (effective_tsl != NULL) { 1517bd670b35SErik Nordmark label_rele(effective_tsl); 1518bd670b35SErik Nordmark *effective_tslp = NULL; 1519bd670b35SErik Nordmark } 152045916cd2Sjpk return (EINVAL); 152145916cd2Sjpk } 1522