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