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 /*
22*11457SErik.Nordmark@Sun.COM * Copyright 2010 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
tsol2cipso_tt1(const bslabel_t * sl,unsigned char * cop,uint32_t doi)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
tsol_get_option_v4(mblk_t * mp,tsol_ip_label_t * label_type,uchar_t ** buffer)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
tsol_get_option_v6(mblk_t * mp,tsol_ip_label_t * label_type,uchar_t ** buffer)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
27411042SErik.Nordmark@Sun.COM * based on the security label. If any adjustments to the label are needed
27511042SErik.Nordmark@Sun.COM * due to the connection's MAC mode or the destination's ability
27611042SErik.Nordmark@Sun.COM * to receive labels, an "effective label" will be returned.
2779710SKen.Powell@Sun.COM *
27811042SErik.Nordmark@Sun.COM * zone_is_global is set if the actual zoneid is global. That is, it is
27911042SErik.Nordmark@Sun.COM * not set for an exclusive-IP zone.
28011042SErik.Nordmark@Sun.COM *
28111042SErik.Nordmark@Sun.COM * On successful return, effective_tsl will point to the new label needed
28211042SErik.Nordmark@Sun.COM * or will be NULL if a new label isn't needed. On error, effective_tsl will
28311042SErik.Nordmark@Sun.COM * point to NULL.
2849710SKen.Powell@Sun.COM *
2859710SKen.Powell@Sun.COM * Returns:
28611042SErik.Nordmark@Sun.COM * 0 Label (was|is now) correct
28711042SErik.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
tsol_check_dest(const ts_label_t * tsl,const void * dst,uchar_t version,uint_t mac_mode,boolean_t zone_is_global,ts_label_t ** effective_tsl)29111042SErik.Nordmark@Sun.COM tsol_check_dest(const ts_label_t *tsl, const void *dst,
29211042SErik.Nordmark@Sun.COM uchar_t version, uint_t mac_mode, boolean_t zone_is_global,
29311042SErik.Nordmark@Sun.COM ts_label_t **effective_tsl)
2949710SKen.Powell@Sun.COM {
29511042SErik.Nordmark@Sun.COM ts_label_t *newtsl = NULL;
2969710SKen.Powell@Sun.COM tsol_tpc_t *dst_rhtp;
2979710SKen.Powell@Sun.COM
29811042SErik.Nordmark@Sun.COM if (effective_tsl != NULL)
29911042SErik.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) */
30511042SErik.Nordmark@Sun.COM if (tsl == NULL) {
3069710SKen.Powell@Sun.COM DTRACE_PROBE2(tx__tnopt__log__info__labeling__mac__allownull,
30711042SErik.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 ||
36411042SErik.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 /*
44211042SErik.Nordmark@Sun.COM * Return the new label.
4439710SKen.Powell@Sun.COM */
4449710SKen.Powell@Sun.COM if (newtsl != NULL) {
44511042SErik.Nordmark@Sun.COM if (effective_tsl != NULL)
44611042SErik.Nordmark@Sun.COM *effective_tsl = newtsl;
44711042SErik.Nordmark@Sun.COM else
44811042SErik.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 /*
45511042SErik.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 *
46011042SErik.Nordmark@Sun.COM * The zoneid is the IP zoneid (i.e., GLOBAL_ZONEID for exlusive-IP zones).
46111042SErik.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
tsol_compute_label_v4(const ts_label_t * tsl,zoneid_t zoneid,ipaddr_t dst,uchar_t * opt_storage,ip_stack_t * ipst)46811042SErik.Nordmark@Sun.COM tsol_compute_label_v4(const ts_label_t *tsl, zoneid_t zoneid, ipaddr_t dst,
46911042SErik.Nordmark@Sun.COM uchar_t *opt_storage, ip_stack_t *ipst)
4701676Sjpk {
4711676Sjpk uint_t sec_opt_len;
47211042SErik.Nordmark@Sun.COM ire_t *ire;
47311042SErik.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
47811042SErik.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 */
49511042SErik.Nordmark@Sun.COM ire = ire_route_recursive_v4(dst, 0, NULL, zoneid, tsl,
496*11457SErik.Nordmark@Sun.COM MATCH_IRE_SECATTR, IRR_ALLOCATE, 0, ipst, NULL, &attrp,
497*11457SErik.Nordmark@Sun.COM NULL);
49811042SErik.Nordmark@Sun.COM ASSERT(ire != NULL);
49911042SErik.Nordmark@Sun.COM if (ire->ire_flags & (RTF_REJECT|RTF_BLACKHOLE)) {
50011042SErik.Nordmark@Sun.COM /* no route to destination */
50111042SErik.Nordmark@Sun.COM ire_refrele(ire);
5029710SKen.Powell@Sun.COM DTRACE_PROBE3(
5031676Sjpk tx__tnopt__log__info__labeling__routedst__v4,
5049710SKen.Powell@Sun.COM char *, "No route to unlabeled dest ip(1) with "
50511042SErik.Nordmark@Sun.COM "with label(2).", ipaddr_t, dst, ts_label_t *, tsl);
5069710SKen.Powell@Sun.COM return (EHOSTUNREACH);
5071676Sjpk }
50811042SErik.Nordmark@Sun.COM if (ire->ire_type & (IRE_BROADCAST | IRE_LOCAL | IRE_LOOPBACK |
50911042SErik.Nordmark@Sun.COM IRE_INTERFACE)) {
51011042SErik.Nordmark@Sun.COM ire_refrele(ire);
51111042SErik.Nordmark@Sun.COM return (0);
51211042SErik.Nordmark@Sun.COM }
5131676Sjpk
5141676Sjpk /*
51511042SErik.Nordmark@Sun.COM * ire_route_recursive gives us the first attrp it finds
51611042SErik.Nordmark@Sun.COM * in the recursive lookup.
5171676Sjpk */
5181676Sjpk /*
5199710SKen.Powell@Sun.COM * Return now if next hop gateway is unlabeled. There is
5209710SKen.Powell@Sun.COM * no need to generate a CIPSO option for this message.
5211676Sjpk */
5229710SKen.Powell@Sun.COM if (attrp == NULL || attrp->igsa_rhc == NULL ||
5239710SKen.Powell@Sun.COM attrp->igsa_rhc->rhc_tpc->tpc_tp.host_type == UNLABELED) {
52411042SErik.Nordmark@Sun.COM ire_refrele(ire);
5259710SKen.Powell@Sun.COM return (0);
5261676Sjpk }
52711042SErik.Nordmark@Sun.COM ire_refrele(ire);
5281676Sjpk }
5291676Sjpk
5301676Sjpk /* compute the CIPSO option */
5319710SKen.Powell@Sun.COM sec_opt_len = tsol2cipso_tt1(&tsl->tsl_label, opt_storage,
5329710SKen.Powell@Sun.COM tsl->tsl_doi);
5331676Sjpk
5341676Sjpk if (sec_opt_len == 0) {
5359710SKen.Powell@Sun.COM DTRACE_PROBE3(tx__tnopt__log__error__labeling__lostops__v4,
53611042SErik.Nordmark@Sun.COM char *, "options lack length for dest ip(1) with label(2).",
53711042SErik.Nordmark@Sun.COM ipaddr_t, dst, ts_label_t *, tsl);
5381676Sjpk return (EINVAL);
5391676Sjpk }
5401676Sjpk
5411676Sjpk return (0);
5421676Sjpk }
5431676Sjpk
5441676Sjpk /*
5451676Sjpk * Remove any existing security option (CIPSO) from the given IP
5461676Sjpk * header, move the 'buflen' bytes back to fill the gap, and return the number
5471676Sjpk * of bytes removed (as zero or negative number). Assumes that the headers are
5481676Sjpk * sane.
54911042SErik.Nordmark@Sun.COM *
55011042SErik.Nordmark@Sun.COM * Note that tsol_remove_secopt does not adjust ipha_length but
55111042SErik.Nordmark@Sun.COM * tsol_remove_secopt_v6 does adjust ip6_plen.
5521676Sjpk */
5531676Sjpk int
tsol_remove_secopt(ipha_t * ipha,int buflen)5541676Sjpk tsol_remove_secopt(ipha_t *ipha, int buflen)
5551676Sjpk {
5561676Sjpk int remlen, olen, oval, delta;
5571676Sjpk uchar_t *fptr, *tptr;
5581676Sjpk boolean_t noop_keep;
5591676Sjpk
5601676Sjpk remlen = IPH_HDR_LENGTH(ipha) - IP_SIMPLE_HDR_LENGTH;
5611676Sjpk fptr = tptr = (uchar_t *)(ipha + 1);
5621676Sjpk noop_keep = B_TRUE;
5631676Sjpk while (remlen > 0) {
5641676Sjpk oval = fptr[IPOPT_OPTVAL];
5651676Sjpk
5661676Sjpk /* terminate on end of list */
5671676Sjpk if (oval == IPOPT_EOL)
5681676Sjpk break;
5691676Sjpk
5701676Sjpk /*
5711676Sjpk * Delete any no-ops following a deleted option, at least up
5721676Sjpk * to a 4 octet alignment; copy others.
5731676Sjpk */
5741676Sjpk if (oval == IPOPT_NOP) {
5751676Sjpk if (((fptr - (uchar_t *)ipha) & 3) == 0)
5761676Sjpk noop_keep = B_TRUE;
5771676Sjpk if (noop_keep)
5781676Sjpk *tptr++ = oval;
5791676Sjpk fptr++;
5801676Sjpk remlen--;
5811676Sjpk continue;
5821676Sjpk }
5831676Sjpk
5841676Sjpk /* stop on corrupted list; just do nothing. */
5851676Sjpk if (remlen < 2)
5861676Sjpk return (0);
5871676Sjpk olen = fptr[IPOPT_OLEN];
5881676Sjpk if (olen < 2 || olen > remlen)
5891676Sjpk return (0);
5901676Sjpk
5911676Sjpk /* skip over security options to delete them */
5921676Sjpk if (oval == IPOPT_COMSEC || oval == IPOPT_SECURITY) {
5931676Sjpk noop_keep = B_FALSE;
5941676Sjpk fptr += olen;
5951676Sjpk remlen -= olen;
5961676Sjpk continue;
5971676Sjpk }
5981676Sjpk
5991676Sjpk /* copy the rest */
6001676Sjpk noop_keep = B_TRUE;
6011676Sjpk if (tptr != fptr)
6021676Sjpk ovbcopy(fptr, tptr, olen);
6031676Sjpk fptr += olen;
6041676Sjpk tptr += olen;
6051676Sjpk remlen -= olen;
6061676Sjpk }
6071676Sjpk
6081676Sjpk fptr += remlen;
6091676Sjpk
6101676Sjpk /* figure how much padding we'll need for header alignment */
6111676Sjpk olen = (tptr - (uchar_t *)ipha) & 3;
6121676Sjpk if (olen > 0) {
6131676Sjpk olen = 4 - olen;
6141676Sjpk /* pad with end-of-list */
6151676Sjpk bzero(tptr, olen);
6161676Sjpk tptr += olen;
6171676Sjpk }
6181676Sjpk
6191676Sjpk /* slide back the headers that follow and update the IP header */
6201676Sjpk delta = fptr - tptr;
6211676Sjpk if (delta != 0) {
6221676Sjpk ovbcopy(fptr, tptr, ((uchar_t *)ipha + buflen) - fptr);
6231676Sjpk ipha->ipha_version_and_hdr_length -= delta / 4;
6241676Sjpk }
6251676Sjpk return (-delta);
6261676Sjpk }
6271676Sjpk
6281676Sjpk /*
6291676Sjpk * Insert the option in 'optbuf' into the IP header pointed to by 'ipha', and
6301676Sjpk * move the data following the IP header (up to buflen) to accomodate the new
6311676Sjpk * option. Assumes that up to IP_MAX_OPT_LENGTH bytes are available (in total)
6321676Sjpk * for IP options. Returns the number of bytes actually inserted, or -1 if the
6331676Sjpk * option cannot be inserted. (Note that negative return values are possible
6341676Sjpk * when noops must be compressed, and that only -1 indicates error. Successful
6351676Sjpk * return value is always evenly divisible by 4, by definition.)
63611042SErik.Nordmark@Sun.COM *
63711042SErik.Nordmark@Sun.COM * Note that tsol_prepend_option does not adjust ipha_length but
63811042SErik.Nordmark@Sun.COM * tsol_prepend_option_v6 does adjust ip6_plen.
6391676Sjpk */
6401676Sjpk int
tsol_prepend_option(uchar_t * optbuf,ipha_t * ipha,int buflen)6411676Sjpk tsol_prepend_option(uchar_t *optbuf, ipha_t *ipha, int buflen)
6421676Sjpk {
6431676Sjpk int remlen, padding, lastpad, totlen;
6441676Sjpk int oval, olen;
6451676Sjpk int delta;
6461676Sjpk uchar_t *optr;
6471676Sjpk uchar_t tempopt[IP_MAX_OPT_LENGTH], *toptr;
6481676Sjpk
6491676Sjpk if (optbuf[IPOPT_OPTVAL] == IPOPT_EOL ||
6501676Sjpk optbuf[IPOPT_OPTVAL] == IPOPT_NOP ||
6511676Sjpk optbuf[IPOPT_OLEN] == 0)
6521676Sjpk return (0);
6531676Sjpk
6541676Sjpk ASSERT(optbuf[IPOPT_OLEN] >= 2 &&
6551676Sjpk optbuf[IPOPT_OLEN] <= IP_MAX_OPT_LENGTH);
6561676Sjpk
6571676Sjpk /* first find the real (unpadded) length of the existing options */
6581676Sjpk remlen = IPH_HDR_LENGTH(ipha) - IP_SIMPLE_HDR_LENGTH;
6591676Sjpk padding = totlen = lastpad = 0;
6601676Sjpk optr = (uchar_t *)(ipha + 1);
6611676Sjpk while (remlen > 0) {
6621676Sjpk oval = optr[IPOPT_OPTVAL];
6631676Sjpk
6641676Sjpk /* stop at end of list */
6651676Sjpk if (oval == IPOPT_EOL)
6661676Sjpk break;
6671676Sjpk
6681676Sjpk /* skip no-ops, noting that length byte isn't present */
6691676Sjpk if (oval == IPOPT_NOP) {
6701676Sjpk optr++;
6711676Sjpk padding++;
6721676Sjpk lastpad++;
6731676Sjpk totlen++;
6741676Sjpk remlen--;
6751676Sjpk continue;
6761676Sjpk }
6771676Sjpk
6781676Sjpk /* give up on a corrupted list; report failure */
6791676Sjpk if (remlen < 2)
6801676Sjpk return (-1);
6811676Sjpk olen = optr[IPOPT_OLEN];
6821676Sjpk if (olen < 2 || olen > remlen)
6831676Sjpk return (-1);
6841676Sjpk
6851676Sjpk lastpad = 0;
6861676Sjpk optr += olen;
6871676Sjpk totlen += olen;
6881676Sjpk remlen -= olen;
6891676Sjpk }
6901676Sjpk
6911676Sjpk /* completely ignore any trailing padding */
6921676Sjpk totlen -= lastpad;
6931676Sjpk padding -= lastpad;
6941676Sjpk
6951676Sjpk /*
6961676Sjpk * If some sort of inter-option alignment was present, try to preserve
6971676Sjpk * that alignment. If alignment pushes us out past the maximum, then
6981676Sjpk * discard it and try to compress to fit. (We just "assume" that any
6991676Sjpk * padding added was attempting to get 32 bit alignment. If that's
7001676Sjpk * wrong, that's just too bad.)
7011676Sjpk */
7021676Sjpk if (padding > 0) {
7031676Sjpk olen = (optbuf[IPOPT_OLEN] + 3) & ~3;
7041676Sjpk if (olen + totlen > IP_MAX_OPT_LENGTH) {
7051676Sjpk totlen -= padding;
7061676Sjpk if (olen + totlen > IP_MAX_OPT_LENGTH)
7071676Sjpk return (-1);
7081676Sjpk padding = 0;
7091676Sjpk }
7101676Sjpk }
7111676Sjpk
7121676Sjpk /*
7131676Sjpk * Since we may need to compress or expand the option list, we write to
7141676Sjpk * a temporary buffer and then copy the results back to the IP header.
7151676Sjpk */
7161676Sjpk toptr = tempopt;
7171676Sjpk
7181676Sjpk /* compute actual option to insert */
7191676Sjpk olen = optbuf[IPOPT_OLEN];
7201676Sjpk bcopy(optbuf, toptr, olen);
7211676Sjpk toptr += olen;
7221676Sjpk if (padding > 0) {
7231676Sjpk while ((olen & 3) != 0) {
7241676Sjpk *toptr++ = IPOPT_NOP;
7251676Sjpk olen++;
7261676Sjpk }
7271676Sjpk }
7281676Sjpk
7291676Sjpk /* copy over the existing options */
7301676Sjpk optr = (uchar_t *)(ipha + 1);
7311676Sjpk while (totlen > 0) {
7321676Sjpk oval = optr[IPOPT_OPTVAL];
7331676Sjpk
7341676Sjpk /* totlen doesn't include end-of-list marker */
7351676Sjpk ASSERT(oval != IPOPT_EOL);
7361676Sjpk
7371676Sjpk /* handle no-ops; copy if desired, ignore otherwise */
7381676Sjpk if (oval == IPOPT_NOP) {
7391676Sjpk if (padding > 0) {
7401676Sjpk /* note: cannot overflow due to checks above */
7411676Sjpk ASSERT(toptr < tempopt + IP_MAX_OPT_LENGTH);
7421676Sjpk *toptr++ = oval;
7431676Sjpk }
7441676Sjpk optr++;
7451676Sjpk totlen--;
7461676Sjpk continue;
7471676Sjpk }
7481676Sjpk
7491676Sjpk /* list cannot be corrupt at this point */
7501676Sjpk ASSERT(totlen >= 2);
7511676Sjpk olen = optr[IPOPT_OLEN];
7521676Sjpk ASSERT(olen >= 2 && olen <= totlen);
7531676Sjpk
7541676Sjpk /* cannot run out of room due to tests above */
7551676Sjpk ASSERT(toptr + olen <= tempopt + IP_MAX_OPT_LENGTH);
7561676Sjpk
7571676Sjpk bcopy(optr, toptr, olen);
7581676Sjpk optr += olen;
7591676Sjpk toptr += olen;
7601676Sjpk totlen -= olen;
7611676Sjpk }
7621676Sjpk
7631676Sjpk /* figure how much padding we'll need for header alignment */
7641676Sjpk olen = (toptr - tempopt) & 3;
7651676Sjpk if (olen > 0) {
7661676Sjpk olen = 4 - olen;
7671676Sjpk ASSERT(toptr + olen <= tempopt + IP_MAX_OPT_LENGTH);
7681676Sjpk /* pad with end-of-list value */
7691676Sjpk bzero(toptr, olen);
7701676Sjpk toptr += olen;
7711676Sjpk }
7721676Sjpk
7731676Sjpk /* move the headers as needed and update IP header */
7741676Sjpk olen = (toptr - tempopt) + IP_SIMPLE_HDR_LENGTH;
7751676Sjpk remlen = IPH_HDR_LENGTH(ipha);
7761676Sjpk delta = olen - remlen;
7771676Sjpk if (delta != 0) {
7781676Sjpk ovbcopy((uchar_t *)ipha + remlen, (uchar_t *)ipha + olen,
7791676Sjpk buflen - remlen);
7801676Sjpk ipha->ipha_version_and_hdr_length += delta / 4;
7811676Sjpk }
7821676Sjpk
7831676Sjpk /* slap in the new options */
7841676Sjpk bcopy(tempopt, ipha + 1, olen - IP_SIMPLE_HDR_LENGTH);
7851676Sjpk
7861676Sjpk return (delta);
7871676Sjpk }
7881676Sjpk
7891676Sjpk /*
79011042SErik.Nordmark@Sun.COM * tsol_check_label_v4()
7911676Sjpk *
7921676Sjpk * This routine computes the IP label that should be on the packet based on the
79311042SErik.Nordmark@Sun.COM * connection and destination information. It's called by the IP forwarding
79411042SErik.Nordmark@Sun.COM * logic and by ip_output_simple. The ULPs generate the labels before calling
79511042SErik.Nordmark@Sun.COM * conn_ip_output. If any adjustments to
79611042SErik.Nordmark@Sun.COM * the label are needed due to the connection's MAC-exempt status or
79711042SErik.Nordmark@Sun.COM * the destination's ability to receive labels, an "effective label"
79811042SErik.Nordmark@Sun.COM * will be returned.
7991676Sjpk *
8006596Skp158701 * The packet's header is clear before entering IPsec's engine.
8011676Sjpk *
80211042SErik.Nordmark@Sun.COM * The zoneid is the IP zoneid (i.e., GLOBAL_ZONEID for exlusive-IP zones).
80311042SErik.Nordmark@Sun.COM * zone_is_global is set if the actual zoneid is global.
80411042SErik.Nordmark@Sun.COM *
80511042SErik.Nordmark@Sun.COM * On successful return, effective_tslp will point to the new label needed
80611042SErik.Nordmark@Sun.COM * or will be NULL if a new label isn't needed. On error, effective_tsl will
80711042SErik.Nordmark@Sun.COM * point to NULL.
80811042SErik.Nordmark@Sun.COM *
8091676Sjpk * Returns:
81011077SErik.Nordmark@Sun.COM * 0 Label (was|is now) correct
8111676Sjpk * EACCES The packet failed the remote host accreditation.
8121676Sjpk * ENOMEM Memory allocation failure.
8131676Sjpk * EINVAL Label cannot be computed
8141676Sjpk */
8151676Sjpk int
tsol_check_label_v4(const ts_label_t * tsl,zoneid_t zoneid,mblk_t ** mpp,uint_t mac_mode,boolean_t zone_is_global,ip_stack_t * ipst,ts_label_t ** effective_tslp)81611042SErik.Nordmark@Sun.COM tsol_check_label_v4(const ts_label_t *tsl, zoneid_t zoneid, mblk_t **mpp,
81711042SErik.Nordmark@Sun.COM uint_t mac_mode, boolean_t zone_is_global, ip_stack_t *ipst,
81811042SErik.Nordmark@Sun.COM ts_label_t **effective_tslp)
8191676Sjpk {
8201676Sjpk mblk_t *mp = *mpp;
8211676Sjpk ipha_t *ipha;
82211042SErik.Nordmark@Sun.COM ts_label_t *effective_tsl = NULL;
8231676Sjpk uchar_t opt_storage[IP_MAX_OPT_LENGTH];
8241676Sjpk uint_t hlen;
8251676Sjpk uint_t sec_opt_len;
8261676Sjpk uchar_t *optr;
8276596Skp158701 int delta_remove = 0, delta_add, adjust;
8281676Sjpk int retv;
8291676Sjpk
83011042SErik.Nordmark@Sun.COM *effective_tslp = NULL;
8311676Sjpk opt_storage[IPOPT_OPTVAL] = 0;
8321676Sjpk
8331676Sjpk ipha = (ipha_t *)mp->b_rptr;
8341676Sjpk
8359710SKen.Powell@Sun.COM /*
8369710SKen.Powell@Sun.COM * Verify the destination is allowed to receive packets at
83711042SErik.Nordmark@Sun.COM * the security label of the message data. tsol_check_dest()
83811042SErik.Nordmark@Sun.COM * may create a new effective label or label flags.
8399710SKen.Powell@Sun.COM */
84011042SErik.Nordmark@Sun.COM retv = tsol_check_dest(tsl, &ipha->ipha_dst, IPV4_VERSION,
84111042SErik.Nordmark@Sun.COM mac_mode, zone_is_global, &effective_tsl);
8421676Sjpk if (retv != 0)
8431676Sjpk return (retv);
8441676Sjpk
8459710SKen.Powell@Sun.COM /*
8469710SKen.Powell@Sun.COM * Calculate the security label to be placed in the text
8479710SKen.Powell@Sun.COM * of the message (if any).
8489710SKen.Powell@Sun.COM */
84911042SErik.Nordmark@Sun.COM if (effective_tsl != NULL) {
85011042SErik.Nordmark@Sun.COM if ((retv = tsol_compute_label_v4(effective_tsl, zoneid,
8519710SKen.Powell@Sun.COM ipha->ipha_dst, opt_storage, ipst)) != 0) {
85211042SErik.Nordmark@Sun.COM label_rele(effective_tsl);
8539710SKen.Powell@Sun.COM return (retv);
8549710SKen.Powell@Sun.COM }
85511042SErik.Nordmark@Sun.COM *effective_tslp = effective_tsl;
8569710SKen.Powell@Sun.COM } else {
85711042SErik.Nordmark@Sun.COM if ((retv = tsol_compute_label_v4(tsl, zoneid,
8589710SKen.Powell@Sun.COM ipha->ipha_dst, opt_storage, ipst)) != 0) {
8599710SKen.Powell@Sun.COM return (retv);
8609710SKen.Powell@Sun.COM }
8619710SKen.Powell@Sun.COM }
8629710SKen.Powell@Sun.COM
8631676Sjpk optr = (uchar_t *)(ipha + 1);
8641676Sjpk hlen = IPH_HDR_LENGTH(ipha) - IP_SIMPLE_HDR_LENGTH;
8651676Sjpk sec_opt_len = opt_storage[IPOPT_OLEN];
8661676Sjpk
8671676Sjpk if (hlen >= sec_opt_len) {
8681676Sjpk /* If no option is supposed to be there, make sure it's not */
8691676Sjpk if (sec_opt_len == 0 && hlen > 0 &&
8701676Sjpk optr[IPOPT_OPTVAL] != IPOPT_COMSEC &&
8711676Sjpk optr[IPOPT_OPTVAL] != IPOPT_SECURITY)
8721676Sjpk return (0);
8731676Sjpk /* if the option is there, it's always first */
8741676Sjpk if (sec_opt_len != 0 &&
8751676Sjpk bcmp(opt_storage, optr, sec_opt_len) == 0)
8761676Sjpk return (0);
8771676Sjpk }
8781676Sjpk
8791676Sjpk /*
8801676Sjpk * If there is an option there, then it must be the wrong one; delete.
8811676Sjpk */
8826596Skp158701 if (hlen > 0) {
8836596Skp158701 delta_remove = tsol_remove_secopt(ipha, MBLKL(mp));
8846596Skp158701 mp->b_wptr += delta_remove;
8856596Skp158701 }
8861676Sjpk
8871676Sjpk /* Make sure we have room for the worst-case addition */
8881676Sjpk hlen = IPH_HDR_LENGTH(ipha) + opt_storage[IPOPT_OLEN];
8891676Sjpk hlen = (hlen + 3) & ~3;
8901676Sjpk if (hlen > IP_MAX_HDR_LENGTH)
8911676Sjpk hlen = IP_MAX_HDR_LENGTH;
8921676Sjpk hlen -= IPH_HDR_LENGTH(ipha);
8931676Sjpk if (mp->b_wptr + hlen > mp->b_datap->db_lim) {
8941676Sjpk int copylen;
8951676Sjpk mblk_t *new_mp;
8961676Sjpk
8971676Sjpk /* allocate enough to be meaningful, but not *too* much */
8981676Sjpk copylen = MBLKL(mp);
8991676Sjpk if (copylen > 256)
9001676Sjpk copylen = 256;
9018778SErik.Nordmark@Sun.COM new_mp = allocb_tmpl(hlen + copylen +
9028778SErik.Nordmark@Sun.COM (mp->b_rptr - mp->b_datap->db_base), mp);
90311042SErik.Nordmark@Sun.COM if (new_mp == NULL) {
90411042SErik.Nordmark@Sun.COM if (effective_tsl != NULL) {
90511042SErik.Nordmark@Sun.COM label_rele(effective_tsl);
90611042SErik.Nordmark@Sun.COM *effective_tslp = NULL;
90711042SErik.Nordmark@Sun.COM }
9081676Sjpk return (ENOMEM);
90911042SErik.Nordmark@Sun.COM }
9101676Sjpk
9111676Sjpk /* keep the bias */
9121676Sjpk new_mp->b_rptr += mp->b_rptr - mp->b_datap->db_base;
9131676Sjpk new_mp->b_wptr = new_mp->b_rptr + copylen;
9141676Sjpk bcopy(mp->b_rptr, new_mp->b_rptr, copylen);
9151676Sjpk new_mp->b_cont = mp;
9161676Sjpk if ((mp->b_rptr += copylen) >= mp->b_wptr) {
9171676Sjpk new_mp->b_cont = mp->b_cont;
9181676Sjpk freeb(mp);
9191676Sjpk }
9201676Sjpk *mpp = mp = new_mp;
9211676Sjpk ipha = (ipha_t *)mp->b_rptr;
9221676Sjpk }
9231676Sjpk
9246596Skp158701 delta_add = tsol_prepend_option(opt_storage, ipha, MBLKL(mp));
9256596Skp158701 if (delta_add == -1)
9261676Sjpk goto param_prob;
9271676Sjpk
9286596Skp158701 ASSERT((mp->b_wptr + delta_add) <= DB_LIM(mp));
9296596Skp158701 mp->b_wptr += delta_add;
9301676Sjpk
9316596Skp158701 adjust = delta_remove + delta_add;
9326596Skp158701 adjust += ntohs(ipha->ipha_length);
9336596Skp158701 ipha->ipha_length = htons(adjust);
9341676Sjpk
9351676Sjpk return (0);
9361676Sjpk
9371676Sjpk param_prob:
93811042SErik.Nordmark@Sun.COM if (effective_tsl != NULL) {
93911042SErik.Nordmark@Sun.COM label_rele(effective_tsl);
94011042SErik.Nordmark@Sun.COM *effective_tslp = NULL;
94111042SErik.Nordmark@Sun.COM }
9421676Sjpk return (EINVAL);
9431676Sjpk }
9441676Sjpk
9451676Sjpk /*
9461676Sjpk * IPv6 HopOpt extension header for the label option layout:
9471676Sjpk * - One octet giving the type of the 'next extension header'
9481676Sjpk * - Header extension length in 8-byte words, not including the
9491676Sjpk * 1st 8 bytes, but including any pad bytes at the end.
9501676Sjpk * Eg. A value of 2 means 16 bytes not including the 1st 8 bytes.
9511676Sjpk * - Followed by TLV encoded IPv6 label option. Option layout is
9521676Sjpk * * One octet, IP6OPT_LS
9531676Sjpk * * One octet option length in bytes of the option data following
9541676Sjpk * the length, but not including any pad bytes at the end.
9551676Sjpk * * Four-octet DOI (IP6LS_DOI_V4)
9561676Sjpk * * One octet suboption, IP6LS_TT_V4
9571676Sjpk * * One octet suboption length in bytes of the suboption
9581676Sjpk * following the suboption length, including the suboption
9591676Sjpk * header length, but not including any pad bytes at the end.
9601676Sjpk * - Pad to make the extension header a multiple of 8 bytes.
9611676Sjpk *
9621676Sjpk * This function returns the contents of 'IPv6 option structure' in the above.
9631676Sjpk * i.e starting from the IP6OPT_LS but not including the pad at the end.
9641676Sjpk * The user must prepend two octets (either padding or next header / length)
9651676Sjpk * and append padding out to the next 8 octet boundary.
96611042SErik.Nordmark@Sun.COM *
96711042SErik.Nordmark@Sun.COM * The zoneid is the IP zoneid (i.e., GLOBAL_ZONEID for exlusive-IP zones).
9681676Sjpk */
9691676Sjpk int
tsol_compute_label_v6(const ts_label_t * tsl,zoneid_t zoneid,const in6_addr_t * dst,uchar_t * opt_storage,ip_stack_t * ipst)97011042SErik.Nordmark@Sun.COM tsol_compute_label_v6(const ts_label_t *tsl, zoneid_t zoneid,
97111042SErik.Nordmark@Sun.COM const in6_addr_t *dst, uchar_t *opt_storage, ip_stack_t *ipst)
9721676Sjpk {
9731676Sjpk uint_t sec_opt_len;
9741676Sjpk uint32_t doi;
97511042SErik.Nordmark@Sun.COM ire_t *ire;
97611042SErik.Nordmark@Sun.COM tsol_ire_gw_secattr_t *attrp = NULL;
9778778SErik.Nordmark@Sun.COM
9781676Sjpk if (ip6opt_ls == 0)
9791676Sjpk return (EINVAL);
9801676Sjpk
9811676Sjpk if (opt_storage != NULL)
9821676Sjpk opt_storage[IPOPT_OLEN] = 0;
9831676Sjpk
98411042SErik.Nordmark@Sun.COM if (tsl == NULL)
9851676Sjpk return (0);
9861676Sjpk
9871676Sjpk /* Always pass multicast */
9881676Sjpk if (IN6_IS_ADDR_MULTICAST(dst))
9891676Sjpk return (0);
9901676Sjpk
9911676Sjpk /*
9921676Sjpk * Fill in a V6 label. If a new format is added here, make certain
9931676Sjpk * that the maximum size of this label is reflected in sys/tsol/tnet.h
9941676Sjpk * as TSOL_MAX_IPV6_OPTION.
9951676Sjpk */
99610934Ssommerfeld@sun.com if (tsl->tsl_flags & TSLF_IMPLICIT_OUT)
99710934Ssommerfeld@sun.com return (0);
99810934Ssommerfeld@sun.com
9999710SKen.Powell@Sun.COM if (tsl->tsl_flags & TSLF_UNLABELED) {
10001676Sjpk /*
10019710SKen.Powell@Sun.COM * The destination is unlabeled. Only add a label if the
100211042SErik.Nordmark@Sun.COM * destination is not a broadcast/local/loopback address,
10039710SKen.Powell@Sun.COM * the destination is not on the same subnet, and the
10049710SKen.Powell@Sun.COM * next-hop gateway is labeled.
10051676Sjpk */
100611042SErik.Nordmark@Sun.COM ire = ire_route_recursive_v6(dst, 0, NULL, zoneid, tsl,
1007*11457SErik.Nordmark@Sun.COM MATCH_IRE_SECATTR, IRR_ALLOCATE, 0, ipst, NULL, &attrp,
1008*11457SErik.Nordmark@Sun.COM NULL);
100911042SErik.Nordmark@Sun.COM ASSERT(ire != NULL);
101011042SErik.Nordmark@Sun.COM if (ire->ire_flags & (RTF_REJECT|RTF_BLACKHOLE)) {
101111042SErik.Nordmark@Sun.COM /* no route to destination */
101211042SErik.Nordmark@Sun.COM ire_refrele(ire);
10139710SKen.Powell@Sun.COM DTRACE_PROBE3(
10141676Sjpk tx__tnopt__log__info__labeling__routedst__v6,
10159710SKen.Powell@Sun.COM char *, "No route to unlabeled dest ip6(1) with "
101611042SErik.Nordmark@Sun.COM "label(2).", in6_addr_t *, dst, ts_label_t *, tsl);
10179710SKen.Powell@Sun.COM return (EHOSTUNREACH);
10181676Sjpk }
101911042SErik.Nordmark@Sun.COM if (ire->ire_type & (IRE_LOCAL | IRE_LOOPBACK |
102011042SErik.Nordmark@Sun.COM IRE_INTERFACE)) {
102111042SErik.Nordmark@Sun.COM ire_refrele(ire);
102211042SErik.Nordmark@Sun.COM return (0);
102311042SErik.Nordmark@Sun.COM }
10241676Sjpk /*
102511042SErik.Nordmark@Sun.COM * ire_route_recursive gives us the first attrp it finds
102611042SErik.Nordmark@Sun.COM * in the recursive lookup.
10271676Sjpk */
10289710SKen.Powell@Sun.COM /*
10299710SKen.Powell@Sun.COM * Return now if next hop gateway is unlabeled. There is
10309710SKen.Powell@Sun.COM * no need to generate a CIPSO option for this message.
10319710SKen.Powell@Sun.COM */
10329710SKen.Powell@Sun.COM if (attrp == NULL || attrp->igsa_rhc == NULL ||
10339710SKen.Powell@Sun.COM attrp->igsa_rhc->rhc_tpc->tpc_tp.host_type == UNLABELED) {
103411042SErik.Nordmark@Sun.COM ire_refrele(ire);
10359710SKen.Powell@Sun.COM return (0);
10361676Sjpk }
103711042SErik.Nordmark@Sun.COM ire_refrele(ire);
10381676Sjpk }
10391676Sjpk
10401676Sjpk /* compute the CIPSO option */
10411676Sjpk if (opt_storage != NULL)
10421676Sjpk opt_storage += 8;
10439710SKen.Powell@Sun.COM sec_opt_len = tsol2cipso_tt1(&tsl->tsl_label, opt_storage,
10449710SKen.Powell@Sun.COM tsl->tsl_doi);
10451676Sjpk
10461676Sjpk if (sec_opt_len == 0) {
10479710SKen.Powell@Sun.COM DTRACE_PROBE3(tx__tnopt__log__error__labeling__lostops__v6,
10489710SKen.Powell@Sun.COM char *, "options lack length for dest ip6(1) with "
104911042SErik.Nordmark@Sun.COM "label(2).", in6_addr_t *, dst, ts_label_t *, tsl);
10501676Sjpk return (EINVAL);
10511676Sjpk }
10521676Sjpk
10531676Sjpk if (opt_storage == NULL)
10541676Sjpk return (0);
10551676Sjpk
10561676Sjpk if (sec_opt_len < IP_MAX_OPT_LENGTH)
10571676Sjpk opt_storage[sec_opt_len] = IPOPT_EOL;
10581676Sjpk
10591676Sjpk /*
10601676Sjpk * Just in case the option length is odd, round it up to the next even
10611676Sjpk * multiple. The IPv6 option definition doesn't like odd numbers for
10621676Sjpk * some reason.
10631676Sjpk *
10641676Sjpk * Length in the overall option header (IP6OPT_LS) does not include the
10651676Sjpk * option header itself, but the length in the suboption does include
10661676Sjpk * the suboption header. Thus, when there's just one suboption, the
10671676Sjpk * length in the option header is the suboption length plus 4 (for the
10681676Sjpk * DOI value).
10691676Sjpk */
10701676Sjpk opt_storage[-2] = IP6LS_TT_V4;
10711676Sjpk opt_storage[-1] = (sec_opt_len + 2 + 1) & ~1;
10721676Sjpk opt_storage[-8] = ip6opt_ls;
10731676Sjpk opt_storage[-7] = opt_storage[-1] + 4;
10741676Sjpk doi = htons(IP6LS_DOI_V4);
10751676Sjpk bcopy(&doi, opt_storage - 6, 4);
10761676Sjpk
10771676Sjpk return (0);
10781676Sjpk }
10791676Sjpk
10801676Sjpk /*
10811676Sjpk * Locate the start of the IP6OPT_LS label option and return it.
10821676Sjpk * Also return the start of the next non-pad option in after_secoptp.
10831676Sjpk * Usually the label option is the first option at least when packets
10841676Sjpk * are generated, but for generality we don't assume that on received packets.
108510181SKen.Powell@Sun.COM *
108610181SKen.Powell@Sun.COM * The function will return with B_FALSE if an IP format error
108710181SKen.Powell@Sun.COM * or an unexpected label content error is encountered.
10881676Sjpk */
108910181SKen.Powell@Sun.COM boolean_t
tsol_find_secopt_v6(const uchar_t * ip6hbh,uint_t hbhlen,uchar_t ** secoptp,uchar_t ** after_secoptp,boolean_t * hbh_needed)10901676Sjpk tsol_find_secopt_v6(
10911676Sjpk const uchar_t *ip6hbh, /* Start of the hop-by-hop extension header */
10921676Sjpk uint_t hbhlen, /* Length of the hop-by-hop extension header */
109310181SKen.Powell@Sun.COM uchar_t **secoptp, /* Location of IP6OPT_LS label option */
10941676Sjpk uchar_t **after_secoptp, /* Non-pad option following the label option */
10951676Sjpk boolean_t *hbh_needed) /* Is hop-by-hop hdr needed w/o label */
10961676Sjpk {
10971676Sjpk uint_t optlen;
10981676Sjpk uint_t optused;
10991676Sjpk const uchar_t *optptr;
11001676Sjpk uchar_t opt_type;
11011676Sjpk
110210181SKen.Powell@Sun.COM *secoptp = NULL;
11031676Sjpk *hbh_needed = B_FALSE;
11041676Sjpk *after_secoptp = NULL;
11051676Sjpk optlen = hbhlen - 2;
11061676Sjpk optptr = ip6hbh + 2;
11071676Sjpk while (optlen != 0) {
11081676Sjpk opt_type = *optptr;
11091676Sjpk if (opt_type == IP6OPT_PAD1) {
11101676Sjpk optptr++;
11111676Sjpk optlen--;
11121676Sjpk continue;
11131676Sjpk }
11141676Sjpk if (optlen == 1)
111510181SKen.Powell@Sun.COM return (B_FALSE);
11161676Sjpk optused = 2 + optptr[1];
11171676Sjpk if (optused > optlen)
111810181SKen.Powell@Sun.COM return (B_FALSE);
11191676Sjpk /*
11201676Sjpk * if we get here, ip6opt_ls can
11211676Sjpk * not be 0 because it will always
11221676Sjpk * match the IP6OPT_PAD1 above.
11231676Sjpk * Therefore ip6opt_ls == 0 forces
11241676Sjpk * this test to always fail here.
11251676Sjpk */
112610181SKen.Powell@Sun.COM if (opt_type == ip6opt_ls) {
112710181SKen.Powell@Sun.COM if (*secoptp != NULL)
112810181SKen.Powell@Sun.COM /* More than one security option found */
112910181SKen.Powell@Sun.COM return (B_FALSE);
113010181SKen.Powell@Sun.COM *secoptp = (uchar_t *)optptr;
113110181SKen.Powell@Sun.COM } else switch (opt_type) {
11321676Sjpk case IP6OPT_PADN:
11331676Sjpk break;
11341676Sjpk default:
11351676Sjpk /*
11361676Sjpk * There is at least 1 option other than
11371676Sjpk * the label option. So the hop-by-hop header is needed
11381676Sjpk */
11391676Sjpk *hbh_needed = B_TRUE;
114010181SKen.Powell@Sun.COM if (*secoptp != NULL) {
11411676Sjpk *after_secoptp = (uchar_t *)optptr;
114210181SKen.Powell@Sun.COM return (B_TRUE);
11431676Sjpk }
11441676Sjpk break;
11451676Sjpk }
11461676Sjpk optlen -= optused;
11471676Sjpk optptr += optused;
11481676Sjpk }
114910181SKen.Powell@Sun.COM return (B_TRUE);
11501676Sjpk }
11511676Sjpk
11521676Sjpk /*
11531676Sjpk * Remove the label option from the hop-by-hop options header if it exists.
11541676Sjpk * 'buflen' is the total length of the packet typically b_wptr - b_rptr.
11551676Sjpk * Header and data following the label option that is deleted are copied
11564564Swy83408 * (i.e. slid backward) to the right position, and returns the number
11574564Swy83408 * of bytes removed (as zero or negative number.)
115811042SErik.Nordmark@Sun.COM *
115911042SErik.Nordmark@Sun.COM * Note that tsol_remove_secopt does not adjust ipha_length but
116011042SErik.Nordmark@Sun.COM * tsol_remove_secopt_v6 does adjust ip6_plen.
11611676Sjpk */
11621676Sjpk int
tsol_remove_secopt_v6(ip6_t * ip6h,int buflen)11631676Sjpk tsol_remove_secopt_v6(ip6_t *ip6h, int buflen)
11641676Sjpk {
11651676Sjpk uchar_t *ip6hbh; /* hop-by-hop header */
11661676Sjpk uint_t hbhlen; /* hop-by-hop extension header length */
11671676Sjpk uchar_t *secopt = NULL;
11681676Sjpk uchar_t *after_secopt;
11691676Sjpk uint_t pad;
11701676Sjpk uint_t delta;
11711676Sjpk boolean_t hbh_needed;
11721676Sjpk
11731676Sjpk /*
11741676Sjpk * hop-by-hop extension header must appear first, if it does not
11751676Sjpk * exist, there is no label option.
11761676Sjpk */
11771676Sjpk if (ip6h->ip6_nxt != IPPROTO_HOPOPTS)
11781676Sjpk return (0);
11791676Sjpk
11801676Sjpk ip6hbh = (uchar_t *)&ip6h[1];
11811676Sjpk hbhlen = (ip6hbh[1] + 1) << 3;
11821676Sjpk /*
11831676Sjpk * Locate the start of the label option if it exists and the end
11841676Sjpk * of the label option including pads if any.
11851676Sjpk */
118610181SKen.Powell@Sun.COM if (!tsol_find_secopt_v6(ip6hbh, hbhlen, &secopt, &after_secopt,
118710181SKen.Powell@Sun.COM &hbh_needed)) {
118810181SKen.Powell@Sun.COM /*
118910181SKen.Powell@Sun.COM * This function should not see invalid messages.
119010181SKen.Powell@Sun.COM * If one occurs, it would indicate either an
119110181SKen.Powell@Sun.COM * option previously verified in the forwarding
119210181SKen.Powell@Sun.COM * path has been corrupted or an option was
119310181SKen.Powell@Sun.COM * incorrectly generated locally.
119410181SKen.Powell@Sun.COM */
119510181SKen.Powell@Sun.COM ASSERT(0);
119610181SKen.Powell@Sun.COM return (0);
119710181SKen.Powell@Sun.COM }
11981676Sjpk if (secopt == NULL)
11991676Sjpk return (0);
12001676Sjpk if (!hbh_needed) {
12011676Sjpk uchar_t next_hdr;
12021676Sjpk /*
12031676Sjpk * The label option was the only option in the hop-by-hop
12041676Sjpk * header. We don't need the hop-by-hop header itself any
12051676Sjpk * longer.
12061676Sjpk */
12071676Sjpk next_hdr = ip6hbh[0];
12081676Sjpk ovbcopy(ip6hbh + hbhlen, ip6hbh,
12091676Sjpk buflen - (IPV6_HDR_LEN + hbhlen));
12102776Skp158701 ip6h->ip6_plen = htons(ntohs(ip6h->ip6_plen) - hbhlen);
12111676Sjpk ip6h->ip6_nxt = next_hdr;
12124564Swy83408 return (-hbhlen);
12131676Sjpk }
12141676Sjpk
12151676Sjpk if (after_secopt == NULL) {
12161676Sjpk /* There is no option following the label option */
12171676Sjpk after_secopt = ip6hbh + hbhlen;
12181676Sjpk }
12191676Sjpk
12201676Sjpk /*
12211676Sjpk * After deleting the label option, we need to slide the headers
12221676Sjpk * and data back, while still maintaining the same alignment (module 8)
12231676Sjpk * for the other options. So we slide the headers and data back only
12241676Sjpk * by an integral multiple of 8 bytes, and fill the remaining bytes
12251676Sjpk * with pads.
12261676Sjpk */
12271676Sjpk delta = after_secopt - secopt;
12281676Sjpk pad = delta % 8;
12291676Sjpk if (pad == 1) {
12301676Sjpk secopt[0] = IP6OPT_PAD1;
12311676Sjpk } else if (pad > 1) {
12321676Sjpk secopt[0] = IP6OPT_PADN;
12331676Sjpk secopt[1] = pad - 2;
12341676Sjpk if (pad > 2)
12351676Sjpk bzero(&secopt[2], pad - 2);
12361676Sjpk }
12371676Sjpk secopt += pad;
12381676Sjpk delta -= pad;
12391676Sjpk ovbcopy(after_secopt, secopt,
12401676Sjpk (uchar_t *)ip6h + buflen - after_secopt);
12411676Sjpk ip6hbh[1] -= delta/8;
12422776Skp158701 ip6h->ip6_plen = htons(ntohs(ip6h->ip6_plen) - delta);
12431676Sjpk
12444564Swy83408 return (-delta);
12451676Sjpk }
12461676Sjpk
12471676Sjpk /*
12481676Sjpk * 'optbuf' contains a CIPSO label embedded in an IPv6 hop-by-hop option,
12491676Sjpk * starting with the IP6OPT_LS option type. The format of this hop-by-hop
12501676Sjpk * option is described in the block comment above tsol_compute_label_v6.
12511676Sjpk * This function prepends this hop-by-hop option before any other hop-by-hop
12521676Sjpk * options in the hop-by-hop header if one already exists, else a new
12531676Sjpk * hop-by-hop header is created and stuffed into the packet following
12541676Sjpk * the IPv6 header. 'buflen' is the total length of the packet i.e.
12551676Sjpk * b_wptr - b_rptr. The caller ensures that there is enough space for the
12561676Sjpk * extra option being added. Header and data following the position where
12571676Sjpk * the label option is inserted are copied (i.e. slid forward) to the right
12581676Sjpk * position.
125911042SErik.Nordmark@Sun.COM *
126011042SErik.Nordmark@Sun.COM * Note that tsol_prepend_option does not adjust ipha_length but
126111042SErik.Nordmark@Sun.COM * tsol_prepend_option_v6 does adjust ip6_plen.
12621676Sjpk */
12631676Sjpk int
tsol_prepend_option_v6(uchar_t * optbuf,ip6_t * ip6h,int buflen)12641676Sjpk tsol_prepend_option_v6(uchar_t *optbuf, ip6_t *ip6h, int buflen)
12651676Sjpk {
12661676Sjpk /*
12671676Sjpk * rawlen is the length of the label option in bytes, not including
12681676Sjpk * any pads, starting from the IP6OPT_LS (option type) byte.
12691676Sjpk */
12701676Sjpk uint_t rawlen;
12711676Sjpk
12721676Sjpk uint_t optlen; /* rawlen rounded to an 8 byte multiple */
12731676Sjpk uchar_t *ip6hbh; /* start of the hop-by-hop extension header */
12741676Sjpk uint_t hbhlen; /* Length of the hop-by-hop extension header */
12751676Sjpk uint_t pad_len;
12761676Sjpk uchar_t *pad_position;
12771676Sjpk int delta; /* Actual number of bytes inserted */
12781676Sjpk
12791676Sjpk rawlen = optbuf[1] + 2; /* Add 2 for the option type, option length */
12801676Sjpk ip6hbh = (uchar_t *)&ip6h[1];
12811676Sjpk if (ip6h->ip6_nxt == IPPROTO_HOPOPTS) {
12821676Sjpk /*
12831676Sjpk * There is a hop-by-hop header present already. In order to
12841676Sjpk * preserve the alignment of the other options at the existing
12851676Sjpk * value (modulo 8) we need to pad the label option to a
12861676Sjpk * multiple of 8 bytes before prepending it to the other
12871676Sjpk * options. Slide the extension headers and data forward to
12881676Sjpk * accomodate the label option at the start of the hop-by-hop
12891676Sjpk * header
12901676Sjpk */
12911676Sjpk delta = optlen = (rawlen + 7) & ~7;
12921676Sjpk pad_len = optlen - rawlen;
12931676Sjpk pad_position = ip6hbh + 2 + rawlen;
12941676Sjpk ovbcopy(ip6hbh + 2, ip6hbh + 2 + optlen,
12951676Sjpk buflen - (IPV6_HDR_LEN + 2));
12961676Sjpk /*
12971676Sjpk * Bump up the hop-by-hop extension header length by
12981676Sjpk * the number of 8-byte words added
12991676Sjpk */
13001676Sjpk optlen >>= 3;
13011676Sjpk if (ip6hbh[1] + optlen > 255)
13021676Sjpk return (-1);
13031676Sjpk ip6hbh[1] += optlen;
13041676Sjpk } else {
13051676Sjpk /*
13061676Sjpk * There is no hop-by-hop header in the packet. Construct a
13071676Sjpk * new Hop-by-hop extension header (a multiple of 8 bytes).
13081676Sjpk * Slide any other extension headers and data forward to
13091676Sjpk * accomodate this hop-by-hop header
13101676Sjpk */
13111676Sjpk delta = hbhlen = (2 + rawlen + 7) & ~7; /* +2 for nxthdr, len */
13121676Sjpk pad_len = hbhlen - (2 + rawlen);
13131676Sjpk pad_position = ip6hbh + 2 + rawlen;
13141676Sjpk ovbcopy(ip6hbh, ip6hbh + hbhlen, buflen - IPV6_HDR_LEN);
13151676Sjpk ip6hbh[0] = ip6h->ip6_nxt;
13161676Sjpk /*
13171676Sjpk * hop-by-hop extension header length in 8-byte words, not
13181676Sjpk * including the 1st 8 bytes of the hop-by-hop header.
13191676Sjpk */
13201676Sjpk ip6hbh[1] = (hbhlen >> 3) - 1;
13211676Sjpk ip6h->ip6_nxt = IPPROTO_HOPOPTS;
13221676Sjpk }
13231676Sjpk /*
13241676Sjpk * Copy the label option into the hop-by-hop header and insert any
13251676Sjpk * needed pads
13261676Sjpk */
13271676Sjpk bcopy(optbuf, ip6hbh + 2, rawlen);
13281676Sjpk if (pad_len == 1) {
13291676Sjpk pad_position[0] = IP6OPT_PAD1;
13301676Sjpk } else if (pad_len > 1) {
13311676Sjpk pad_position[0] = IP6OPT_PADN;
13321676Sjpk pad_position[1] = pad_len - 2;
13331676Sjpk if (pad_len > 2)
13341676Sjpk bzero(pad_position + 2, pad_len - 2);
13351676Sjpk }
13362776Skp158701 ip6h->ip6_plen = htons(ntohs(ip6h->ip6_plen) + delta);
13371676Sjpk return (delta);
13381676Sjpk }
13391676Sjpk
13401676Sjpk /*
13411676Sjpk * tsol_check_label_v6()
13421676Sjpk *
13431676Sjpk * This routine computes the IP label that should be on the packet based on the
134411042SErik.Nordmark@Sun.COM * connection and destination information. It's called by the IP forwarding
134511042SErik.Nordmark@Sun.COM * logic and by ip_output_simple. The ULPs generate the labels before calling
134611042SErik.Nordmark@Sun.COM * conn_ip_output. If any adjustments to
134711042SErik.Nordmark@Sun.COM * the label are needed due to the connection's MAC-exempt status or
134811042SErik.Nordmark@Sun.COM * the destination's ability to receive labels, an "effective label"
134911042SErik.Nordmark@Sun.COM * will be returned.
135011042SErik.Nordmark@Sun.COM *
135111042SErik.Nordmark@Sun.COM * The packet's header is clear before entering IPsec's engine.
135211042SErik.Nordmark@Sun.COM *
135311042SErik.Nordmark@Sun.COM * The zoneid is the IP zoneid (i.e., GLOBAL_ZONEID for exlusive-IP zones).
135411042SErik.Nordmark@Sun.COM * zone_is_global is set if the actual zoneid is global.
135511042SErik.Nordmark@Sun.COM *
135611042SErik.Nordmark@Sun.COM * On successful return, effective_tslp will point to the new label needed
135711042SErik.Nordmark@Sun.COM * or will be NULL if a new label isn't needed. On error, effective_tsl will
135811042SErik.Nordmark@Sun.COM * point to NULL.
13591676Sjpk *
13601676Sjpk * Returns:
136111077SErik.Nordmark@Sun.COM * 0 Label (was|is now) correct
13629710SKen.Powell@Sun.COM * EACCES The packet failed the remote host accreditation.
13631676Sjpk * ENOMEM Memory allocation failure.
136411042SErik.Nordmark@Sun.COM * EINVAL Label cannot be computed
13651676Sjpk */
13661676Sjpk int
tsol_check_label_v6(const ts_label_t * tsl,zoneid_t zoneid,mblk_t ** mpp,uint_t mac_mode,boolean_t zone_is_global,ip_stack_t * ipst,ts_label_t ** effective_tslp)136711042SErik.Nordmark@Sun.COM tsol_check_label_v6(const ts_label_t *tsl, zoneid_t zoneid, mblk_t **mpp,
136811042SErik.Nordmark@Sun.COM uint_t mac_mode, boolean_t zone_is_global, ip_stack_t *ipst,
136911042SErik.Nordmark@Sun.COM ts_label_t **effective_tslp)
13701676Sjpk {
13711676Sjpk mblk_t *mp = *mpp;
13721676Sjpk ip6_t *ip6h;
137311042SErik.Nordmark@Sun.COM ts_label_t *effective_tsl = NULL;
13741676Sjpk /*
13751676Sjpk * Label option length is limited to IP_MAX_OPT_LENGTH for
13761676Sjpk * symmetry with IPv4. Can be relaxed if needed
13771676Sjpk */
13781676Sjpk uchar_t opt_storage[TSOL_MAX_IPV6_OPTION];
13791676Sjpk uint_t hlen;
13801676Sjpk uint_t sec_opt_len; /* label option length not including type, len */
13816596Skp158701 int delta_remove = 0, delta_add;
13821676Sjpk int retv;
13831676Sjpk uchar_t *after_secopt;
13841676Sjpk uchar_t *secopt = NULL;
13851676Sjpk uchar_t *ip6hbh;
13861676Sjpk uint_t hbhlen;
13871676Sjpk boolean_t hbh_needed;
13881676Sjpk
138911042SErik.Nordmark@Sun.COM *effective_tslp = NULL;
139011042SErik.Nordmark@Sun.COM
13919710SKen.Powell@Sun.COM /*
13929710SKen.Powell@Sun.COM * Verify the destination is allowed to receive packets at
139311042SErik.Nordmark@Sun.COM * the security label of the message data. tsol_check_dest()
139411042SErik.Nordmark@Sun.COM * may create a new effective label or label flags.
13959710SKen.Powell@Sun.COM */
13961676Sjpk ip6h = (ip6_t *)mp->b_rptr;
139711042SErik.Nordmark@Sun.COM retv = tsol_check_dest(tsl, &ip6h->ip6_dst, IPV6_VERSION,
139811042SErik.Nordmark@Sun.COM mac_mode, zone_is_global, &effective_tsl);
13991676Sjpk if (retv != 0)
14001676Sjpk return (retv);
14011676Sjpk
14029710SKen.Powell@Sun.COM /*
14039710SKen.Powell@Sun.COM * Calculate the security label to be placed in the text
14049710SKen.Powell@Sun.COM * of the message (if any).
14059710SKen.Powell@Sun.COM */
140611042SErik.Nordmark@Sun.COM if (effective_tsl != NULL) {
140711042SErik.Nordmark@Sun.COM if ((retv = tsol_compute_label_v6(effective_tsl, zoneid,
14089710SKen.Powell@Sun.COM &ip6h->ip6_dst, opt_storage, ipst)) != 0) {
140911042SErik.Nordmark@Sun.COM label_rele(effective_tsl);
14109710SKen.Powell@Sun.COM return (retv);
14119710SKen.Powell@Sun.COM }
141211042SErik.Nordmark@Sun.COM *effective_tslp = effective_tsl;
14139710SKen.Powell@Sun.COM } else {
141411042SErik.Nordmark@Sun.COM if ((retv = tsol_compute_label_v6(tsl, zoneid,
14159710SKen.Powell@Sun.COM &ip6h->ip6_dst, opt_storage, ipst)) != 0)
14169710SKen.Powell@Sun.COM return (retv);
14179710SKen.Powell@Sun.COM }
14189710SKen.Powell@Sun.COM
14191676Sjpk sec_opt_len = opt_storage[1];
14201676Sjpk
14211676Sjpk if (ip6h->ip6_nxt == IPPROTO_HOPOPTS) {
14221676Sjpk ip6hbh = (uchar_t *)&ip6h[1];
14231676Sjpk hbhlen = (ip6hbh[1] + 1) << 3;
142410181SKen.Powell@Sun.COM if (!tsol_find_secopt_v6(ip6hbh, hbhlen, &secopt,
142510181SKen.Powell@Sun.COM &after_secopt, &hbh_needed)) {
142610181SKen.Powell@Sun.COM /*
142710181SKen.Powell@Sun.COM * This function should not see invalid messages.
142810181SKen.Powell@Sun.COM * If one occurs, it would indicate either an
142910181SKen.Powell@Sun.COM * option previously verified in the forwarding
143010181SKen.Powell@Sun.COM * path has been corrupted or an option was
143110181SKen.Powell@Sun.COM * incorrectly generated locally.
143210181SKen.Powell@Sun.COM */
143310181SKen.Powell@Sun.COM ASSERT(0);
143410181SKen.Powell@Sun.COM return (EACCES);
143510181SKen.Powell@Sun.COM }
14361676Sjpk }
14371676Sjpk
14381676Sjpk if (sec_opt_len == 0 && secopt == NULL) {
14391676Sjpk /*
14401676Sjpk * The packet is not supposed to have a label, and it
14411676Sjpk * does not have one currently
14421676Sjpk */
14431676Sjpk return (0);
14441676Sjpk }
144510934Ssommerfeld@sun.com
14461676Sjpk if (secopt != NULL && sec_opt_len != 0 &&
14471676Sjpk (bcmp(opt_storage, secopt, sec_opt_len + 2) == 0)) {
14481676Sjpk /* The packet has the correct label already */
14491676Sjpk return (0);
14501676Sjpk }
14511676Sjpk
14521676Sjpk /*
14531676Sjpk * If there is an option there, then it must be the wrong one; delete.
14541676Sjpk */
14556596Skp158701 if (secopt != NULL) {
14566596Skp158701 delta_remove = tsol_remove_secopt_v6(ip6h, MBLKL(mp));
14576596Skp158701 mp->b_wptr += delta_remove;
14586596Skp158701 }
14591676Sjpk
14601676Sjpk /*
14611676Sjpk * Make sure we have room for the worst-case addition. Add 2 bytes for
14621676Sjpk * the hop-by-hop ext header's next header and length fields. Add
14631676Sjpk * another 2 bytes for the label option type, len and then round
14641676Sjpk * up to the next 8-byte multiple.
14651676Sjpk */
14661676Sjpk hlen = (4 + sec_opt_len + 7) & ~7;
14671676Sjpk if (mp->b_wptr + hlen > mp->b_datap->db_lim) {
14681676Sjpk int copylen;
14691676Sjpk mblk_t *new_mp;
14701676Sjpk uint16_t hdr_len;
14711676Sjpk
14721676Sjpk hdr_len = ip_hdr_length_v6(mp, ip6h);
14731676Sjpk /*
14741676Sjpk * Allocate enough to be meaningful, but not *too* much.
14751676Sjpk * Also all the IPv6 extension headers must be in the same mblk
14761676Sjpk */
14771676Sjpk copylen = MBLKL(mp);
14781676Sjpk if (copylen > 256)
14791676Sjpk copylen = 256;
14801676Sjpk if (copylen < hdr_len)
14811676Sjpk copylen = hdr_len;
14828778SErik.Nordmark@Sun.COM new_mp = allocb_tmpl(hlen + copylen +
14838778SErik.Nordmark@Sun.COM (mp->b_rptr - mp->b_datap->db_base), mp);
148411042SErik.Nordmark@Sun.COM if (new_mp == NULL) {
148511042SErik.Nordmark@Sun.COM if (effective_tsl != NULL) {
148611042SErik.Nordmark@Sun.COM label_rele(effective_tsl);
148711042SErik.Nordmark@Sun.COM *effective_tslp = NULL;
148811042SErik.Nordmark@Sun.COM }
14891676Sjpk return (ENOMEM);
149011042SErik.Nordmark@Sun.COM }
14911676Sjpk
14921676Sjpk /* keep the bias */
14931676Sjpk new_mp->b_rptr += mp->b_rptr - mp->b_datap->db_base;
14941676Sjpk new_mp->b_wptr = new_mp->b_rptr + copylen;
14951676Sjpk bcopy(mp->b_rptr, new_mp->b_rptr, copylen);
14961676Sjpk new_mp->b_cont = mp;
14971676Sjpk if ((mp->b_rptr += copylen) >= mp->b_wptr) {
14981676Sjpk new_mp->b_cont = mp->b_cont;
14991676Sjpk freeb(mp);
15001676Sjpk }
15011676Sjpk *mpp = mp = new_mp;
15021676Sjpk ip6h = (ip6_t *)mp->b_rptr;
15031676Sjpk }
15041676Sjpk
15056596Skp158701 delta_add = tsol_prepend_option_v6(opt_storage, ip6h, MBLKL(mp));
15066596Skp158701 if (delta_add == -1)
15071676Sjpk goto param_prob;
15081676Sjpk
15096596Skp158701 ASSERT(mp->b_wptr + delta_add <= DB_LIM(mp));
15106596Skp158701 mp->b_wptr += delta_add;
15111676Sjpk
151211042SErik.Nordmark@Sun.COM /* tsol_prepend_option_v6 has adjusted ip6_plen */
15131676Sjpk return (0);
15141676Sjpk
15151676Sjpk param_prob:
151611042SErik.Nordmark@Sun.COM if (effective_tsl != NULL) {
151711042SErik.Nordmark@Sun.COM label_rele(effective_tsl);
151811042SErik.Nordmark@Sun.COM *effective_tslp = NULL;
151911042SErik.Nordmark@Sun.COM }
15201676Sjpk return (EINVAL);
15211676Sjpk }
1522