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