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 2008 Sun Microsystems, Inc. All rights reserved. 24 * Use is subject to license terms. 25 */ 26 27 /* 28 * Main door handler functions used by dlmgmtd to process the different door 29 * call requests. Door call requests can come from the user-land applications, 30 * or from the kernel. 31 */ 32 33 #include <assert.h> 34 #include <alloca.h> 35 #include <errno.h> 36 #include <priv_utils.h> 37 #include <stdlib.h> 38 #include <strings.h> 39 #include <syslog.h> 40 #include <sys/sysevent/eventdefs.h> 41 #include <libsysevent.h> 42 #include <libdlmgmt.h> 43 #include <librcm.h> 44 #include "dlmgmt_impl.h" 45 46 typedef void dlmgmt_door_handler_t(void *, void *); 47 48 typedef struct dlmgmt_door_info_s { 49 uint_t di_cmd; 50 boolean_t di_set; 51 size_t di_reqsz; 52 size_t di_acksz; 53 dlmgmt_door_handler_t *di_handler; 54 } dlmgmt_door_info_t; 55 56 57 static dlmgmt_link_t * 58 dlmgmt_getlink_by_dev(char *devname) 59 { 60 dlmgmt_link_t *linkp = avl_first(&dlmgmt_id_avl); 61 62 for (; linkp != NULL; linkp = AVL_NEXT(&dlmgmt_id_avl, linkp)) { 63 if ((linkp->ll_class == DATALINK_CLASS_PHYS) && 64 linkattr_equal(&(linkp->ll_head), FDEVNAME, devname, 65 strlen(devname) + 1)) { 66 return (linkp); 67 } 68 } 69 return (NULL); 70 } 71 72 /* 73 * Post the EC_DATALINK sysevent for the given linkid. This sysevent will 74 * be consumed by the datalink sysevent module. 75 */ 76 static void 77 dlmgmt_post_sysevent(const char *subclass, datalink_id_t linkid) 78 { 79 nvlist_t *nvl = NULL; 80 sysevent_id_t eid; 81 int err; 82 83 if (((err = nvlist_alloc(&nvl, NV_UNIQUE_NAME_TYPE, 0)) != 0) || 84 ((err = nvlist_add_uint64(nvl, RCM_NV_LINKID, linkid)) != 0)) { 85 goto done; 86 } 87 88 if (sysevent_post_event(EC_DATALINK, (char *)subclass, SUNW_VENDOR, 89 (char *)progname, nvl, &eid) == -1) { 90 err = errno; 91 } 92 93 done: 94 if (err != 0) { 95 dlmgmt_log(LOG_WARNING, "dlmgmt_post_sysevent(%d) failed: %s", 96 linkid, strerror(err)); 97 } 98 nvlist_free(nvl); 99 } 100 101 static void 102 dlmgmt_upcall_create(void *argp, void *retp) 103 { 104 dlmgmt_upcall_arg_create_t *create = argp; 105 dlmgmt_create_retval_t *retvalp = retp; 106 datalink_class_t class; 107 uint32_t media; 108 dlmgmt_link_t *linkp; 109 char link[MAXLINKNAMELEN]; 110 uint32_t flags; 111 int err = 0; 112 boolean_t created = B_FALSE; 113 114 /* 115 * Determine whether this link is persistent. Note that this request 116 * is coming from kernel so this link must be active. 117 */ 118 flags = DLMGMT_ACTIVE | (create->ld_persist ? DLMGMT_PERSIST : 0); 119 120 class = create->ld_class; 121 media = create->ld_media; 122 123 /* 124 * Hold the writer lock to update the link table. 125 */ 126 dlmgmt_table_lock(B_TRUE); 127 128 /* 129 * Check to see whether this is the reattachment of an existing 130 * physical link. If so, return its linkid. 131 */ 132 if ((class == DATALINK_CLASS_PHYS) && 133 (linkp = dlmgmt_getlink_by_dev(create->ld_devname)) != NULL) { 134 135 if (linkattr_equal(&(linkp->ll_head), FPHYMAJ, 136 &create->ld_phymaj, sizeof (uint64_t)) && 137 linkattr_equal(&(linkp->ll_head), FPHYINST, 138 &create->ld_phyinst, sizeof (uint64_t)) && 139 (linkp->ll_flags & flags) == flags) { 140 /* 141 * If nothing has been changed, directly return. 142 */ 143 goto noupdate; 144 } 145 146 err = linkattr_set(&(linkp->ll_head), FPHYMAJ, 147 &create->ld_phymaj, sizeof (uint64_t), DLADM_TYPE_UINT64); 148 if (err != 0) 149 goto done; 150 151 err = linkattr_set(&(linkp->ll_head), FPHYINST, 152 &create->ld_phyinst, sizeof (uint64_t), DLADM_TYPE_UINT64); 153 if (err != 0) 154 goto done; 155 156 linkp->ll_flags |= flags; 157 linkp->ll_gen++; 158 goto done; 159 } 160 161 if ((err = dlmgmt_create_common(create->ld_devname, class, media, 162 flags, &linkp)) == EEXIST) { 163 /* 164 * The link name already exists. Return error if this is a 165 * non-physical link (in that case, the link name must be 166 * the same as the given name). 167 */ 168 if (class != DATALINK_CLASS_PHYS) 169 goto done; 170 171 /* 172 * The physical link's name already exists, request 173 * a suggested link name: net<nextppa> 174 */ 175 err = dlmgmt_generate_name("net", link, MAXLINKNAMELEN); 176 if (err != 0) 177 goto done; 178 179 err = dlmgmt_create_common(link, class, media, flags, &linkp); 180 } 181 182 if (err != 0) 183 goto done; 184 185 created = B_TRUE; 186 187 /* 188 * This is a new link. Only need to persist link attributes for 189 * physical links. 190 */ 191 if (class == DATALINK_CLASS_PHYS && 192 (((err = linkattr_set(&linkp->ll_head, FDEVNAME, create->ld_devname, 193 strlen(create->ld_devname) + 1, DLADM_TYPE_STR)) != 0) || 194 ((err = linkattr_set(&linkp->ll_head, FPHYMAJ, &create->ld_phymaj, 195 sizeof (uint64_t), DLADM_TYPE_UINT64)) != 0) || 196 ((err = linkattr_set(&linkp->ll_head, FPHYINST, &create->ld_phyinst, 197 sizeof (uint64_t), DLADM_TYPE_UINT64)) != 0))) { 198 (void) dlmgmt_destroy_common(linkp, flags); 199 } 200 201 done: 202 if ((err == 0) && ((err = dlmgmt_write_db_entry(linkp->ll_linkid, 203 linkp->ll_flags)) != 0) && created) { 204 (void) dlmgmt_destroy_common(linkp, flags); 205 } 206 207 noupdate: 208 if (err == 0) 209 retvalp->lr_linkid = linkp->ll_linkid; 210 211 dlmgmt_table_unlock(); 212 213 if ((err == 0) && (class == DATALINK_CLASS_PHYS)) { 214 /* 215 * Post the ESC_DATALINK_PHYS_ADD sysevent. This sysevent 216 * is consumed by the datalink sysevent module which in 217 * turn generates the RCM_RESOURCE_LINK_NEW RCM event. 218 */ 219 dlmgmt_post_sysevent(ESC_DATALINK_PHYS_ADD, retvalp->lr_linkid); 220 } 221 222 retvalp->lr_err = err; 223 } 224 225 static void 226 dlmgmt_upcall_update(void *argp, void *retp) 227 { 228 dlmgmt_upcall_arg_update_t *update = argp; 229 dlmgmt_update_retval_t *retvalp = retp; 230 uint32_t media = update->ld_media; 231 dlmgmt_link_t *linkp; 232 int err = 0; 233 234 /* 235 * Hold the writer lock to update the link table. 236 */ 237 dlmgmt_table_lock(B_TRUE); 238 239 /* 240 * Check to see whether this is the reattachment of an existing 241 * physical link. If so, return its linkid. 242 */ 243 if ((linkp = dlmgmt_getlink_by_dev(update->ld_devname)) == NULL) { 244 err = ENOENT; 245 goto done; 246 } 247 248 retvalp->lr_linkid = linkp->ll_linkid; 249 retvalp->lr_media = media; 250 if (linkp->ll_media != media && linkp->ll_media != DL_OTHER) { 251 /* 252 * Assume a DL_ETHER link ce0, a DL_WIFI link ath0 253 * 1. # dladm rename-link ce0 net0 254 * 2. DR out ce0. net0 is down. 255 * 3. use rename-link to have the ath0 device inherit 256 * the configuration from net0 257 * # dladm rename-link ath0 net0 258 * 4. DR in ath0. 259 * As ath0 and ce0 do not have the same media type, ath0 260 * cannot inherit the configuration of net0. 261 */ 262 err = EEXIST; 263 264 /* 265 * Return the media type of the existing link to indicate the 266 * reason for the name conflict. 267 */ 268 retvalp->lr_media = linkp->ll_media; 269 goto done; 270 } 271 272 if (update->ld_novanity && 273 (strcmp(update->ld_devname, linkp->ll_link) != 0)) { 274 /* 275 * Return an error if this is a physical link that does not 276 * support vanity naming, but the link name is not the same 277 * as the given device name. 278 */ 279 err = EEXIST; 280 goto done; 281 } 282 283 if (linkp->ll_media != media) { 284 linkp->ll_media = media; 285 linkp->ll_gen++; 286 (void) dlmgmt_write_db_entry(linkp->ll_linkid, linkp->ll_flags); 287 } 288 289 done: 290 dlmgmt_table_unlock(); 291 retvalp->lr_err = err; 292 } 293 294 static void 295 dlmgmt_upcall_destroy(void *argp, void *retp) 296 { 297 dlmgmt_upcall_arg_destroy_t *destroy = argp; 298 dlmgmt_destroy_retval_t *retvalp = retp; 299 datalink_id_t linkid = destroy->ld_linkid; 300 dlmgmt_link_t *linkp = NULL; 301 uint32_t flags, dflags = 0; 302 int err = 0; 303 304 flags = DLMGMT_ACTIVE | (destroy->ld_persist ? DLMGMT_PERSIST : 0); 305 306 /* 307 * Hold the writer lock to update the link table. 308 */ 309 dlmgmt_table_lock(B_TRUE); 310 311 if ((linkp = link_by_id(linkid)) == NULL) { 312 err = ENOENT; 313 goto done; 314 } 315 316 if (((linkp->ll_flags & flags) & DLMGMT_ACTIVE) && 317 ((err = dlmgmt_delete_db_entry(linkid, DLMGMT_ACTIVE)) != 0)) { 318 dflags = DLMGMT_ACTIVE; 319 goto done; 320 } 321 322 if (((linkp->ll_flags & flags) & DLMGMT_PERSIST) && 323 ((err = dlmgmt_delete_db_entry(linkid, DLMGMT_PERSIST)) != 0)) { 324 if (dflags != 0) 325 (void) dlmgmt_write_db_entry(linkp->ll_linkid, dflags); 326 dflags |= DLMGMT_PERSIST; 327 goto done; 328 } 329 330 if ((err = dlmgmt_destroy_common(linkp, flags)) != 0 && dflags != 0) 331 (void) dlmgmt_write_db_entry(linkp->ll_linkid, dflags); 332 333 done: 334 dlmgmt_table_unlock(); 335 retvalp->lr_err = err; 336 } 337 338 static void 339 dlmgmt_getname(void *argp, void *retp) 340 { 341 dlmgmt_door_getname_t *getname = argp; 342 dlmgmt_getname_retval_t *retvalp = retp; 343 dlmgmt_link_t *linkp; 344 int err = 0; 345 346 /* 347 * Hold the reader lock to access the link 348 */ 349 dlmgmt_table_lock(B_FALSE); 350 if ((linkp = link_by_id(getname->ld_linkid)) == NULL) { 351 /* 352 * The link does not exists. 353 */ 354 err = ENOENT; 355 goto done; 356 } 357 358 if (strlcpy(retvalp->lr_link, linkp->ll_link, MAXLINKNAMELEN) >= 359 MAXLINKNAMELEN) { 360 err = ENOSPC; 361 goto done; 362 } 363 retvalp->lr_flags = linkp->ll_flags; 364 retvalp->lr_class = linkp->ll_class; 365 retvalp->lr_media = linkp->ll_media; 366 367 done: 368 dlmgmt_table_unlock(); 369 retvalp->lr_err = err; 370 } 371 372 static void 373 dlmgmt_getlinkid(void *argp, void *retp) 374 { 375 dlmgmt_door_getlinkid_t *getlinkid = argp; 376 dlmgmt_getlinkid_retval_t *retvalp = retp; 377 dlmgmt_link_t *linkp; 378 int err = 0; 379 380 /* 381 * Hold the reader lock to access the link 382 */ 383 dlmgmt_table_lock(B_FALSE); 384 if ((linkp = link_by_name(getlinkid->ld_link)) == NULL) { 385 /* 386 * The link does not exists. 387 */ 388 err = ENOENT; 389 goto done; 390 } 391 392 retvalp->lr_linkid = linkp->ll_linkid; 393 retvalp->lr_flags = linkp->ll_flags; 394 retvalp->lr_class = linkp->ll_class; 395 retvalp->lr_media = linkp->ll_media; 396 397 done: 398 dlmgmt_table_unlock(); 399 retvalp->lr_err = err; 400 } 401 402 static void 403 dlmgmt_getnext(void *argp, void *retp) 404 { 405 dlmgmt_door_getnext_t *getnext = argp; 406 dlmgmt_getnext_retval_t *retvalp = retp; 407 dlmgmt_link_t link, *linkp; 408 datalink_id_t linkid = getnext->ld_linkid; 409 avl_index_t where; 410 int err = 0; 411 412 /* 413 * Hold the reader lock to access the link 414 */ 415 dlmgmt_table_lock(B_FALSE); 416 417 link.ll_linkid = (linkid + 1); 418 linkp = avl_find(&dlmgmt_id_avl, &link, &where); 419 if (linkp == NULL) 420 linkp = avl_nearest(&dlmgmt_id_avl, where, AVL_AFTER); 421 422 for (; linkp != NULL; linkp = AVL_NEXT(&dlmgmt_id_avl, linkp)) { 423 if ((linkp->ll_class & getnext->ld_class) && 424 (linkp->ll_flags & getnext->ld_flags) && 425 DATALINK_MEDIA_ACCEPTED(getnext->ld_dmedia, 426 linkp->ll_media)) 427 break; 428 } 429 430 if (linkp == NULL) { 431 err = ENOENT; 432 } else { 433 retvalp->lr_linkid = linkp->ll_linkid; 434 retvalp->lr_class = linkp->ll_class; 435 retvalp->lr_media = linkp->ll_media; 436 retvalp->lr_flags = linkp->ll_flags; 437 } 438 439 dlmgmt_table_unlock(); 440 retvalp->lr_err = err; 441 } 442 443 static void 444 dlmgmt_upcall_getattr(void *argp, void *retp) 445 { 446 dlmgmt_upcall_arg_getattr_t *getattr = argp; 447 dlmgmt_getattr_retval_t *retvalp = retp; 448 dlmgmt_link_t *linkp; 449 450 /* 451 * Hold the reader lock to access the link 452 */ 453 dlmgmt_table_lock(B_FALSE); 454 if ((linkp = link_by_id(getattr->ld_linkid)) == NULL) { 455 /* 456 * The link does not exist. 457 */ 458 retvalp->lr_err = ENOENT; 459 goto done; 460 } 461 462 dlmgmt_getattr_common(&linkp->ll_head, getattr->ld_attr, retvalp); 463 464 done: 465 dlmgmt_table_unlock(); 466 } 467 468 static void 469 dlmgmt_createid(void *argp, void *retp) 470 { 471 dlmgmt_door_createid_t *createid = argp; 472 dlmgmt_createid_retval_t *retvalp = retp; 473 dlmgmt_link_t *linkp; 474 datalink_id_t linkid = DATALINK_INVALID_LINKID; 475 char link[MAXLINKNAMELEN]; 476 int err; 477 478 /* 479 * Hold the writer lock to update the dlconf table. 480 */ 481 dlmgmt_table_lock(B_TRUE); 482 483 if (createid->ld_prefix) { 484 err = dlmgmt_generate_name(createid->ld_link, link, 485 MAXLINKNAMELEN); 486 if (err != 0) 487 goto done; 488 489 err = dlmgmt_create_common(link, createid->ld_class, 490 createid->ld_media, createid->ld_flags, &linkp); 491 } else { 492 err = dlmgmt_create_common(createid->ld_link, 493 createid->ld_class, createid->ld_media, createid->ld_flags, 494 &linkp); 495 } 496 497 if (err == 0) { 498 /* 499 * Keep the active mapping. 500 */ 501 linkid = linkp->ll_linkid; 502 if (createid->ld_flags & DLMGMT_ACTIVE) 503 (void) dlmgmt_write_db_entry(linkid, DLMGMT_ACTIVE); 504 } 505 506 done: 507 dlmgmt_table_unlock(); 508 retvalp->lr_linkid = linkid; 509 retvalp->lr_err = err; 510 } 511 512 static void 513 dlmgmt_destroyid(void *argp, void *retp) 514 { 515 dlmgmt_door_destroyid_t *destroyid = argp; 516 dlmgmt_destroyid_retval_t *retvalp = retp; 517 datalink_id_t linkid = destroyid->ld_linkid; 518 uint32_t flags = destroyid->ld_flags; 519 dlmgmt_link_t *linkp = NULL; 520 int err = 0; 521 522 /* 523 * Hold the writer lock to update the link table. 524 */ 525 dlmgmt_table_lock(B_TRUE); 526 if ((linkp = link_by_id(linkid)) == NULL) { 527 err = ENOENT; 528 goto done; 529 } 530 531 if ((err = dlmgmt_destroy_common(linkp, flags)) != 0) 532 goto done; 533 534 /* 535 * Delete the active mapping. 536 */ 537 if (flags & DLMGMT_ACTIVE) 538 (void) dlmgmt_delete_db_entry(linkid, DLMGMT_ACTIVE); 539 540 done: 541 dlmgmt_table_unlock(); 542 retvalp->lr_err = err; 543 } 544 545 /* 546 * Remap a linkid to a given link name, i.e., rename an existing link1 547 * (ld_linkid) to a non-existent link2 (ld_link): rename link1's name to 548 * the given link name. 549 */ 550 static void 551 dlmgmt_remapid(void *argp, void *retp) 552 { 553 dlmgmt_door_remapid_t *remapid = argp; 554 dlmgmt_remapid_retval_t *retvalp = retp; 555 datalink_id_t linkid1 = remapid->ld_linkid; 556 dlmgmt_link_t link, *linkp1, *tmp; 557 avl_index_t where; 558 int err = 0; 559 560 if (!dladm_valid_linkname(remapid->ld_link)) { 561 retvalp->lr_err = EINVAL; 562 return; 563 } 564 565 /* 566 * Hold the writer lock to update the link table. 567 */ 568 dlmgmt_table_lock(B_TRUE); 569 if ((linkp1 = link_by_id(linkid1)) == NULL) { 570 err = ENOENT; 571 goto done; 572 } 573 574 if (link_by_name(remapid->ld_link) != NULL) { 575 err = EEXIST; 576 goto done; 577 } 578 579 avl_remove(&dlmgmt_name_avl, linkp1); 580 (void) strlcpy(link.ll_link, remapid->ld_link, MAXLINKNAMELEN); 581 tmp = avl_find(&dlmgmt_name_avl, &link, &where); 582 assert(tmp == NULL); 583 (void) strlcpy(linkp1->ll_link, remapid->ld_link, MAXLINKNAMELEN); 584 avl_insert(&dlmgmt_name_avl, linkp1, where); 585 dlmgmt_advance(linkp1); 586 587 /* 588 * If we renamed a temporary link, update the temporary repository. 589 */ 590 if (linkp1->ll_flags & DLMGMT_ACTIVE) 591 (void) dlmgmt_write_db_entry(linkid1, DLMGMT_ACTIVE); 592 done: 593 dlmgmt_table_unlock(); 594 retvalp->lr_err = err; 595 } 596 597 static void 598 dlmgmt_upid(void *argp, void *retp) 599 { 600 dlmgmt_door_upid_t *upid = argp; 601 dlmgmt_upid_retval_t *retvalp = retp; 602 dlmgmt_link_t *linkp; 603 int err = 0; 604 605 /* 606 * Hold the writer lock to update the link table. 607 */ 608 dlmgmt_table_lock(B_TRUE); 609 if ((linkp = link_by_id(upid->ld_linkid)) == NULL) { 610 err = ENOENT; 611 goto done; 612 } 613 614 if (linkp->ll_flags & DLMGMT_ACTIVE) { 615 err = EINVAL; 616 goto done; 617 } 618 619 linkp->ll_flags |= DLMGMT_ACTIVE; 620 (void) dlmgmt_write_db_entry(linkp->ll_linkid, DLMGMT_ACTIVE); 621 done: 622 dlmgmt_table_unlock(); 623 retvalp->lr_err = err; 624 } 625 626 static void 627 dlmgmt_createconf(void *argp, void *retp) 628 { 629 dlmgmt_door_createconf_t *createconf = argp; 630 dlmgmt_createconf_retval_t *retvalp = retp; 631 dlmgmt_dlconf_t dlconf, *dlconfp, *tmp; 632 avl_index_t where; 633 int err; 634 635 /* 636 * Hold the writer lock to update the dlconf table. 637 */ 638 dlmgmt_dlconf_table_lock(B_TRUE); 639 640 if ((err = dlconf_create(createconf->ld_link, createconf->ld_linkid, 641 createconf->ld_class, createconf->ld_media, &dlconfp)) != 0) { 642 goto done; 643 } 644 645 dlconf.ld_id = dlconfp->ld_id; 646 tmp = avl_find(&dlmgmt_dlconf_avl, &dlconf, &where); 647 assert(tmp == NULL); 648 avl_insert(&dlmgmt_dlconf_avl, dlconfp, where); 649 dlmgmt_advance_dlconfid(dlconfp); 650 651 retvalp->lr_conf = (dladm_conf_t)dlconfp->ld_id; 652 done: 653 dlmgmt_dlconf_table_unlock(); 654 retvalp->lr_err = err; 655 } 656 657 static void 658 dlmgmt_setattr(void *argp, void *retp) 659 { 660 dlmgmt_door_setattr_t *setattr = argp; 661 dlmgmt_setattr_retval_t *retvalp = retp; 662 dlmgmt_dlconf_t dlconf, *dlconfp; 663 int err = 0; 664 665 /* 666 * Hold the writer lock to update the dlconf table. 667 */ 668 dlmgmt_dlconf_table_lock(B_TRUE); 669 670 dlconf.ld_id = (int)setattr->ld_conf; 671 dlconfp = avl_find(&dlmgmt_dlconf_avl, &dlconf, NULL); 672 if (dlconfp == NULL) { 673 err = ENOENT; 674 goto done; 675 } 676 677 err = linkattr_set(&(dlconfp->ld_head), setattr->ld_attr, 678 &setattr->ld_attrval, setattr->ld_attrsz, setattr->ld_type); 679 680 done: 681 dlmgmt_dlconf_table_unlock(); 682 retvalp->lr_err = err; 683 } 684 685 static void 686 dlmgmt_unsetconfattr(void *argp, void *retp) 687 { 688 dlmgmt_door_unsetattr_t *unsetattr = argp; 689 dlmgmt_unsetattr_retval_t *retvalp = retp; 690 dlmgmt_dlconf_t dlconf, *dlconfp; 691 int err = 0; 692 693 /* 694 * Hold the writer lock to update the dlconf table. 695 */ 696 dlmgmt_dlconf_table_lock(B_TRUE); 697 698 dlconf.ld_id = (int)unsetattr->ld_conf; 699 dlconfp = avl_find(&dlmgmt_dlconf_avl, &dlconf, NULL); 700 if (dlconfp == NULL) { 701 err = ENOENT; 702 goto done; 703 } 704 705 err = linkattr_unset(&(dlconfp->ld_head), unsetattr->ld_attr); 706 707 done: 708 dlmgmt_dlconf_table_unlock(); 709 retvalp->lr_err = err; 710 } 711 712 /* 713 * Note that dlmgmt_readconf() returns a conf ID of a conf AVL tree entry, 714 * which is managed by dlmgmtd. The ID is used to find the conf entry when 715 * dlmgmt_write_conf() is called. The conf entry contains an ld_gen value 716 * (which is the generation number - ll_gen) of the dlmgmt_link_t at the time 717 * of dlmgmt_readconf(), and ll_gen changes every time the dlmgmt_link_t 718 * changes its attributes. Therefore, dlmgmt_write_conf() can compare ld_gen 719 * in the conf entry against the latest dlmgmt_link_t ll_gen value to see if 720 * anything has changed between the dlmgmt_read_conf() and dlmgmt_writeconf() 721 * calls. If so, EAGAIN is returned. This mechanism can ensures atomicity 722 * across the pair of dladm_read_conf() and dladm_write_conf() calls. 723 */ 724 static void 725 dlmgmt_writeconf(void *argp, void *retp) 726 { 727 dlmgmt_door_writeconf_t *writeconf = argp; 728 dlmgmt_writeconf_retval_t *retvalp = retp; 729 dlmgmt_dlconf_t dlconf, *dlconfp; 730 dlmgmt_link_t *linkp; 731 dlmgmt_linkattr_t *attrp, *next; 732 int err = 0; 733 734 /* 735 * Hold the read lock to access the dlconf table. 736 */ 737 dlmgmt_dlconf_table_lock(B_TRUE); 738 739 dlconf.ld_id = (int)writeconf->ld_conf; 740 dlconfp = avl_find(&dlmgmt_dlconf_avl, &dlconf, NULL); 741 if (dlconfp == NULL) { 742 err = ENOENT; 743 goto done; 744 } 745 746 /* 747 * Hold the writer lock to update the link table. 748 */ 749 dlmgmt_table_lock(B_TRUE); 750 linkp = link_by_id(dlconfp->ld_linkid); 751 if ((linkp == NULL) || (linkp->ll_class != dlconfp->ld_class) || 752 (linkp->ll_media != dlconfp->ld_media) || 753 (strcmp(linkp->ll_link, dlconfp->ld_link) != 0)) { 754 /* 755 * The link does not exist. 756 */ 757 dlmgmt_table_unlock(); 758 err = ENOENT; 759 goto done; 760 } 761 762 if (linkp->ll_gen != dlconfp->ld_gen) { 763 /* 764 * Something has changed the link configuration; try again. 765 */ 766 dlmgmt_table_unlock(); 767 err = EAGAIN; 768 goto done; 769 } 770 771 /* 772 * Delete the old attribute list. 773 */ 774 for (attrp = linkp->ll_head; attrp != NULL; attrp = next) { 775 next = attrp->lp_next; 776 free(attrp->lp_val); 777 free(attrp); 778 } 779 linkp->ll_head = NULL; 780 781 /* 782 * Set the new attribute. 783 */ 784 for (attrp = dlconfp->ld_head; attrp != NULL; attrp = attrp->lp_next) { 785 if ((err = linkattr_set(&(linkp->ll_head), attrp->lp_name, 786 attrp->lp_val, attrp->lp_sz, attrp->lp_type)) != 0) { 787 dlmgmt_table_unlock(); 788 goto done; 789 } 790 } 791 792 linkp->ll_gen++; 793 err = dlmgmt_write_db_entry(linkp->ll_linkid, DLMGMT_PERSIST); 794 dlmgmt_table_unlock(); 795 done: 796 dlmgmt_dlconf_table_unlock(); 797 retvalp->lr_err = err; 798 } 799 800 static void 801 dlmgmt_removeconf(void *argp, void *retp) 802 { 803 dlmgmt_door_removeconf_t *removeconf = argp; 804 dlmgmt_removeconf_retval_t *retvalp = retp; 805 int err; 806 807 dlmgmt_table_lock(B_TRUE); 808 err = dlmgmt_delete_db_entry(removeconf->ld_linkid, DLMGMT_PERSIST); 809 dlmgmt_table_unlock(); 810 retvalp->lr_err = err; 811 } 812 813 static void 814 dlmgmt_destroyconf(void *argp, void *retp) 815 { 816 dlmgmt_door_destroyconf_t *destroyconf = argp; 817 dlmgmt_destroyconf_retval_t *retvalp = retp; 818 dlmgmt_dlconf_t dlconf, *dlconfp; 819 int err = 0; 820 821 /* 822 * Hold the writer lock to update the dlconf table. 823 */ 824 dlmgmt_dlconf_table_lock(B_TRUE); 825 826 dlconf.ld_id = (int)destroyconf->ld_conf; 827 dlconfp = avl_find(&dlmgmt_dlconf_avl, &dlconf, NULL); 828 if (dlconfp == NULL) { 829 err = ENOENT; 830 goto done; 831 } 832 833 avl_remove(&dlmgmt_dlconf_avl, dlconfp); 834 dlconf_destroy(dlconfp); 835 836 done: 837 dlmgmt_dlconf_table_unlock(); 838 retvalp->lr_err = err; 839 } 840 841 /* 842 * See the comments above dladm_write_conf() to see how ld_gen is used to 843 * ensure atomicity across the {dlmgmt_readconf(), dlmgmt_writeconf()} pair. 844 */ 845 static void 846 dlmgmt_readconf(void *argp, void *retp) 847 { 848 dlmgmt_door_readconf_t *readconf = argp; 849 dlmgmt_readconf_retval_t *retvalp = retp; 850 dlmgmt_link_t *linkp; 851 datalink_id_t linkid = readconf->ld_linkid; 852 dlmgmt_dlconf_t *dlconfp, *tmp, dlconf; 853 dlmgmt_linkattr_t *attrp; 854 avl_index_t where; 855 int err = 0; 856 857 /* 858 * Hold the writer lock to update the dlconf table. 859 */ 860 dlmgmt_dlconf_table_lock(B_TRUE); 861 862 /* 863 * Hold the reader lock to access the link 864 */ 865 dlmgmt_table_lock(B_FALSE); 866 linkp = link_by_id(linkid); 867 if ((linkp == NULL) || !(linkp->ll_flags & DLMGMT_PERSIST)) { 868 /* 869 * The persistent link configuration does not exists. 870 */ 871 err = ENOENT; 872 goto done; 873 } 874 875 if ((err = dlconf_create(linkp->ll_link, linkp->ll_linkid, 876 linkp->ll_class, linkp->ll_media, &dlconfp)) != 0) { 877 goto done; 878 } 879 880 for (attrp = linkp->ll_head; attrp != NULL; attrp = attrp->lp_next) { 881 if ((err = linkattr_set(&(dlconfp->ld_head), attrp->lp_name, 882 attrp->lp_val, attrp->lp_sz, attrp->lp_type)) != 0) { 883 dlconf_destroy(dlconfp); 884 goto done; 885 } 886 } 887 dlconfp->ld_gen = linkp->ll_gen; 888 889 dlconf.ld_id = dlconfp->ld_id; 890 tmp = avl_find(&dlmgmt_dlconf_avl, &dlconf, &where); 891 assert(tmp == NULL); 892 avl_insert(&dlmgmt_dlconf_avl, dlconfp, where); 893 dlmgmt_advance_dlconfid(dlconfp); 894 895 retvalp->lr_conf = (dladm_conf_t)dlconfp->ld_id; 896 done: 897 dlmgmt_table_unlock(); 898 dlmgmt_dlconf_table_unlock(); 899 retvalp->lr_err = err; 900 } 901 902 /* 903 * Note: the caller must free *retvalpp in case of success. 904 */ 905 static void 906 dlmgmt_getattr(void *argp, void *retp) 907 { 908 dlmgmt_door_getattr_t *getattr = argp; 909 dlmgmt_getattr_retval_t *retvalp = retp; 910 dlmgmt_dlconf_t dlconf, *dlconfp; 911 912 /* 913 * Hold the read lock to access the dlconf table. 914 */ 915 dlmgmt_dlconf_table_lock(B_FALSE); 916 917 dlconf.ld_id = (int)getattr->ld_conf; 918 dlconfp = avl_find(&dlmgmt_dlconf_avl, &dlconf, NULL); 919 if (dlconfp == NULL) { 920 retvalp->lr_err = ENOENT; 921 goto done; 922 } 923 924 dlmgmt_getattr_common(&dlconfp->ld_head, getattr->ld_attr, retvalp); 925 926 done: 927 dlmgmt_dlconf_table_unlock(); 928 } 929 930 static void 931 dlmgmt_upcall_linkprop_init(void *argp, void *retp) 932 { 933 dlmgmt_door_linkprop_init_t *lip = argp; 934 dlmgmt_linkprop_init_retval_t *retvalp = retp; 935 dlmgmt_link_t *linkp; 936 boolean_t do_linkprop = B_FALSE; 937 938 /* 939 * Ignore wifi links until wifi property ioctls are converted 940 * to generic property ioctls. This avoids deadlocks due to 941 * wifi property ioctls using their own /dev/net device, 942 * not the DLD control device. 943 */ 944 dlmgmt_table_lock(B_FALSE); 945 if ((linkp = link_by_id(lip->ld_linkid)) == NULL) 946 retvalp->lr_err = ENOENT; 947 else if (linkp->ll_media == DL_WIFI) 948 retvalp->lr_err = 0; 949 else 950 do_linkprop = B_TRUE; 951 dlmgmt_table_unlock(); 952 953 if (do_linkprop) 954 retvalp->lr_err = dladm_init_linkprop(lip->ld_linkid, B_TRUE); 955 } 956 957 static dlmgmt_door_info_t i_dlmgmt_door_info_tbl[] = { 958 { DLMGMT_CMD_DLS_CREATE, B_TRUE, sizeof (dlmgmt_upcall_arg_create_t), 959 sizeof (dlmgmt_create_retval_t), dlmgmt_upcall_create }, 960 { DLMGMT_CMD_DLS_GETATTR, B_FALSE, sizeof (dlmgmt_upcall_arg_getattr_t), 961 sizeof (dlmgmt_getattr_retval_t), dlmgmt_upcall_getattr }, 962 { DLMGMT_CMD_DLS_DESTROY, B_TRUE, sizeof (dlmgmt_upcall_arg_destroy_t), 963 sizeof (dlmgmt_destroy_retval_t), dlmgmt_upcall_destroy }, 964 { DLMGMT_CMD_GETNAME, B_FALSE, sizeof (dlmgmt_door_getname_t), 965 sizeof (dlmgmt_getname_retval_t), dlmgmt_getname }, 966 { DLMGMT_CMD_GETLINKID, B_FALSE, sizeof (dlmgmt_door_getlinkid_t), 967 sizeof (dlmgmt_getlinkid_retval_t), dlmgmt_getlinkid }, 968 { DLMGMT_CMD_GETNEXT, B_FALSE, sizeof (dlmgmt_door_getnext_t), 969 sizeof (dlmgmt_getnext_retval_t), dlmgmt_getnext }, 970 { DLMGMT_CMD_DLS_UPDATE, B_TRUE, sizeof (dlmgmt_upcall_arg_update_t), 971 sizeof (dlmgmt_update_retval_t), dlmgmt_upcall_update }, 972 { DLMGMT_CMD_CREATE_LINKID, B_TRUE, sizeof (dlmgmt_door_createid_t), 973 sizeof (dlmgmt_createid_retval_t), dlmgmt_createid }, 974 { DLMGMT_CMD_DESTROY_LINKID, B_TRUE, sizeof (dlmgmt_door_destroyid_t), 975 sizeof (dlmgmt_destroyid_retval_t), dlmgmt_destroyid }, 976 { DLMGMT_CMD_REMAP_LINKID, B_TRUE, sizeof (dlmgmt_door_remapid_t), 977 sizeof (dlmgmt_remapid_retval_t), dlmgmt_remapid }, 978 { DLMGMT_CMD_CREATECONF, B_TRUE, sizeof (dlmgmt_door_createconf_t), 979 sizeof (dlmgmt_createconf_retval_t), dlmgmt_createconf }, 980 { DLMGMT_CMD_READCONF, B_FALSE, sizeof (dlmgmt_door_readconf_t), 981 sizeof (dlmgmt_readconf_retval_t), dlmgmt_readconf }, 982 { DLMGMT_CMD_WRITECONF, B_TRUE, sizeof (dlmgmt_door_writeconf_t), 983 sizeof (dlmgmt_writeconf_retval_t), dlmgmt_writeconf }, 984 { DLMGMT_CMD_UP_LINKID, B_TRUE, sizeof (dlmgmt_door_upid_t), 985 sizeof (dlmgmt_upid_retval_t), dlmgmt_upid }, 986 { DLMGMT_CMD_SETATTR, B_TRUE, sizeof (dlmgmt_door_setattr_t), 987 sizeof (dlmgmt_setattr_retval_t), dlmgmt_setattr }, 988 { DLMGMT_CMD_UNSETATTR, B_TRUE, sizeof (dlmgmt_door_unsetattr_t), 989 sizeof (dlmgmt_unsetattr_retval_t), dlmgmt_unsetconfattr }, 990 { DLMGMT_CMD_REMOVECONF, B_TRUE, sizeof (dlmgmt_door_removeconf_t), 991 sizeof (dlmgmt_removeconf_retval_t), dlmgmt_removeconf }, 992 { DLMGMT_CMD_DESTROYCONF, B_TRUE, sizeof (dlmgmt_door_destroyconf_t), 993 sizeof (dlmgmt_destroyconf_retval_t), dlmgmt_destroyconf }, 994 { DLMGMT_CMD_GETATTR, B_FALSE, sizeof (dlmgmt_door_getattr_t), 995 sizeof (dlmgmt_getattr_retval_t), dlmgmt_getattr }, 996 { DLMGMT_CMD_LINKPROP_INIT, B_TRUE, 997 sizeof (dlmgmt_door_linkprop_init_t), 998 sizeof (dlmgmt_linkprop_init_retval_t), 999 dlmgmt_upcall_linkprop_init } 1000 }; 1001 1002 #define DLMGMT_INFO_TABLE_SIZE (sizeof (i_dlmgmt_door_info_tbl) / \ 1003 sizeof (i_dlmgmt_door_info_tbl[0])) 1004 1005 /* ARGSUSED */ 1006 void 1007 dlmgmt_handler(void *cookie, char *argp, size_t argsz, door_desc_t *dp, 1008 uint_t n_desc) 1009 { 1010 dlmgmt_door_info_t *infop = NULL; 1011 dlmgmt_retval_t retval; 1012 void *retvalp; 1013 int err = 0; 1014 int i; 1015 1016 for (i = 0; i < DLMGMT_INFO_TABLE_SIZE; i++) { 1017 if (i_dlmgmt_door_info_tbl[i].di_cmd == 1018 ((dlmgmt_door_arg_t *)(void *)argp)->ld_cmd) { 1019 infop = i_dlmgmt_door_info_tbl + i; 1020 break; 1021 } 1022 } 1023 1024 if (infop == NULL || argsz != infop->di_reqsz) { 1025 err = EINVAL; 1026 goto fail; 1027 } 1028 1029 if (infop->di_set) { 1030 ucred_t *cred = NULL; 1031 const priv_set_t *eset; 1032 1033 if (door_ucred(&cred) != 0) { 1034 err = errno; 1035 goto fail; 1036 } 1037 1038 eset = ucred_getprivset(cred, PRIV_EFFECTIVE); 1039 if (eset == NULL || !priv_ismember(eset, PRIV_SYS_DL_CONFIG)) 1040 err = EACCES; 1041 ucred_free(cred); 1042 if (err != 0) 1043 goto fail; 1044 } 1045 1046 /* 1047 * We cannot use malloc() here because door_return never returns, and 1048 * memory allocated by malloc() would get leaked. Use alloca() instead. 1049 */ 1050 retvalp = alloca(infop->di_acksz); 1051 infop->di_handler(argp, retvalp); 1052 (void) door_return(retvalp, infop->di_acksz, NULL, 0); 1053 return; 1054 1055 fail: 1056 retval.lr_err = err; 1057 (void) door_return((char *)&retval, sizeof (retval), NULL, 0); 1058 } 1059