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 dladm_status_t s; 1058 char buf[DLADM_STRSIZE]; 1059 1060 s = dladm_init_linkprop(dld_handle, lip->ld_linkid, B_TRUE); 1061 if (s != DLADM_STATUS_OK) { 1062 dlmgmt_log(LOG_WARNING, 1063 "linkprop initialization failed on link %d: %s", 1064 lip->ld_linkid, dladm_status2str(s, buf)); 1065 err = EINVAL; 1066 } 1067 } 1068 retvalp->lr_err = err; 1069 } 1070 1071 /* 1072 * Get the link property that follows ld_last_attr. 1073 * If ld_last_attr is empty, return the first property. 1074 */ 1075 /* ARGSUSED */ 1076 static void 1077 dlmgmt_linkprop_getnext(void *argp, void *retp, zoneid_t zoneid, ucred_t *cred) 1078 { 1079 dlmgmt_door_linkprop_getnext_t *getnext = argp; 1080 dlmgmt_linkprop_getnext_retval_t *retvalp = retp; 1081 dlmgmt_dlconf_t dlconf, *dlconfp; 1082 char *attr; 1083 void *attrval; 1084 size_t attrsz; 1085 dladm_datatype_t attrtype; 1086 int err = 0; 1087 1088 /* 1089 * Hold the read lock to access the dlconf table. 1090 */ 1091 dlmgmt_dlconf_table_lock(B_FALSE); 1092 1093 dlconf.ld_id = (int)getnext->ld_conf; 1094 dlconfp = avl_find(&dlmgmt_dlconf_avl, &dlconf, NULL); 1095 if (dlconfp == NULL) { 1096 err = ENOENT; 1097 goto done; 1098 } 1099 1100 err = linkprop_getnext(&dlconfp->ld_head, getnext->ld_last_attr, 1101 &attr, &attrval, &attrsz, &attrtype); 1102 if (err != 0) 1103 goto done; 1104 1105 if (attrsz > MAXLINKATTRVALLEN) { 1106 err = EINVAL; 1107 goto done; 1108 } 1109 1110 (void) strlcpy(retvalp->lr_attr, attr, MAXLINKATTRLEN); 1111 retvalp->lr_type = attrtype; 1112 retvalp->lr_attrsz = attrsz; 1113 bcopy(attrval, retvalp->lr_attrval, attrsz); 1114 1115 done: 1116 dlmgmt_dlconf_table_unlock(); 1117 retvalp->lr_err = err; 1118 } 1119 1120 static void 1121 dlmgmt_setzoneid(void *argp, void *retp, zoneid_t zoneid, ucred_t *cred) 1122 { 1123 dlmgmt_door_setzoneid_t *setzoneid = argp; 1124 dlmgmt_setzoneid_retval_t *retvalp = retp; 1125 dlmgmt_link_t *linkp; 1126 datalink_id_t linkid = setzoneid->ld_linkid; 1127 zoneid_t oldzoneid, newzoneid; 1128 int err = 0; 1129 1130 dlmgmt_table_lock(B_TRUE); 1131 1132 /* We currently only allow changing zoneid's from the global zone. */ 1133 if (zoneid != GLOBAL_ZONEID) { 1134 err = EACCES; 1135 goto done; 1136 } 1137 1138 if ((linkp = link_by_id(linkid, zoneid)) == NULL) { 1139 err = ENOENT; 1140 goto done; 1141 } 1142 1143 if ((err = dlmgmt_checkprivs(linkp->ll_class, cred)) != 0) 1144 goto done; 1145 1146 /* We can only assign an active link to a zone. */ 1147 if (!(linkp->ll_flags & DLMGMT_ACTIVE)) { 1148 err = EINVAL; 1149 goto done; 1150 } 1151 1152 oldzoneid = linkp->ll_zoneid; 1153 newzoneid = setzoneid->ld_zoneid; 1154 1155 if (oldzoneid == newzoneid) 1156 goto done; 1157 1158 /* 1159 * Before we remove the link from its current zone, make sure that 1160 * there isn't a link with the same name in the destination zone. 1161 */ 1162 if (zoneid != GLOBAL_ZONEID && 1163 link_by_name(linkp->ll_link, newzoneid) != NULL) { 1164 err = EEXIST; 1165 goto done; 1166 } 1167 1168 if (oldzoneid != GLOBAL_ZONEID) { 1169 if (zone_remove_datalink(oldzoneid, linkid) != 0) { 1170 err = errno; 1171 dlmgmt_log(LOG_WARNING, "unable to remove link %d from " 1172 "zone %d: %s", linkid, oldzoneid, strerror(err)); 1173 goto done; 1174 } 1175 avl_remove(&dlmgmt_loan_avl, linkp); 1176 linkp->ll_onloan = B_FALSE; 1177 } 1178 if (newzoneid != GLOBAL_ZONEID) { 1179 if (zone_add_datalink(newzoneid, linkid) != 0) { 1180 err = errno; 1181 dlmgmt_log(LOG_WARNING, "unable to add link %d to zone " 1182 "%d: %s", linkid, newzoneid, strerror(err)); 1183 (void) zone_add_datalink(oldzoneid, linkid); 1184 goto done; 1185 } 1186 avl_add(&dlmgmt_loan_avl, linkp); 1187 linkp->ll_onloan = B_TRUE; 1188 } 1189 1190 avl_remove(&dlmgmt_name_avl, linkp); 1191 linkp->ll_zoneid = newzoneid; 1192 avl_add(&dlmgmt_name_avl, linkp); 1193 1194 done: 1195 dlmgmt_table_unlock(); 1196 retvalp->lr_err = err; 1197 } 1198 1199 static void 1200 dlmgmt_zoneboot(void *argp, void *retp, zoneid_t zoneid, ucred_t *cred) 1201 { 1202 int err; 1203 dlmgmt_door_zoneboot_t *zoneboot = argp; 1204 dlmgmt_zoneboot_retval_t *retvalp = retp; 1205 1206 dlmgmt_table_lock(B_TRUE); 1207 1208 if ((err = dlmgmt_checkprivs(0, cred)) != 0) 1209 goto done; 1210 1211 if (zoneid != GLOBAL_ZONEID) { 1212 err = EACCES; 1213 goto done; 1214 } 1215 if (zoneboot->ld_zoneid == GLOBAL_ZONEID) { 1216 err = EINVAL; 1217 goto done; 1218 } 1219 1220 if ((err = dlmgmt_elevate_privileges()) == 0) { 1221 err = dlmgmt_zone_init(zoneboot->ld_zoneid); 1222 (void) dlmgmt_drop_privileges(); 1223 } 1224 done: 1225 dlmgmt_table_unlock(); 1226 retvalp->lr_err = err; 1227 } 1228 1229 static void 1230 dlmgmt_zonehalt(void *argp, void *retp, zoneid_t zoneid, ucred_t *cred) 1231 { 1232 int err = 0; 1233 dlmgmt_door_zonehalt_t *zonehalt = argp; 1234 dlmgmt_zonehalt_retval_t *retvalp = retp; 1235 1236 if ((err = dlmgmt_checkprivs(0, cred)) == 0) { 1237 if (zoneid != GLOBAL_ZONEID) { 1238 err = EACCES; 1239 } else if (zonehalt->ld_zoneid == GLOBAL_ZONEID) { 1240 err = EINVAL; 1241 } else { 1242 dlmgmt_table_lock(B_TRUE); 1243 dlmgmt_db_fini(zonehalt->ld_zoneid); 1244 dlmgmt_table_unlock(); 1245 } 1246 } 1247 retvalp->lr_err = err; 1248 } 1249 1250 static dlmgmt_door_info_t i_dlmgmt_door_info_tbl[] = { 1251 { DLMGMT_CMD_DLS_CREATE, sizeof (dlmgmt_upcall_arg_create_t), 1252 sizeof (dlmgmt_create_retval_t), dlmgmt_upcall_create }, 1253 { DLMGMT_CMD_DLS_GETATTR, sizeof (dlmgmt_upcall_arg_getattr_t), 1254 sizeof (dlmgmt_getattr_retval_t), dlmgmt_upcall_getattr }, 1255 { DLMGMT_CMD_DLS_DESTROY, sizeof (dlmgmt_upcall_arg_destroy_t), 1256 sizeof (dlmgmt_destroy_retval_t), dlmgmt_upcall_destroy }, 1257 { DLMGMT_CMD_GETNAME, sizeof (dlmgmt_door_getname_t), 1258 sizeof (dlmgmt_getname_retval_t), dlmgmt_getname }, 1259 { DLMGMT_CMD_GETLINKID, sizeof (dlmgmt_door_getlinkid_t), 1260 sizeof (dlmgmt_getlinkid_retval_t), dlmgmt_getlinkid }, 1261 { DLMGMT_CMD_GETNEXT, sizeof (dlmgmt_door_getnext_t), 1262 sizeof (dlmgmt_getnext_retval_t), dlmgmt_getnext }, 1263 { DLMGMT_CMD_DLS_UPDATE, sizeof (dlmgmt_upcall_arg_update_t), 1264 sizeof (dlmgmt_update_retval_t), dlmgmt_upcall_update }, 1265 { DLMGMT_CMD_CREATE_LINKID, sizeof (dlmgmt_door_createid_t), 1266 sizeof (dlmgmt_createid_retval_t), dlmgmt_createid }, 1267 { DLMGMT_CMD_DESTROY_LINKID, sizeof (dlmgmt_door_destroyid_t), 1268 sizeof (dlmgmt_destroyid_retval_t), dlmgmt_destroyid }, 1269 { DLMGMT_CMD_REMAP_LINKID, sizeof (dlmgmt_door_remapid_t), 1270 sizeof (dlmgmt_remapid_retval_t), dlmgmt_remapid }, 1271 { DLMGMT_CMD_CREATECONF, sizeof (dlmgmt_door_createconf_t), 1272 sizeof (dlmgmt_createconf_retval_t), dlmgmt_createconf }, 1273 { DLMGMT_CMD_READCONF, sizeof (dlmgmt_door_readconf_t), 1274 sizeof (dlmgmt_readconf_retval_t), dlmgmt_readconf }, 1275 { DLMGMT_CMD_WRITECONF, sizeof (dlmgmt_door_writeconf_t), 1276 sizeof (dlmgmt_writeconf_retval_t), dlmgmt_writeconf }, 1277 { DLMGMT_CMD_UP_LINKID, sizeof (dlmgmt_door_upid_t), 1278 sizeof (dlmgmt_upid_retval_t), dlmgmt_upid }, 1279 { DLMGMT_CMD_SETATTR, sizeof (dlmgmt_door_setattr_t), 1280 sizeof (dlmgmt_setattr_retval_t), dlmgmt_setattr }, 1281 { DLMGMT_CMD_UNSETATTR, sizeof (dlmgmt_door_unsetattr_t), 1282 sizeof (dlmgmt_unsetattr_retval_t), dlmgmt_unsetconfattr }, 1283 { DLMGMT_CMD_REMOVECONF, sizeof (dlmgmt_door_removeconf_t), 1284 sizeof (dlmgmt_removeconf_retval_t), dlmgmt_removeconf }, 1285 { DLMGMT_CMD_DESTROYCONF, sizeof (dlmgmt_door_destroyconf_t), 1286 sizeof (dlmgmt_destroyconf_retval_t), dlmgmt_destroyconf }, 1287 { DLMGMT_CMD_GETATTR, sizeof (dlmgmt_door_getattr_t), 1288 sizeof (dlmgmt_getattr_retval_t), dlmgmt_getattr }, 1289 { DLMGMT_CMD_LINKPROP_INIT, sizeof (dlmgmt_door_linkprop_init_t), 1290 sizeof (dlmgmt_linkprop_init_retval_t), 1291 dlmgmt_upcall_linkprop_init }, 1292 { DLMGMT_CMD_LINKPROP_GETNEXT, sizeof (dlmgmt_door_linkprop_getnext_t), 1293 sizeof (dlmgmt_linkprop_getnext_retval_t), 1294 dlmgmt_linkprop_getnext }, 1295 { DLMGMT_CMD_SETZONEID, sizeof (dlmgmt_door_setzoneid_t), 1296 sizeof (dlmgmt_setzoneid_retval_t), dlmgmt_setzoneid }, 1297 { DLMGMT_CMD_ZONEBOOT, sizeof (dlmgmt_door_zoneboot_t), 1298 sizeof (dlmgmt_zoneboot_retval_t), dlmgmt_zoneboot }, 1299 { DLMGMT_CMD_ZONEHALT, sizeof (dlmgmt_door_zonehalt_t), 1300 sizeof (dlmgmt_zonehalt_retval_t), dlmgmt_zonehalt }, 1301 { 0, 0, 0, NULL } 1302 }; 1303 1304 static dlmgmt_door_info_t * 1305 dlmgmt_getcmdinfo(int cmd) 1306 { 1307 dlmgmt_door_info_t *infop = i_dlmgmt_door_info_tbl; 1308 1309 while (infop->di_handler != NULL) { 1310 if (infop->di_cmd == cmd) 1311 break; 1312 infop++; 1313 } 1314 return (infop); 1315 } 1316 1317 /* ARGSUSED */ 1318 void 1319 dlmgmt_handler(void *cookie, char *argp, size_t argsz, door_desc_t *dp, 1320 uint_t n_desc) 1321 { 1322 dlmgmt_door_arg_t *door_arg = (dlmgmt_door_arg_t *)(void *)argp; 1323 dlmgmt_door_info_t *infop = NULL; 1324 dlmgmt_retval_t retval; 1325 ucred_t *cred = NULL; 1326 zoneid_t zoneid; 1327 void *retvalp; 1328 int err = 0; 1329 1330 infop = dlmgmt_getcmdinfo(door_arg->ld_cmd); 1331 if (infop == NULL || argsz != infop->di_reqsz) { 1332 err = EINVAL; 1333 goto done; 1334 } 1335 1336 if (door_ucred(&cred) != 0 || (zoneid = ucred_getzoneid(cred)) == -1) { 1337 err = errno; 1338 goto done; 1339 } 1340 1341 /* 1342 * We cannot use malloc() here because door_return never returns, and 1343 * memory allocated by malloc() would get leaked. Use alloca() instead. 1344 */ 1345 retvalp = alloca(infop->di_acksz); 1346 infop->di_handler(argp, retvalp, zoneid, cred); 1347 1348 done: 1349 if (cred != NULL) 1350 ucred_free(cred); 1351 if (err == 0) { 1352 (void) door_return(retvalp, infop->di_acksz, NULL, 0); 1353 } else { 1354 retval.lr_err = err; 1355 (void) door_return((char *)&retval, sizeof (retval), NULL, 0); 1356 } 1357 } 1358