1 /* 2 * CDDL HEADER START 3 * 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. 7 * 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. 12 * 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] 18 * 19 * CDDL HEADER END 20 */ 21 /* 22 * Copyright 2007 Sun Microsystems, Inc. All rights reserved. 23 * Use is subject to license terms. 24 */ 25 26 #pragma ident "%Z%%M% %I% %E% SMI" 27 28 #include <sys/types.h> 29 #include <sys/systm.h> 30 #include <sys/kmem.h> 31 #include <sys/disp.h> 32 #include <sys/stream.h> 33 #include <sys/strsubr.h> 34 #include <sys/strsun.h> 35 #include <sys/policy.h> 36 #include <sys/tsol/label_macro.h> 37 #include <sys/tsol/tndb.h> 38 #include <sys/tsol/tnet.h> 39 #include <inet/ip.h> 40 #include <inet/ip6.h> 41 #include <inet/tcp.h> 42 #include <inet/ipclassifier.h> 43 #include <inet/ip_ire.h> 44 #include <inet/ip_ftable.h> 45 46 /* 47 * This routine takes a sensitivity label as input and creates a CIPSO 48 * option in the specified buffer. It returns the size of the CIPSO option. 49 * If the sensitivity label is too large for the CIPSO option, then 0 50 * is returned. 51 * 52 * tsol2cipso_tt1 returns 0 for failure and greater than 0 for success 53 * (more accurately, success means a return value between 10 and 40). 54 */ 55 56 static int 57 tsol2cipso_tt1(const bslabel_t *sl, unsigned char *cop, uint32_t doi) 58 { 59 struct cipso_tag_type_1 *tt1; 60 const _bslabel_impl_t *bsl; 61 const uchar_t *ucp; 62 int i; 63 64 if (doi == 0) 65 return (0); 66 67 /* check for Admin High sensitivity label */ 68 if (blequal(sl, label2bslabel(l_admin_high))) 69 return (0); 70 71 /* check whether classification will fit in one octet */ 72 bsl = (const _bslabel_impl_t *)sl; 73 if (LCLASS(bsl) & 0xFF00) 74 return (0); 75 76 /* 77 * Check whether compartments will fit in 30 octets. 78 * Compartments 241 - 256 are not allowed. 79 */ 80 if (ntohl(bsl->compartments.c8) & 0x0000FFFF) 81 return (0); 82 83 /* 84 * Compute option length and tag length. 85 * 'p' points to the last two bytes in the Sensitivity Label's 86 * compartments; these cannot be mapped into CIPSO compartments. 87 */ 88 ucp = (const uchar_t *)&bsl->compartments.c8 + 2; 89 while (--ucp >= (const uchar_t *)&bsl->compartments.c1) 90 if (*ucp != 0) 91 break; 92 93 i = ucp - (const uchar_t *)&bsl->compartments.c1 + 1; 94 95 if (cop == NULL) 96 return (10 + i); 97 98 doi = htonl(doi); 99 ucp = (const uchar_t *)&doi; 100 cop[IPOPT_OPTVAL] = IPOPT_COMSEC; 101 cop[IPOPT_OLEN] = 10 + i; 102 cop[IPOPT_OLEN+1] = ucp[0]; 103 cop[IPOPT_OLEN+2] = ucp[1]; 104 cop[IPOPT_OLEN+3] = ucp[2]; 105 cop[IPOPT_OLEN+4] = ucp[3]; 106 tt1 = (struct cipso_tag_type_1 *)&cop[IPOPT_OLEN + 5]; 107 tt1->tag_type = 1; 108 tt1->tag_align = 0; 109 tt1->tag_sl = LCLASS(bsl); 110 tt1->tag_length = 4 + i; 111 112 bcopy(&bsl->compartments.c1, tt1->tag_cat, i); 113 114 return (cop[IPOPT_OLEN]); 115 } 116 117 /* 118 * The following routine copies a datagram's option into the specified buffer 119 * (if buffer pointer is non-null), or returns a pointer to the label within 120 * the streams message (if buffer is null). In both cases, tsol_get_option 121 * returns the option's type. 122 * 123 * tsol_get_option assumes that the specified buffer is large enough to 124 * hold the largest valid CIPSO option. Since the total number of 125 * IP header options cannot exceed 40 bytes, a 40 byte buffer is a good choice. 126 */ 127 128 tsol_ip_label_t 129 tsol_get_option(mblk_t *mp, uchar_t **buffer) 130 { 131 ipha_t *ipha; 132 uchar_t *opt; 133 uint32_t totallen; 134 uint32_t optval; 135 uint32_t optlen; 136 137 ipha = (ipha_t *)mp->b_rptr; 138 139 /* 140 * Get length (in 4 byte octets) of IP header options. 141 * If header doesn't contain options, then return OPT_NONE. 142 */ 143 totallen = ipha->ipha_version_and_hdr_length - 144 (uint8_t)((IP_VERSION << 4) + IP_SIMPLE_HDR_LENGTH_IN_WORDS); 145 146 if (totallen == 0) 147 return (OPT_NONE); 148 149 totallen <<= 2; 150 151 /* 152 * Search for CIPSO option. 153 * If no such option is present, then return OPT_NONE. 154 */ 155 opt = (uchar_t *)&ipha[1]; 156 while (totallen != 0) { 157 switch (optval = opt[IPOPT_OPTVAL]) { 158 case IPOPT_EOL: 159 return (OPT_NONE); 160 case IPOPT_NOP: 161 optlen = 1; 162 break; 163 default: 164 if (totallen <= IPOPT_OLEN) 165 return (OPT_NONE); 166 optlen = opt[IPOPT_OLEN]; 167 if (optlen < 2) 168 return (OPT_NONE); 169 } 170 if (optlen > totallen) 171 return (OPT_NONE); 172 /* 173 * Copy pointer to option into '*buffer' and 174 * return the option type. 175 */ 176 switch (optval) { 177 case IPOPT_COMSEC: 178 *buffer = opt; 179 if (TSOL_CIPSO_TAG_OFFSET < optlen && 180 opt[TSOL_CIPSO_TAG_OFFSET] == 1) 181 return (OPT_CIPSO); 182 return (OPT_NONE); 183 } 184 totallen -= optlen; 185 opt += optlen; 186 } 187 return (OPT_NONE); 188 } 189 190 /* 191 * tsol_compute_label() 192 * 193 * This routine computes the IP label that should be on a packet based on the 194 * connection and destination information. 195 * 196 * Returns: 197 * 0 Fetched label 198 * EACCES The packet failed the remote host accreditation 199 * ENOMEM Memory allocation failure 200 * EINVAL Label cannot be computed 201 */ 202 int 203 tsol_compute_label(const cred_t *credp, ipaddr_t dst, uchar_t *opt_storage, 204 boolean_t isexempt, ip_stack_t *ipst) 205 { 206 uint_t sec_opt_len; 207 ts_label_t *tsl; 208 tsol_tpc_t *dst_rhtp; 209 ire_t *ire, *sire = NULL; 210 boolean_t compute_label = B_FALSE; 211 tsol_ire_gw_secattr_t *attrp; 212 zoneid_t zoneid, ip_zoneid; 213 214 if (opt_storage != NULL) 215 opt_storage[IPOPT_OLEN] = 0; 216 217 if ((tsl = crgetlabel(credp)) == NULL) 218 return (0); 219 220 /* always pass multicast */ 221 if (CLASSD(dst)) 222 return (0); 223 224 if ((dst_rhtp = find_tpc(&dst, IPV4_VERSION, B_FALSE)) == NULL) { 225 DTRACE_PROBE3(tx__tnopt__log__info__labeling__lookupdst__v4, 226 char *, "destination ip(1) not in database (with creds(2))", 227 ipaddr_t, dst, cred_t *, credp); 228 return (EINVAL); 229 } 230 231 zoneid = crgetzoneid(credp); 232 233 /* 234 * For exclusive stacks we set the zoneid to zero 235 * to operate as if in the global zone for IRE and conn_t comparisons. 236 */ 237 if (ipst->ips_netstack->netstack_stackid != GLOBAL_NETSTACKID) 238 ip_zoneid = GLOBAL_ZONEID; 239 else 240 ip_zoneid = zoneid; 241 242 switch (dst_rhtp->tpc_tp.host_type) { 243 case UNLABELED: 244 /* 245 * Only add a label if the unlabeled destination is 246 * not broadcast/local/loopback address, that it is 247 * not on the same subnet, and that the next-hop 248 * gateway is labeled. 249 */ 250 ire = ire_cache_lookup(dst, ip_zoneid, tsl, ipst); 251 252 if (ire != NULL && (ire->ire_type & (IRE_BROADCAST | IRE_LOCAL | 253 IRE_LOOPBACK | IRE_INTERFACE)) != 0) { 254 IRE_REFRELE(ire); 255 TPC_RELE(dst_rhtp); 256 return (0); 257 } else if (ire == NULL) { 258 ire = ire_ftable_lookup(dst, 0, 0, 0, NULL, &sire, 259 ip_zoneid, 0, tsl, (MATCH_IRE_RECURSIVE | 260 MATCH_IRE_DEFAULT | MATCH_IRE_SECATTR), ipst); 261 } 262 263 /* no route to destination */ 264 if (ire == NULL) { 265 DTRACE_PROBE4( 266 tx__tnopt__log__info__labeling__routedst__v4, 267 char *, "No route to unlabeled dest ip(1)/tpc(2) " 268 "with creds(3).", ipaddr_t, dst, tsol_tpc_t *, 269 dst_rhtp, cred_t *, credp); 270 TPC_RELE(dst_rhtp); 271 return (EINVAL); 272 } 273 274 /* 275 * Prefix IRE from f-table lookup means that the destination 276 * is not directly connected; check the next-hop attributes. 277 */ 278 if (sire != NULL) { 279 ASSERT(ire != NULL); 280 IRE_REFRELE(ire); 281 ire = sire; 282 } 283 284 attrp = ire->ire_gw_secattr; 285 if (attrp != NULL && attrp->igsa_rhc != NULL && 286 attrp->igsa_rhc->rhc_tpc->tpc_tp.host_type != UNLABELED) 287 compute_label = B_TRUE; 288 289 /* 290 * Can talk to unlabeled hosts if 291 * (1) zone's label matches the default label, or 292 * (2) SO_MAC_EXEMPT is on and we dominate the peer's label 293 * (3) SO_MAC_EXEMPT is on and this is the global zone 294 */ 295 if (dst_rhtp->tpc_tp.tp_doi != tsl->tsl_doi || 296 (!blequal(&dst_rhtp->tpc_tp.tp_def_label, 297 &tsl->tsl_label) && (!isexempt || 298 (zoneid != GLOBAL_ZONEID && !bldominates(&tsl->tsl_label, 299 &dst_rhtp->tpc_tp.tp_def_label))))) { 300 DTRACE_PROBE4(tx__tnopt__log__info__labeling__mac__v4, 301 char *, "unlabeled dest ip(1)/tpc(2) " 302 "non-matching creds(3).", ipaddr_t, dst, 303 tsol_tpc_t *, dst_rhtp, cred_t *, credp); 304 IRE_REFRELE(ire); 305 TPC_RELE(dst_rhtp); 306 return (EACCES); 307 } 308 309 IRE_REFRELE(ire); 310 break; 311 312 case SUN_CIPSO: 313 /* 314 * Can talk to labeled hosts if zone's label is within target's 315 * label range or set. 316 */ 317 if (dst_rhtp->tpc_tp.tp_cipso_doi_cipso != tsl->tsl_doi || 318 (!_blinrange(&tsl->tsl_label, 319 &dst_rhtp->tpc_tp.tp_sl_range_cipso) && 320 !blinlset(&tsl->tsl_label, 321 dst_rhtp->tpc_tp.tp_sl_set_cipso))) { 322 DTRACE_PROBE4(tx__tnopt__log__info__labeling__mac__v4, 323 char *, "labeled dest ip(1)/tpc(2) " 324 "non-matching creds(3).", ipaddr_t, dst, 325 tsol_tpc_t *, dst_rhtp, cred_t *, credp); 326 TPC_RELE(dst_rhtp); 327 return (EACCES); 328 } 329 compute_label = B_TRUE; 330 break; 331 332 default: 333 TPC_RELE(dst_rhtp); 334 return (EACCES); 335 } 336 337 if (!compute_label) { 338 TPC_RELE(dst_rhtp); 339 return (0); 340 } 341 342 /* compute the CIPSO option */ 343 if (dst_rhtp->tpc_tp.host_type != UNLABELED) 344 sec_opt_len = tsol2cipso_tt1(&tsl->tsl_label, opt_storage, 345 tsl->tsl_doi); 346 else 347 sec_opt_len = tsol2cipso_tt1(&dst_rhtp->tpc_tp.tp_def_label, 348 opt_storage, tsl->tsl_doi); 349 TPC_RELE(dst_rhtp); 350 351 if (sec_opt_len == 0) { 352 DTRACE_PROBE4(tx__tnopt__log__error__labeling__lostops__v4, 353 char *, 354 "options lack length for dest ip(1)/tpc(2) with creds(3).", 355 ipaddr_t, dst, tsol_tpc_t *, dst_rhtp, cred_t *, credp); 356 return (EINVAL); 357 } 358 359 return (0); 360 } 361 362 /* 363 * Remove any existing security option (CIPSO) from the given IP 364 * header, move the 'buflen' bytes back to fill the gap, and return the number 365 * of bytes removed (as zero or negative number). Assumes that the headers are 366 * sane. 367 */ 368 int 369 tsol_remove_secopt(ipha_t *ipha, int buflen) 370 { 371 int remlen, olen, oval, delta; 372 uchar_t *fptr, *tptr; 373 boolean_t noop_keep; 374 375 remlen = IPH_HDR_LENGTH(ipha) - IP_SIMPLE_HDR_LENGTH; 376 fptr = tptr = (uchar_t *)(ipha + 1); 377 noop_keep = B_TRUE; 378 while (remlen > 0) { 379 oval = fptr[IPOPT_OPTVAL]; 380 381 /* terminate on end of list */ 382 if (oval == IPOPT_EOL) 383 break; 384 385 /* 386 * Delete any no-ops following a deleted option, at least up 387 * to a 4 octet alignment; copy others. 388 */ 389 if (oval == IPOPT_NOP) { 390 if (((fptr - (uchar_t *)ipha) & 3) == 0) 391 noop_keep = B_TRUE; 392 if (noop_keep) 393 *tptr++ = oval; 394 fptr++; 395 remlen--; 396 continue; 397 } 398 399 /* stop on corrupted list; just do nothing. */ 400 if (remlen < 2) 401 return (0); 402 olen = fptr[IPOPT_OLEN]; 403 if (olen < 2 || olen > remlen) 404 return (0); 405 406 /* skip over security options to delete them */ 407 if (oval == IPOPT_COMSEC || oval == IPOPT_SECURITY) { 408 noop_keep = B_FALSE; 409 fptr += olen; 410 remlen -= olen; 411 continue; 412 } 413 414 /* copy the rest */ 415 noop_keep = B_TRUE; 416 if (tptr != fptr) 417 ovbcopy(fptr, tptr, olen); 418 fptr += olen; 419 tptr += olen; 420 remlen -= olen; 421 } 422 423 fptr += remlen; 424 425 /* figure how much padding we'll need for header alignment */ 426 olen = (tptr - (uchar_t *)ipha) & 3; 427 if (olen > 0) { 428 olen = 4 - olen; 429 /* pad with end-of-list */ 430 bzero(tptr, olen); 431 tptr += olen; 432 } 433 434 /* slide back the headers that follow and update the IP header */ 435 delta = fptr - tptr; 436 if (delta != 0) { 437 ovbcopy(fptr, tptr, ((uchar_t *)ipha + buflen) - fptr); 438 ipha->ipha_version_and_hdr_length -= delta / 4; 439 } 440 return (-delta); 441 } 442 443 /* 444 * Insert the option in 'optbuf' into the IP header pointed to by 'ipha', and 445 * move the data following the IP header (up to buflen) to accomodate the new 446 * option. Assumes that up to IP_MAX_OPT_LENGTH bytes are available (in total) 447 * for IP options. Returns the number of bytes actually inserted, or -1 if the 448 * option cannot be inserted. (Note that negative return values are possible 449 * when noops must be compressed, and that only -1 indicates error. Successful 450 * return value is always evenly divisible by 4, by definition.) 451 */ 452 int 453 tsol_prepend_option(uchar_t *optbuf, ipha_t *ipha, int buflen) 454 { 455 int remlen, padding, lastpad, totlen; 456 int oval, olen; 457 int delta; 458 uchar_t *optr; 459 uchar_t tempopt[IP_MAX_OPT_LENGTH], *toptr; 460 461 if (optbuf[IPOPT_OPTVAL] == IPOPT_EOL || 462 optbuf[IPOPT_OPTVAL] == IPOPT_NOP || 463 optbuf[IPOPT_OLEN] == 0) 464 return (0); 465 466 ASSERT(optbuf[IPOPT_OLEN] >= 2 && 467 optbuf[IPOPT_OLEN] <= IP_MAX_OPT_LENGTH); 468 469 /* first find the real (unpadded) length of the existing options */ 470 remlen = IPH_HDR_LENGTH(ipha) - IP_SIMPLE_HDR_LENGTH; 471 padding = totlen = lastpad = 0; 472 optr = (uchar_t *)(ipha + 1); 473 while (remlen > 0) { 474 oval = optr[IPOPT_OPTVAL]; 475 476 /* stop at end of list */ 477 if (oval == IPOPT_EOL) 478 break; 479 480 /* skip no-ops, noting that length byte isn't present */ 481 if (oval == IPOPT_NOP) { 482 optr++; 483 padding++; 484 lastpad++; 485 totlen++; 486 remlen--; 487 continue; 488 } 489 490 /* give up on a corrupted list; report failure */ 491 if (remlen < 2) 492 return (-1); 493 olen = optr[IPOPT_OLEN]; 494 if (olen < 2 || olen > remlen) 495 return (-1); 496 497 lastpad = 0; 498 optr += olen; 499 totlen += olen; 500 remlen -= olen; 501 } 502 503 /* completely ignore any trailing padding */ 504 totlen -= lastpad; 505 padding -= lastpad; 506 507 /* 508 * If some sort of inter-option alignment was present, try to preserve 509 * that alignment. If alignment pushes us out past the maximum, then 510 * discard it and try to compress to fit. (We just "assume" that any 511 * padding added was attempting to get 32 bit alignment. If that's 512 * wrong, that's just too bad.) 513 */ 514 if (padding > 0) { 515 olen = (optbuf[IPOPT_OLEN] + 3) & ~3; 516 if (olen + totlen > IP_MAX_OPT_LENGTH) { 517 totlen -= padding; 518 if (olen + totlen > IP_MAX_OPT_LENGTH) 519 return (-1); 520 padding = 0; 521 } 522 } 523 524 /* 525 * Since we may need to compress or expand the option list, we write to 526 * a temporary buffer and then copy the results back to the IP header. 527 */ 528 toptr = tempopt; 529 530 /* compute actual option to insert */ 531 olen = optbuf[IPOPT_OLEN]; 532 bcopy(optbuf, toptr, olen); 533 toptr += olen; 534 if (padding > 0) { 535 while ((olen & 3) != 0) { 536 *toptr++ = IPOPT_NOP; 537 olen++; 538 } 539 } 540 541 /* copy over the existing options */ 542 optr = (uchar_t *)(ipha + 1); 543 while (totlen > 0) { 544 oval = optr[IPOPT_OPTVAL]; 545 546 /* totlen doesn't include end-of-list marker */ 547 ASSERT(oval != IPOPT_EOL); 548 549 /* handle no-ops; copy if desired, ignore otherwise */ 550 if (oval == IPOPT_NOP) { 551 if (padding > 0) { 552 /* note: cannot overflow due to checks above */ 553 ASSERT(toptr < tempopt + IP_MAX_OPT_LENGTH); 554 *toptr++ = oval; 555 } 556 optr++; 557 totlen--; 558 continue; 559 } 560 561 /* list cannot be corrupt at this point */ 562 ASSERT(totlen >= 2); 563 olen = optr[IPOPT_OLEN]; 564 ASSERT(olen >= 2 && olen <= totlen); 565 566 /* cannot run out of room due to tests above */ 567 ASSERT(toptr + olen <= tempopt + IP_MAX_OPT_LENGTH); 568 569 bcopy(optr, toptr, olen); 570 optr += olen; 571 toptr += olen; 572 totlen -= olen; 573 } 574 575 /* figure how much padding we'll need for header alignment */ 576 olen = (toptr - tempopt) & 3; 577 if (olen > 0) { 578 olen = 4 - olen; 579 ASSERT(toptr + olen <= tempopt + IP_MAX_OPT_LENGTH); 580 /* pad with end-of-list value */ 581 bzero(toptr, olen); 582 toptr += olen; 583 } 584 585 /* move the headers as needed and update IP header */ 586 olen = (toptr - tempopt) + IP_SIMPLE_HDR_LENGTH; 587 remlen = IPH_HDR_LENGTH(ipha); 588 delta = olen - remlen; 589 if (delta != 0) { 590 ovbcopy((uchar_t *)ipha + remlen, (uchar_t *)ipha + olen, 591 buflen - remlen); 592 ipha->ipha_version_and_hdr_length += delta / 4; 593 } 594 595 /* slap in the new options */ 596 bcopy(tempopt, ipha + 1, olen - IP_SIMPLE_HDR_LENGTH); 597 598 return (delta); 599 } 600 601 /* 602 * tsol_check_label() 603 * 604 * This routine computes the IP label that should be on the packet based on the 605 * connection and destination information. If the label is there, it returns 606 * zero, so the caller knows that the label is syncronized, and further calls 607 * are not required. If the label isn't right, then the right one is inserted. 608 * 609 * The packet's header is clear, before entering IPSec's engine. 610 * 611 * Returns: 612 * 0 Label on packet (was|is now) correct 613 * EACCES The packet failed the remote host accreditation. 614 * ENOMEM Memory allocation failure. 615 * EINVAL Label cannot be computed 616 */ 617 int 618 tsol_check_label(const cred_t *credp, mblk_t **mpp, int *addedp, 619 boolean_t isexempt, ip_stack_t *ipst) 620 { 621 mblk_t *mp = *mpp; 622 ipha_t *ipha; 623 uchar_t opt_storage[IP_MAX_OPT_LENGTH]; 624 uint_t hlen; 625 uint_t sec_opt_len; 626 uchar_t *optr; 627 int added; 628 int retv; 629 630 if (addedp != NULL) 631 *addedp = 0; 632 633 opt_storage[IPOPT_OPTVAL] = 0; 634 635 ipha = (ipha_t *)mp->b_rptr; 636 637 retv = tsol_compute_label(credp, ipha->ipha_dst, opt_storage, isexempt, 638 ipst); 639 if (retv != 0) 640 return (retv); 641 642 optr = (uchar_t *)(ipha + 1); 643 hlen = IPH_HDR_LENGTH(ipha) - IP_SIMPLE_HDR_LENGTH; 644 sec_opt_len = opt_storage[IPOPT_OLEN]; 645 646 if (hlen >= sec_opt_len) { 647 /* If no option is supposed to be there, make sure it's not */ 648 if (sec_opt_len == 0 && hlen > 0 && 649 optr[IPOPT_OPTVAL] != IPOPT_COMSEC && 650 optr[IPOPT_OPTVAL] != IPOPT_SECURITY) 651 return (0); 652 /* if the option is there, it's always first */ 653 if (sec_opt_len != 0 && 654 bcmp(opt_storage, optr, sec_opt_len) == 0) 655 return (0); 656 } 657 658 /* 659 * If there is an option there, then it must be the wrong one; delete. 660 */ 661 if (hlen > 0) 662 mp->b_wptr += tsol_remove_secopt(ipha, MBLKL(mp)); 663 664 /* Make sure we have room for the worst-case addition */ 665 hlen = IPH_HDR_LENGTH(ipha) + opt_storage[IPOPT_OLEN]; 666 hlen = (hlen + 3) & ~3; 667 if (hlen > IP_MAX_HDR_LENGTH) 668 hlen = IP_MAX_HDR_LENGTH; 669 hlen -= IPH_HDR_LENGTH(ipha); 670 if (mp->b_wptr + hlen > mp->b_datap->db_lim) { 671 int copylen; 672 mblk_t *new_mp; 673 674 /* allocate enough to be meaningful, but not *too* much */ 675 copylen = MBLKL(mp); 676 if (copylen > 256) 677 copylen = 256; 678 new_mp = allocb(hlen + copylen + 679 (mp->b_rptr - mp->b_datap->db_base), BPRI_HI); 680 if (new_mp == NULL) 681 return (ENOMEM); 682 mblk_setcred(new_mp, DB_CRED(mp)); 683 684 /* keep the bias */ 685 new_mp->b_rptr += mp->b_rptr - mp->b_datap->db_base; 686 new_mp->b_wptr = new_mp->b_rptr + copylen; 687 bcopy(mp->b_rptr, new_mp->b_rptr, copylen); 688 new_mp->b_cont = mp; 689 if ((mp->b_rptr += copylen) >= mp->b_wptr) { 690 new_mp->b_cont = mp->b_cont; 691 freeb(mp); 692 } 693 *mpp = mp = new_mp; 694 ipha = (ipha_t *)mp->b_rptr; 695 } 696 697 added = tsol_prepend_option(opt_storage, ipha, MBLKL(mp)); 698 if (added == -1) 699 goto param_prob; 700 701 if (addedp != NULL) 702 *addedp = added; 703 704 ASSERT((mp->b_wptr + added) <= DB_LIM(mp)); 705 mp->b_wptr += added; 706 707 return (0); 708 709 param_prob: 710 return (EINVAL); 711 } 712 713 /* 714 * IPv6 HopOpt extension header for the label option layout: 715 * - One octet giving the type of the 'next extension header' 716 * - Header extension length in 8-byte words, not including the 717 * 1st 8 bytes, but including any pad bytes at the end. 718 * Eg. A value of 2 means 16 bytes not including the 1st 8 bytes. 719 * - Followed by TLV encoded IPv6 label option. Option layout is 720 * * One octet, IP6OPT_LS 721 * * One octet option length in bytes of the option data following 722 * the length, but not including any pad bytes at the end. 723 * * Four-octet DOI (IP6LS_DOI_V4) 724 * * One octet suboption, IP6LS_TT_V4 725 * * One octet suboption length in bytes of the suboption 726 * following the suboption length, including the suboption 727 * header length, but not including any pad bytes at the end. 728 * - Pad to make the extension header a multiple of 8 bytes. 729 * 730 * This function returns the contents of 'IPv6 option structure' in the above. 731 * i.e starting from the IP6OPT_LS but not including the pad at the end. 732 * The user must prepend two octets (either padding or next header / length) 733 * and append padding out to the next 8 octet boundary. 734 */ 735 int 736 tsol_compute_label_v6(const cred_t *credp, const in6_addr_t *dst, 737 uchar_t *opt_storage, boolean_t isexempt, ip_stack_t *ipst) 738 { 739 tsol_tpc_t *dst_rhtp; 740 ts_label_t *tsl; 741 uint_t sec_opt_len; 742 uint32_t doi; 743 zoneid_t zoneid, ip_zoneid; 744 ire_t *ire, *sire; 745 tsol_ire_gw_secattr_t *attrp; 746 boolean_t compute_label; 747 748 if (ip6opt_ls == 0) 749 return (EINVAL); 750 751 if (opt_storage != NULL) 752 opt_storage[IPOPT_OLEN] = 0; 753 754 if ((tsl = crgetlabel(credp)) == NULL) 755 return (0); 756 757 /* Always pass multicast */ 758 if (IN6_IS_ADDR_MULTICAST(dst)) 759 return (0); 760 761 if ((dst_rhtp = find_tpc(dst, IPV6_VERSION, B_FALSE)) == NULL) { 762 DTRACE_PROBE3(tx__tnopt__log__info__labeling__lookupdst__v6, 763 char *, "destination ip6(1) not in database with creds(2)", 764 in6_addr_t *, dst, cred_t *, credp); 765 return (EINVAL); 766 } 767 768 zoneid = crgetzoneid(credp); 769 770 /* 771 * For exclusive stacks we set the zoneid to zero 772 * to operate as if in the global zone for IRE and conn_t comparisons. 773 */ 774 if (ipst->ips_netstack->netstack_stackid != GLOBAL_NETSTACKID) 775 ip_zoneid = GLOBAL_ZONEID; 776 else 777 ip_zoneid = zoneid; 778 779 /* 780 * Fill in a V6 label. If a new format is added here, make certain 781 * that the maximum size of this label is reflected in sys/tsol/tnet.h 782 * as TSOL_MAX_IPV6_OPTION. 783 */ 784 compute_label = B_FALSE; 785 switch (dst_rhtp->tpc_tp.host_type) { 786 case UNLABELED: 787 /* 788 * Only add a label if the unlabeled destination is 789 * not local or loopback address, that it is 790 * not on the same subnet, and that the next-hop 791 * gateway is labeled. 792 */ 793 sire = NULL; 794 ire = ire_cache_lookup_v6(dst, ip_zoneid, tsl, ipst); 795 796 if (ire != NULL && (ire->ire_type & (IRE_LOCAL | 797 IRE_LOOPBACK | IRE_INTERFACE)) != 0) { 798 IRE_REFRELE(ire); 799 TPC_RELE(dst_rhtp); 800 return (0); 801 } else if (ire == NULL) { 802 ire = ire_ftable_lookup_v6(dst, NULL, NULL, 0, NULL, 803 &sire, ip_zoneid, 0, tsl, (MATCH_IRE_RECURSIVE | 804 MATCH_IRE_DEFAULT | MATCH_IRE_SECATTR), ipst); 805 } 806 807 /* no route to destination */ 808 if (ire == NULL) { 809 DTRACE_PROBE4( 810 tx__tnopt__log__info__labeling__routedst__v6, 811 char *, "No route to unlabeled dest ip6(1)/tpc(2) " 812 "with creds(3).", in6_addr_t *, dst, tsol_tpc_t *, 813 dst_rhtp, cred_t *, credp); 814 TPC_RELE(dst_rhtp); 815 return (EINVAL); 816 } 817 818 /* 819 * Prefix IRE from f-table lookup means that the destination 820 * is not directly connected; check the next-hop attributes. 821 */ 822 if (sire != NULL) { 823 ASSERT(ire != NULL); 824 IRE_REFRELE(ire); 825 ire = sire; 826 } 827 828 attrp = ire->ire_gw_secattr; 829 if (attrp != NULL && attrp->igsa_rhc != NULL && 830 attrp->igsa_rhc->rhc_tpc->tpc_tp.host_type != UNLABELED) 831 compute_label = B_TRUE; 832 833 if (dst_rhtp->tpc_tp.tp_doi != tsl->tsl_doi || 834 (!blequal(&dst_rhtp->tpc_tp.tp_def_label, 835 &tsl->tsl_label) && (!isexempt || 836 (zoneid != GLOBAL_ZONEID && !bldominates(&tsl->tsl_label, 837 &dst_rhtp->tpc_tp.tp_def_label))))) { 838 DTRACE_PROBE4(tx__tnopt__log__info__labeling__mac__v6, 839 char *, "unlabeled dest ip6(1)/tpc(2) " 840 "non-matching creds(3)", in6_addr_t *, dst, 841 tsol_tpc_t *, dst_rhtp, cred_t *, credp); 842 IRE_REFRELE(ire); 843 TPC_RELE(dst_rhtp); 844 return (EACCES); 845 } 846 847 IRE_REFRELE(ire); 848 break; 849 850 case SUN_CIPSO: 851 if (dst_rhtp->tpc_tp.tp_cipso_doi_cipso != tsl->tsl_doi || 852 (!_blinrange(&tsl->tsl_label, 853 &dst_rhtp->tpc_tp.tp_sl_range_cipso) && 854 !blinlset(&tsl->tsl_label, 855 dst_rhtp->tpc_tp.tp_sl_set_cipso))) { 856 DTRACE_PROBE4(tx__tnopt__log__info__labeling__mac__v6, 857 char *, 858 "labeled dest ip6(1)/tpc(2) non-matching creds(3).", 859 in6_addr_t *, dst, tsol_tpc_t *, dst_rhtp, 860 cred_t *, credp); 861 TPC_RELE(dst_rhtp); 862 return (EACCES); 863 } 864 compute_label = B_TRUE; 865 break; 866 867 default: 868 TPC_RELE(dst_rhtp); 869 return (EACCES); 870 } 871 872 if (!compute_label) { 873 TPC_RELE(dst_rhtp); 874 return (0); 875 } 876 877 /* compute the CIPSO option */ 878 if (opt_storage != NULL) 879 opt_storage += 8; 880 if (dst_rhtp->tpc_tp.host_type != UNLABELED) { 881 sec_opt_len = tsol2cipso_tt1(&tsl->tsl_label, opt_storage, 882 tsl->tsl_doi); 883 } else { 884 sec_opt_len = tsol2cipso_tt1(&dst_rhtp->tpc_tp.tp_def_label, 885 opt_storage, tsl->tsl_doi); 886 } 887 TPC_RELE(dst_rhtp); 888 889 if (sec_opt_len == 0) { 890 DTRACE_PROBE4(tx__tnopt__log__error__labeling__lostops__v6, 891 char *, 892 "options lack length for dest ip6(1)/tpc(2) with creds(3).", 893 in6_addr_t *, dst, tsol_tpc_t *, dst_rhtp, cred_t *, credp); 894 return (EINVAL); 895 } 896 897 if (opt_storage == NULL) 898 return (0); 899 900 if (sec_opt_len < IP_MAX_OPT_LENGTH) 901 opt_storage[sec_opt_len] = IPOPT_EOL; 902 903 /* 904 * Just in case the option length is odd, round it up to the next even 905 * multiple. The IPv6 option definition doesn't like odd numbers for 906 * some reason. 907 * 908 * Length in the overall option header (IP6OPT_LS) does not include the 909 * option header itself, but the length in the suboption does include 910 * the suboption header. Thus, when there's just one suboption, the 911 * length in the option header is the suboption length plus 4 (for the 912 * DOI value). 913 */ 914 opt_storage[-2] = IP6LS_TT_V4; 915 opt_storage[-1] = (sec_opt_len + 2 + 1) & ~1; 916 opt_storage[-8] = ip6opt_ls; 917 opt_storage[-7] = opt_storage[-1] + 4; 918 doi = htons(IP6LS_DOI_V4); 919 bcopy(&doi, opt_storage - 6, 4); 920 921 return (0); 922 } 923 924 /* 925 * Locate the start of the IP6OPT_LS label option and return it. 926 * Also return the start of the next non-pad option in after_secoptp. 927 * Usually the label option is the first option at least when packets 928 * are generated, but for generality we don't assume that on received packets. 929 */ 930 uchar_t * 931 tsol_find_secopt_v6( 932 const uchar_t *ip6hbh, /* Start of the hop-by-hop extension header */ 933 uint_t hbhlen, /* Length of the hop-by-hop extension header */ 934 uchar_t **after_secoptp, /* Non-pad option following the label option */ 935 boolean_t *hbh_needed) /* Is hop-by-hop hdr needed w/o label */ 936 { 937 uint_t optlen; 938 uint_t optused; 939 const uchar_t *optptr; 940 uchar_t opt_type; 941 const uchar_t *secopt = NULL; 942 943 *hbh_needed = B_FALSE; 944 *after_secoptp = NULL; 945 optlen = hbhlen - 2; 946 optptr = ip6hbh + 2; 947 while (optlen != 0) { 948 opt_type = *optptr; 949 if (opt_type == IP6OPT_PAD1) { 950 optptr++; 951 optlen--; 952 continue; 953 } 954 if (optlen == 1) 955 break; 956 optused = 2 + optptr[1]; 957 if (optused > optlen) 958 break; 959 /* 960 * if we get here, ip6opt_ls can 961 * not be 0 because it will always 962 * match the IP6OPT_PAD1 above. 963 * Therefore ip6opt_ls == 0 forces 964 * this test to always fail here. 965 */ 966 if (opt_type == ip6opt_ls) 967 secopt = optptr; 968 else switch (opt_type) { 969 case IP6OPT_PADN: 970 break; 971 default: 972 /* 973 * There is at least 1 option other than 974 * the label option. So the hop-by-hop header is needed 975 */ 976 *hbh_needed = B_TRUE; 977 if (secopt != NULL) { 978 *after_secoptp = (uchar_t *)optptr; 979 return ((uchar_t *)secopt); 980 } 981 break; 982 } 983 optlen -= optused; 984 optptr += optused; 985 } 986 return ((uchar_t *)secopt); 987 } 988 989 /* 990 * Remove the label option from the hop-by-hop options header if it exists. 991 * 'buflen' is the total length of the packet typically b_wptr - b_rptr. 992 * Header and data following the label option that is deleted are copied 993 * (i.e. slid backward) to the right position, and returns the number 994 * of bytes removed (as zero or negative number.) 995 */ 996 int 997 tsol_remove_secopt_v6(ip6_t *ip6h, int buflen) 998 { 999 uchar_t *ip6hbh; /* hop-by-hop header */ 1000 uint_t hbhlen; /* hop-by-hop extension header length */ 1001 uchar_t *secopt = NULL; 1002 uchar_t *after_secopt; 1003 uint_t pad; 1004 uint_t delta; 1005 boolean_t hbh_needed; 1006 1007 /* 1008 * hop-by-hop extension header must appear first, if it does not 1009 * exist, there is no label option. 1010 */ 1011 if (ip6h->ip6_nxt != IPPROTO_HOPOPTS) 1012 return (0); 1013 1014 ip6hbh = (uchar_t *)&ip6h[1]; 1015 hbhlen = (ip6hbh[1] + 1) << 3; 1016 /* 1017 * Locate the start of the label option if it exists and the end 1018 * of the label option including pads if any. 1019 */ 1020 secopt = tsol_find_secopt_v6(ip6hbh, hbhlen, &after_secopt, 1021 &hbh_needed); 1022 if (secopt == NULL) 1023 return (0); 1024 if (!hbh_needed) { 1025 uchar_t next_hdr; 1026 /* 1027 * The label option was the only option in the hop-by-hop 1028 * header. We don't need the hop-by-hop header itself any 1029 * longer. 1030 */ 1031 next_hdr = ip6hbh[0]; 1032 ovbcopy(ip6hbh + hbhlen, ip6hbh, 1033 buflen - (IPV6_HDR_LEN + hbhlen)); 1034 ip6h->ip6_plen = htons(ntohs(ip6h->ip6_plen) - hbhlen); 1035 ip6h->ip6_nxt = next_hdr; 1036 return (-hbhlen); 1037 } 1038 1039 if (after_secopt == NULL) { 1040 /* There is no option following the label option */ 1041 after_secopt = ip6hbh + hbhlen; 1042 } 1043 1044 /* 1045 * After deleting the label option, we need to slide the headers 1046 * and data back, while still maintaining the same alignment (module 8) 1047 * for the other options. So we slide the headers and data back only 1048 * by an integral multiple of 8 bytes, and fill the remaining bytes 1049 * with pads. 1050 */ 1051 delta = after_secopt - secopt; 1052 pad = delta % 8; 1053 if (pad == 1) { 1054 secopt[0] = IP6OPT_PAD1; 1055 } else if (pad > 1) { 1056 secopt[0] = IP6OPT_PADN; 1057 secopt[1] = pad - 2; 1058 if (pad > 2) 1059 bzero(&secopt[2], pad - 2); 1060 } 1061 secopt += pad; 1062 delta -= pad; 1063 ovbcopy(after_secopt, secopt, 1064 (uchar_t *)ip6h + buflen - after_secopt); 1065 ip6hbh[1] -= delta/8; 1066 ip6h->ip6_plen = htons(ntohs(ip6h->ip6_plen) - delta); 1067 1068 return (-delta); 1069 } 1070 1071 /* 1072 * 'optbuf' contains a CIPSO label embedded in an IPv6 hop-by-hop option, 1073 * starting with the IP6OPT_LS option type. The format of this hop-by-hop 1074 * option is described in the block comment above tsol_compute_label_v6. 1075 * This function prepends this hop-by-hop option before any other hop-by-hop 1076 * options in the hop-by-hop header if one already exists, else a new 1077 * hop-by-hop header is created and stuffed into the packet following 1078 * the IPv6 header. 'buflen' is the total length of the packet i.e. 1079 * b_wptr - b_rptr. The caller ensures that there is enough space for the 1080 * extra option being added. Header and data following the position where 1081 * the label option is inserted are copied (i.e. slid forward) to the right 1082 * position. 1083 */ 1084 int 1085 tsol_prepend_option_v6(uchar_t *optbuf, ip6_t *ip6h, int buflen) 1086 { 1087 /* 1088 * rawlen is the length of the label option in bytes, not including 1089 * any pads, starting from the IP6OPT_LS (option type) byte. 1090 */ 1091 uint_t rawlen; 1092 1093 uint_t optlen; /* rawlen rounded to an 8 byte multiple */ 1094 uchar_t *ip6hbh; /* start of the hop-by-hop extension header */ 1095 uint_t hbhlen; /* Length of the hop-by-hop extension header */ 1096 uint_t pad_len; 1097 uchar_t *pad_position; 1098 int delta; /* Actual number of bytes inserted */ 1099 1100 rawlen = optbuf[1] + 2; /* Add 2 for the option type, option length */ 1101 ip6hbh = (uchar_t *)&ip6h[1]; 1102 if (ip6h->ip6_nxt == IPPROTO_HOPOPTS) { 1103 /* 1104 * There is a hop-by-hop header present already. In order to 1105 * preserve the alignment of the other options at the existing 1106 * value (modulo 8) we need to pad the label option to a 1107 * multiple of 8 bytes before prepending it to the other 1108 * options. Slide the extension headers and data forward to 1109 * accomodate the label option at the start of the hop-by-hop 1110 * header 1111 */ 1112 delta = optlen = (rawlen + 7) & ~7; 1113 pad_len = optlen - rawlen; 1114 pad_position = ip6hbh + 2 + rawlen; 1115 ovbcopy(ip6hbh + 2, ip6hbh + 2 + optlen, 1116 buflen - (IPV6_HDR_LEN + 2)); 1117 /* 1118 * Bump up the hop-by-hop extension header length by 1119 * the number of 8-byte words added 1120 */ 1121 optlen >>= 3; 1122 if (ip6hbh[1] + optlen > 255) 1123 return (-1); 1124 ip6hbh[1] += optlen; 1125 } else { 1126 /* 1127 * There is no hop-by-hop header in the packet. Construct a 1128 * new Hop-by-hop extension header (a multiple of 8 bytes). 1129 * Slide any other extension headers and data forward to 1130 * accomodate this hop-by-hop header 1131 */ 1132 delta = hbhlen = (2 + rawlen + 7) & ~7; /* +2 for nxthdr, len */ 1133 pad_len = hbhlen - (2 + rawlen); 1134 pad_position = ip6hbh + 2 + rawlen; 1135 ovbcopy(ip6hbh, ip6hbh + hbhlen, buflen - IPV6_HDR_LEN); 1136 ip6hbh[0] = ip6h->ip6_nxt; 1137 /* 1138 * hop-by-hop extension header length in 8-byte words, not 1139 * including the 1st 8 bytes of the hop-by-hop header. 1140 */ 1141 ip6hbh[1] = (hbhlen >> 3) - 1; 1142 ip6h->ip6_nxt = IPPROTO_HOPOPTS; 1143 } 1144 /* 1145 * Copy the label option into the hop-by-hop header and insert any 1146 * needed pads 1147 */ 1148 bcopy(optbuf, ip6hbh + 2, rawlen); 1149 if (pad_len == 1) { 1150 pad_position[0] = IP6OPT_PAD1; 1151 } else if (pad_len > 1) { 1152 pad_position[0] = IP6OPT_PADN; 1153 pad_position[1] = pad_len - 2; 1154 if (pad_len > 2) 1155 bzero(pad_position + 2, pad_len - 2); 1156 } 1157 ip6h->ip6_plen = htons(ntohs(ip6h->ip6_plen) + delta); 1158 return (delta); 1159 } 1160 1161 /* 1162 * tsol_check_label_v6() 1163 * 1164 * This routine computes the IP label that should be on the packet based on the 1165 * connection and destination information. It's called only by the IP 1166 * forwarding logic, because all internal modules atop IP know how to generate 1167 * their own labels. 1168 * 1169 * Returns: 1170 * 0 Label on packet was already correct 1171 * EACCESS The packet failed the remote host accreditation. 1172 * ENOMEM Memory allocation failure. 1173 */ 1174 int 1175 tsol_check_label_v6(const cred_t *credp, mblk_t **mpp, int *addedp, 1176 boolean_t isexempt, ip_stack_t *ipst) 1177 { 1178 mblk_t *mp = *mpp; 1179 ip6_t *ip6h; 1180 /* 1181 * Label option length is limited to IP_MAX_OPT_LENGTH for 1182 * symmetry with IPv4. Can be relaxed if needed 1183 */ 1184 uchar_t opt_storage[TSOL_MAX_IPV6_OPTION]; 1185 uint_t hlen; 1186 uint_t sec_opt_len; /* label option length not including type, len */ 1187 int added; 1188 int retv; 1189 uchar_t *after_secopt; 1190 uchar_t *secopt = NULL; 1191 uchar_t *ip6hbh; 1192 uint_t hbhlen; 1193 boolean_t hbh_needed; 1194 1195 if (addedp != NULL) 1196 *addedp = 0; 1197 1198 ip6h = (ip6_t *)mp->b_rptr; 1199 retv = tsol_compute_label_v6(credp, &ip6h->ip6_dst, opt_storage, 1200 isexempt, ipst); 1201 if (retv != 0) 1202 return (retv); 1203 1204 sec_opt_len = opt_storage[1]; 1205 1206 if (ip6h->ip6_nxt == IPPROTO_HOPOPTS) { 1207 ip6hbh = (uchar_t *)&ip6h[1]; 1208 hbhlen = (ip6hbh[1] + 1) << 3; 1209 secopt = tsol_find_secopt_v6(ip6hbh, hbhlen, &after_secopt, 1210 &hbh_needed); 1211 } 1212 1213 if (sec_opt_len == 0 && secopt == NULL) { 1214 /* 1215 * The packet is not supposed to have a label, and it 1216 * does not have one currently 1217 */ 1218 return (0); 1219 } 1220 if (secopt != NULL && sec_opt_len != 0 && 1221 (bcmp(opt_storage, secopt, sec_opt_len + 2) == 0)) { 1222 /* The packet has the correct label already */ 1223 return (0); 1224 } 1225 1226 /* 1227 * If there is an option there, then it must be the wrong one; delete. 1228 */ 1229 if (secopt != NULL) 1230 mp->b_wptr += tsol_remove_secopt_v6(ip6h, MBLKL(mp)); 1231 1232 /* 1233 * Make sure we have room for the worst-case addition. Add 2 bytes for 1234 * the hop-by-hop ext header's next header and length fields. Add 1235 * another 2 bytes for the label option type, len and then round 1236 * up to the next 8-byte multiple. 1237 */ 1238 hlen = (4 + sec_opt_len + 7) & ~7; 1239 if (mp->b_wptr + hlen > mp->b_datap->db_lim) { 1240 int copylen; 1241 mblk_t *new_mp; 1242 uint16_t hdr_len; 1243 1244 hdr_len = ip_hdr_length_v6(mp, ip6h); 1245 /* 1246 * Allocate enough to be meaningful, but not *too* much. 1247 * Also all the IPv6 extension headers must be in the same mblk 1248 */ 1249 copylen = MBLKL(mp); 1250 if (copylen > 256) 1251 copylen = 256; 1252 if (copylen < hdr_len) 1253 copylen = hdr_len; 1254 new_mp = allocb(hlen + copylen + 1255 (mp->b_rptr - mp->b_datap->db_base), BPRI_HI); 1256 if (new_mp == NULL) 1257 return (ENOMEM); 1258 mblk_setcred(new_mp, DB_CRED(mp)); 1259 1260 /* keep the bias */ 1261 new_mp->b_rptr += mp->b_rptr - mp->b_datap->db_base; 1262 new_mp->b_wptr = new_mp->b_rptr + copylen; 1263 bcopy(mp->b_rptr, new_mp->b_rptr, copylen); 1264 new_mp->b_cont = mp; 1265 if ((mp->b_rptr += copylen) >= mp->b_wptr) { 1266 new_mp->b_cont = mp->b_cont; 1267 freeb(mp); 1268 } 1269 *mpp = mp = new_mp; 1270 ip6h = (ip6_t *)mp->b_rptr; 1271 } 1272 1273 added = tsol_prepend_option_v6(opt_storage, ip6h, MBLKL(mp)); 1274 if (added == -1) 1275 goto param_prob; 1276 1277 if (addedp != NULL) 1278 *addedp = added; 1279 1280 ASSERT(mp->b_wptr + added <= DB_LIM(mp)); 1281 mp->b_wptr += added; 1282 1283 return (0); 1284 1285 param_prob: 1286 return (EINVAL); 1287 } 1288 1289 /* 1290 * Update the given IPv6 "sticky options" structure to contain the provided 1291 * label, which is encoded as an IPv6 option. Existing label is removed if 1292 * necessary, and storage is allocated/freed/resized. 1293 * 1294 * Returns 0 on success, errno on failure. 1295 */ 1296 int 1297 tsol_update_sticky(ip6_pkt_t *ipp, uint_t *labellen, const uchar_t *labelopt) 1298 { 1299 int rawlen, optlen, newlen; 1300 uchar_t *newopts; 1301 1302 /* 1303 * rawlen is the size of the IPv6 label to be inserted from labelopt. 1304 * optlen is the total length of that option, including any necessary 1305 * headers and padding. newlen is the new size of the total hop-by-hop 1306 * options buffer, including user options. 1307 */ 1308 ASSERT(*labellen <= ipp->ipp_hopoptslen); 1309 ASSERT((ipp->ipp_hopopts == NULL && ipp->ipp_hopoptslen == 0) || 1310 (ipp->ipp_hopopts != NULL && ipp->ipp_hopoptslen != 0)); 1311 1312 if ((rawlen = labelopt[1]) != 0) { 1313 rawlen += 2; /* add in header size */ 1314 optlen = (2 + rawlen + 7) & ~7; 1315 } else { 1316 optlen = 0; 1317 } 1318 newlen = ipp->ipp_hopoptslen + optlen - *labellen; 1319 if (newlen == 0 && ipp->ipp_hopopts != NULL) { 1320 /* Deleting all existing hop-by-hop options */ 1321 kmem_free(ipp->ipp_hopopts, ipp->ipp_hopoptslen); 1322 ipp->ipp_hopopts = NULL; 1323 ipp->ipp_fields &= ~IPPF_HOPOPTS; 1324 } else if (optlen != *labellen) { 1325 /* If the label not same size as last time, then reallocate */ 1326 if (newlen > IP6_MAX_OPT_LENGTH) 1327 return (EHOSTUNREACH); 1328 newopts = kmem_alloc(newlen, KM_NOSLEEP); 1329 if (newopts == NULL) 1330 return (ENOMEM); 1331 /* 1332 * If the user has hop-by-hop stickyoptions set, then copy his 1333 * options in after the security label. 1334 */ 1335 if (ipp->ipp_hopoptslen > *labellen) { 1336 bcopy(ipp->ipp_hopopts + *labellen, newopts + optlen, 1337 ipp->ipp_hopoptslen - *labellen); 1338 /* 1339 * Stomp out any header gunk here - this was the 1340 * previous next-header and option length field. 1341 */ 1342 newopts[optlen] = IP6OPT_PADN; 1343 newopts[optlen + 1] = 0; 1344 } 1345 if (ipp->ipp_hopopts != NULL) 1346 kmem_free(ipp->ipp_hopopts, ipp->ipp_hopoptslen); 1347 ipp->ipp_hopopts = (ip6_hbh_t *)newopts; 1348 } 1349 ipp->ipp_hopoptslen = newlen; 1350 *labellen = optlen; 1351 1352 newopts = (uchar_t *)ipp->ipp_hopopts; 1353 1354 /* If there are any options, then fix up reported length */ 1355 if (newlen > 0) { 1356 newopts[1] = (newlen + 7) / 8 - 1; 1357 ipp->ipp_fields |= IPPF_HOPOPTS; 1358 } 1359 1360 /* If there's a label, then insert it now */ 1361 if (optlen > 0) { 1362 /* skip next-header and length fields */ 1363 newopts += 2; 1364 bcopy(labelopt, newopts, rawlen); 1365 newopts += rawlen; 1366 /* make sure padding comes out right */ 1367 optlen -= 2 + rawlen; 1368 if (optlen == 1) { 1369 newopts[0] = IP6OPT_PAD1; 1370 } else if (optlen > 1) { 1371 newopts[0] = IP6OPT_PADN; 1372 optlen -= 2; 1373 newopts[1] = optlen; 1374 if (optlen > 0) 1375 bzero(newopts + 2, optlen); 1376 } 1377 } 1378 return (0); 1379 } 1380 1381 int 1382 tsol_update_options(uchar_t **opts, uint_t *totlen, uint_t *labellen, 1383 const uchar_t *labelopt) 1384 { 1385 int optlen, newlen; 1386 uchar_t *newopts; 1387 1388 optlen = (labelopt[IPOPT_OLEN] + 3) & ~3; 1389 newlen = *totlen + optlen - *labellen; 1390 if (optlen > *labellen) { 1391 if (newlen > IP_MAX_OPT_LENGTH) 1392 return (EHOSTUNREACH); 1393 newopts = (uchar_t *)mi_alloc(newlen, BPRI_HI); 1394 if (newopts == NULL) 1395 return (ENOMEM); 1396 if (*totlen > *labellen) { 1397 bcopy(*opts + *labellen, newopts + optlen, 1398 *totlen - *labellen); 1399 } 1400 if (*opts != NULL) 1401 mi_free((char *)*opts); 1402 *opts = newopts; 1403 } else if (optlen < *labellen) { 1404 if (newlen == 0 && *opts != NULL) { 1405 mi_free((char *)*opts); 1406 *opts = NULL; 1407 } 1408 if (*totlen > *labellen) { 1409 ovbcopy(*opts + *labellen, *opts + optlen, 1410 *totlen - *labellen); 1411 } 1412 } 1413 *totlen = newlen; 1414 *labellen = optlen; 1415 if (optlen > 0) { 1416 newopts = *opts; 1417 bcopy(labelopt, newopts, optlen); 1418 /* check if there are user-supplied options that follow */ 1419 if (optlen < newlen) { 1420 /* compute amount of embedded alignment needed */ 1421 optlen -= newopts[IPOPT_OLEN]; 1422 newopts += newopts[IPOPT_OLEN]; 1423 while (--optlen >= 0) 1424 *newopts++ = IPOPT_NOP; 1425 } else if (optlen != newopts[IPOPT_OLEN]) { 1426 /* 1427 * The label option is the only option and it is 1428 * not a multiple of 4 bytes. 1429 */ 1430 optlen -= newopts[IPOPT_OLEN]; 1431 newopts += newopts[IPOPT_OLEN]; 1432 while (--optlen >= 0) 1433 *newopts++ = IPOPT_EOL; 1434 } 1435 } 1436 return (0); 1437 } 1438 1439 /* 1440 * This does the bulk of the processing for setting IPPROTO_IP {T_,}IP_OPTIONS. 1441 */ 1442 boolean_t 1443 tsol_option_set(uchar_t **opts, uint_t *optlen, uint_t labellen, 1444 const uchar_t *useropts, uint_t userlen) 1445 { 1446 int newlen; 1447 uchar_t *newopts; 1448 1449 newlen = userlen + labellen; 1450 if (newlen > *optlen) { 1451 /* need more room */ 1452 newopts = (uchar_t *)mi_alloc(newlen, BPRI_HI); 1453 if (newopts == NULL) 1454 return (B_FALSE); 1455 /* 1456 * The supplied *opts can't be NULL in this case, 1457 * since there's an existing label. 1458 */ 1459 if (labellen > 0) 1460 bcopy(*opts, newopts, labellen); 1461 if (*opts != NULL) 1462 mi_free((char *)*opts); 1463 *opts = newopts; 1464 } 1465 1466 if (newlen == 0) { 1467 /* special case -- no remaining IP options at all */ 1468 if (*opts != NULL) { 1469 mi_free((char *)*opts); 1470 *opts = NULL; 1471 } 1472 } else if (userlen > 0) { 1473 /* merge in the user's options */ 1474 newopts = *opts; 1475 if (labellen > 0) { 1476 int extra = labellen - newopts[IPOPT_OLEN]; 1477 1478 newopts += newopts[IPOPT_OLEN]; 1479 while (--extra >= 0) 1480 *newopts++ = IPOPT_NOP; 1481 } 1482 bcopy(useropts, newopts, userlen); 1483 } 1484 1485 *optlen = newlen; 1486 return (B_TRUE); 1487 } 1488