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) && 329 ((err = dlmgmt_delete_db_entry(linkid, DLMGMT_ACTIVE)) != 0)) { 330 dflags = DLMGMT_ACTIVE; 331 goto done; 332 } 333 334 if (((linkp->ll_flags & flags) & DLMGMT_PERSIST) && 335 ((err = dlmgmt_delete_db_entry(linkid, DLMGMT_PERSIST)) != 0)) { 336 if (dflags != 0) 337 (void) dlmgmt_write_db_entry(linkp->ll_linkid, dflags); 338 dflags |= DLMGMT_PERSIST; 339 goto done; 340 } 341 342 if ((err = dlmgmt_destroy_common(linkp, flags)) != 0 && dflags != 0) 343 (void) dlmgmt_write_db_entry(linkp->ll_linkid, dflags); 344 345 done: 346 dlmgmt_table_unlock(); 347 retvalp->lr_err = err; 348 } 349 350 static void 351 dlmgmt_getname(void *argp, void *retp) 352 { 353 dlmgmt_door_getname_t *getname = argp; 354 dlmgmt_getname_retval_t *retvalp = retp; 355 dlmgmt_link_t *linkp; 356 int err = 0; 357 358 /* 359 * Hold the reader lock to access the link 360 */ 361 dlmgmt_table_lock(B_FALSE); 362 if ((linkp = link_by_id(getname->ld_linkid)) == NULL) { 363 /* 364 * The link does not exists. 365 */ 366 err = ENOENT; 367 goto done; 368 } 369 370 if (strlcpy(retvalp->lr_link, linkp->ll_link, MAXLINKNAMELEN) >= 371 MAXLINKNAMELEN) { 372 err = ENOSPC; 373 goto done; 374 } 375 retvalp->lr_flags = linkp->ll_flags; 376 retvalp->lr_class = linkp->ll_class; 377 retvalp->lr_media = linkp->ll_media; 378 379 done: 380 dlmgmt_table_unlock(); 381 retvalp->lr_err = err; 382 } 383 384 static void 385 dlmgmt_getlinkid(void *argp, void *retp) 386 { 387 dlmgmt_door_getlinkid_t *getlinkid = argp; 388 dlmgmt_getlinkid_retval_t *retvalp = retp; 389 dlmgmt_link_t *linkp; 390 int err = 0; 391 392 /* 393 * Hold the reader lock to access the link 394 */ 395 dlmgmt_table_lock(B_FALSE); 396 if ((linkp = link_by_name(getlinkid->ld_link)) == NULL) { 397 /* 398 * The link does not exists. 399 */ 400 err = ENOENT; 401 goto done; 402 } 403 404 retvalp->lr_linkid = linkp->ll_linkid; 405 retvalp->lr_flags = linkp->ll_flags; 406 retvalp->lr_class = linkp->ll_class; 407 retvalp->lr_media = linkp->ll_media; 408 409 done: 410 dlmgmt_table_unlock(); 411 retvalp->lr_err = err; 412 } 413 414 static void 415 dlmgmt_getnext(void *argp, void *retp) 416 { 417 dlmgmt_door_getnext_t *getnext = argp; 418 dlmgmt_getnext_retval_t *retvalp = retp; 419 dlmgmt_link_t link, *linkp; 420 datalink_id_t linkid = getnext->ld_linkid; 421 avl_index_t where; 422 int err = 0; 423 424 /* 425 * Hold the reader lock to access the link 426 */ 427 dlmgmt_table_lock(B_FALSE); 428 429 link.ll_linkid = (linkid + 1); 430 linkp = avl_find(&dlmgmt_id_avl, &link, &where); 431 if (linkp == NULL) 432 linkp = avl_nearest(&dlmgmt_id_avl, where, AVL_AFTER); 433 434 for (; linkp != NULL; linkp = AVL_NEXT(&dlmgmt_id_avl, linkp)) { 435 if ((linkp->ll_class & getnext->ld_class) && 436 (linkp->ll_flags & getnext->ld_flags) && 437 DATALINK_MEDIA_ACCEPTED(getnext->ld_dmedia, 438 linkp->ll_media)) 439 break; 440 } 441 442 if (linkp == NULL) { 443 err = ENOENT; 444 } else { 445 retvalp->lr_linkid = linkp->ll_linkid; 446 retvalp->lr_class = linkp->ll_class; 447 retvalp->lr_media = linkp->ll_media; 448 retvalp->lr_flags = linkp->ll_flags; 449 } 450 451 dlmgmt_table_unlock(); 452 retvalp->lr_err = err; 453 } 454 455 static void 456 dlmgmt_upcall_getattr(void *argp, void *retp) 457 { 458 dlmgmt_upcall_arg_getattr_t *getattr = argp; 459 dlmgmt_getattr_retval_t *retvalp = retp; 460 dlmgmt_link_t *linkp; 461 int err = 0; 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 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 retvalp->lr_err = err; 480 } 481 482 static void 483 dlmgmt_createid(void *argp, void *retp) 484 { 485 dlmgmt_door_createid_t *createid = argp; 486 dlmgmt_createid_retval_t *retvalp = retp; 487 dlmgmt_link_t *linkp; 488 datalink_id_t linkid = DATALINK_INVALID_LINKID; 489 char link[MAXLINKNAMELEN]; 490 int err; 491 492 /* 493 * Hold the writer lock to update the dlconf table. 494 */ 495 dlmgmt_table_lock(B_TRUE); 496 497 if (createid->ld_prefix) { 498 err = dlmgmt_generate_name(createid->ld_link, link, 499 MAXLINKNAMELEN); 500 if (err != 0) 501 goto done; 502 503 err = dlmgmt_create_common(link, createid->ld_class, 504 createid->ld_media, createid->ld_flags, &linkp); 505 } else { 506 err = dlmgmt_create_common(createid->ld_link, 507 createid->ld_class, createid->ld_media, createid->ld_flags, 508 &linkp); 509 } 510 511 if (err == 0) { 512 /* 513 * Keep the active mapping. 514 */ 515 linkid = linkp->ll_linkid; 516 if (createid->ld_flags & DLMGMT_ACTIVE) 517 (void) dlmgmt_write_db_entry(linkid, DLMGMT_ACTIVE); 518 } 519 520 done: 521 dlmgmt_table_unlock(); 522 retvalp->lr_linkid = linkid; 523 retvalp->lr_err = err; 524 } 525 526 static void 527 dlmgmt_destroyid(void *argp, void *retp) 528 { 529 dlmgmt_door_destroyid_t *destroyid = argp; 530 dlmgmt_destroyid_retval_t *retvalp = retp; 531 datalink_id_t linkid = destroyid->ld_linkid; 532 uint32_t flags = destroyid->ld_flags; 533 dlmgmt_link_t *linkp = NULL; 534 int err = 0; 535 536 /* 537 * Hold the writer lock to update the link table. 538 */ 539 dlmgmt_table_lock(B_TRUE); 540 if ((linkp = link_by_id(linkid)) == NULL) { 541 err = ENOENT; 542 goto done; 543 } 544 545 if ((err = dlmgmt_destroy_common(linkp, flags)) != 0) 546 goto done; 547 548 /* 549 * Delete the active mapping. 550 */ 551 if (flags & DLMGMT_ACTIVE) 552 (void) dlmgmt_delete_db_entry(linkid, DLMGMT_ACTIVE); 553 554 done: 555 dlmgmt_table_unlock(); 556 retvalp->lr_err = err; 557 } 558 559 /* 560 * Remap a linkid to a given link name, i.e., rename an existing link1 561 * (ld_linkid) to a non-existent link2 (ld_link): rename link1's name to 562 * the given link name. 563 */ 564 static void 565 dlmgmt_remapid(void *argp, void *retp) 566 { 567 dlmgmt_door_remapid_t *remapid = argp; 568 dlmgmt_remapid_retval_t *retvalp = retp; 569 datalink_id_t linkid1 = remapid->ld_linkid; 570 dlmgmt_link_t link, *linkp1, *tmp; 571 avl_index_t where; 572 int err = 0; 573 574 if (!dladm_valid_linkname(remapid->ld_link)) { 575 retvalp->lr_err = EINVAL; 576 return; 577 } 578 579 /* 580 * Hold the writer lock to update the link table. 581 */ 582 dlmgmt_table_lock(B_TRUE); 583 if ((linkp1 = link_by_id(linkid1)) == NULL) { 584 err = ENOENT; 585 goto done; 586 } 587 588 if (link_by_name(remapid->ld_link) != NULL) { 589 err = EEXIST; 590 goto done; 591 } 592 593 avl_remove(&dlmgmt_name_avl, linkp1); 594 (void) strlcpy(link.ll_link, remapid->ld_link, MAXLINKNAMELEN); 595 tmp = avl_find(&dlmgmt_name_avl, &link, &where); 596 assert(tmp == NULL); 597 (void) strlcpy(linkp1->ll_link, remapid->ld_link, MAXLINKNAMELEN); 598 avl_insert(&dlmgmt_name_avl, linkp1, where); 599 dlmgmt_advance(linkp1); 600 601 /* 602 * If we renamed a temporary link, update the temporary repository. 603 */ 604 if (linkp1->ll_flags & DLMGMT_ACTIVE) 605 (void) dlmgmt_write_db_entry(linkid1, DLMGMT_ACTIVE); 606 done: 607 dlmgmt_table_unlock(); 608 retvalp->lr_err = err; 609 } 610 611 static void 612 dlmgmt_upid(void *argp, void *retp) 613 { 614 dlmgmt_door_upid_t *upid = argp; 615 dlmgmt_upid_retval_t *retvalp = retp; 616 dlmgmt_link_t *linkp; 617 int err = 0; 618 619 /* 620 * Hold the writer lock to update the link table. 621 */ 622 dlmgmt_table_lock(B_TRUE); 623 if ((linkp = link_by_id(upid->ld_linkid)) == NULL) { 624 err = ENOENT; 625 goto done; 626 } 627 628 if (linkp->ll_flags & DLMGMT_ACTIVE) { 629 err = EINVAL; 630 goto done; 631 } 632 633 linkp->ll_flags |= DLMGMT_ACTIVE; 634 (void) dlmgmt_write_db_entry(linkp->ll_linkid, DLMGMT_ACTIVE); 635 done: 636 dlmgmt_table_unlock(); 637 retvalp->lr_err = err; 638 } 639 640 static void 641 dlmgmt_createconf(void *argp, void *retp) 642 { 643 dlmgmt_door_createconf_t *createconf = argp; 644 dlmgmt_createconf_retval_t *retvalp = retp; 645 dlmgmt_dlconf_t dlconf, *dlconfp, *tmp; 646 avl_index_t where; 647 int err; 648 649 /* 650 * Hold the writer lock to update the dlconf table. 651 */ 652 dlmgmt_dlconf_table_lock(B_TRUE); 653 654 if ((err = dlconf_create(createconf->ld_link, createconf->ld_linkid, 655 createconf->ld_class, createconf->ld_media, &dlconfp)) != 0) { 656 goto done; 657 } 658 659 dlconf.ld_id = dlconfp->ld_id; 660 tmp = avl_find(&dlmgmt_dlconf_avl, &dlconf, &where); 661 assert(tmp == NULL); 662 avl_insert(&dlmgmt_dlconf_avl, dlconfp, where); 663 dlmgmt_advance_dlconfid(dlconfp); 664 665 retvalp->lr_conf = (dladm_conf_t)dlconfp->ld_id; 666 done: 667 dlmgmt_dlconf_table_unlock(); 668 retvalp->lr_err = err; 669 } 670 671 static void 672 dlmgmt_setattr(void *argp, void *retp) 673 { 674 dlmgmt_door_setattr_t *setattr = argp; 675 dlmgmt_setattr_retval_t *retvalp = retp; 676 dlmgmt_dlconf_t dlconf, *dlconfp; 677 int err = 0; 678 679 /* 680 * Hold the writer lock to update the dlconf table. 681 */ 682 dlmgmt_dlconf_table_lock(B_TRUE); 683 684 dlconf.ld_id = (int)setattr->ld_conf; 685 dlconfp = avl_find(&dlmgmt_dlconf_avl, &dlconf, NULL); 686 if (dlconfp == NULL) { 687 err = ENOENT; 688 goto done; 689 } 690 691 err = linkattr_set(&(dlconfp->ld_head), setattr->ld_attr, 692 &setattr->ld_attrval, setattr->ld_attrsz, setattr->ld_type); 693 694 done: 695 dlmgmt_dlconf_table_unlock(); 696 retvalp->lr_err = err; 697 } 698 699 static void 700 dlmgmt_unsetconfattr(void *argp, void *retp) 701 { 702 dlmgmt_door_unsetattr_t *unsetattr = argp; 703 dlmgmt_unsetattr_retval_t *retvalp = retp; 704 dlmgmt_dlconf_t dlconf, *dlconfp; 705 int err = 0; 706 707 /* 708 * Hold the writer lock to update the dlconf table. 709 */ 710 dlmgmt_dlconf_table_lock(B_TRUE); 711 712 dlconf.ld_id = (int)unsetattr->ld_conf; 713 dlconfp = avl_find(&dlmgmt_dlconf_avl, &dlconf, NULL); 714 if (dlconfp == NULL) { 715 err = ENOENT; 716 goto done; 717 } 718 719 err = linkattr_unset(&(dlconfp->ld_head), unsetattr->ld_attr); 720 721 done: 722 dlmgmt_dlconf_table_unlock(); 723 retvalp->lr_err = err; 724 } 725 726 /* 727 * Note that dlmgmt_readconf() returns a conf ID of a conf AVL tree entry, 728 * which is managed by dlmgmtd. The ID is used to find the conf entry when 729 * dlmgmt_write_conf() is called. The conf entry contains an ld_gen value 730 * (which is the generation number - ll_gen) of the dlmgmt_link_t at the time 731 * of dlmgmt_readconf(), and ll_gen changes every time the dlmgmt_link_t 732 * changes its attributes. Therefore, dlmgmt_write_conf() can compare ld_gen 733 * in the conf entry against the latest dlmgmt_link_t ll_gen value to see if 734 * anything has changed between the dlmgmt_read_conf() and dlmgmt_writeconf() 735 * calls. If so, EAGAIN is returned. This mechanism can ensures atomicity 736 * across the pair of dladm_read_conf() and dladm_write_conf() calls. 737 */ 738 static void 739 dlmgmt_writeconf(void *argp, void *retp) 740 { 741 dlmgmt_door_writeconf_t *writeconf = argp; 742 dlmgmt_writeconf_retval_t *retvalp = retp; 743 dlmgmt_dlconf_t dlconf, *dlconfp; 744 dlmgmt_link_t *linkp; 745 dlmgmt_linkattr_t *attrp, *next; 746 int err = 0; 747 748 /* 749 * Hold the read lock to access the dlconf table. 750 */ 751 dlmgmt_dlconf_table_lock(B_TRUE); 752 753 dlconf.ld_id = (int)writeconf->ld_conf; 754 dlconfp = avl_find(&dlmgmt_dlconf_avl, &dlconf, NULL); 755 if (dlconfp == NULL) { 756 err = ENOENT; 757 goto done; 758 } 759 760 /* 761 * Hold the writer lock to update the link table. 762 */ 763 dlmgmt_table_lock(B_TRUE); 764 linkp = link_by_id(dlconfp->ld_linkid); 765 if ((linkp == NULL) || (linkp->ll_class != dlconfp->ld_class) || 766 (linkp->ll_media != dlconfp->ld_media) || 767 (strcmp(linkp->ll_link, dlconfp->ld_link) != 0)) { 768 /* 769 * The link does not exist. 770 */ 771 dlmgmt_table_unlock(); 772 err = ENOENT; 773 goto done; 774 } 775 776 if (linkp->ll_gen != dlconfp->ld_gen) { 777 /* 778 * Something has changed the link configuration; try again. 779 */ 780 dlmgmt_table_unlock(); 781 err = EAGAIN; 782 goto done; 783 } 784 785 /* 786 * Delete the old attribute list. 787 */ 788 for (attrp = linkp->ll_head; attrp != NULL; attrp = next) { 789 next = attrp->lp_next; 790 free(attrp->lp_val); 791 free(attrp); 792 } 793 linkp->ll_head = NULL; 794 795 /* 796 * Set the new attribute. 797 */ 798 for (attrp = dlconfp->ld_head; attrp != NULL; attrp = attrp->lp_next) { 799 if ((err = linkattr_set(&(linkp->ll_head), attrp->lp_name, 800 attrp->lp_val, attrp->lp_sz, attrp->lp_type)) != 0) { 801 dlmgmt_table_unlock(); 802 goto done; 803 } 804 } 805 806 linkp->ll_gen++; 807 err = dlmgmt_write_db_entry(linkp->ll_linkid, DLMGMT_PERSIST); 808 dlmgmt_table_unlock(); 809 done: 810 dlmgmt_dlconf_table_unlock(); 811 retvalp->lr_err = err; 812 } 813 814 static void 815 dlmgmt_removeconf(void *argp, void *retp) 816 { 817 dlmgmt_door_removeconf_t *removeconf = argp; 818 dlmgmt_removeconf_retval_t *retvalp = retp; 819 int err; 820 821 dlmgmt_table_lock(B_TRUE); 822 err = dlmgmt_delete_db_entry(removeconf->ld_linkid, DLMGMT_PERSIST); 823 dlmgmt_table_unlock(); 824 retvalp->lr_err = err; 825 } 826 827 static void 828 dlmgmt_destroyconf(void *argp, void *retp) 829 { 830 dlmgmt_door_destroyconf_t *destroyconf = argp; 831 dlmgmt_destroyconf_retval_t *retvalp = retp; 832 dlmgmt_dlconf_t dlconf, *dlconfp; 833 int err = 0; 834 835 /* 836 * Hold the writer lock to update the dlconf table. 837 */ 838 dlmgmt_dlconf_table_lock(B_TRUE); 839 840 dlconf.ld_id = (int)destroyconf->ld_conf; 841 dlconfp = avl_find(&dlmgmt_dlconf_avl, &dlconf, NULL); 842 if (dlconfp == NULL) { 843 err = ENOENT; 844 goto done; 845 } 846 847 avl_remove(&dlmgmt_dlconf_avl, dlconfp); 848 dlconf_destroy(dlconfp); 849 850 done: 851 dlmgmt_dlconf_table_unlock(); 852 retvalp->lr_err = err; 853 } 854 855 /* 856 * See the comments above dladm_write_conf() to see how ld_gen is used to 857 * ensure atomicity across the {dlmgmt_readconf(), dlmgmt_writeconf()} pair. 858 */ 859 static void 860 dlmgmt_readconf(void *argp, void *retp) 861 { 862 dlmgmt_door_readconf_t *readconf = argp; 863 dlmgmt_readconf_retval_t *retvalp = retp; 864 dlmgmt_link_t *linkp; 865 datalink_id_t linkid = readconf->ld_linkid; 866 dlmgmt_dlconf_t *dlconfp, *tmp, dlconf; 867 dlmgmt_linkattr_t *attrp; 868 avl_index_t where; 869 int err = 0; 870 871 /* 872 * Hold the writer lock to update the dlconf table. 873 */ 874 dlmgmt_dlconf_table_lock(B_TRUE); 875 876 /* 877 * Hold the reader lock to access the link 878 */ 879 dlmgmt_table_lock(B_FALSE); 880 linkp = link_by_id(linkid); 881 if ((linkp == NULL) || !(linkp->ll_flags & DLMGMT_PERSIST)) { 882 /* 883 * The persistent link configuration does not exists. 884 */ 885 err = ENOENT; 886 goto done; 887 } 888 889 if ((err = dlconf_create(linkp->ll_link, linkp->ll_linkid, 890 linkp->ll_class, linkp->ll_media, &dlconfp)) != 0) { 891 goto done; 892 } 893 894 for (attrp = linkp->ll_head; attrp != NULL; attrp = attrp->lp_next) { 895 if ((err = linkattr_set(&(dlconfp->ld_head), attrp->lp_name, 896 attrp->lp_val, attrp->lp_sz, attrp->lp_type)) != 0) { 897 dlconf_destroy(dlconfp); 898 goto done; 899 } 900 } 901 dlconfp->ld_gen = linkp->ll_gen; 902 903 dlconf.ld_id = dlconfp->ld_id; 904 tmp = avl_find(&dlmgmt_dlconf_avl, &dlconf, &where); 905 assert(tmp == NULL); 906 avl_insert(&dlmgmt_dlconf_avl, dlconfp, where); 907 dlmgmt_advance_dlconfid(dlconfp); 908 909 retvalp->lr_conf = (dladm_conf_t)dlconfp->ld_id; 910 done: 911 dlmgmt_table_unlock(); 912 dlmgmt_dlconf_table_unlock(); 913 retvalp->lr_err = err; 914 } 915 916 /* 917 * Note: the caller must free *retvalpp in case of success. 918 */ 919 static void 920 dlmgmt_getattr(void *argp, void *retp) 921 { 922 dlmgmt_door_getattr_t *getattr = argp; 923 dlmgmt_getattr_retval_t *retvalp = retp; 924 dlmgmt_dlconf_t dlconf, *dlconfp; 925 926 /* 927 * Hold the read lock to access the dlconf table. 928 */ 929 dlmgmt_dlconf_table_lock(B_FALSE); 930 931 dlconf.ld_id = (int)getattr->ld_conf; 932 dlconfp = avl_find(&dlmgmt_dlconf_avl, &dlconf, NULL); 933 if (dlconfp == NULL) { 934 retvalp->lr_err = ENOENT; 935 goto done; 936 } 937 938 dlmgmt_getattr_common(&dlconfp->ld_head, getattr->ld_attr, retvalp); 939 940 done: 941 dlmgmt_dlconf_table_unlock(); 942 } 943 944 static void 945 dlmgmt_upcall_linkprop_init(void *argp, void *retp) 946 { 947 dlmgmt_door_linkprop_init_t *lip = argp; 948 dlmgmt_linkprop_init_retval_t *retvalp = retp; 949 dlmgmt_link_t *linkp; 950 boolean_t do_linkprop = B_FALSE; 951 952 /* 953 * Ignore wifi links until wifi property ioctls are converted 954 * to generic property ioctls. This avoids deadlocks due to 955 * wifi property ioctls using their own /dev/net device, 956 * not the DLD control device. 957 */ 958 dlmgmt_table_lock(B_FALSE); 959 if ((linkp = link_by_id(lip->ld_linkid)) == NULL) 960 retvalp->lr_err = ENOENT; 961 else if (linkp->ll_media == DL_WIFI) 962 retvalp->lr_err = 0; 963 else 964 do_linkprop = B_TRUE; 965 dlmgmt_table_unlock(); 966 967 if (do_linkprop) 968 retvalp->lr_err = dladm_init_linkprop(dld_handle, 969 lip->ld_linkid, B_TRUE); 970 } 971 972 /* 973 * Get the link property that follows ld_last_attr. 974 * If ld_last_attr is empty, return the first property. 975 */ 976 static void 977 dlmgmt_linkprop_getnext(void *argp, void *retp) 978 { 979 dlmgmt_door_linkprop_getnext_t *getnext = argp; 980 dlmgmt_linkprop_getnext_retval_t *retvalp = retp; 981 dlmgmt_dlconf_t dlconf, *dlconfp; 982 char *attr; 983 void *attrval; 984 size_t attrsz; 985 dladm_datatype_t attrtype; 986 int err = 0; 987 988 /* 989 * Hold the read lock to access the dlconf table. 990 */ 991 dlmgmt_dlconf_table_lock(B_FALSE); 992 993 dlconf.ld_id = (int)getnext->ld_conf; 994 dlconfp = avl_find(&dlmgmt_dlconf_avl, &dlconf, NULL); 995 if (dlconfp == NULL) { 996 err = ENOENT; 997 goto done; 998 } 999 1000 err = linkprop_getnext(&dlconfp->ld_head, getnext->ld_last_attr, 1001 &attr, &attrval, &attrsz, &attrtype); 1002 if (err != 0) 1003 goto done; 1004 1005 if (attrsz > MAXLINKATTRVALLEN) { 1006 err = EINVAL; 1007 goto done; 1008 } 1009 1010 (void) strlcpy(retvalp->lr_attr, attr, MAXLINKATTRLEN); 1011 retvalp->lr_type = attrtype; 1012 retvalp->lr_attrsz = attrsz; 1013 bcopy(attrval, retvalp->lr_attrval, attrsz); 1014 1015 done: 1016 dlmgmt_dlconf_table_unlock(); 1017 retvalp->lr_err = err; 1018 } 1019 1020 static dlmgmt_door_info_t i_dlmgmt_door_info_tbl[] = { 1021 { DLMGMT_CMD_DLS_CREATE, B_TRUE, sizeof (dlmgmt_upcall_arg_create_t), 1022 sizeof (dlmgmt_create_retval_t), dlmgmt_upcall_create }, 1023 { DLMGMT_CMD_DLS_GETATTR, B_FALSE, sizeof (dlmgmt_upcall_arg_getattr_t), 1024 sizeof (dlmgmt_getattr_retval_t), dlmgmt_upcall_getattr }, 1025 { DLMGMT_CMD_DLS_DESTROY, B_TRUE, sizeof (dlmgmt_upcall_arg_destroy_t), 1026 sizeof (dlmgmt_destroy_retval_t), dlmgmt_upcall_destroy }, 1027 { DLMGMT_CMD_GETNAME, B_FALSE, sizeof (dlmgmt_door_getname_t), 1028 sizeof (dlmgmt_getname_retval_t), dlmgmt_getname }, 1029 { DLMGMT_CMD_GETLINKID, B_FALSE, sizeof (dlmgmt_door_getlinkid_t), 1030 sizeof (dlmgmt_getlinkid_retval_t), dlmgmt_getlinkid }, 1031 { DLMGMT_CMD_GETNEXT, B_FALSE, sizeof (dlmgmt_door_getnext_t), 1032 sizeof (dlmgmt_getnext_retval_t), dlmgmt_getnext }, 1033 { DLMGMT_CMD_DLS_UPDATE, B_TRUE, sizeof (dlmgmt_upcall_arg_update_t), 1034 sizeof (dlmgmt_update_retval_t), dlmgmt_upcall_update }, 1035 { DLMGMT_CMD_CREATE_LINKID, B_TRUE, sizeof (dlmgmt_door_createid_t), 1036 sizeof (dlmgmt_createid_retval_t), dlmgmt_createid }, 1037 { DLMGMT_CMD_DESTROY_LINKID, B_TRUE, sizeof (dlmgmt_door_destroyid_t), 1038 sizeof (dlmgmt_destroyid_retval_t), dlmgmt_destroyid }, 1039 { DLMGMT_CMD_REMAP_LINKID, B_TRUE, sizeof (dlmgmt_door_remapid_t), 1040 sizeof (dlmgmt_remapid_retval_t), dlmgmt_remapid }, 1041 { DLMGMT_CMD_CREATECONF, B_TRUE, sizeof (dlmgmt_door_createconf_t), 1042 sizeof (dlmgmt_createconf_retval_t), dlmgmt_createconf }, 1043 { DLMGMT_CMD_READCONF, B_FALSE, sizeof (dlmgmt_door_readconf_t), 1044 sizeof (dlmgmt_readconf_retval_t), dlmgmt_readconf }, 1045 { DLMGMT_CMD_WRITECONF, B_TRUE, sizeof (dlmgmt_door_writeconf_t), 1046 sizeof (dlmgmt_writeconf_retval_t), dlmgmt_writeconf }, 1047 { DLMGMT_CMD_UP_LINKID, B_TRUE, sizeof (dlmgmt_door_upid_t), 1048 sizeof (dlmgmt_upid_retval_t), dlmgmt_upid }, 1049 { DLMGMT_CMD_SETATTR, B_TRUE, sizeof (dlmgmt_door_setattr_t), 1050 sizeof (dlmgmt_setattr_retval_t), dlmgmt_setattr }, 1051 { DLMGMT_CMD_UNSETATTR, B_TRUE, sizeof (dlmgmt_door_unsetattr_t), 1052 sizeof (dlmgmt_unsetattr_retval_t), dlmgmt_unsetconfattr }, 1053 { DLMGMT_CMD_REMOVECONF, B_TRUE, sizeof (dlmgmt_door_removeconf_t), 1054 sizeof (dlmgmt_removeconf_retval_t), dlmgmt_removeconf }, 1055 { DLMGMT_CMD_DESTROYCONF, B_TRUE, sizeof (dlmgmt_door_destroyconf_t), 1056 sizeof (dlmgmt_destroyconf_retval_t), dlmgmt_destroyconf }, 1057 { DLMGMT_CMD_GETATTR, B_FALSE, sizeof (dlmgmt_door_getattr_t), 1058 sizeof (dlmgmt_getattr_retval_t), dlmgmt_getattr }, 1059 { DLMGMT_CMD_LINKPROP_INIT, B_TRUE, 1060 sizeof (dlmgmt_door_linkprop_init_t), 1061 sizeof (dlmgmt_linkprop_init_retval_t), 1062 dlmgmt_upcall_linkprop_init }, 1063 { DLMGMT_CMD_LINKPROP_GETNEXT, B_FALSE, 1064 sizeof (dlmgmt_door_linkprop_getnext_t), 1065 sizeof (dlmgmt_linkprop_getnext_retval_t), 1066 dlmgmt_linkprop_getnext } 1067 }; 1068 1069 #define DLMGMT_INFO_TABLE_SIZE (sizeof (i_dlmgmt_door_info_tbl) / \ 1070 sizeof (i_dlmgmt_door_info_tbl[0])) 1071 1072 /* ARGSUSED */ 1073 void 1074 dlmgmt_handler(void *cookie, char *argp, size_t argsz, door_desc_t *dp, 1075 uint_t n_desc) 1076 { 1077 dlmgmt_door_info_t *infop = NULL; 1078 dlmgmt_retval_t retval; 1079 void *retvalp; 1080 int err = 0; 1081 int i; 1082 1083 for (i = 0; i < DLMGMT_INFO_TABLE_SIZE; i++) { 1084 if (i_dlmgmt_door_info_tbl[i].di_cmd == 1085 ((dlmgmt_door_arg_t *)(void *)argp)->ld_cmd) { 1086 infop = i_dlmgmt_door_info_tbl + i; 1087 break; 1088 } 1089 } 1090 1091 if (infop == NULL || argsz != infop->di_reqsz) { 1092 err = EINVAL; 1093 goto fail; 1094 } 1095 1096 if (infop->di_set) { 1097 ucred_t *cred = NULL; 1098 const priv_set_t *eset; 1099 1100 if (door_ucred(&cred) != 0) { 1101 err = errno; 1102 goto fail; 1103 } 1104 1105 eset = ucred_getprivset(cred, PRIV_EFFECTIVE); 1106 if ((eset == NULL) || 1107 (!priv_ismember(eset, PRIV_SYS_DL_CONFIG) && 1108 !priv_ismember(eset, PRIV_SYS_NET_CONFIG))) { 1109 err = EACCES; 1110 } 1111 ucred_free(cred); 1112 if (err != 0) 1113 goto fail; 1114 } 1115 1116 /* 1117 * We cannot use malloc() here because door_return never returns, and 1118 * memory allocated by malloc() would get leaked. Use alloca() instead. 1119 */ 1120 retvalp = alloca(infop->di_acksz); 1121 infop->di_handler(argp, retvalp); 1122 (void) door_return(retvalp, infop->di_acksz, NULL, 0); 1123 return; 1124 1125 fail: 1126 retval.lr_err = err; 1127 (void) door_return((char *)&retval, sizeof (retval), NULL, 0); 1128 } 1129