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