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