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