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 /* 23 * Copyright 2009 Sun Microsystems, Inc. All rights reserved. 24 * Use is subject to license terms. 25 */ 26 27 /* 28 * Main door handler functions used by dlmgmtd to process the different door 29 * call requests. Door call requests can come from the user-land applications, 30 * or from the kernel. 31 * 32 * Note on zones handling: 33 * 34 * There are two zoneid's associated with a link. One is the zoneid of the 35 * zone in which the link was created (ll_zoneid in the dlmgmt_link_t), and 36 * the other is the zoneid of the zone where the link is currently assigned 37 * (the "zone" link property). The two can be different if a datalink is 38 * created in the global zone and subsequently assigned to a non-global zone 39 * via zonecfg or via explicitly setting the "zone" link property. 40 * 41 * Door clients can see links that were created in their zone, and links that 42 * are currently assigned to their zone. Door clients in a zone can only 43 * modify links that were created in their zone. 44 * 45 * The datalink ID space is global, while each zone has its own datalink name 46 * space. This allows each zone to have complete freedom over the names that 47 * they assign to links created within the zone. 48 */ 49 50 #include <assert.h> 51 #include <alloca.h> 52 #include <errno.h> 53 #include <priv_utils.h> 54 #include <stdlib.h> 55 #include <strings.h> 56 #include <syslog.h> 57 #include <sys/sysevent/eventdefs.h> 58 #include <zone.h> 59 #include <libsysevent.h> 60 #include <libdlmgmt.h> 61 #include <librcm.h> 62 #include "dlmgmt_impl.h" 63 64 typedef void dlmgmt_door_handler_t(void *, void *, zoneid_t, ucred_t *); 65 66 typedef struct dlmgmt_door_info_s { 67 uint_t di_cmd; 68 size_t di_reqsz; 69 size_t di_acksz; 70 dlmgmt_door_handler_t *di_handler; 71 } dlmgmt_door_info_t; 72 73 /* 74 * Check if the caller has the required privileges to operate on a link of the 75 * given class. 76 */ 77 static int 78 dlmgmt_checkprivs(datalink_class_t class, ucred_t *cred) 79 { 80 const priv_set_t *eset; 81 82 eset = ucred_getprivset(cred, PRIV_EFFECTIVE); 83 if (eset != NULL && ((class == DATALINK_CLASS_IPTUN && 84 priv_ismember(eset, PRIV_SYS_IPTUN_CONFIG)) || 85 priv_ismember(eset, PRIV_SYS_DL_CONFIG) || 86 priv_ismember(eset, PRIV_SYS_NET_CONFIG))) 87 return (0); 88 return (EACCES); 89 } 90 91 static dlmgmt_link_t * 92 dlmgmt_getlink_by_dev(char *devname, zoneid_t zoneid) 93 { 94 dlmgmt_link_t *linkp = avl_first(&dlmgmt_id_avl); 95 96 for (; linkp != NULL; linkp = AVL_NEXT(&dlmgmt_id_avl, linkp)) { 97 if (link_is_visible(linkp, zoneid) && 98 (linkp->ll_class == DATALINK_CLASS_PHYS) && 99 linkattr_equal(&(linkp->ll_head), FDEVNAME, devname, 100 strlen(devname) + 1)) { 101 return (linkp); 102 } 103 } 104 return (NULL); 105 } 106 107 /* 108 * Post the EC_DATALINK sysevent for the given linkid. This sysevent will 109 * be consumed by the datalink sysevent module. 110 */ 111 static void 112 dlmgmt_post_sysevent(const char *subclass, datalink_id_t linkid, 113 boolean_t reconfigured) 114 { 115 nvlist_t *nvl = NULL; 116 sysevent_id_t eid; 117 int err; 118 119 if (((err = nvlist_alloc(&nvl, NV_UNIQUE_NAME_TYPE, 0)) != 0) || 120 ((err = nvlist_add_uint64(nvl, RCM_NV_LINKID, linkid)) != 0) || 121 ((err = nvlist_add_boolean_value(nvl, RCM_NV_RECONFIGURED, 122 reconfigured)) != 0)) { 123 goto done; 124 } 125 126 if (sysevent_post_event(EC_DATALINK, (char *)subclass, SUNW_VENDOR, 127 (char *)progname, nvl, &eid) == -1) { 128 err = errno; 129 } 130 131 done: 132 if (err != 0) { 133 dlmgmt_log(LOG_WARNING, "dlmgmt_post_sysevent(%d) failed: %s", 134 linkid, strerror(err)); 135 } 136 nvlist_free(nvl); 137 } 138 139 static void 140 dlmgmt_upcall_create(void *argp, void *retp, zoneid_t zoneid, ucred_t *cred) 141 { 142 dlmgmt_upcall_arg_create_t *create = argp; 143 dlmgmt_create_retval_t *retvalp = retp; 144 datalink_class_t class; 145 uint32_t media; 146 dlmgmt_link_t *linkp; 147 char link[MAXLINKNAMELEN]; 148 uint32_t flags; 149 int err = 0; 150 boolean_t created = B_FALSE; 151 boolean_t reconfigured = B_FALSE; 152 153 /* 154 * Determine whether this link is persistent. Note that this request 155 * is coming from kernel so this link must be active. 156 */ 157 flags = DLMGMT_ACTIVE | (create->ld_persist ? DLMGMT_PERSIST : 0); 158 159 class = create->ld_class; 160 media = create->ld_media; 161 162 /* 163 * Hold the writer lock to update the link table. 164 */ 165 dlmgmt_table_lock(B_TRUE); 166 167 if ((err = dlmgmt_checkprivs(class, cred)) != 0) 168 goto done; 169 170 /* 171 * Check to see whether this is the reattachment of an existing 172 * physical link. If so, return its linkid. 173 */ 174 if ((class == DATALINK_CLASS_PHYS) && (linkp = 175 dlmgmt_getlink_by_dev(create->ld_devname, zoneid)) != NULL) { 176 if (linkattr_equal(&(linkp->ll_head), FPHYMAJ, 177 &create->ld_phymaj, sizeof (uint64_t)) && 178 linkattr_equal(&(linkp->ll_head), FPHYINST, 179 &create->ld_phyinst, sizeof (uint64_t)) && 180 (linkp->ll_flags & flags) == flags) { 181 /* 182 * If nothing has been changed, directly return. 183 */ 184 goto noupdate; 185 } 186 187 err = linkattr_set(&(linkp->ll_head), FPHYMAJ, 188 &create->ld_phymaj, sizeof (uint64_t), DLADM_TYPE_UINT64); 189 if (err != 0) 190 goto done; 191 192 err = linkattr_set(&(linkp->ll_head), FPHYINST, 193 &create->ld_phyinst, sizeof (uint64_t), DLADM_TYPE_UINT64); 194 if (err != 0) 195 goto done; 196 197 /* 198 * This is a device that is dynamic reconfigured. 199 */ 200 if ((linkp->ll_flags & DLMGMT_ACTIVE) == 0) 201 reconfigured = B_TRUE; 202 203 if ((err = link_activate(linkp)) != 0) 204 goto done; 205 linkp->ll_flags |= flags; 206 linkp->ll_gen++; 207 208 goto done; 209 } 210 211 if ((err = dlmgmt_create_common(create->ld_devname, class, media, 212 zoneid, flags, &linkp)) == EEXIST) { 213 /* 214 * The link name already exists. Return error if this is a 215 * non-physical link (in that case, the link name must be 216 * the same as the given name). 217 */ 218 if (class != DATALINK_CLASS_PHYS) 219 goto done; 220 221 /* 222 * The physical link's name already exists, request 223 * a suggested link name: net<nextppa> 224 */ 225 err = dlmgmt_generate_name("net", link, MAXLINKNAMELEN, zoneid); 226 if (err != 0) 227 goto done; 228 229 err = dlmgmt_create_common(link, class, media, zoneid, flags, 230 &linkp); 231 } 232 233 if (err != 0) 234 goto done; 235 236 created = B_TRUE; 237 238 /* 239 * This is a new link. Only need to persist link attributes for 240 * physical links. 241 */ 242 if (class == DATALINK_CLASS_PHYS && 243 (((err = linkattr_set(&linkp->ll_head, FDEVNAME, create->ld_devname, 244 strlen(create->ld_devname) + 1, DLADM_TYPE_STR)) != 0) || 245 ((err = linkattr_set(&linkp->ll_head, FPHYMAJ, &create->ld_phymaj, 246 sizeof (uint64_t), DLADM_TYPE_UINT64)) != 0) || 247 ((err = linkattr_set(&linkp->ll_head, FPHYINST, &create->ld_phyinst, 248 sizeof (uint64_t), DLADM_TYPE_UINT64)) != 0))) { 249 (void) dlmgmt_destroy_common(linkp, flags); 250 } 251 252 done: 253 if ((err == 0) && ((err = dlmgmt_write_db_entry(linkp->ll_link, linkp, 254 linkp->ll_flags)) != 0) && created) { 255 (void) dlmgmt_destroy_common(linkp, flags); 256 } 257 258 noupdate: 259 if (err == 0) 260 retvalp->lr_linkid = linkp->ll_linkid; 261 262 dlmgmt_table_unlock(); 263 264 if ((err == 0) && (class == DATALINK_CLASS_PHYS)) { 265 /* 266 * Post the ESC_DATALINK_PHYS_ADD sysevent. This sysevent 267 * is consumed by the datalink sysevent module which in 268 * turn generates the RCM_RESOURCE_LINK_NEW RCM event. 269 */ 270 dlmgmt_post_sysevent(ESC_DATALINK_PHYS_ADD, 271 retvalp->lr_linkid, reconfigured); 272 } 273 274 retvalp->lr_err = err; 275 } 276 277 static void 278 dlmgmt_upcall_update(void *argp, void *retp, zoneid_t zoneid, ucred_t *cred) 279 { 280 dlmgmt_upcall_arg_update_t *update = argp; 281 dlmgmt_update_retval_t *retvalp = retp; 282 uint32_t media = update->ld_media; 283 dlmgmt_link_t *linkp; 284 int err = 0; 285 286 /* 287 * Hold the writer lock to update the link table. 288 */ 289 dlmgmt_table_lock(B_TRUE); 290 291 /* 292 * Check to see whether this is the reattachment of an existing 293 * physical link. If so, return its linkid. 294 */ 295 if ((linkp = dlmgmt_getlink_by_dev(update->ld_devname, zoneid)) == 296 NULL) { 297 err = ENOENT; 298 goto done; 299 } 300 301 if ((err = dlmgmt_checkprivs(linkp->ll_class, cred)) != 0) 302 goto done; 303 304 retvalp->lr_linkid = linkp->ll_linkid; 305 retvalp->lr_media = media; 306 if (linkp->ll_media != media && linkp->ll_media != DL_OTHER) { 307 /* 308 * Assume a DL_ETHER link ce0, a DL_WIFI link ath0 309 * 1. # dladm rename-link ce0 net0 310 * 2. DR out ce0. net0 is down. 311 * 3. use rename-link to have the ath0 device inherit 312 * the configuration from net0 313 * # dladm rename-link ath0 net0 314 * 4. DR in ath0. 315 * As ath0 and ce0 do not have the same media type, ath0 316 * cannot inherit the configuration of net0. 317 */ 318 err = EEXIST; 319 320 /* 321 * Return the media type of the existing link to indicate the 322 * reason for the name conflict. 323 */ 324 retvalp->lr_media = linkp->ll_media; 325 goto done; 326 } 327 328 if (update->ld_novanity && 329 (strcmp(update->ld_devname, linkp->ll_link) != 0)) { 330 /* 331 * Return an error if this is a physical link that does not 332 * support vanity naming, but the link name is not the same 333 * as the given device name. 334 */ 335 err = EEXIST; 336 goto done; 337 } 338 339 if (linkp->ll_media != media) { 340 linkp->ll_media = media; 341 linkp->ll_gen++; 342 (void) dlmgmt_write_db_entry(linkp->ll_link, linkp, 343 linkp->ll_flags); 344 } 345 346 done: 347 dlmgmt_table_unlock(); 348 retvalp->lr_err = err; 349 } 350 351 static void 352 dlmgmt_upcall_destroy(void *argp, void *retp, zoneid_t zoneid, ucred_t *cred) 353 { 354 dlmgmt_upcall_arg_destroy_t *destroy = argp; 355 dlmgmt_destroy_retval_t *retvalp = retp; 356 datalink_id_t linkid = destroy->ld_linkid; 357 dlmgmt_link_t *linkp = NULL; 358 uint32_t flags, dflags = 0; 359 int err = 0; 360 361 flags = DLMGMT_ACTIVE | (destroy->ld_persist ? DLMGMT_PERSIST : 0); 362 363 /* 364 * Hold the writer lock to update the link table. 365 */ 366 dlmgmt_table_lock(B_TRUE); 367 368 if ((linkp = link_by_id(linkid, zoneid)) == NULL) { 369 err = ENOENT; 370 goto done; 371 } 372 373 if ((err = dlmgmt_checkprivs(linkp->ll_class, cred)) != 0) 374 goto done; 375 376 if (((linkp->ll_flags & flags) & DLMGMT_ACTIVE) != 0) { 377 if ((err = dlmgmt_delete_db_entry(linkp, DLMGMT_ACTIVE)) != 0) 378 goto done; 379 dflags |= DLMGMT_ACTIVE; 380 } 381 382 if (((linkp->ll_flags & flags) & DLMGMT_PERSIST) != 0) { 383 if ((err = dlmgmt_delete_db_entry(linkp, DLMGMT_PERSIST)) != 0) 384 goto done; 385 dflags |= DLMGMT_PERSIST; 386 } 387 388 err = dlmgmt_destroy_common(linkp, flags); 389 done: 390 if (err != 0 && dflags != 0) 391 (void) dlmgmt_write_db_entry(linkp->ll_link, linkp, dflags); 392 393 dlmgmt_table_unlock(); 394 retvalp->lr_err = err; 395 } 396 397 /* ARGSUSED */ 398 static void 399 dlmgmt_getname(void *argp, void *retp, zoneid_t zoneid, ucred_t *cred) 400 { 401 dlmgmt_door_getname_t *getname = argp; 402 dlmgmt_getname_retval_t *retvalp = retp; 403 dlmgmt_link_t *linkp; 404 int err = 0; 405 406 /* 407 * Hold the reader lock to access the link 408 */ 409 dlmgmt_table_lock(B_FALSE); 410 if ((linkp = link_by_id(getname->ld_linkid, zoneid)) == NULL) { 411 err = ENOENT; 412 } else if (strlcpy(retvalp->lr_link, linkp->ll_link, MAXLINKNAMELEN) >= 413 MAXLINKNAMELEN) { 414 err = ENOSPC; 415 } else { 416 retvalp->lr_flags = linkp->ll_flags; 417 retvalp->lr_class = linkp->ll_class; 418 retvalp->lr_media = linkp->ll_media; 419 } 420 421 dlmgmt_table_unlock(); 422 retvalp->lr_err = err; 423 } 424 425 /* ARGSUSED */ 426 static void 427 dlmgmt_getlinkid(void *argp, void *retp, zoneid_t zoneid, ucred_t *cred) 428 { 429 dlmgmt_door_getlinkid_t *getlinkid = argp; 430 dlmgmt_getlinkid_retval_t *retvalp = retp; 431 dlmgmt_link_t *linkp; 432 int err = 0; 433 434 /* 435 * Hold the reader lock to access the link 436 */ 437 dlmgmt_table_lock(B_FALSE); 438 439 if ((linkp = link_by_name(getlinkid->ld_link, zoneid)) == NULL) { 440 /* 441 * The link does not exist in this zone. 442 */ 443 err = ENOENT; 444 goto done; 445 } 446 447 retvalp->lr_linkid = linkp->ll_linkid; 448 retvalp->lr_flags = linkp->ll_flags; 449 retvalp->lr_class = linkp->ll_class; 450 retvalp->lr_media = linkp->ll_media; 451 452 done: 453 dlmgmt_table_unlock(); 454 retvalp->lr_err = err; 455 } 456 457 /* ARGSUSED */ 458 static void 459 dlmgmt_getnext(void *argp, void *retp, zoneid_t zoneid, ucred_t *cred) 460 { 461 dlmgmt_door_getnext_t *getnext = argp; 462 dlmgmt_getnext_retval_t *retvalp = retp; 463 dlmgmt_link_t link, *linkp; 464 avl_index_t where; 465 int err = 0; 466 467 /* 468 * Hold the reader lock to access the link 469 */ 470 dlmgmt_table_lock(B_FALSE); 471 472 link.ll_linkid = (getnext->ld_linkid + 1); 473 if ((linkp = avl_find(&dlmgmt_id_avl, &link, &where)) == NULL) 474 linkp = avl_nearest(&dlmgmt_id_avl, where, AVL_AFTER); 475 476 for (; linkp != NULL; linkp = AVL_NEXT(&dlmgmt_id_avl, linkp)) { 477 if (!link_is_visible(linkp, zoneid)) 478 continue; 479 if ((linkp->ll_class & getnext->ld_class) && 480 (linkp->ll_flags & getnext->ld_flags) && 481 DATALINK_MEDIA_ACCEPTED(getnext->ld_dmedia, 482 linkp->ll_media)) 483 break; 484 } 485 486 if (linkp == NULL) { 487 err = ENOENT; 488 } else { 489 retvalp->lr_linkid = linkp->ll_linkid; 490 retvalp->lr_class = linkp->ll_class; 491 retvalp->lr_media = linkp->ll_media; 492 retvalp->lr_flags = linkp->ll_flags; 493 } 494 495 dlmgmt_table_unlock(); 496 retvalp->lr_err = err; 497 } 498 499 /* ARGSUSED */ 500 static void 501 dlmgmt_upcall_getattr(void *argp, void *retp, zoneid_t zoneid, ucred_t *cred) 502 { 503 dlmgmt_upcall_arg_getattr_t *getattr = argp; 504 dlmgmt_getattr_retval_t *retvalp = retp; 505 dlmgmt_link_t *linkp; 506 507 /* 508 * Hold the reader lock to access the link 509 */ 510 dlmgmt_table_lock(B_FALSE); 511 if ((linkp = link_by_id(getattr->ld_linkid, zoneid)) == NULL) { 512 retvalp->lr_err = ENOENT; 513 } else { 514 retvalp->lr_err = dlmgmt_getattr_common(&linkp->ll_head, 515 getattr->ld_attr, retvalp); 516 } 517 dlmgmt_table_unlock(); 518 } 519 520 static void 521 dlmgmt_createid(void *argp, void *retp, zoneid_t zoneid, ucred_t *cred) 522 { 523 dlmgmt_door_createid_t *createid = argp; 524 dlmgmt_createid_retval_t *retvalp = retp; 525 dlmgmt_link_t *linkp; 526 datalink_id_t linkid = DATALINK_INVALID_LINKID; 527 char link[MAXLINKNAMELEN]; 528 int err; 529 530 /* 531 * Hold the writer lock to update the dlconf table. 532 */ 533 dlmgmt_table_lock(B_TRUE); 534 535 if ((err = dlmgmt_checkprivs(createid->ld_class, cred)) != 0) 536 goto done; 537 538 if (createid->ld_prefix) { 539 err = dlmgmt_generate_name(createid->ld_link, link, 540 MAXLINKNAMELEN, zoneid); 541 if (err != 0) 542 goto done; 543 544 err = dlmgmt_create_common(link, createid->ld_class, 545 createid->ld_media, zoneid, createid->ld_flags, &linkp); 546 } else { 547 err = dlmgmt_create_common(createid->ld_link, 548 createid->ld_class, createid->ld_media, zoneid, 549 createid->ld_flags, &linkp); 550 } 551 552 if (err == 0) { 553 /* 554 * Keep the active mapping. 555 */ 556 linkid = linkp->ll_linkid; 557 if (createid->ld_flags & DLMGMT_ACTIVE) { 558 (void) dlmgmt_write_db_entry(linkp->ll_link, linkp, 559 DLMGMT_ACTIVE); 560 } 561 } 562 563 done: 564 dlmgmt_table_unlock(); 565 retvalp->lr_linkid = linkid; 566 retvalp->lr_err = err; 567 } 568 569 static void 570 dlmgmt_destroyid(void *argp, void *retp, zoneid_t zoneid, ucred_t *cred) 571 { 572 dlmgmt_door_destroyid_t *destroyid = argp; 573 dlmgmt_destroyid_retval_t *retvalp = retp; 574 datalink_id_t linkid = destroyid->ld_linkid; 575 uint32_t flags = destroyid->ld_flags; 576 dlmgmt_link_t *linkp = NULL; 577 int err = 0; 578 579 /* 580 * Hold the writer lock to update the link table. 581 */ 582 dlmgmt_table_lock(B_TRUE); 583 if ((linkp = link_by_id(linkid, zoneid)) == NULL) { 584 err = ENOENT; 585 goto done; 586 } 587 588 if ((err = dlmgmt_checkprivs(linkp->ll_class, cred)) != 0) 589 goto done; 590 591 /* 592 * Delete the active mapping. 593 */ 594 if (flags & DLMGMT_ACTIVE) 595 err = dlmgmt_delete_db_entry(linkp, DLMGMT_ACTIVE); 596 if (err == 0) 597 err = dlmgmt_destroy_common(linkp, flags); 598 done: 599 dlmgmt_table_unlock(); 600 retvalp->lr_err = err; 601 } 602 603 /* 604 * Remap a linkid to a given link name, i.e., rename an existing link1 605 * (ld_linkid) to a non-existent link2 (ld_link): rename link1's name to 606 * the given link name. 607 */ 608 static void 609 dlmgmt_remapid(void *argp, void *retp, zoneid_t zoneid, ucred_t *cred) 610 { 611 dlmgmt_door_remapid_t *remapid = argp; 612 dlmgmt_remapid_retval_t *retvalp = retp; 613 dlmgmt_link_t *linkp; 614 char oldname[MAXLINKNAMELEN]; 615 boolean_t renamed = B_FALSE; 616 int err = 0; 617 618 if (!dladm_valid_linkname(remapid->ld_link)) { 619 retvalp->lr_err = EINVAL; 620 return; 621 } 622 623 /* 624 * Hold the writer lock to update the link table. 625 */ 626 dlmgmt_table_lock(B_TRUE); 627 if ((linkp = link_by_id(remapid->ld_linkid, zoneid)) == NULL) { 628 err = ENOENT; 629 goto done; 630 } 631 632 if ((err = dlmgmt_checkprivs(linkp->ll_class, cred)) != 0) 633 goto done; 634 635 if (link_by_name(remapid->ld_link, linkp->ll_zoneid) != NULL) { 636 err = EEXIST; 637 goto done; 638 } 639 640 (void) strlcpy(oldname, linkp->ll_link, MAXLINKNAMELEN); 641 avl_remove(&dlmgmt_name_avl, linkp); 642 (void) strlcpy(linkp->ll_link, remapid->ld_link, MAXLINKNAMELEN); 643 avl_add(&dlmgmt_name_avl, linkp); 644 renamed = B_TRUE; 645 646 if (linkp->ll_flags & DLMGMT_ACTIVE) { 647 err = dlmgmt_write_db_entry(oldname, linkp, DLMGMT_ACTIVE); 648 if (err != 0) 649 goto done; 650 } 651 if (linkp->ll_flags & DLMGMT_PERSIST) { 652 err = dlmgmt_write_db_entry(oldname, linkp, DLMGMT_PERSIST); 653 if (err != 0) { 654 if (linkp->ll_flags & DLMGMT_ACTIVE) { 655 (void) dlmgmt_write_db_entry(remapid->ld_link, 656 linkp, DLMGMT_ACTIVE); 657 } 658 goto done; 659 } 660 } 661 662 dlmgmt_advance(linkp); 663 linkp->ll_gen++; 664 done: 665 if (err != 0 && renamed) { 666 avl_remove(&dlmgmt_name_avl, linkp); 667 (void) strlcpy(linkp->ll_link, oldname, MAXLINKNAMELEN); 668 avl_add(&dlmgmt_name_avl, linkp); 669 } 670 dlmgmt_table_unlock(); 671 retvalp->lr_err = err; 672 } 673 674 static void 675 dlmgmt_upid(void *argp, void *retp, zoneid_t zoneid, ucred_t *cred) 676 { 677 dlmgmt_door_upid_t *upid = argp; 678 dlmgmt_upid_retval_t *retvalp = retp; 679 dlmgmt_link_t *linkp; 680 int err = 0; 681 682 /* 683 * Hold the writer lock to update the link table. 684 */ 685 dlmgmt_table_lock(B_TRUE); 686 if ((linkp = link_by_id(upid->ld_linkid, zoneid)) == NULL) { 687 err = ENOENT; 688 goto done; 689 } 690 691 if ((err = dlmgmt_checkprivs(linkp->ll_class, cred)) != 0) 692 goto done; 693 694 if (linkp->ll_flags & DLMGMT_ACTIVE) { 695 err = EINVAL; 696 goto done; 697 } 698 699 if ((err = link_activate(linkp)) == 0) { 700 (void) dlmgmt_write_db_entry(linkp->ll_link, linkp, 701 DLMGMT_ACTIVE); 702 } 703 done: 704 dlmgmt_table_unlock(); 705 retvalp->lr_err = err; 706 } 707 708 static void 709 dlmgmt_createconf(void *argp, void *retp, zoneid_t zoneid, ucred_t *cred) 710 { 711 dlmgmt_door_createconf_t *createconf = argp; 712 dlmgmt_createconf_retval_t *retvalp = retp; 713 dlmgmt_dlconf_t *dlconfp; 714 int err; 715 716 /* 717 * Hold the writer lock to update the dlconf table. 718 */ 719 dlmgmt_dlconf_table_lock(B_TRUE); 720 721 if ((err = dlmgmt_checkprivs(createconf->ld_class, cred)) != 0) 722 goto done; 723 724 err = dlconf_create(createconf->ld_link, createconf->ld_linkid, 725 createconf->ld_class, createconf->ld_media, zoneid, &dlconfp); 726 if (err == 0) { 727 avl_add(&dlmgmt_dlconf_avl, dlconfp); 728 dlmgmt_advance_dlconfid(dlconfp); 729 retvalp->lr_conf = (dladm_conf_t)dlconfp->ld_id; 730 } 731 done: 732 dlmgmt_dlconf_table_unlock(); 733 retvalp->lr_err = err; 734 } 735 736 static void 737 dlmgmt_setattr(void *argp, void *retp, zoneid_t zoneid, ucred_t *cred) 738 { 739 dlmgmt_door_setattr_t *setattr = argp; 740 dlmgmt_setattr_retval_t *retvalp = retp; 741 dlmgmt_dlconf_t dlconf, *dlconfp; 742 int err = 0; 743 744 /* 745 * Hold the writer lock to update the dlconf table. 746 */ 747 dlmgmt_dlconf_table_lock(B_TRUE); 748 749 dlconf.ld_id = (int)setattr->ld_conf; 750 dlconfp = avl_find(&dlmgmt_dlconf_avl, &dlconf, NULL); 751 if (dlconfp == NULL || zoneid != dlconfp->ld_zoneid) { 752 err = ENOENT; 753 goto done; 754 } 755 756 if ((err = dlmgmt_checkprivs(dlconfp->ld_class, cred)) != 0) 757 goto done; 758 759 err = linkattr_set(&(dlconfp->ld_head), setattr->ld_attr, 760 &setattr->ld_attrval, setattr->ld_attrsz, setattr->ld_type); 761 762 done: 763 dlmgmt_dlconf_table_unlock(); 764 retvalp->lr_err = err; 765 } 766 767 static void 768 dlmgmt_unsetconfattr(void *argp, void *retp, zoneid_t zoneid, ucred_t *cred) 769 { 770 dlmgmt_door_unsetattr_t *unsetattr = argp; 771 dlmgmt_unsetattr_retval_t *retvalp = retp; 772 dlmgmt_dlconf_t dlconf, *dlconfp; 773 int err = 0; 774 775 /* 776 * Hold the writer lock to update the dlconf table. 777 */ 778 dlmgmt_dlconf_table_lock(B_TRUE); 779 780 dlconf.ld_id = (int)unsetattr->ld_conf; 781 dlconfp = avl_find(&dlmgmt_dlconf_avl, &dlconf, NULL); 782 if (dlconfp == NULL || zoneid != dlconfp->ld_zoneid) { 783 err = ENOENT; 784 goto done; 785 } 786 787 if ((err = dlmgmt_checkprivs(dlconfp->ld_class, cred)) != 0) 788 goto done; 789 790 linkattr_unset(&(dlconfp->ld_head), unsetattr->ld_attr); 791 792 done: 793 dlmgmt_dlconf_table_unlock(); 794 retvalp->lr_err = err; 795 } 796 797 /* 798 * Note that dlmgmt_readconf() returns a conf ID of a conf AVL tree entry, 799 * which is managed by dlmgmtd. The ID is used to find the conf entry when 800 * dlmgmt_write_conf() is called. The conf entry contains an ld_gen value 801 * (which is the generation number - ll_gen) of the dlmgmt_link_t at the time 802 * of dlmgmt_readconf(), and ll_gen changes every time the dlmgmt_link_t 803 * changes its attributes. Therefore, dlmgmt_write_conf() can compare ld_gen 804 * in the conf entry against the latest dlmgmt_link_t ll_gen value to see if 805 * anything has changed between the dlmgmt_read_conf() and dlmgmt_writeconf() 806 * calls. If so, EAGAIN is returned. This mechanism can ensures atomicity 807 * across the pair of dladm_read_conf() and dladm_write_conf() calls. 808 */ 809 static void 810 dlmgmt_writeconf(void *argp, void *retp, zoneid_t zoneid, ucred_t *cred) 811 { 812 dlmgmt_door_writeconf_t *writeconf = argp; 813 dlmgmt_writeconf_retval_t *retvalp = retp; 814 dlmgmt_dlconf_t dlconf, *dlconfp; 815 dlmgmt_link_t *linkp; 816 dlmgmt_linkattr_t *attrp, *next; 817 int err = 0; 818 819 /* 820 * Hold the read lock to access the dlconf table. 821 */ 822 dlmgmt_dlconf_table_lock(B_TRUE); 823 824 dlconf.ld_id = (int)writeconf->ld_conf; 825 dlconfp = avl_find(&dlmgmt_dlconf_avl, &dlconf, NULL); 826 if (dlconfp == NULL || zoneid != dlconfp->ld_zoneid) { 827 err = ENOENT; 828 goto done; 829 } 830 831 if ((err = dlmgmt_checkprivs(dlconfp->ld_class, cred)) != 0) 832 goto done; 833 834 /* 835 * Hold the writer lock to update the link table. 836 */ 837 dlmgmt_table_lock(B_TRUE); 838 linkp = link_by_id(dlconfp->ld_linkid, zoneid); 839 if ((linkp == NULL) || (linkp->ll_class != dlconfp->ld_class) || 840 (linkp->ll_media != dlconfp->ld_media) || 841 (strcmp(linkp->ll_link, dlconfp->ld_link) != 0)) { 842 /* 843 * The link does not exist. 844 */ 845 dlmgmt_table_unlock(); 846 err = ENOENT; 847 goto done; 848 } 849 850 if (linkp->ll_gen != dlconfp->ld_gen) { 851 /* 852 * Something has changed the link configuration; try again. 853 */ 854 dlmgmt_table_unlock(); 855 err = EAGAIN; 856 goto done; 857 } 858 859 /* 860 * Delete the old attribute list. 861 */ 862 for (attrp = linkp->ll_head; attrp != NULL; attrp = next) { 863 next = attrp->lp_next; 864 free(attrp->lp_val); 865 free(attrp); 866 } 867 linkp->ll_head = NULL; 868 869 /* 870 * Set the new attribute. 871 */ 872 for (attrp = dlconfp->ld_head; attrp != NULL; attrp = attrp->lp_next) { 873 if ((err = linkattr_set(&(linkp->ll_head), attrp->lp_name, 874 attrp->lp_val, attrp->lp_sz, attrp->lp_type)) != 0) { 875 dlmgmt_table_unlock(); 876 goto done; 877 } 878 } 879 880 linkp->ll_gen++; 881 err = dlmgmt_write_db_entry(linkp->ll_link, linkp, DLMGMT_PERSIST); 882 dlmgmt_table_unlock(); 883 done: 884 dlmgmt_dlconf_table_unlock(); 885 retvalp->lr_err = err; 886 } 887 888 static void 889 dlmgmt_removeconf(void *argp, void *retp, zoneid_t zoneid, ucred_t *cred) 890 { 891 dlmgmt_door_removeconf_t *removeconf = argp; 892 dlmgmt_removeconf_retval_t *retvalp = retp; 893 dlmgmt_link_t *linkp; 894 int err; 895 896 dlmgmt_table_lock(B_TRUE); 897 if ((linkp = link_by_id(removeconf->ld_linkid, zoneid)) == NULL) { 898 err = ENOENT; 899 goto done; 900 } 901 if (zoneid != GLOBAL_ZONEID && linkp->ll_onloan) { 902 /* 903 * A non-global zone cannot remove the persistent 904 * configuration of a link that is on loan from the global 905 * zone. 906 */ 907 err = EACCES; 908 goto done; 909 } 910 if ((err = dlmgmt_checkprivs(linkp->ll_class, cred)) != 0) 911 goto done; 912 913 err = dlmgmt_delete_db_entry(linkp, DLMGMT_PERSIST); 914 done: 915 dlmgmt_table_unlock(); 916 retvalp->lr_err = err; 917 } 918 919 static void 920 dlmgmt_destroyconf(void *argp, void *retp, zoneid_t zoneid, ucred_t *cred) 921 { 922 dlmgmt_door_destroyconf_t *destroyconf = argp; 923 dlmgmt_destroyconf_retval_t *retvalp = retp; 924 dlmgmt_dlconf_t dlconf, *dlconfp; 925 int err = 0; 926 927 /* 928 * Hold the writer lock to update the dlconf table. 929 */ 930 dlmgmt_dlconf_table_lock(B_TRUE); 931 932 dlconf.ld_id = (int)destroyconf->ld_conf; 933 dlconfp = avl_find(&dlmgmt_dlconf_avl, &dlconf, NULL); 934 if (dlconfp == NULL || zoneid != dlconfp->ld_zoneid) { 935 err = ENOENT; 936 goto done; 937 } 938 939 if ((err = dlmgmt_checkprivs(dlconfp->ld_class, cred)) != 0) 940 goto done; 941 942 avl_remove(&dlmgmt_dlconf_avl, dlconfp); 943 dlconf_destroy(dlconfp); 944 945 done: 946 dlmgmt_dlconf_table_unlock(); 947 retvalp->lr_err = err; 948 } 949 950 /* 951 * See the comments above dladm_write_conf() to see how ld_gen is used to 952 * ensure atomicity across the {dlmgmt_readconf(), dlmgmt_writeconf()} pair. 953 */ 954 /* ARGSUSED */ 955 static void 956 dlmgmt_readconf(void *argp, void *retp, zoneid_t zoneid, ucred_t *cred) 957 { 958 dlmgmt_door_readconf_t *readconf = argp; 959 dlmgmt_readconf_retval_t *retvalp = retp; 960 dlmgmt_link_t *linkp; 961 datalink_id_t linkid = readconf->ld_linkid; 962 dlmgmt_dlconf_t *dlconfp; 963 dlmgmt_linkattr_t *attrp; 964 int err = 0; 965 966 /* 967 * Hold the writer lock to update the dlconf table. 968 */ 969 dlmgmt_dlconf_table_lock(B_TRUE); 970 971 /* 972 * Hold the reader lock to access the link 973 */ 974 dlmgmt_table_lock(B_FALSE); 975 linkp = link_by_id(linkid, zoneid); 976 if ((linkp == NULL) || !(linkp->ll_flags & DLMGMT_PERSIST)) { 977 /* The persistent link configuration does not exist. */ 978 err = ENOENT; 979 goto done; 980 } 981 if (linkp->ll_onloan && zoneid != GLOBAL_ZONEID) { 982 /* 983 * The caller is in a non-global zone and the persistent 984 * configuration belongs to the global zone. 985 */ 986 err = EACCES; 987 goto done; 988 } 989 990 if ((err = dlconf_create(linkp->ll_link, linkp->ll_linkid, 991 linkp->ll_class, linkp->ll_media, zoneid, &dlconfp)) != 0) 992 goto done; 993 994 for (attrp = linkp->ll_head; attrp != NULL; attrp = attrp->lp_next) { 995 if ((err = linkattr_set(&(dlconfp->ld_head), attrp->lp_name, 996 attrp->lp_val, attrp->lp_sz, attrp->lp_type)) != 0) { 997 dlconf_destroy(dlconfp); 998 goto done; 999 } 1000 } 1001 dlconfp->ld_gen = linkp->ll_gen; 1002 avl_add(&dlmgmt_dlconf_avl, dlconfp); 1003 dlmgmt_advance_dlconfid(dlconfp); 1004 1005 retvalp->lr_conf = (dladm_conf_t)dlconfp->ld_id; 1006 done: 1007 dlmgmt_table_unlock(); 1008 dlmgmt_dlconf_table_unlock(); 1009 retvalp->lr_err = err; 1010 } 1011 1012 /* 1013 * Note: the caller must free *retvalpp in case of success. 1014 */ 1015 /* ARGSUSED */ 1016 static void 1017 dlmgmt_getattr(void *argp, void *retp, zoneid_t zoneid, ucred_t *cred) 1018 { 1019 dlmgmt_door_getattr_t *getattr = argp; 1020 dlmgmt_getattr_retval_t *retvalp = retp; 1021 dlmgmt_dlconf_t dlconf, *dlconfp; 1022 1023 /* 1024 * Hold the read lock to access the dlconf table. 1025 */ 1026 dlmgmt_dlconf_table_lock(B_FALSE); 1027 1028 dlconf.ld_id = (int)getattr->ld_conf; 1029 if ((dlconfp = avl_find(&dlmgmt_dlconf_avl, &dlconf, NULL)) == NULL || 1030 zoneid != dlconfp->ld_zoneid) { 1031 retvalp->lr_err = ENOENT; 1032 } else { 1033 retvalp->lr_err = dlmgmt_getattr_common(&dlconfp->ld_head, 1034 getattr->ld_attr, retvalp); 1035 } 1036 1037 dlmgmt_dlconf_table_unlock(); 1038 } 1039 1040 static void 1041 dlmgmt_upcall_linkprop_init(void *argp, void *retp, zoneid_t zoneid, 1042 ucred_t *cred) 1043 { 1044 dlmgmt_door_linkprop_init_t *lip = argp; 1045 dlmgmt_linkprop_init_retval_t *retvalp = retp; 1046 dlmgmt_link_t *linkp; 1047 int err; 1048 1049 dlmgmt_table_lock(B_FALSE); 1050 if ((linkp = link_by_id(lip->ld_linkid, zoneid)) == NULL) 1051 err = ENOENT; 1052 else 1053 err = dlmgmt_checkprivs(linkp->ll_class, cred); 1054 dlmgmt_table_unlock(); 1055 1056 if (err == 0) 1057 err = dladm_init_linkprop(dld_handle, lip->ld_linkid, B_TRUE); 1058 retvalp->lr_err = err; 1059 } 1060 1061 /* 1062 * Get the link property that follows ld_last_attr. 1063 * If ld_last_attr is empty, return the first property. 1064 */ 1065 /* ARGSUSED */ 1066 static void 1067 dlmgmt_linkprop_getnext(void *argp, void *retp, zoneid_t zoneid, ucred_t *cred) 1068 { 1069 dlmgmt_door_linkprop_getnext_t *getnext = argp; 1070 dlmgmt_linkprop_getnext_retval_t *retvalp = retp; 1071 dlmgmt_dlconf_t dlconf, *dlconfp; 1072 char *attr; 1073 void *attrval; 1074 size_t attrsz; 1075 dladm_datatype_t attrtype; 1076 int err = 0; 1077 1078 /* 1079 * Hold the read lock to access the dlconf table. 1080 */ 1081 dlmgmt_dlconf_table_lock(B_FALSE); 1082 1083 dlconf.ld_id = (int)getnext->ld_conf; 1084 dlconfp = avl_find(&dlmgmt_dlconf_avl, &dlconf, NULL); 1085 if (dlconfp == NULL) { 1086 err = ENOENT; 1087 goto done; 1088 } 1089 1090 err = linkprop_getnext(&dlconfp->ld_head, getnext->ld_last_attr, 1091 &attr, &attrval, &attrsz, &attrtype); 1092 if (err != 0) 1093 goto done; 1094 1095 if (attrsz > MAXLINKATTRVALLEN) { 1096 err = EINVAL; 1097 goto done; 1098 } 1099 1100 (void) strlcpy(retvalp->lr_attr, attr, MAXLINKATTRLEN); 1101 retvalp->lr_type = attrtype; 1102 retvalp->lr_attrsz = attrsz; 1103 bcopy(attrval, retvalp->lr_attrval, attrsz); 1104 1105 done: 1106 dlmgmt_dlconf_table_unlock(); 1107 retvalp->lr_err = err; 1108 } 1109 1110 static void 1111 dlmgmt_setzoneid(void *argp, void *retp, zoneid_t zoneid, ucred_t *cred) 1112 { 1113 dlmgmt_door_setzoneid_t *setzoneid = argp; 1114 dlmgmt_setzoneid_retval_t *retvalp = retp; 1115 dlmgmt_link_t *linkp; 1116 datalink_id_t linkid = setzoneid->ld_linkid; 1117 zoneid_t oldzoneid, newzoneid; 1118 int err = 0; 1119 1120 dlmgmt_table_lock(B_TRUE); 1121 1122 /* We currently only allow changing zoneid's from the global zone. */ 1123 if (zoneid != GLOBAL_ZONEID) { 1124 err = EACCES; 1125 goto done; 1126 } 1127 1128 if ((linkp = link_by_id(linkid, zoneid)) == NULL) { 1129 err = ENOENT; 1130 goto done; 1131 } 1132 1133 if ((err = dlmgmt_checkprivs(linkp->ll_class, cred)) != 0) 1134 goto done; 1135 1136 /* We can only assign an active link to a zone. */ 1137 if (!(linkp->ll_flags & DLMGMT_ACTIVE)) { 1138 err = EINVAL; 1139 goto done; 1140 } 1141 1142 oldzoneid = linkp->ll_zoneid; 1143 newzoneid = setzoneid->ld_zoneid; 1144 1145 if (oldzoneid == newzoneid) 1146 goto done; 1147 1148 /* 1149 * Before we remove the link from its current zone, make sure that 1150 * there isn't a link with the same name in the destination zone. 1151 */ 1152 if (zoneid != GLOBAL_ZONEID && 1153 link_by_name(linkp->ll_link, newzoneid) != NULL) { 1154 err = EEXIST; 1155 goto done; 1156 } 1157 1158 if (oldzoneid != GLOBAL_ZONEID) { 1159 if (zone_remove_datalink(oldzoneid, linkid) != 0) { 1160 err = errno; 1161 dlmgmt_log(LOG_WARNING, "unable to remove link %d from " 1162 "zone %d: %s", linkid, oldzoneid, strerror(err)); 1163 goto done; 1164 } 1165 avl_remove(&dlmgmt_loan_avl, linkp); 1166 linkp->ll_onloan = B_FALSE; 1167 } 1168 if (newzoneid != GLOBAL_ZONEID) { 1169 if (zone_add_datalink(newzoneid, linkid) != 0) { 1170 err = errno; 1171 dlmgmt_log(LOG_WARNING, "unable to add link %d to zone " 1172 "%d: %s", linkid, newzoneid, strerror(err)); 1173 (void) zone_add_datalink(oldzoneid, linkid); 1174 goto done; 1175 } 1176 avl_add(&dlmgmt_loan_avl, linkp); 1177 linkp->ll_onloan = B_TRUE; 1178 } 1179 1180 avl_remove(&dlmgmt_name_avl, linkp); 1181 linkp->ll_zoneid = newzoneid; 1182 avl_add(&dlmgmt_name_avl, linkp); 1183 1184 done: 1185 dlmgmt_table_unlock(); 1186 retvalp->lr_err = err; 1187 } 1188 1189 static void 1190 dlmgmt_zoneboot(void *argp, void *retp, zoneid_t zoneid, ucred_t *cred) 1191 { 1192 int err; 1193 dlmgmt_door_zoneboot_t *zoneboot = argp; 1194 dlmgmt_zoneboot_retval_t *retvalp = retp; 1195 1196 dlmgmt_table_lock(B_TRUE); 1197 1198 if ((err = dlmgmt_checkprivs(0, cred)) != 0) 1199 goto done; 1200 1201 if (zoneid != GLOBAL_ZONEID) { 1202 err = EACCES; 1203 goto done; 1204 } 1205 if (zoneboot->ld_zoneid == GLOBAL_ZONEID) { 1206 err = EINVAL; 1207 goto done; 1208 } 1209 1210 if ((err = dlmgmt_elevate_privileges()) == 0) { 1211 err = dlmgmt_zone_init(zoneboot->ld_zoneid); 1212 (void) dlmgmt_drop_privileges(); 1213 } 1214 done: 1215 dlmgmt_table_unlock(); 1216 retvalp->lr_err = err; 1217 } 1218 1219 static void 1220 dlmgmt_zonehalt(void *argp, void *retp, zoneid_t zoneid, ucred_t *cred) 1221 { 1222 int err = 0; 1223 dlmgmt_door_zonehalt_t *zonehalt = argp; 1224 dlmgmt_zonehalt_retval_t *retvalp = retp; 1225 1226 if ((err = dlmgmt_checkprivs(0, cred)) == 0) { 1227 if (zoneid != GLOBAL_ZONEID) { 1228 err = EACCES; 1229 } else if (zonehalt->ld_zoneid == GLOBAL_ZONEID) { 1230 err = EINVAL; 1231 } else { 1232 dlmgmt_table_lock(B_TRUE); 1233 dlmgmt_db_fini(zonehalt->ld_zoneid); 1234 dlmgmt_table_unlock(); 1235 } 1236 } 1237 retvalp->lr_err = err; 1238 } 1239 1240 static dlmgmt_door_info_t i_dlmgmt_door_info_tbl[] = { 1241 { DLMGMT_CMD_DLS_CREATE, sizeof (dlmgmt_upcall_arg_create_t), 1242 sizeof (dlmgmt_create_retval_t), dlmgmt_upcall_create }, 1243 { DLMGMT_CMD_DLS_GETATTR, sizeof (dlmgmt_upcall_arg_getattr_t), 1244 sizeof (dlmgmt_getattr_retval_t), dlmgmt_upcall_getattr }, 1245 { DLMGMT_CMD_DLS_DESTROY, sizeof (dlmgmt_upcall_arg_destroy_t), 1246 sizeof (dlmgmt_destroy_retval_t), dlmgmt_upcall_destroy }, 1247 { DLMGMT_CMD_GETNAME, sizeof (dlmgmt_door_getname_t), 1248 sizeof (dlmgmt_getname_retval_t), dlmgmt_getname }, 1249 { DLMGMT_CMD_GETLINKID, sizeof (dlmgmt_door_getlinkid_t), 1250 sizeof (dlmgmt_getlinkid_retval_t), dlmgmt_getlinkid }, 1251 { DLMGMT_CMD_GETNEXT, sizeof (dlmgmt_door_getnext_t), 1252 sizeof (dlmgmt_getnext_retval_t), dlmgmt_getnext }, 1253 { DLMGMT_CMD_DLS_UPDATE, sizeof (dlmgmt_upcall_arg_update_t), 1254 sizeof (dlmgmt_update_retval_t), dlmgmt_upcall_update }, 1255 { DLMGMT_CMD_CREATE_LINKID, sizeof (dlmgmt_door_createid_t), 1256 sizeof (dlmgmt_createid_retval_t), dlmgmt_createid }, 1257 { DLMGMT_CMD_DESTROY_LINKID, sizeof (dlmgmt_door_destroyid_t), 1258 sizeof (dlmgmt_destroyid_retval_t), dlmgmt_destroyid }, 1259 { DLMGMT_CMD_REMAP_LINKID, sizeof (dlmgmt_door_remapid_t), 1260 sizeof (dlmgmt_remapid_retval_t), dlmgmt_remapid }, 1261 { DLMGMT_CMD_CREATECONF, sizeof (dlmgmt_door_createconf_t), 1262 sizeof (dlmgmt_createconf_retval_t), dlmgmt_createconf }, 1263 { DLMGMT_CMD_READCONF, sizeof (dlmgmt_door_readconf_t), 1264 sizeof (dlmgmt_readconf_retval_t), dlmgmt_readconf }, 1265 { DLMGMT_CMD_WRITECONF, sizeof (dlmgmt_door_writeconf_t), 1266 sizeof (dlmgmt_writeconf_retval_t), dlmgmt_writeconf }, 1267 { DLMGMT_CMD_UP_LINKID, sizeof (dlmgmt_door_upid_t), 1268 sizeof (dlmgmt_upid_retval_t), dlmgmt_upid }, 1269 { DLMGMT_CMD_SETATTR, sizeof (dlmgmt_door_setattr_t), 1270 sizeof (dlmgmt_setattr_retval_t), dlmgmt_setattr }, 1271 { DLMGMT_CMD_UNSETATTR, sizeof (dlmgmt_door_unsetattr_t), 1272 sizeof (dlmgmt_unsetattr_retval_t), dlmgmt_unsetconfattr }, 1273 { DLMGMT_CMD_REMOVECONF, sizeof (dlmgmt_door_removeconf_t), 1274 sizeof (dlmgmt_removeconf_retval_t), dlmgmt_removeconf }, 1275 { DLMGMT_CMD_DESTROYCONF, sizeof (dlmgmt_door_destroyconf_t), 1276 sizeof (dlmgmt_destroyconf_retval_t), dlmgmt_destroyconf }, 1277 { DLMGMT_CMD_GETATTR, sizeof (dlmgmt_door_getattr_t), 1278 sizeof (dlmgmt_getattr_retval_t), dlmgmt_getattr }, 1279 { DLMGMT_CMD_LINKPROP_INIT, sizeof (dlmgmt_door_linkprop_init_t), 1280 sizeof (dlmgmt_linkprop_init_retval_t), 1281 dlmgmt_upcall_linkprop_init }, 1282 { DLMGMT_CMD_LINKPROP_GETNEXT, sizeof (dlmgmt_door_linkprop_getnext_t), 1283 sizeof (dlmgmt_linkprop_getnext_retval_t), 1284 dlmgmt_linkprop_getnext }, 1285 { DLMGMT_CMD_SETZONEID, sizeof (dlmgmt_door_setzoneid_t), 1286 sizeof (dlmgmt_setzoneid_retval_t), dlmgmt_setzoneid }, 1287 { DLMGMT_CMD_ZONEBOOT, sizeof (dlmgmt_door_zoneboot_t), 1288 sizeof (dlmgmt_zoneboot_retval_t), dlmgmt_zoneboot }, 1289 { DLMGMT_CMD_ZONEHALT, sizeof (dlmgmt_door_zonehalt_t), 1290 sizeof (dlmgmt_zonehalt_retval_t), dlmgmt_zonehalt }, 1291 { 0, 0, 0, NULL } 1292 }; 1293 1294 static dlmgmt_door_info_t * 1295 dlmgmt_getcmdinfo(int cmd) 1296 { 1297 dlmgmt_door_info_t *infop = i_dlmgmt_door_info_tbl; 1298 1299 while (infop->di_handler != NULL) { 1300 if (infop->di_cmd == cmd) 1301 break; 1302 infop++; 1303 } 1304 return (infop); 1305 } 1306 1307 /* ARGSUSED */ 1308 void 1309 dlmgmt_handler(void *cookie, char *argp, size_t argsz, door_desc_t *dp, 1310 uint_t n_desc) 1311 { 1312 dlmgmt_door_arg_t *door_arg = (dlmgmt_door_arg_t *)(void *)argp; 1313 dlmgmt_door_info_t *infop = NULL; 1314 dlmgmt_retval_t retval; 1315 ucred_t *cred = NULL; 1316 zoneid_t zoneid; 1317 void *retvalp; 1318 int err = 0; 1319 1320 infop = dlmgmt_getcmdinfo(door_arg->ld_cmd); 1321 if (infop == NULL || argsz != infop->di_reqsz) { 1322 err = EINVAL; 1323 goto done; 1324 } 1325 1326 if (door_ucred(&cred) != 0 || (zoneid = ucred_getzoneid(cred)) == -1) { 1327 err = errno; 1328 goto done; 1329 } 1330 1331 /* 1332 * We cannot use malloc() here because door_return never returns, and 1333 * memory allocated by malloc() would get leaked. Use alloca() instead. 1334 */ 1335 retvalp = alloca(infop->di_acksz); 1336 infop->di_handler(argp, retvalp, zoneid, cred); 1337 1338 done: 1339 if (cred != NULL) 1340 ucred_free(cred); 1341 if (err == 0) { 1342 (void) door_return(retvalp, infop->di_acksz, NULL, 0); 1343 } else { 1344 retval.lr_err = err; 1345 (void) door_return((char *)&retval, sizeof (retval), NULL, 0); 1346 } 1347 } 1348