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