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