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