Merge branch 'less_closed'
[unleashed.git] / kernel / net / ip / tn_ipopt.c
blobfe92c91f7c72980f995f845e0d2220a327659e25
1 /*
2 * CDDL HEADER START
4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License (the "License").
6 * You may not use this file except in compliance with the License.
8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 * or http://www.opensolaris.org/os/licensing.
10 * See the License for the specific language governing permissions
11 * and limitations under the License.
13 * When distributing Covered Code, include this CDDL HEADER in each
14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15 * If applicable, add the following below this CDDL HEADER, with the
16 * fields enclosed by brackets "[]" replaced with your own identifying
17 * information: Portions Copyright [yyyy] [name of copyright owner]
19 * CDDL HEADER END
22 * Copyright 2010 Sun Microsystems, Inc. All rights reserved.
23 * Use is subject to license terms.
26 #include <sys/types.h>
27 #include <sys/systm.h>
28 #include <sys/kmem.h>
29 #include <sys/disp.h>
30 #include <sys/stream.h>
31 #include <sys/strsubr.h>
32 #include <sys/strsun.h>
33 #include <sys/policy.h>
34 #include <sys/tsol/label_macro.h>
35 #include <sys/tsol/tndb.h>
36 #include <sys/tsol/tnet.h>
37 #include <inet/ip.h>
38 #include <inet/ip6.h>
39 #include <inet/tcp.h>
40 #include <inet/ipclassifier.h>
41 #include <inet/ip_ire.h>
42 #include <inet/ip_ftable.h>
45 * This routine takes a sensitivity label as input and creates a CIPSO
46 * option in the specified buffer. It returns the size of the CIPSO option.
47 * If the sensitivity label is too large for the CIPSO option, then 0
48 * is returned.
50 * tsol2cipso_tt1 returns 0 for failure and greater than 0 for success
51 * (more accurately, success means a return value between 10 and 40).
54 static int
55 tsol2cipso_tt1(const bslabel_t *sl, unsigned char *cop, uint32_t doi)
57 struct cipso_tag_type_1 *tt1;
58 const _bslabel_impl_t *bsl;
59 const uchar_t *ucp;
60 int i;
62 if (doi == 0)
63 return (0);
65 /* check for Admin High sensitivity label */
66 if (blequal(sl, label2bslabel(l_admin_high)))
67 return (0);
69 /* check whether classification will fit in one octet */
70 bsl = (const _bslabel_impl_t *)sl;
71 if (LCLASS(bsl) & 0xFF00)
72 return (0);
75 * Check whether compartments will fit in 30 octets.
76 * Compartments 241 - 256 are not allowed.
78 if (ntohl(bsl->compartments.c8) & 0x0000FFFF)
79 return (0);
82 * Compute option length and tag length.
83 * 'p' points to the last two bytes in the Sensitivity Label's
84 * compartments; these cannot be mapped into CIPSO compartments.
86 ucp = (const uchar_t *)&bsl->compartments.c8 + 2;
87 while (--ucp >= (const uchar_t *)&bsl->compartments.c1)
88 if (*ucp != 0)
89 break;
91 i = ucp - (const uchar_t *)&bsl->compartments.c1 + 1;
93 if (cop == NULL)
94 return (10 + i);
96 doi = htonl(doi);
97 ucp = (const uchar_t *)&doi;
98 cop[IPOPT_OPTVAL] = IPOPT_COMSEC;
99 cop[IPOPT_OLEN] = 10 + i;
100 cop[IPOPT_OLEN+1] = ucp[0];
101 cop[IPOPT_OLEN+2] = ucp[1];
102 cop[IPOPT_OLEN+3] = ucp[2];
103 cop[IPOPT_OLEN+4] = ucp[3];
104 tt1 = (struct cipso_tag_type_1 *)&cop[IPOPT_OLEN + 5];
105 tt1->tag_type = 1;
106 tt1->tag_align = 0;
107 tt1->tag_sl = LCLASS(bsl);
108 tt1->tag_length = 4 + i;
110 bcopy(&bsl->compartments.c1, tt1->tag_cat, i);
112 return (cop[IPOPT_OLEN]);
116 * The following routine searches for a security label in an IPv4 datagram.
117 * It returns label_type of:
118 * OPT_CIPSO if a CIPSO IP option is found.
119 * OPT_NONE if no security label is found.
121 * If OPT_CIPSO, a pointer to the CIPSO IP option will be returned in
122 * the buffer parameter.
124 * The function will return with B_FALSE if an IP format error
125 * is encountered.
128 boolean_t
129 tsol_get_option_v4(mblk_t *mp, tsol_ip_label_t *label_type, uchar_t **buffer)
131 ipha_t *ipha;
132 uchar_t *opt;
133 uint32_t totallen;
134 uint32_t optval;
135 uint32_t optlen;
137 *label_type = OPT_NONE;
140 * Get length (in 4 byte octets) of IP header options.
141 * If header doesn't contain options, then return a label_type
142 * of OPT_NONE.
144 ipha = (ipha_t *)mp->b_rptr;
145 totallen = ipha->ipha_version_and_hdr_length -
146 (uint8_t)((IP_VERSION << 4));
147 totallen <<= 2;
148 if (totallen < IP_SIMPLE_HDR_LENGTH || totallen > MBLKL(mp))
149 return (B_FALSE);
150 totallen -= IP_SIMPLE_HDR_LENGTH;
151 if (totallen == 0)
152 return (B_TRUE);
155 * Search for CIPSO option.
156 * If no such option is present, then return OPT_NONE.
158 opt = (uchar_t *)&ipha[1];
159 while (totallen != 0) {
160 switch (optval = opt[IPOPT_OPTVAL]) {
161 case IPOPT_EOL:
162 return (B_TRUE);
163 case IPOPT_NOP:
164 optlen = 1;
165 break;
166 default:
167 if (totallen <= IPOPT_OLEN)
168 return (B_FALSE);
169 optlen = opt[IPOPT_OLEN];
170 if (optlen < 2)
171 return (B_FALSE);
173 if (optlen > totallen)
174 return (B_FALSE);
176 * Copy pointer to option into '*buffer' and
177 * return the option type.
179 switch (optval) {
180 case IPOPT_COMSEC:
181 if (TSOL_CIPSO_TAG_OFFSET < optlen &&
182 opt[TSOL_CIPSO_TAG_OFFSET] == 1) {
183 *label_type = OPT_CIPSO;
184 *buffer = opt;
185 return (B_TRUE);
187 return (B_FALSE);
189 totallen -= optlen;
190 opt += optlen;
192 return (B_TRUE);
196 * The following routine searches for a security label in an IPv6 datagram.
197 * It returns label_type of:
198 * OPT_CIPSO if a CIPSO IP option is found.
199 * OPT_NONE if no security label is found.
201 * If OPT_CIPSO, a pointer to the IPv4 portion of the CIPSO IP option will
202 * be returned in the buffer parameter.
204 * The function will return with B_FALSE if an IP format error
205 * or an unexpected label content error is encountered.
208 boolean_t
209 tsol_get_option_v6(mblk_t *mp, tsol_ip_label_t *label_type, uchar_t **buffer)
211 uchar_t *opt_ptr = NULL;
212 uchar_t *after_secopt;
213 boolean_t hbh_needed;
214 const uchar_t *ip6hbh;
215 size_t optlen;
216 uint32_t doi;
217 const ip6_t *ip6h;
219 *label_type = OPT_NONE;
220 *buffer = NULL;
221 ip6h = (const ip6_t *)mp->b_rptr;
222 if (ip6h->ip6_nxt != IPPROTO_HOPOPTS)
223 return (B_TRUE);
224 ip6hbh = (const uchar_t *)&ip6h[1];
225 if (ip6hbh + MIN_EHDR_LEN > mp->b_wptr)
226 return (B_FALSE);
227 optlen = (ip6hbh[1] + 1) << 3;
228 if (ip6hbh + optlen > mp->b_wptr)
229 return (B_FALSE);
230 if (!tsol_find_secopt_v6(ip6hbh, optlen,
231 &opt_ptr, &after_secopt, &hbh_needed))
232 return (B_FALSE);
233 /* tsol_find_secopt_v6 guarantees some sanity */
234 if (opt_ptr != NULL) {
236 * IPv6 Option
237 * opt_ptr[0]: Option type
238 * opt_ptr[1]: Length of option data in bytes
239 * opt_ptr[2]: First byte of option data
241 if ((optlen = opt_ptr[1]) < 8)
242 return (B_FALSE);
243 opt_ptr += 2;
245 * From "Generalized Labeled Security Option for IPv6" draft
246 * opt_ptr[0] - opt_ptr[4]: DOI = IP6LS_DOI_V4
247 * opt_ptr[4]: Tag type = IP6LS_TT_V4
248 * opt_ptr[5]: Tag length in bytes starting at Tag type field
249 * IPv4 CIPSO Option
250 * opt_ptr[6]: option type
251 * opt_ptr[7]: option length in bytes starting at type field
253 bcopy(opt_ptr, &doi, sizeof (doi));
254 doi = ntohl(doi);
255 if (doi == IP6LS_DOI_V4 &&
256 opt_ptr[4] == IP6LS_TT_V4 &&
257 opt_ptr[5] <= optlen - 4 &&
258 opt_ptr[7] <= optlen - 6 &&
259 opt_ptr[7] <= opt_ptr[5] - 2) {
260 opt_ptr += sizeof (doi) + 2;
261 *label_type = OPT_CIPSO;
262 *buffer = opt_ptr;
263 return (B_TRUE);
265 return (B_FALSE);
267 return (B_TRUE);
271 * tsol_check_dest()
273 * This routine verifies if a destination is allowed to recieve messages
274 * based on the security label. If any adjustments to the label are needed
275 * due to the connection's MAC mode or the destination's ability
276 * to receive labels, an "effective label" will be returned.
278 * zone_is_global is set if the actual zoneid is global. That is, it is
279 * not set for an exclusive-IP zone.
281 * On successful return, effective_tsl will point to the new label needed
282 * or will be NULL if a new label isn't needed. On error, effective_tsl will
283 * point to NULL.
285 * Returns:
286 * 0 Label (was|is now) correct
287 * EHOSTUNREACH The label failed the remote host accreditation
288 * ENOMEM Memory allocation failure
291 tsol_check_dest(const ts_label_t *tsl, const void *dst,
292 uchar_t version, uint_t mac_mode, boolean_t zone_is_global,
293 ts_label_t **effective_tsl)
295 ts_label_t *newtsl = NULL;
296 tsol_tpc_t *dst_rhtp;
298 if (effective_tsl != NULL)
299 *effective_tsl = NULL;
300 ASSERT(version == IPV4_VERSION ||
301 (version == IPV6_VERSION &&
302 !IN6_IS_ADDR_V4MAPPED((in6_addr_t *)dst)));
304 /* Always pass kernel level communication (NULL label) */
305 if (tsl == NULL) {
306 DTRACE_PROBE2(tx__tnopt__log__info__labeling__mac__allownull,
307 char *, "destination ip(1) with null label was passed",
308 ipaddr_t, dst);
309 return (0);
312 if (tsl->tsl_flags & TSLF_IMPLICIT_IN) {
313 DTRACE_PROBE3(tx__tnopt__log__info__labeling__unresolved__label,
314 char *,
315 "implicit-in packet to ip(1) reached tsol_check_dest "
316 "with implied security label sl(2)",
317 ipaddr_t, dst, ts_label_t *, tsl);
320 /* Always pass multicast */
321 if (version == IPV4_VERSION &&
322 CLASSD(*(ipaddr_t *)dst)) {
323 DTRACE_PROBE2(tx__tnopt__log__info__labeling__mac__allowmult,
324 char *, "destination ip(1) with multicast dest was passed",
325 ipaddr_t, dst);
326 return (0);
327 } else if (version == IPV6_VERSION &&
328 IN6_IS_ADDR_MULTICAST((in6_addr_t *)dst)) {
329 DTRACE_PROBE2(tx__tnopt__log__info__labeling__mac__allowmult_v6,
330 char *, "destination ip(1) with multicast dest was passed",
331 in6_addr_t *, dst);
332 return (0);
335 /* Never pass an undefined destination */
336 if ((dst_rhtp = find_tpc(dst, version, B_FALSE)) == NULL) {
337 DTRACE_PROBE2(tx__tnopt__log__info__labeling__lookupdst,
338 char *, "destination ip(1) not in tn database.",
339 void *, dst);
340 return (EHOSTUNREACH);
343 switch (dst_rhtp->tpc_tp.host_type) {
344 case UNLABELED:
346 * Can talk to unlabeled hosts if
347 * (1) zone's label matches the default label, or
348 * (2) SO_MAC_EXEMPT is on and we
349 * dominate the peer's label, or
350 * (3) SO_MAC_EXEMPT is on and
351 * this is the global zone
353 if (dst_rhtp->tpc_tp.tp_doi != tsl->tsl_doi) {
354 DTRACE_PROBE4(tx__tnopt__log__info__labeling__doi,
355 char *, "unlabeled dest ip(1)/tpc(2) doi does "
356 "not match msg label(3) doi.", void *, dst,
357 tsol_tpc_t *, dst_rhtp, ts_label_t *, tsl);
358 TPC_RELE(dst_rhtp);
359 return (EHOSTUNREACH);
361 if (!blequal(&dst_rhtp->tpc_tp.tp_def_label,
362 &tsl->tsl_label)) {
363 if (mac_mode != CONN_MAC_AWARE ||
364 !(zone_is_global ||
365 bldominates(&tsl->tsl_label,
366 &dst_rhtp->tpc_tp.tp_def_label))) {
367 DTRACE_PROBE4(
368 tx__tnopt__log__info__labeling__mac,
369 char *, "unlabeled dest ip(1)/tpc(2) does "
370 "not match msg label(3).", void *, dst,
371 tsol_tpc_t *, dst_rhtp, ts_label_t *, tsl);
372 TPC_RELE(dst_rhtp);
373 return (EHOSTUNREACH);
376 * This is a downlabel MAC-exempt exchange.
377 * Use the remote destination's default label
378 * as the label of the message data.
380 if ((newtsl = labelalloc(&dst_rhtp->tpc_tp.tp_def_label,
381 dst_rhtp->tpc_tp.tp_doi, KM_NOSLEEP)) == NULL) {
382 TPC_RELE(dst_rhtp);
383 return (ENOMEM);
385 newtsl->tsl_flags |= TSLF_UNLABELED;
387 } else if (!(tsl->tsl_flags & TSLF_UNLABELED)) {
389 * The security labels are the same but we need
390 * to flag that the remote node is unlabeled.
392 if ((newtsl = labeldup(tsl, KM_NOSLEEP)) == NULL) {
393 TPC_RELE(dst_rhtp);
394 return (ENOMEM);
396 newtsl->tsl_flags |= TSLF_UNLABELED;
398 break;
400 case SUN_CIPSO:
402 * Can talk to labeled hosts if zone's label is within target's
403 * label range or set.
405 if (dst_rhtp->tpc_tp.tp_cipso_doi_cipso != tsl->tsl_doi ||
406 (!_blinrange(&tsl->tsl_label,
407 &dst_rhtp->tpc_tp.tp_sl_range_cipso) &&
408 !blinlset(&tsl->tsl_label,
409 dst_rhtp->tpc_tp.tp_sl_set_cipso))) {
410 DTRACE_PROBE4(tx__tnopt__log__info__labeling__mac,
411 char *, "labeled dest ip(1)/tpc(2) does not "
412 "match msg label(3).", void *, dst,
413 tsol_tpc_t *, dst_rhtp, ts_label_t *, tsl);
414 TPC_RELE(dst_rhtp);
415 return (EHOSTUNREACH);
417 if ((tsl->tsl_flags & TSLF_UNLABELED) ||
418 (mac_mode == CONN_MAC_IMPLICIT)) {
420 * Copy label so we can modify the flags
422 if ((newtsl = labeldup(tsl, KM_NOSLEEP)) == NULL) {
423 TPC_RELE(dst_rhtp);
424 return (ENOMEM);
427 * The security label is a match but we need to
428 * clear the unlabeled flag for this remote node.
430 newtsl->tsl_flags &= ~TSLF_UNLABELED;
431 if (mac_mode == CONN_MAC_IMPLICIT)
432 newtsl->tsl_flags |= TSLF_IMPLICIT_OUT;
434 break;
436 default:
437 TPC_RELE(dst_rhtp);
438 return (EHOSTUNREACH);
442 * Return the new label.
444 if (newtsl != NULL) {
445 if (effective_tsl != NULL)
446 *effective_tsl = newtsl;
447 else
448 label_rele(newtsl);
450 TPC_RELE(dst_rhtp);
451 return (0);
455 * tsol_compute_label_v4()
457 * This routine computes the IP label that should be on a packet based on the
458 * connection and destination information.
460 * The zoneid is the IP zoneid (i.e., GLOBAL_ZONEID for exlusive-IP zones).
462 * Returns:
463 * 0 Fetched label
464 * EHOSTUNREACH No route to destination
465 * EINVAL Label cannot be computed
468 tsol_compute_label_v4(const ts_label_t *tsl, zoneid_t zoneid, ipaddr_t dst,
469 uchar_t *opt_storage, ip_stack_t *ipst)
471 uint_t sec_opt_len;
472 ire_t *ire;
473 tsol_ire_gw_secattr_t *attrp = NULL;
475 if (opt_storage != NULL)
476 opt_storage[IPOPT_OLEN] = 0;
478 if (tsl == NULL)
479 return (0);
481 /* always pass multicast */
482 if (CLASSD(dst))
483 return (0);
485 if (tsl->tsl_flags & TSLF_IMPLICIT_OUT)
486 return (0);
488 if (tsl->tsl_flags & TSLF_UNLABELED) {
490 * The destination is unlabeled. Only add a label if the
491 * destination is not a broadcast/local/loopback address,
492 * the destination is not on the same subnet, and the
493 * next-hop gateway is labeled.
495 ire = ire_route_recursive_v4(dst, 0, NULL, zoneid, tsl,
496 MATCH_IRE_SECATTR, IRR_ALLOCATE, 0, ipst, NULL, &attrp,
497 NULL);
498 ASSERT(ire != NULL);
499 if (ire->ire_flags & (RTF_REJECT|RTF_BLACKHOLE)) {
500 /* no route to destination */
501 ire_refrele(ire);
502 DTRACE_PROBE3(
503 tx__tnopt__log__info__labeling__routedst__v4,
504 char *, "No route to unlabeled dest ip(1) with "
505 "with label(2).", ipaddr_t, dst, ts_label_t *, tsl);
506 return (EHOSTUNREACH);
508 if (ire->ire_type & (IRE_BROADCAST | IRE_LOCAL | IRE_LOOPBACK |
509 IRE_INTERFACE)) {
510 ire_refrele(ire);
511 return (0);
515 * ire_route_recursive gives us the first attrp it finds
516 * in the recursive lookup.
519 * Return now if next hop gateway is unlabeled. There is
520 * no need to generate a CIPSO option for this message.
522 if (attrp == NULL || attrp->igsa_rhc == NULL ||
523 attrp->igsa_rhc->rhc_tpc->tpc_tp.host_type == UNLABELED) {
524 ire_refrele(ire);
525 return (0);
527 ire_refrele(ire);
530 /* compute the CIPSO option */
531 sec_opt_len = tsol2cipso_tt1(&tsl->tsl_label, opt_storage,
532 tsl->tsl_doi);
534 if (sec_opt_len == 0) {
535 DTRACE_PROBE3(tx__tnopt__log__error__labeling__lostops__v4,
536 char *, "options lack length for dest ip(1) with label(2).",
537 ipaddr_t, dst, ts_label_t *, tsl);
538 return (EINVAL);
541 return (0);
545 * Remove any existing security option (CIPSO) from the given IP
546 * header, move the 'buflen' bytes back to fill the gap, and return the number
547 * of bytes removed (as zero or negative number). Assumes that the headers are
548 * sane.
550 * Note that tsol_remove_secopt does not adjust ipha_length but
551 * tsol_remove_secopt_v6 does adjust ip6_plen.
554 tsol_remove_secopt(ipha_t *ipha, int buflen)
556 int remlen, olen, oval, delta;
557 uchar_t *fptr, *tptr;
558 boolean_t noop_keep;
560 remlen = IPH_HDR_LENGTH(ipha) - IP_SIMPLE_HDR_LENGTH;
561 fptr = tptr = (uchar_t *)(ipha + 1);
562 noop_keep = B_TRUE;
563 while (remlen > 0) {
564 oval = fptr[IPOPT_OPTVAL];
566 /* terminate on end of list */
567 if (oval == IPOPT_EOL)
568 break;
571 * Delete any no-ops following a deleted option, at least up
572 * to a 4 octet alignment; copy others.
574 if (oval == IPOPT_NOP) {
575 if (((fptr - (uchar_t *)ipha) & 3) == 0)
576 noop_keep = B_TRUE;
577 if (noop_keep)
578 *tptr++ = oval;
579 fptr++;
580 remlen--;
581 continue;
584 /* stop on corrupted list; just do nothing. */
585 if (remlen < 2)
586 return (0);
587 olen = fptr[IPOPT_OLEN];
588 if (olen < 2 || olen > remlen)
589 return (0);
591 /* skip over security options to delete them */
592 if (oval == IPOPT_COMSEC || oval == IPOPT_SECURITY) {
593 noop_keep = B_FALSE;
594 fptr += olen;
595 remlen -= olen;
596 continue;
599 /* copy the rest */
600 noop_keep = B_TRUE;
601 if (tptr != fptr)
602 ovbcopy(fptr, tptr, olen);
603 fptr += olen;
604 tptr += olen;
605 remlen -= olen;
608 fptr += remlen;
610 /* figure how much padding we'll need for header alignment */
611 olen = (tptr - (uchar_t *)ipha) & 3;
612 if (olen > 0) {
613 olen = 4 - olen;
614 /* pad with end-of-list */
615 bzero(tptr, olen);
616 tptr += olen;
619 /* slide back the headers that follow and update the IP header */
620 delta = fptr - tptr;
621 if (delta != 0) {
622 ovbcopy(fptr, tptr, ((uchar_t *)ipha + buflen) - fptr);
623 ipha->ipha_version_and_hdr_length -= delta / 4;
625 return (-delta);
629 * Insert the option in 'optbuf' into the IP header pointed to by 'ipha', and
630 * move the data following the IP header (up to buflen) to accomodate the new
631 * option. Assumes that up to IP_MAX_OPT_LENGTH bytes are available (in total)
632 * for IP options. Returns the number of bytes actually inserted, or -1 if the
633 * option cannot be inserted. (Note that negative return values are possible
634 * when noops must be compressed, and that only -1 indicates error. Successful
635 * return value is always evenly divisible by 4, by definition.)
637 * Note that tsol_prepend_option does not adjust ipha_length but
638 * tsol_prepend_option_v6 does adjust ip6_plen.
641 tsol_prepend_option(uchar_t *optbuf, ipha_t *ipha, int buflen)
643 int remlen, padding, lastpad, totlen;
644 int oval, olen;
645 int delta;
646 uchar_t *optr;
647 uchar_t tempopt[IP_MAX_OPT_LENGTH], *toptr;
649 if (optbuf[IPOPT_OPTVAL] == IPOPT_EOL ||
650 optbuf[IPOPT_OPTVAL] == IPOPT_NOP ||
651 optbuf[IPOPT_OLEN] == 0)
652 return (0);
654 ASSERT(optbuf[IPOPT_OLEN] >= 2 &&
655 optbuf[IPOPT_OLEN] <= IP_MAX_OPT_LENGTH);
657 /* first find the real (unpadded) length of the existing options */
658 remlen = IPH_HDR_LENGTH(ipha) - IP_SIMPLE_HDR_LENGTH;
659 padding = totlen = lastpad = 0;
660 optr = (uchar_t *)(ipha + 1);
661 while (remlen > 0) {
662 oval = optr[IPOPT_OPTVAL];
664 /* stop at end of list */
665 if (oval == IPOPT_EOL)
666 break;
668 /* skip no-ops, noting that length byte isn't present */
669 if (oval == IPOPT_NOP) {
670 optr++;
671 padding++;
672 lastpad++;
673 totlen++;
674 remlen--;
675 continue;
678 /* give up on a corrupted list; report failure */
679 if (remlen < 2)
680 return (-1);
681 olen = optr[IPOPT_OLEN];
682 if (olen < 2 || olen > remlen)
683 return (-1);
685 lastpad = 0;
686 optr += olen;
687 totlen += olen;
688 remlen -= olen;
691 /* completely ignore any trailing padding */
692 totlen -= lastpad;
693 padding -= lastpad;
696 * If some sort of inter-option alignment was present, try to preserve
697 * that alignment. If alignment pushes us out past the maximum, then
698 * discard it and try to compress to fit. (We just "assume" that any
699 * padding added was attempting to get 32 bit alignment. If that's
700 * wrong, that's just too bad.)
702 if (padding > 0) {
703 olen = (optbuf[IPOPT_OLEN] + 3) & ~3;
704 if (olen + totlen > IP_MAX_OPT_LENGTH) {
705 totlen -= padding;
706 if (olen + totlen > IP_MAX_OPT_LENGTH)
707 return (-1);
708 padding = 0;
713 * Since we may need to compress or expand the option list, we write to
714 * a temporary buffer and then copy the results back to the IP header.
716 toptr = tempopt;
718 /* compute actual option to insert */
719 olen = optbuf[IPOPT_OLEN];
720 bcopy(optbuf, toptr, olen);
721 toptr += olen;
722 if (padding > 0) {
723 while ((olen & 3) != 0) {
724 *toptr++ = IPOPT_NOP;
725 olen++;
729 /* copy over the existing options */
730 optr = (uchar_t *)(ipha + 1);
731 while (totlen > 0) {
732 oval = optr[IPOPT_OPTVAL];
734 /* totlen doesn't include end-of-list marker */
735 ASSERT(oval != IPOPT_EOL);
737 /* handle no-ops; copy if desired, ignore otherwise */
738 if (oval == IPOPT_NOP) {
739 if (padding > 0) {
740 /* note: cannot overflow due to checks above */
741 ASSERT(toptr < tempopt + IP_MAX_OPT_LENGTH);
742 *toptr++ = oval;
744 optr++;
745 totlen--;
746 continue;
749 /* list cannot be corrupt at this point */
750 ASSERT(totlen >= 2);
751 olen = optr[IPOPT_OLEN];
752 ASSERT(olen >= 2 && olen <= totlen);
754 /* cannot run out of room due to tests above */
755 ASSERT(toptr + olen <= tempopt + IP_MAX_OPT_LENGTH);
757 bcopy(optr, toptr, olen);
758 optr += olen;
759 toptr += olen;
760 totlen -= olen;
763 /* figure how much padding we'll need for header alignment */
764 olen = (toptr - tempopt) & 3;
765 if (olen > 0) {
766 olen = 4 - olen;
767 ASSERT(toptr + olen <= tempopt + IP_MAX_OPT_LENGTH);
768 /* pad with end-of-list value */
769 bzero(toptr, olen);
770 toptr += olen;
773 /* move the headers as needed and update IP header */
774 olen = (toptr - tempopt) + IP_SIMPLE_HDR_LENGTH;
775 remlen = IPH_HDR_LENGTH(ipha);
776 delta = olen - remlen;
777 if (delta != 0) {
778 ovbcopy((uchar_t *)ipha + remlen, (uchar_t *)ipha + olen,
779 buflen - remlen);
780 ipha->ipha_version_and_hdr_length += delta / 4;
783 /* slap in the new options */
784 bcopy(tempopt, ipha + 1, olen - IP_SIMPLE_HDR_LENGTH);
786 return (delta);
790 * tsol_check_label_v4()
792 * This routine computes the IP label that should be on the packet based on the
793 * connection and destination information. It's called by the IP forwarding
794 * logic and by ip_output_simple. The ULPs generate the labels before calling
795 * conn_ip_output. If any adjustments to
796 * the label are needed due to the connection's MAC-exempt status or
797 * the destination's ability to receive labels, an "effective label"
798 * will be returned.
800 * The packet's header is clear before entering IPsec's engine.
802 * The zoneid is the IP zoneid (i.e., GLOBAL_ZONEID for exlusive-IP zones).
803 * zone_is_global is set if the actual zoneid is global.
805 * On successful return, effective_tslp will point to the new label needed
806 * or will be NULL if a new label isn't needed. On error, effective_tsl will
807 * point to NULL.
809 * Returns:
810 * 0 Label (was|is now) correct
811 * EACCES The packet failed the remote host accreditation.
812 * ENOMEM Memory allocation failure.
813 * EINVAL Label cannot be computed
816 tsol_check_label_v4(const ts_label_t *tsl, zoneid_t zoneid, mblk_t **mpp,
817 uint_t mac_mode, boolean_t zone_is_global, ip_stack_t *ipst,
818 ts_label_t **effective_tslp)
820 mblk_t *mp = *mpp;
821 ipha_t *ipha;
822 ts_label_t *effective_tsl = NULL;
823 uchar_t opt_storage[IP_MAX_OPT_LENGTH];
824 uint_t hlen;
825 uint_t sec_opt_len;
826 uchar_t *optr;
827 int delta_remove = 0, delta_add, adjust;
828 int retv;
830 *effective_tslp = NULL;
831 opt_storage[IPOPT_OPTVAL] = 0;
833 ipha = (ipha_t *)mp->b_rptr;
836 * Verify the destination is allowed to receive packets at
837 * the security label of the message data. tsol_check_dest()
838 * may create a new effective label or label flags.
840 retv = tsol_check_dest(tsl, &ipha->ipha_dst, IPV4_VERSION,
841 mac_mode, zone_is_global, &effective_tsl);
842 if (retv != 0)
843 return (retv);
846 * Calculate the security label to be placed in the text
847 * of the message (if any).
849 if (effective_tsl != NULL) {
850 if ((retv = tsol_compute_label_v4(effective_tsl, zoneid,
851 ipha->ipha_dst, opt_storage, ipst)) != 0) {
852 label_rele(effective_tsl);
853 return (retv);
855 *effective_tslp = effective_tsl;
856 } else {
857 if ((retv = tsol_compute_label_v4(tsl, zoneid,
858 ipha->ipha_dst, opt_storage, ipst)) != 0) {
859 return (retv);
863 optr = (uchar_t *)(ipha + 1);
864 hlen = IPH_HDR_LENGTH(ipha) - IP_SIMPLE_HDR_LENGTH;
865 sec_opt_len = opt_storage[IPOPT_OLEN];
867 if (hlen >= sec_opt_len) {
868 /* If no option is supposed to be there, make sure it's not */
869 if (sec_opt_len == 0 && hlen > 0 &&
870 optr[IPOPT_OPTVAL] != IPOPT_COMSEC &&
871 optr[IPOPT_OPTVAL] != IPOPT_SECURITY)
872 return (0);
873 /* if the option is there, it's always first */
874 if (sec_opt_len != 0 &&
875 bcmp(opt_storage, optr, sec_opt_len) == 0)
876 return (0);
880 * If there is an option there, then it must be the wrong one; delete.
882 if (hlen > 0) {
883 delta_remove = tsol_remove_secopt(ipha, MBLKL(mp));
884 mp->b_wptr += delta_remove;
887 /* Make sure we have room for the worst-case addition */
888 hlen = IPH_HDR_LENGTH(ipha) + opt_storage[IPOPT_OLEN];
889 hlen = (hlen + 3) & ~3;
890 if (hlen > IP_MAX_HDR_LENGTH)
891 hlen = IP_MAX_HDR_LENGTH;
892 hlen -= IPH_HDR_LENGTH(ipha);
893 if (mp->b_wptr + hlen > mp->b_datap->db_lim) {
894 int copylen;
895 mblk_t *new_mp;
897 /* allocate enough to be meaningful, but not *too* much */
898 copylen = MBLKL(mp);
899 if (copylen > 256)
900 copylen = 256;
901 new_mp = allocb_tmpl(hlen + copylen +
902 (mp->b_rptr - mp->b_datap->db_base), mp);
903 if (new_mp == NULL) {
904 if (effective_tsl != NULL) {
905 label_rele(effective_tsl);
906 *effective_tslp = NULL;
908 return (ENOMEM);
911 /* keep the bias */
912 new_mp->b_rptr += mp->b_rptr - mp->b_datap->db_base;
913 new_mp->b_wptr = new_mp->b_rptr + copylen;
914 bcopy(mp->b_rptr, new_mp->b_rptr, copylen);
915 new_mp->b_cont = mp;
916 if ((mp->b_rptr += copylen) >= mp->b_wptr) {
917 new_mp->b_cont = mp->b_cont;
918 freeb(mp);
920 *mpp = mp = new_mp;
921 ipha = (ipha_t *)mp->b_rptr;
924 delta_add = tsol_prepend_option(opt_storage, ipha, MBLKL(mp));
925 if (delta_add == -1)
926 goto param_prob;
928 ASSERT((mp->b_wptr + delta_add) <= DB_LIM(mp));
929 mp->b_wptr += delta_add;
931 adjust = delta_remove + delta_add;
932 adjust += ntohs(ipha->ipha_length);
933 ipha->ipha_length = htons(adjust);
935 return (0);
937 param_prob:
938 if (effective_tsl != NULL) {
939 label_rele(effective_tsl);
940 *effective_tslp = NULL;
942 return (EINVAL);
946 * IPv6 HopOpt extension header for the label option layout:
947 * - One octet giving the type of the 'next extension header'
948 * - Header extension length in 8-byte words, not including the
949 * 1st 8 bytes, but including any pad bytes at the end.
950 * Eg. A value of 2 means 16 bytes not including the 1st 8 bytes.
951 * - Followed by TLV encoded IPv6 label option. Option layout is
952 * * One octet, IP6OPT_LS
953 * * One octet option length in bytes of the option data following
954 * the length, but not including any pad bytes at the end.
955 * * Four-octet DOI (IP6LS_DOI_V4)
956 * * One octet suboption, IP6LS_TT_V4
957 * * One octet suboption length in bytes of the suboption
958 * following the suboption length, including the suboption
959 * header length, but not including any pad bytes at the end.
960 * - Pad to make the extension header a multiple of 8 bytes.
962 * This function returns the contents of 'IPv6 option structure' in the above.
963 * i.e starting from the IP6OPT_LS but not including the pad at the end.
964 * The user must prepend two octets (either padding or next header / length)
965 * and append padding out to the next 8 octet boundary.
967 * The zoneid is the IP zoneid (i.e., GLOBAL_ZONEID for exlusive-IP zones).
970 tsol_compute_label_v6(const ts_label_t *tsl, zoneid_t zoneid,
971 const in6_addr_t *dst, uchar_t *opt_storage, ip_stack_t *ipst)
973 uint_t sec_opt_len;
974 uint32_t doi;
975 ire_t *ire;
976 tsol_ire_gw_secattr_t *attrp = NULL;
978 if (ip6opt_ls == 0)
979 return (EINVAL);
981 if (opt_storage != NULL)
982 opt_storage[IPOPT_OLEN] = 0;
984 if (tsl == NULL)
985 return (0);
987 /* Always pass multicast */
988 if (IN6_IS_ADDR_MULTICAST(dst))
989 return (0);
992 * Fill in a V6 label. If a new format is added here, make certain
993 * that the maximum size of this label is reflected in sys/tsol/tnet.h
994 * as TSOL_MAX_IPV6_OPTION.
996 if (tsl->tsl_flags & TSLF_IMPLICIT_OUT)
997 return (0);
999 if (tsl->tsl_flags & TSLF_UNLABELED) {
1001 * The destination is unlabeled. Only add a label if the
1002 * destination is not a broadcast/local/loopback address,
1003 * the destination is not on the same subnet, and the
1004 * next-hop gateway is labeled.
1006 ire = ire_route_recursive_v6(dst, 0, NULL, zoneid, tsl,
1007 MATCH_IRE_SECATTR, IRR_ALLOCATE, 0, ipst, NULL, &attrp,
1008 NULL);
1009 ASSERT(ire != NULL);
1010 if (ire->ire_flags & (RTF_REJECT|RTF_BLACKHOLE)) {
1011 /* no route to destination */
1012 ire_refrele(ire);
1013 DTRACE_PROBE3(
1014 tx__tnopt__log__info__labeling__routedst__v6,
1015 char *, "No route to unlabeled dest ip6(1) with "
1016 "label(2).", in6_addr_t *, dst, ts_label_t *, tsl);
1017 return (EHOSTUNREACH);
1019 if (ire->ire_type & (IRE_LOCAL | IRE_LOOPBACK |
1020 IRE_INTERFACE)) {
1021 ire_refrele(ire);
1022 return (0);
1025 * ire_route_recursive gives us the first attrp it finds
1026 * in the recursive lookup.
1029 * Return now if next hop gateway is unlabeled. There is
1030 * no need to generate a CIPSO option for this message.
1032 if (attrp == NULL || attrp->igsa_rhc == NULL ||
1033 attrp->igsa_rhc->rhc_tpc->tpc_tp.host_type == UNLABELED) {
1034 ire_refrele(ire);
1035 return (0);
1037 ire_refrele(ire);
1040 /* compute the CIPSO option */
1041 if (opt_storage != NULL)
1042 opt_storage += 8;
1043 sec_opt_len = tsol2cipso_tt1(&tsl->tsl_label, opt_storage,
1044 tsl->tsl_doi);
1046 if (sec_opt_len == 0) {
1047 DTRACE_PROBE3(tx__tnopt__log__error__labeling__lostops__v6,
1048 char *, "options lack length for dest ip6(1) with "
1049 "label(2).", in6_addr_t *, dst, ts_label_t *, tsl);
1050 return (EINVAL);
1053 if (opt_storage == NULL)
1054 return (0);
1056 if (sec_opt_len < IP_MAX_OPT_LENGTH)
1057 opt_storage[sec_opt_len] = IPOPT_EOL;
1060 * Just in case the option length is odd, round it up to the next even
1061 * multiple. The IPv6 option definition doesn't like odd numbers for
1062 * some reason.
1064 * Length in the overall option header (IP6OPT_LS) does not include the
1065 * option header itself, but the length in the suboption does include
1066 * the suboption header. Thus, when there's just one suboption, the
1067 * length in the option header is the suboption length plus 4 (for the
1068 * DOI value).
1070 opt_storage[-2] = IP6LS_TT_V4;
1071 opt_storage[-1] = (sec_opt_len + 2 + 1) & ~1;
1072 opt_storage[-8] = ip6opt_ls;
1073 opt_storage[-7] = opt_storage[-1] + 4;
1074 doi = htons(IP6LS_DOI_V4);
1075 bcopy(&doi, opt_storage - 6, 4);
1077 return (0);
1081 * Locate the start of the IP6OPT_LS label option and return it.
1082 * Also return the start of the next non-pad option in after_secoptp.
1083 * Usually the label option is the first option at least when packets
1084 * are generated, but for generality we don't assume that on received packets.
1086 * The function will return with B_FALSE if an IP format error
1087 * or an unexpected label content error is encountered.
1089 boolean_t
1090 tsol_find_secopt_v6(
1091 const uchar_t *ip6hbh, /* Start of the hop-by-hop extension header */
1092 uint_t hbhlen, /* Length of the hop-by-hop extension header */
1093 uchar_t **secoptp, /* Location of IP6OPT_LS label option */
1094 uchar_t **after_secoptp, /* Non-pad option following the label option */
1095 boolean_t *hbh_needed) /* Is hop-by-hop hdr needed w/o label */
1097 uint_t optlen;
1098 uint_t optused;
1099 const uchar_t *optptr;
1100 uchar_t opt_type;
1102 *secoptp = NULL;
1103 *hbh_needed = B_FALSE;
1104 *after_secoptp = NULL;
1105 optlen = hbhlen - 2;
1106 optptr = ip6hbh + 2;
1107 while (optlen != 0) {
1108 opt_type = *optptr;
1109 if (opt_type == IP6OPT_PAD1) {
1110 optptr++;
1111 optlen--;
1112 continue;
1114 if (optlen == 1)
1115 return (B_FALSE);
1116 optused = 2 + optptr[1];
1117 if (optused > optlen)
1118 return (B_FALSE);
1120 * if we get here, ip6opt_ls can
1121 * not be 0 because it will always
1122 * match the IP6OPT_PAD1 above.
1123 * Therefore ip6opt_ls == 0 forces
1124 * this test to always fail here.
1126 if (opt_type == ip6opt_ls) {
1127 if (*secoptp != NULL)
1128 /* More than one security option found */
1129 return (B_FALSE);
1130 *secoptp = (uchar_t *)optptr;
1131 } else switch (opt_type) {
1132 case IP6OPT_PADN:
1133 break;
1134 default:
1136 * There is at least 1 option other than
1137 * the label option. So the hop-by-hop header is needed
1139 *hbh_needed = B_TRUE;
1140 if (*secoptp != NULL) {
1141 *after_secoptp = (uchar_t *)optptr;
1142 return (B_TRUE);
1144 break;
1146 optlen -= optused;
1147 optptr += optused;
1149 return (B_TRUE);
1153 * Remove the label option from the hop-by-hop options header if it exists.
1154 * 'buflen' is the total length of the packet typically b_wptr - b_rptr.
1155 * Header and data following the label option that is deleted are copied
1156 * (i.e. slid backward) to the right position, and returns the number
1157 * of bytes removed (as zero or negative number.)
1159 * Note that tsol_remove_secopt does not adjust ipha_length but
1160 * tsol_remove_secopt_v6 does adjust ip6_plen.
1163 tsol_remove_secopt_v6(ip6_t *ip6h, int buflen)
1165 uchar_t *ip6hbh; /* hop-by-hop header */
1166 uint_t hbhlen; /* hop-by-hop extension header length */
1167 uchar_t *secopt = NULL;
1168 uchar_t *after_secopt;
1169 uint_t pad;
1170 uint_t delta;
1171 boolean_t hbh_needed;
1174 * hop-by-hop extension header must appear first, if it does not
1175 * exist, there is no label option.
1177 if (ip6h->ip6_nxt != IPPROTO_HOPOPTS)
1178 return (0);
1180 ip6hbh = (uchar_t *)&ip6h[1];
1181 hbhlen = (ip6hbh[1] + 1) << 3;
1183 * Locate the start of the label option if it exists and the end
1184 * of the label option including pads if any.
1186 if (!tsol_find_secopt_v6(ip6hbh, hbhlen, &secopt, &after_secopt,
1187 &hbh_needed)) {
1189 * This function should not see invalid messages.
1190 * If one occurs, it would indicate either an
1191 * option previously verified in the forwarding
1192 * path has been corrupted or an option was
1193 * incorrectly generated locally.
1195 ASSERT(0);
1196 return (0);
1198 if (secopt == NULL)
1199 return (0);
1200 if (!hbh_needed) {
1201 uchar_t next_hdr;
1203 * The label option was the only option in the hop-by-hop
1204 * header. We don't need the hop-by-hop header itself any
1205 * longer.
1207 next_hdr = ip6hbh[0];
1208 ovbcopy(ip6hbh + hbhlen, ip6hbh,
1209 buflen - (IPV6_HDR_LEN + hbhlen));
1210 ip6h->ip6_plen = htons(ntohs(ip6h->ip6_plen) - hbhlen);
1211 ip6h->ip6_nxt = next_hdr;
1212 return (-hbhlen);
1215 if (after_secopt == NULL) {
1216 /* There is no option following the label option */
1217 after_secopt = ip6hbh + hbhlen;
1221 * After deleting the label option, we need to slide the headers
1222 * and data back, while still maintaining the same alignment (module 8)
1223 * for the other options. So we slide the headers and data back only
1224 * by an integral multiple of 8 bytes, and fill the remaining bytes
1225 * with pads.
1227 delta = after_secopt - secopt;
1228 pad = delta % 8;
1229 if (pad == 1) {
1230 secopt[0] = IP6OPT_PAD1;
1231 } else if (pad > 1) {
1232 secopt[0] = IP6OPT_PADN;
1233 secopt[1] = pad - 2;
1234 if (pad > 2)
1235 bzero(&secopt[2], pad - 2);
1237 secopt += pad;
1238 delta -= pad;
1239 ovbcopy(after_secopt, secopt,
1240 (uchar_t *)ip6h + buflen - after_secopt);
1241 ip6hbh[1] -= delta/8;
1242 ip6h->ip6_plen = htons(ntohs(ip6h->ip6_plen) - delta);
1244 return (-delta);
1248 * 'optbuf' contains a CIPSO label embedded in an IPv6 hop-by-hop option,
1249 * starting with the IP6OPT_LS option type. The format of this hop-by-hop
1250 * option is described in the block comment above tsol_compute_label_v6.
1251 * This function prepends this hop-by-hop option before any other hop-by-hop
1252 * options in the hop-by-hop header if one already exists, else a new
1253 * hop-by-hop header is created and stuffed into the packet following
1254 * the IPv6 header. 'buflen' is the total length of the packet i.e.
1255 * b_wptr - b_rptr. The caller ensures that there is enough space for the
1256 * extra option being added. Header and data following the position where
1257 * the label option is inserted are copied (i.e. slid forward) to the right
1258 * position.
1260 * Note that tsol_prepend_option does not adjust ipha_length but
1261 * tsol_prepend_option_v6 does adjust ip6_plen.
1264 tsol_prepend_option_v6(uchar_t *optbuf, ip6_t *ip6h, int buflen)
1267 * rawlen is the length of the label option in bytes, not including
1268 * any pads, starting from the IP6OPT_LS (option type) byte.
1270 uint_t rawlen;
1272 uint_t optlen; /* rawlen rounded to an 8 byte multiple */
1273 uchar_t *ip6hbh; /* start of the hop-by-hop extension header */
1274 uint_t hbhlen; /* Length of the hop-by-hop extension header */
1275 uint_t pad_len;
1276 uchar_t *pad_position;
1277 int delta; /* Actual number of bytes inserted */
1279 rawlen = optbuf[1] + 2; /* Add 2 for the option type, option length */
1280 ip6hbh = (uchar_t *)&ip6h[1];
1281 if (ip6h->ip6_nxt == IPPROTO_HOPOPTS) {
1283 * There is a hop-by-hop header present already. In order to
1284 * preserve the alignment of the other options at the existing
1285 * value (modulo 8) we need to pad the label option to a
1286 * multiple of 8 bytes before prepending it to the other
1287 * options. Slide the extension headers and data forward to
1288 * accomodate the label option at the start of the hop-by-hop
1289 * header
1291 delta = optlen = (rawlen + 7) & ~7;
1292 pad_len = optlen - rawlen;
1293 pad_position = ip6hbh + 2 + rawlen;
1294 ovbcopy(ip6hbh + 2, ip6hbh + 2 + optlen,
1295 buflen - (IPV6_HDR_LEN + 2));
1297 * Bump up the hop-by-hop extension header length by
1298 * the number of 8-byte words added
1300 optlen >>= 3;
1301 if (ip6hbh[1] + optlen > 255)
1302 return (-1);
1303 ip6hbh[1] += optlen;
1304 } else {
1306 * There is no hop-by-hop header in the packet. Construct a
1307 * new Hop-by-hop extension header (a multiple of 8 bytes).
1308 * Slide any other extension headers and data forward to
1309 * accomodate this hop-by-hop header
1311 delta = hbhlen = (2 + rawlen + 7) & ~7; /* +2 for nxthdr, len */
1312 pad_len = hbhlen - (2 + rawlen);
1313 pad_position = ip6hbh + 2 + rawlen;
1314 ovbcopy(ip6hbh, ip6hbh + hbhlen, buflen - IPV6_HDR_LEN);
1315 ip6hbh[0] = ip6h->ip6_nxt;
1317 * hop-by-hop extension header length in 8-byte words, not
1318 * including the 1st 8 bytes of the hop-by-hop header.
1320 ip6hbh[1] = (hbhlen >> 3) - 1;
1321 ip6h->ip6_nxt = IPPROTO_HOPOPTS;
1324 * Copy the label option into the hop-by-hop header and insert any
1325 * needed pads
1327 bcopy(optbuf, ip6hbh + 2, rawlen);
1328 if (pad_len == 1) {
1329 pad_position[0] = IP6OPT_PAD1;
1330 } else if (pad_len > 1) {
1331 pad_position[0] = IP6OPT_PADN;
1332 pad_position[1] = pad_len - 2;
1333 if (pad_len > 2)
1334 bzero(pad_position + 2, pad_len - 2);
1336 ip6h->ip6_plen = htons(ntohs(ip6h->ip6_plen) + delta);
1337 return (delta);
1341 * tsol_check_label_v6()
1343 * This routine computes the IP label that should be on the packet based on the
1344 * connection and destination information. It's called by the IP forwarding
1345 * logic and by ip_output_simple. The ULPs generate the labels before calling
1346 * conn_ip_output. If any adjustments to
1347 * the label are needed due to the connection's MAC-exempt status or
1348 * the destination's ability to receive labels, an "effective label"
1349 * will be returned.
1351 * The packet's header is clear before entering IPsec's engine.
1353 * The zoneid is the IP zoneid (i.e., GLOBAL_ZONEID for exlusive-IP zones).
1354 * zone_is_global is set if the actual zoneid is global.
1356 * On successful return, effective_tslp will point to the new label needed
1357 * or will be NULL if a new label isn't needed. On error, effective_tsl will
1358 * point to NULL.
1360 * Returns:
1361 * 0 Label (was|is now) correct
1362 * EACCES The packet failed the remote host accreditation.
1363 * ENOMEM Memory allocation failure.
1364 * EINVAL Label cannot be computed
1367 tsol_check_label_v6(const ts_label_t *tsl, zoneid_t zoneid, mblk_t **mpp,
1368 uint_t mac_mode, boolean_t zone_is_global, ip_stack_t *ipst,
1369 ts_label_t **effective_tslp)
1371 mblk_t *mp = *mpp;
1372 ip6_t *ip6h;
1373 ts_label_t *effective_tsl = NULL;
1375 * Label option length is limited to IP_MAX_OPT_LENGTH for
1376 * symmetry with IPv4. Can be relaxed if needed
1378 uchar_t opt_storage[TSOL_MAX_IPV6_OPTION];
1379 uint_t hlen;
1380 uint_t sec_opt_len; /* label option length not including type, len */
1381 int delta_remove = 0, delta_add;
1382 int retv;
1383 uchar_t *after_secopt;
1384 uchar_t *secopt = NULL;
1385 uchar_t *ip6hbh;
1386 uint_t hbhlen;
1387 boolean_t hbh_needed;
1389 *effective_tslp = NULL;
1392 * Verify the destination is allowed to receive packets at
1393 * the security label of the message data. tsol_check_dest()
1394 * may create a new effective label or label flags.
1396 ip6h = (ip6_t *)mp->b_rptr;
1397 retv = tsol_check_dest(tsl, &ip6h->ip6_dst, IPV6_VERSION,
1398 mac_mode, zone_is_global, &effective_tsl);
1399 if (retv != 0)
1400 return (retv);
1403 * Calculate the security label to be placed in the text
1404 * of the message (if any).
1406 if (effective_tsl != NULL) {
1407 if ((retv = tsol_compute_label_v6(effective_tsl, zoneid,
1408 &ip6h->ip6_dst, opt_storage, ipst)) != 0) {
1409 label_rele(effective_tsl);
1410 return (retv);
1412 *effective_tslp = effective_tsl;
1413 } else {
1414 if ((retv = tsol_compute_label_v6(tsl, zoneid,
1415 &ip6h->ip6_dst, opt_storage, ipst)) != 0)
1416 return (retv);
1419 sec_opt_len = opt_storage[1];
1421 if (ip6h->ip6_nxt == IPPROTO_HOPOPTS) {
1422 ip6hbh = (uchar_t *)&ip6h[1];
1423 hbhlen = (ip6hbh[1] + 1) << 3;
1424 if (!tsol_find_secopt_v6(ip6hbh, hbhlen, &secopt,
1425 &after_secopt, &hbh_needed)) {
1427 * This function should not see invalid messages.
1428 * If one occurs, it would indicate either an
1429 * option previously verified in the forwarding
1430 * path has been corrupted or an option was
1431 * incorrectly generated locally.
1433 ASSERT(0);
1434 return (EACCES);
1438 if (sec_opt_len == 0 && secopt == NULL) {
1440 * The packet is not supposed to have a label, and it
1441 * does not have one currently
1443 return (0);
1446 if (secopt != NULL && sec_opt_len != 0 &&
1447 (bcmp(opt_storage, secopt, sec_opt_len + 2) == 0)) {
1448 /* The packet has the correct label already */
1449 return (0);
1453 * If there is an option there, then it must be the wrong one; delete.
1455 if (secopt != NULL) {
1456 delta_remove = tsol_remove_secopt_v6(ip6h, MBLKL(mp));
1457 mp->b_wptr += delta_remove;
1461 * Make sure we have room for the worst-case addition. Add 2 bytes for
1462 * the hop-by-hop ext header's next header and length fields. Add
1463 * another 2 bytes for the label option type, len and then round
1464 * up to the next 8-byte multiple.
1466 hlen = (4 + sec_opt_len + 7) & ~7;
1467 if (mp->b_wptr + hlen > mp->b_datap->db_lim) {
1468 int copylen;
1469 mblk_t *new_mp;
1470 uint16_t hdr_len;
1472 hdr_len = ip_hdr_length_v6(mp, ip6h);
1474 * Allocate enough to be meaningful, but not *too* much.
1475 * Also all the IPv6 extension headers must be in the same mblk
1477 copylen = MBLKL(mp);
1478 if (copylen > 256)
1479 copylen = 256;
1480 if (copylen < hdr_len)
1481 copylen = hdr_len;
1482 new_mp = allocb_tmpl(hlen + copylen +
1483 (mp->b_rptr - mp->b_datap->db_base), mp);
1484 if (new_mp == NULL) {
1485 if (effective_tsl != NULL) {
1486 label_rele(effective_tsl);
1487 *effective_tslp = NULL;
1489 return (ENOMEM);
1492 /* keep the bias */
1493 new_mp->b_rptr += mp->b_rptr - mp->b_datap->db_base;
1494 new_mp->b_wptr = new_mp->b_rptr + copylen;
1495 bcopy(mp->b_rptr, new_mp->b_rptr, copylen);
1496 new_mp->b_cont = mp;
1497 if ((mp->b_rptr += copylen) >= mp->b_wptr) {
1498 new_mp->b_cont = mp->b_cont;
1499 freeb(mp);
1501 *mpp = mp = new_mp;
1502 ip6h = (ip6_t *)mp->b_rptr;
1505 delta_add = tsol_prepend_option_v6(opt_storage, ip6h, MBLKL(mp));
1506 if (delta_add == -1)
1507 goto param_prob;
1509 ASSERT(mp->b_wptr + delta_add <= DB_LIM(mp));
1510 mp->b_wptr += delta_add;
1512 /* tsol_prepend_option_v6 has adjusted ip6_plen */
1513 return (0);
1515 param_prob:
1516 if (effective_tsl != NULL) {
1517 label_rele(effective_tsl);
1518 *effective_tslp = NULL;
1520 return (EINVAL);