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
tsol2cipso_tt1(const bslabel_t * sl,unsigned char * cop,uint32_t doi)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
tsol_get_option_v4(mblk_t * mp,tsol_ip_label_t * label_type,uchar_t ** buffer)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
tsol_get_option_v6(mblk_t * mp,tsol_ip_label_t * label_type,uchar_t ** buffer)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
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)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
tsol_compute_label_v4(const ts_label_t * tsl,zoneid_t zoneid,ipaddr_t dst,uchar_t * opt_storage,ip_stack_t * ipst)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
tsol_remove_secopt(ipha_t * ipha,int buflen)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
tsol_prepend_option(uchar_t * optbuf,ipha_t * ipha,int buflen)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
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)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
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)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
tsol_find_secopt_v6(const uchar_t * ip6hbh,uint_t hbhlen,uchar_t ** secoptp,uchar_t ** after_secoptp,boolean_t * hbh_needed)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
tsol_remove_secopt_v6(ip6_t * ip6h,int buflen)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
tsol_prepend_option_v6(uchar_t * optbuf,ip6_t * ip6h,int buflen)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
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)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