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 * Copyright 2008 Sun Microsystems, Inc. All rights reserved. 23 * Use is subject to license terms. 24 */ 25 26 /* 27 * Data-Link Driver 28 */ 29 30 #include <sys/conf.h> 31 #include <sys/mkdev.h> 32 #include <sys/modctl.h> 33 #include <sys/stat.h> 34 #include <sys/vlan.h> 35 #include <sys/mac.h> 36 #include <sys/dld_impl.h> 37 #include <sys/dls_impl.h> 38 #include <sys/softmac.h> 39 #include <sys/vlan.h> 40 #include <sys/policy.h> 41 #include <inet/common.h> 42 43 static void drv_init(void); 44 static int drv_fini(void); 45 46 static int drv_getinfo(dev_info_t *, ddi_info_cmd_t, void *, void **); 47 static int drv_attach(dev_info_t *, ddi_attach_cmd_t); 48 static int drv_detach(dev_info_t *, ddi_detach_cmd_t); 49 50 /* 51 * Secure objects declarations 52 */ 53 #define SECOBJ_WEP_HASHSZ 67 54 static krwlock_t drv_secobj_lock; 55 static kmem_cache_t *drv_secobj_cachep; 56 static mod_hash_t *drv_secobj_hash; 57 static void drv_secobj_init(void); 58 static void drv_secobj_fini(void); 59 static int drv_ioc_setap(datalink_id_t, struct dlautopush *); 60 static int drv_ioc_getap(datalink_id_t, struct dlautopush *); 61 static int drv_ioc_clrap(datalink_id_t); 62 63 64 /* 65 * The following entry points are private to dld and are used for control 66 * operations only. The entry points exported to mac drivers are defined 67 * in dld_str.c. Refer to the comment on top of dld_str.c for details. 68 */ 69 static int drv_open(dev_t *, int, int, cred_t *); 70 static int drv_ioctl(dev_t, int, intptr_t, int, cred_t *, int *); 71 72 static dev_info_t *dld_dip; /* dev_info_t for the driver */ 73 uint32_t dld_opt = 0; /* Global options */ 74 75 #define NAUTOPUSH 32 76 static mod_hash_t *dld_ap_hashp; 77 static krwlock_t dld_ap_hash_lock; 78 79 static struct cb_ops drv_cb_ops = { 80 drv_open, /* open */ 81 nulldev, /* close */ 82 nulldev, /* strategy */ 83 nulldev, /* print */ 84 nodev, /* dump */ 85 nodev, /* read */ 86 nodev, /* write */ 87 drv_ioctl, /* ioctl */ 88 nodev, /* devmap */ 89 nodev, /* mmap */ 90 nodev, /* segmap */ 91 nochpoll, /* poll */ 92 ddi_prop_op, /* cb_prop_op */ 93 0, /* streamtab */ 94 D_MP /* Driver compatibility flag */ 95 }; 96 97 static struct dev_ops drv_ops = { 98 DEVO_REV, /* devo_rev */ 99 0, /* refcnt */ 100 drv_getinfo, /* get_dev_info */ 101 nulldev, /* identify */ 102 nulldev, /* probe */ 103 drv_attach, /* attach */ 104 drv_detach, /* detach */ 105 nodev, /* reset */ 106 &drv_cb_ops, /* driver operations */ 107 NULL, /* bus operations */ 108 nodev, /* dev power */ 109 ddi_quiesce_not_supported, /* dev quiesce */ 110 }; 111 112 /* 113 * Module linkage information for the kernel. 114 */ 115 static struct modldrv drv_modldrv = { 116 &mod_driverops, 117 DLD_INFO, 118 &drv_ops 119 }; 120 121 static struct modlinkage drv_modlinkage = { 122 MODREV_1, 123 &drv_modldrv, 124 NULL 125 }; 126 127 int 128 _init(void) 129 { 130 return (mod_install(&drv_modlinkage)); 131 } 132 133 int 134 _fini(void) 135 { 136 return (mod_remove(&drv_modlinkage)); 137 } 138 139 int 140 _info(struct modinfo *modinfop) 141 { 142 return (mod_info(&drv_modlinkage, modinfop)); 143 } 144 145 /* 146 * Initialize component modules. 147 */ 148 static void 149 drv_init(void) 150 { 151 drv_secobj_init(); 152 dld_str_init(); 153 /* 154 * Create a hash table for autopush configuration. 155 */ 156 dld_ap_hashp = mod_hash_create_idhash("dld_autopush_hash", 157 NAUTOPUSH, mod_hash_null_valdtor); 158 159 ASSERT(dld_ap_hashp != NULL); 160 rw_init(&dld_ap_hash_lock, NULL, RW_DRIVER, NULL); 161 } 162 163 /* ARGSUSED */ 164 static uint_t 165 drv_ap_exist(mod_hash_key_t key, mod_hash_val_t *val, void *arg) 166 { 167 boolean_t *pexist = arg; 168 169 *pexist = B_TRUE; 170 return (MH_WALK_TERMINATE); 171 } 172 173 static int 174 drv_fini(void) 175 { 176 int err; 177 boolean_t exist = B_FALSE; 178 179 rw_enter(&dld_ap_hash_lock, RW_READER); 180 mod_hash_walk(dld_ap_hashp, drv_ap_exist, &exist); 181 rw_exit(&dld_ap_hash_lock); 182 183 if (exist) 184 return (EBUSY); 185 186 if ((err = dld_str_fini()) != 0) 187 return (err); 188 189 drv_secobj_fini(); 190 mod_hash_destroy_idhash(dld_ap_hashp); 191 rw_destroy(&dld_ap_hash_lock); 192 return (0); 193 } 194 195 /* 196 * devo_getinfo: getinfo(9e) 197 */ 198 /*ARGSUSED*/ 199 static int 200 drv_getinfo(dev_info_t *dip, ddi_info_cmd_t cmd, void *arg, void **resp) 201 { 202 if (dld_dip == NULL) 203 return (DDI_FAILURE); 204 205 switch (cmd) { 206 case DDI_INFO_DEVT2INSTANCE: 207 *resp = 0; 208 break; 209 case DDI_INFO_DEVT2DEVINFO: 210 *resp = dld_dip; 211 break; 212 default: 213 return (DDI_FAILURE); 214 } 215 216 return (DDI_SUCCESS); 217 } 218 219 /* 220 * Check properties to set options. (See dld.h for property definitions). 221 */ 222 static void 223 drv_set_opt(dev_info_t *dip) 224 { 225 if (ddi_prop_get_int(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS, 226 DLD_PROP_NO_FASTPATH, 0) != 0) { 227 dld_opt |= DLD_OPT_NO_FASTPATH; 228 } 229 230 if (ddi_prop_get_int(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS, 231 DLD_PROP_NO_POLL, 0) != 0) { 232 dld_opt |= DLD_OPT_NO_POLL; 233 } 234 235 if (ddi_prop_get_int(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS, 236 DLD_PROP_NO_ZEROCOPY, 0) != 0) { 237 dld_opt |= DLD_OPT_NO_ZEROCOPY; 238 } 239 240 if (ddi_prop_get_int(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS, 241 DLD_PROP_NO_SOFTRING, 0) != 0) { 242 dld_opt |= DLD_OPT_NO_SOFTRING; 243 } 244 } 245 246 /* 247 * devo_attach: attach(9e) 248 */ 249 static int 250 drv_attach(dev_info_t *dip, ddi_attach_cmd_t cmd) 251 { 252 if (cmd != DDI_ATTACH) 253 return (DDI_FAILURE); 254 255 ASSERT(ddi_get_instance(dip) == 0); 256 drv_init(); 257 drv_set_opt(dip); 258 259 /* 260 * Create control node. DLPI provider nodes will be created on demand. 261 */ 262 if (ddi_create_minor_node(dip, DLD_CONTROL_MINOR_NAME, S_IFCHR, 263 DLD_CONTROL_MINOR, DDI_PSEUDO, 0) != DDI_SUCCESS) 264 return (DDI_FAILURE); 265 266 dld_dip = dip; 267 268 /* 269 * Log the fact that the driver is now attached. 270 */ 271 ddi_report_dev(dip); 272 return (DDI_SUCCESS); 273 } 274 275 /* 276 * devo_detach: detach(9e) 277 */ 278 static int 279 drv_detach(dev_info_t *dip, ddi_detach_cmd_t cmd) 280 { 281 if (cmd != DDI_DETACH) 282 return (DDI_FAILURE); 283 284 ASSERT(dld_dip == dip); 285 if (drv_fini() != 0) 286 return (DDI_FAILURE); 287 288 /* 289 * Remove the control node. 290 */ 291 ddi_remove_minor_node(dip, DLD_CONTROL_MINOR_NAME); 292 dld_dip = NULL; 293 294 return (DDI_SUCCESS); 295 } 296 297 /* 298 * dld control node open procedure. 299 */ 300 /*ARGSUSED*/ 301 static int 302 drv_open(dev_t *devp, int flag, int sflag, cred_t *credp) 303 { 304 /* 305 * Only the control node can be opened. 306 */ 307 if (getminor(*devp) != DLD_CONTROL_MINOR) 308 return (ENODEV); 309 return (0); 310 } 311 312 /* 313 * DLDIOC_ATTR 314 */ 315 /* ARGSUSED */ 316 static int 317 drv_ioc_attr(void *karg, intptr_t arg, int mode, cred_t *cred) 318 { 319 dld_ioc_attr_t *diap = karg; 320 dls_dl_handle_t dlh; 321 dls_vlan_t *dvp; 322 int err; 323 324 if ((err = dls_devnet_hold_tmp(diap->dia_linkid, &dlh)) != 0) 325 return (err); 326 327 if ((err = dls_vlan_hold(dls_devnet_mac(dlh), 328 dls_devnet_vid(dlh), &dvp, B_FALSE, B_FALSE)) != 0) { 329 dls_devnet_rele_tmp(dlh); 330 return (err); 331 } 332 mac_sdu_get(dvp->dv_dlp->dl_mh, NULL, &diap->dia_max_sdu); 333 334 dls_vlan_rele(dvp); 335 dls_devnet_rele_tmp(dlh); 336 337 return (0); 338 } 339 340 /* 341 * DLDIOC_PHYS_ATTR 342 */ 343 /* ARGSUSED */ 344 static int 345 drv_ioc_phys_attr(void *karg, intptr_t arg, int mode, cred_t *cred) 346 { 347 dld_ioc_phys_attr_t *dipp = karg; 348 int err; 349 dls_dl_handle_t dlh; 350 dls_dev_handle_t ddh; 351 dev_t phydev; 352 353 /* 354 * Every physical link should have its physical dev_t kept in the 355 * daemon. If not, it is not a valid physical link. 356 */ 357 if (dls_mgmt_get_phydev(dipp->dip_linkid, &phydev) != 0) 358 return (EINVAL); 359 360 /* 361 * Although this is a valid physical link, it might already be removed 362 * by DR or during system shutdown. softmac_hold_device() would return 363 * ENOENT in this case. 364 */ 365 if ((err = softmac_hold_device(phydev, &ddh)) != 0) 366 return (err); 367 368 if (dls_devnet_hold_tmp(dipp->dip_linkid, &dlh) != 0) { 369 /* 370 * Although this is an active physical link, its link type is 371 * not supported by GLDv3, and therefore it does not have 372 * vanity naming support. 373 */ 374 dipp->dip_novanity = B_TRUE; 375 } else { 376 dipp->dip_novanity = B_FALSE; 377 dls_devnet_rele_tmp(dlh); 378 } 379 /* 380 * Get the physical device name from the major number and the instance 381 * number derived from phydev. 382 */ 383 (void) snprintf(dipp->dip_dev, MAXLINKNAMELEN, "%s%d", 384 ddi_major_to_name(getmajor(phydev)), getminor(phydev) - 1); 385 386 softmac_rele_device(ddh); 387 return (0); 388 } 389 390 /* 391 * DLDIOC_SETPROP 392 */ 393 static int 394 drv_ioc_prop_common(dld_ioc_macprop_t *dipp, intptr_t arg, boolean_t set, 395 int mode) 396 { 397 int err = EINVAL; 398 size_t dsize; 399 dld_ioc_macprop_t *kdipp; 400 dls_dl_handle_t dlh; 401 dls_vlan_t *dvp; 402 datalink_id_t linkid; 403 mac_prop_t macprop; 404 uchar_t *cp; 405 struct dlautopush *dlap; 406 dld_ioc_zid_t *dzp; 407 408 /* 409 * We only use pr_valsize from dipp, as the caller only did a 410 * copyin() for sizeof (dld_ioc_prop_t), which doesn't cover 411 * the property data. We copyin the full dld_ioc_prop_t 412 * including the data into kdipp down below. 413 */ 414 dsize = sizeof (dld_ioc_macprop_t) + dipp->pr_valsize - 1; 415 if (dsize < dipp->pr_valsize) 416 return (EINVAL); 417 418 /* 419 * The property data is variable size, so we need to allocate 420 * a buffer for kernel use as this data was not part of the 421 * dipp allocation and copyin() done by the framework. 422 */ 423 if ((kdipp = kmem_alloc(dsize, KM_NOSLEEP)) == NULL) 424 return (ENOMEM); 425 if (ddi_copyin((void *)arg, kdipp, dsize, mode) != 0) { 426 err = EFAULT; 427 goto done; 428 } 429 430 linkid = kdipp->pr_linkid; 431 432 switch (dipp->pr_num) { 433 case MAC_PROP_ZONE: 434 if (set) { 435 dzp = (dld_ioc_zid_t *)kdipp->pr_val; 436 err = dls_devnet_setzid(dzp->diz_link, dzp->diz_zid); 437 goto done; 438 } else { 439 kdipp->pr_perm_flags = MAC_PROP_PERM_RW; 440 cp = (uchar_t *)kdipp->pr_val; 441 err = dls_devnet_getzid(linkid, (zoneid_t *)cp); 442 goto done; 443 } 444 case MAC_PROP_AUTOPUSH: 445 if (set) { 446 if (dipp->pr_valsize != 0) { 447 dlap = (struct dlautopush *)kdipp->pr_val; 448 err = drv_ioc_setap(linkid, dlap); 449 goto done; 450 } else { 451 err = drv_ioc_clrap(linkid); 452 goto done; 453 } 454 } else { 455 kdipp->pr_perm_flags = MAC_PROP_PERM_RW; 456 dlap = (struct dlautopush *)kdipp->pr_val; 457 err = drv_ioc_getap(linkid, dlap); 458 goto done; 459 } 460 461 default: 462 break; 463 } 464 465 if ((err = dls_devnet_hold_tmp(linkid, &dlh)) != 0) 466 goto done; 467 468 if ((err = dls_vlan_hold(dls_devnet_mac(dlh), 469 dls_devnet_vid(dlh), &dvp, B_FALSE, B_FALSE)) != 0) { 470 dls_devnet_rele_tmp(dlh); 471 goto done; 472 } 473 474 macprop.mp_name = kdipp->pr_name; 475 macprop.mp_id = kdipp->pr_num; 476 macprop.mp_flags = kdipp->pr_flags; 477 478 if (set) { 479 err = mac_set_prop(dvp->dv_dlp->dl_mh, &macprop, 480 kdipp->pr_val, kdipp->pr_valsize); 481 } else { 482 kdipp->pr_perm_flags = MAC_PROP_PERM_RW; 483 err = mac_get_prop(dvp->dv_dlp->dl_mh, &macprop, 484 kdipp->pr_val, kdipp->pr_valsize, &kdipp->pr_perm_flags); 485 } 486 487 dls_vlan_rele(dvp); 488 dls_devnet_rele_tmp(dlh); 489 done: 490 if (!set && err == 0 && 491 ddi_copyout(kdipp, (void *)arg, dsize, mode) != 0) 492 err = EFAULT; 493 kmem_free(kdipp, dsize); 494 return (err); 495 } 496 497 /* ARGSUSED */ 498 static int 499 drv_ioc_setprop(void *karg, intptr_t arg, int mode, cred_t *cred) 500 { 501 return (drv_ioc_prop_common(karg, arg, B_TRUE, mode)); 502 } 503 504 /* ARGSUSED */ 505 static int 506 drv_ioc_getprop(void *karg, intptr_t arg, int mode, cred_t *cred) 507 { 508 return (drv_ioc_prop_common(karg, arg, B_FALSE, mode)); 509 } 510 511 /* 512 * DLDIOC_CREATE_VLAN 513 */ 514 /* ARGSUSED */ 515 static int 516 drv_ioc_create_vlan(void *karg, intptr_t arg, int mode, cred_t *cred) 517 { 518 dld_ioc_create_vlan_t *dicp = karg; 519 520 return (dls_devnet_create_vlan(dicp->dic_vlanid, dicp->dic_linkid, 521 dicp->dic_vid, dicp->dic_force)); 522 } 523 524 /* 525 * DLDIOC_DELETE_VLAN 526 */ 527 /* ARGSUSED */ 528 static int 529 drv_ioc_delete_vlan(void *karg, intptr_t arg, int mode, cred_t *cred) 530 { 531 dld_ioc_delete_vlan_t *didp = karg; 532 533 return (dls_devnet_destroy_vlan(didp->did_linkid)); 534 } 535 536 /* 537 * DLDIOC_VLAN_ATTR 538 */ 539 /* ARGSUSED */ 540 static int 541 drv_ioc_vlan_attr(void *karg, intptr_t arg, int mode, cred_t *cred) 542 { 543 dld_ioc_vlan_attr_t *divp = karg; 544 dls_dl_handle_t dlh; 545 uint16_t vid; 546 dls_vlan_t *dvp; 547 int err; 548 549 /* 550 * Hold this link to prevent it from being deleted. 551 */ 552 if ((err = dls_devnet_hold_tmp(divp->div_vlanid, &dlh)) != 0) 553 return (err); 554 555 if ((vid = dls_devnet_vid(dlh)) == VLAN_ID_NONE) { 556 dls_devnet_rele_tmp(dlh); 557 return (EINVAL); 558 } 559 560 err = dls_vlan_hold(dls_devnet_mac(dlh), vid, &dvp, B_FALSE, B_FALSE); 561 if (err != 0) { 562 dls_devnet_rele_tmp(dlh); 563 return (err); 564 } 565 566 divp->div_linkid = dls_devnet_linkid(dlh); 567 divp->div_implicit = !dls_devnet_is_explicit(dlh); 568 divp->div_vid = vid; 569 divp->div_force = dvp->dv_force; 570 571 dls_vlan_rele(dvp); 572 dls_devnet_rele_tmp(dlh); 573 return (0); 574 } 575 576 /* 577 * DLDIOC_RENAME. 578 * 579 * This function handles two cases of link renaming. See more in comments above 580 * dls_datalink_rename(). 581 */ 582 /* ARGSUSED */ 583 static int 584 drv_ioc_rename(void *karg, intptr_t arg, int mode, cred_t *cred) 585 { 586 dld_ioc_rename_t *dir = karg; 587 mod_hash_key_t key; 588 mod_hash_val_t val; 589 int err; 590 591 if ((err = dls_devnet_rename(dir->dir_linkid1, dir->dir_linkid2, 592 dir->dir_link)) != 0) 593 return (err); 594 595 if (dir->dir_linkid2 == DATALINK_INVALID_LINKID) 596 return (0); 597 598 /* 599 * if dir_linkid2 is not DATALINK_INVALID_LINKID, it means this 600 * renaming request is to rename a valid physical link (dir_linkid1) 601 * to a "removed" physical link (dir_linkid2, which is removed by DR 602 * or during system shutdown). In this case, the link (specified by 603 * dir_linkid1) would inherit all the configuration of dir_linkid2, 604 * and dir_linkid1 and its configuration would be lost. 605 * 606 * Remove per-link autopush configuration of dir_linkid1 in this case. 607 */ 608 key = (mod_hash_key_t)(uintptr_t)dir->dir_linkid1; 609 rw_enter(&dld_ap_hash_lock, RW_WRITER); 610 if (mod_hash_find(dld_ap_hashp, key, &val) != 0) { 611 rw_exit(&dld_ap_hash_lock); 612 return (0); 613 } 614 615 VERIFY(mod_hash_remove(dld_ap_hashp, key, &val) == 0); 616 kmem_free(val, sizeof (dld_ap_t)); 617 rw_exit(&dld_ap_hash_lock); 618 return (0); 619 } 620 621 static int 622 drv_ioc_setap(datalink_id_t linkid, struct dlautopush *dlap) 623 { 624 dld_ap_t *dap; 625 int i; 626 mod_hash_key_t key; 627 628 if (dlap->dap_npush == 0 || dlap->dap_npush > MAXAPUSH) 629 return (EINVAL); 630 631 /* 632 * Validate that the specified list of modules exist. 633 */ 634 for (i = 0; i < dlap->dap_npush; i++) { 635 if (fmodsw_find(dlap->dap_aplist[i], FMODSW_LOAD) == NULL) 636 return (EINVAL); 637 } 638 639 640 key = (mod_hash_key_t)(uintptr_t)linkid; 641 642 rw_enter(&dld_ap_hash_lock, RW_WRITER); 643 if (mod_hash_find(dld_ap_hashp, key, (mod_hash_val_t *)&dap) != 0) { 644 dap = kmem_zalloc(sizeof (dld_ap_t), KM_NOSLEEP); 645 if (dap == NULL) { 646 rw_exit(&dld_ap_hash_lock); 647 return (ENOMEM); 648 } 649 650 dap->da_linkid = linkid; 651 VERIFY(mod_hash_insert(dld_ap_hashp, key, 652 (mod_hash_val_t)dap) == 0); 653 } 654 655 /* 656 * Update the configuration. 657 */ 658 dap->da_anchor = dlap->dap_anchor; 659 dap->da_npush = dlap->dap_npush; 660 for (i = 0; i < dlap->dap_npush; i++) { 661 (void) strlcpy(dap->da_aplist[i], dlap->dap_aplist[i], 662 FMNAMESZ + 1); 663 } 664 rw_exit(&dld_ap_hash_lock); 665 666 return (0); 667 } 668 669 static int 670 drv_ioc_getap(datalink_id_t linkid, struct dlautopush *dlap) 671 { 672 dld_ap_t *dap; 673 int i; 674 675 rw_enter(&dld_ap_hash_lock, RW_READER); 676 if (mod_hash_find(dld_ap_hashp, 677 (mod_hash_key_t)(uintptr_t)linkid, 678 (mod_hash_val_t *)&dap) != 0) { 679 rw_exit(&dld_ap_hash_lock); 680 return (ENOENT); 681 } 682 683 /* 684 * Retrieve the configuration. 685 */ 686 dlap->dap_anchor = dap->da_anchor; 687 dlap->dap_npush = dap->da_npush; 688 for (i = 0; i < dap->da_npush; i++) { 689 (void) strlcpy(dlap->dap_aplist[i], dap->da_aplist[i], 690 FMNAMESZ + 1); 691 } 692 rw_exit(&dld_ap_hash_lock); 693 694 return (0); 695 } 696 697 static int 698 drv_ioc_clrap(datalink_id_t linkid) 699 { 700 mod_hash_val_t val; 701 mod_hash_key_t key; 702 703 key = (mod_hash_key_t)(uintptr_t)linkid; 704 705 rw_enter(&dld_ap_hash_lock, RW_WRITER); 706 if (mod_hash_find(dld_ap_hashp, key, &val) != 0) { 707 rw_exit(&dld_ap_hash_lock); 708 return (0); 709 } 710 711 VERIFY(mod_hash_remove(dld_ap_hashp, key, &val) == 0); 712 kmem_free(val, sizeof (dld_ap_t)); 713 rw_exit(&dld_ap_hash_lock); 714 return (0); 715 } 716 717 /* 718 * DLDIOC_DOORSERVER 719 */ 720 /* ARGSUSED */ 721 static int 722 drv_ioc_doorserver(void *karg, intptr_t arg, int mode, cred_t *cred) 723 { 724 dld_ioc_door_t *did = karg; 725 726 return (dls_mgmt_door_set(did->did_start_door)); 727 } 728 729 /* 730 * Check for GLDv3 autopush information. There are three cases: 731 * 732 * 1. If devp points to a GLDv3 datalink and it has autopush configuration, 733 * fill dlap in with that information and return 0. 734 * 735 * 2. If devp points to a GLDv3 datalink but it doesn't have autopush 736 * configuration, then replace devp with the physical device (if one 737 * exists) and return 1. This allows stropen() to find the old-school 738 * per-driver autopush configuration. (For softmac, the result is that 739 * the softmac dev_t is replaced with the legacy device's dev_t). 740 * 741 * 3. If neither of the above apply, don't touch the args and return -1. 742 */ 743 int 744 dld_autopush(dev_t *devp, struct dlautopush *dlap) 745 { 746 dld_ap_t *dap; 747 datalink_id_t linkid; 748 dev_t phydev; 749 750 if (!GLDV3_DRV(getmajor(*devp))) 751 return (-1); 752 753 /* 754 * Find the linkid by the link's dev_t. 755 */ 756 if (dls_devnet_dev2linkid(*devp, &linkid) != 0) 757 return (-1); 758 759 /* 760 * Find the autopush configuration associated with the linkid. 761 */ 762 rw_enter(&dld_ap_hash_lock, RW_READER); 763 if (mod_hash_find(dld_ap_hashp, (mod_hash_key_t)(uintptr_t)linkid, 764 (mod_hash_val_t *)&dap) == 0) { 765 *dlap = dap->da_ap; 766 rw_exit(&dld_ap_hash_lock); 767 return (0); 768 } 769 rw_exit(&dld_ap_hash_lock); 770 771 if (dls_devnet_phydev(linkid, &phydev) != 0) 772 return (-1); 773 774 *devp = phydev; 775 return (1); 776 } 777 778 /* 779 * Secure objects implementation 780 */ 781 782 /* ARGSUSED */ 783 static int 784 drv_secobj_ctor(void *buf, void *arg, int kmflag) 785 { 786 bzero(buf, sizeof (dld_secobj_t)); 787 return (0); 788 } 789 790 static void 791 drv_secobj_init(void) 792 { 793 rw_init(&drv_secobj_lock, NULL, RW_DEFAULT, NULL); 794 drv_secobj_cachep = kmem_cache_create("drv_secobj_cache", 795 sizeof (dld_secobj_t), 0, drv_secobj_ctor, NULL, 796 NULL, NULL, NULL, 0); 797 drv_secobj_hash = mod_hash_create_extended("drv_secobj_hash", 798 SECOBJ_WEP_HASHSZ, mod_hash_null_keydtor, mod_hash_null_valdtor, 799 mod_hash_bystr, NULL, mod_hash_strkey_cmp, KM_SLEEP); 800 } 801 802 static void 803 drv_secobj_fini(void) 804 { 805 mod_hash_destroy_hash(drv_secobj_hash); 806 kmem_cache_destroy(drv_secobj_cachep); 807 rw_destroy(&drv_secobj_lock); 808 } 809 810 /* ARGSUSED */ 811 static int 812 drv_ioc_secobj_set(void *karg, intptr_t arg, int mode, cred_t *cred) 813 { 814 dld_ioc_secobj_set_t *ssp = karg; 815 dld_secobj_t *sobjp, *objp; 816 int err; 817 818 sobjp = &ssp->ss_obj; 819 820 if (sobjp->so_class != DLD_SECOBJ_CLASS_WEP && 821 sobjp->so_class != DLD_SECOBJ_CLASS_WPA) 822 return (EINVAL); 823 824 if (sobjp->so_name[DLD_SECOBJ_NAME_MAX - 1] != '\0' || 825 sobjp->so_len > DLD_SECOBJ_VAL_MAX) 826 return (EINVAL); 827 828 rw_enter(&drv_secobj_lock, RW_WRITER); 829 err = mod_hash_find(drv_secobj_hash, (mod_hash_key_t)sobjp->so_name, 830 (mod_hash_val_t *)&objp); 831 if (err == 0) { 832 if ((ssp->ss_flags & DLD_SECOBJ_OPT_CREATE) != 0) { 833 rw_exit(&drv_secobj_lock); 834 return (EEXIST); 835 } 836 } else { 837 ASSERT(err == MH_ERR_NOTFOUND); 838 if ((ssp->ss_flags & DLD_SECOBJ_OPT_CREATE) == 0) { 839 rw_exit(&drv_secobj_lock); 840 return (ENOENT); 841 } 842 objp = kmem_cache_alloc(drv_secobj_cachep, KM_SLEEP); 843 (void) strlcpy(objp->so_name, sobjp->so_name, 844 DLD_SECOBJ_NAME_MAX); 845 846 VERIFY(mod_hash_insert(drv_secobj_hash, 847 (mod_hash_key_t)objp->so_name, (mod_hash_val_t)objp) == 0); 848 } 849 bcopy(sobjp->so_val, objp->so_val, sobjp->so_len); 850 objp->so_len = sobjp->so_len; 851 objp->so_class = sobjp->so_class; 852 rw_exit(&drv_secobj_lock); 853 return (0); 854 } 855 856 typedef struct dld_secobj_state { 857 uint_t ss_free; 858 uint_t ss_count; 859 int ss_rc; 860 int ss_mode; 861 dld_secobj_t *ss_objp; 862 } dld_secobj_state_t; 863 864 /* ARGSUSED */ 865 static uint_t 866 drv_secobj_walker(mod_hash_key_t key, mod_hash_val_t *val, void *arg) 867 { 868 dld_secobj_state_t *statep = arg; 869 dld_secobj_t *sobjp = (dld_secobj_t *)val; 870 871 if (statep->ss_free < sizeof (dld_secobj_t)) { 872 statep->ss_rc = ENOSPC; 873 return (MH_WALK_TERMINATE); 874 } 875 if (ddi_copyout(sobjp, statep->ss_objp, sizeof (*sobjp), 876 statep->ss_mode) != 0) { 877 statep->ss_rc = EFAULT; 878 return (MH_WALK_TERMINATE); 879 } 880 statep->ss_objp++; 881 statep->ss_free -= sizeof (dld_secobj_t); 882 statep->ss_count++; 883 return (MH_WALK_CONTINUE); 884 } 885 886 /* ARGSUSED */ 887 static int 888 drv_ioc_secobj_get(void *karg, intptr_t arg, int mode, cred_t *cred) 889 { 890 dld_ioc_secobj_get_t *sgp = karg; 891 dld_secobj_t *sobjp, *objp; 892 int err; 893 894 sobjp = &sgp->sg_obj; 895 896 if (sobjp->so_name[DLD_SECOBJ_NAME_MAX - 1] != '\0') 897 return (EINVAL); 898 899 rw_enter(&drv_secobj_lock, RW_READER); 900 if (sobjp->so_name[0] != '\0') { 901 err = mod_hash_find(drv_secobj_hash, 902 (mod_hash_key_t)sobjp->so_name, (mod_hash_val_t *)&objp); 903 if (err != 0) { 904 ASSERT(err == MH_ERR_NOTFOUND); 905 rw_exit(&drv_secobj_lock); 906 return (ENOENT); 907 } 908 bcopy(objp->so_val, sobjp->so_val, objp->so_len); 909 sobjp->so_len = objp->so_len; 910 sobjp->so_class = objp->so_class; 911 sgp->sg_count = 1; 912 } else { 913 dld_secobj_state_t state; 914 915 state.ss_free = sgp->sg_size - sizeof (dld_ioc_secobj_get_t); 916 state.ss_count = 0; 917 state.ss_rc = 0; 918 state.ss_mode = mode; 919 state.ss_objp = (dld_secobj_t *)((uchar_t *)arg + 920 sizeof (dld_ioc_secobj_get_t)); 921 922 mod_hash_walk(drv_secobj_hash, drv_secobj_walker, &state); 923 if (state.ss_rc != 0) { 924 rw_exit(&drv_secobj_lock); 925 return (state.ss_rc); 926 } 927 sgp->sg_count = state.ss_count; 928 } 929 rw_exit(&drv_secobj_lock); 930 return (0); 931 } 932 933 /* ARGSUSED */ 934 static int 935 drv_ioc_secobj_unset(void *karg, intptr_t arg, int mode, cred_t *cred) 936 { 937 dld_ioc_secobj_unset_t *sup = karg; 938 dld_secobj_t *objp; 939 mod_hash_val_t val; 940 int err; 941 942 if (sup->su_name[DLD_SECOBJ_NAME_MAX - 1] != '\0') 943 return (EINVAL); 944 945 rw_enter(&drv_secobj_lock, RW_WRITER); 946 err = mod_hash_find(drv_secobj_hash, (mod_hash_key_t)sup->su_name, 947 (mod_hash_val_t *)&objp); 948 if (err != 0) { 949 ASSERT(err == MH_ERR_NOTFOUND); 950 rw_exit(&drv_secobj_lock); 951 return (ENOENT); 952 } 953 VERIFY(mod_hash_remove(drv_secobj_hash, (mod_hash_key_t)sup->su_name, 954 (mod_hash_val_t *)&val) == 0); 955 ASSERT(objp == (dld_secobj_t *)val); 956 957 kmem_cache_free(drv_secobj_cachep, objp); 958 rw_exit(&drv_secobj_lock); 959 return (0); 960 } 961 962 static dld_ioc_info_t drv_ioc_list[] = { 963 {DLDIOC_ATTR, DLDCOPYINOUT, sizeof (dld_ioc_attr_t), 964 drv_ioc_attr}, 965 {DLDIOC_PHYS_ATTR, DLDCOPYINOUT, sizeof (dld_ioc_phys_attr_t), 966 drv_ioc_phys_attr}, 967 {DLDIOC_SECOBJ_SET, DLDCOPYIN | DLDDLCONFIG, 968 sizeof (dld_ioc_secobj_set_t), drv_ioc_secobj_set}, 969 {DLDIOC_SECOBJ_GET, DLDCOPYINOUT | DLDDLCONFIG, 970 sizeof (dld_ioc_secobj_get_t), drv_ioc_secobj_get}, 971 {DLDIOC_SECOBJ_UNSET, DLDCOPYIN | DLDDLCONFIG, 972 sizeof (dld_ioc_secobj_unset_t), drv_ioc_secobj_unset}, 973 {DLDIOC_CREATE_VLAN, DLDCOPYIN | DLDDLCONFIG, 974 sizeof (dld_ioc_create_vlan_t), drv_ioc_create_vlan}, 975 {DLDIOC_DELETE_VLAN, DLDCOPYIN | DLDDLCONFIG, 976 sizeof (dld_ioc_delete_vlan_t), 977 drv_ioc_delete_vlan}, 978 {DLDIOC_VLAN_ATTR, DLDCOPYINOUT, sizeof (dld_ioc_vlan_attr_t), 979 drv_ioc_vlan_attr}, 980 {DLDIOC_DOORSERVER, DLDCOPYIN | DLDDLCONFIG, sizeof (dld_ioc_door_t), 981 drv_ioc_doorserver}, 982 {DLDIOC_RENAME, DLDCOPYIN | DLDDLCONFIG, sizeof (dld_ioc_rename_t), 983 drv_ioc_rename}, 984 {DLDIOC_GETMACPROP, DLDCOPYIN, sizeof (dld_ioc_macprop_t), 985 drv_ioc_getprop}, 986 {DLDIOC_SETMACPROP, DLDCOPYIN | DLDDLCONFIG, sizeof (dld_ioc_macprop_t), 987 drv_ioc_setprop} 988 }; 989 990 typedef struct dld_ioc_modentry { 991 uint16_t dim_modid; /* Top 16 bits of ioctl command */ 992 char *dim_modname; /* Module to be loaded */ 993 dld_ioc_info_t *dim_list; /* array of ioctl structures */ 994 uint_t dim_count; /* number of elements in dim_list */ 995 } dld_ioc_modentry_t; 996 997 /* 998 * For all modules except for dld, dim_list and dim_count are assigned 999 * when the modules register their ioctls in dld_ioc_register(). We 1000 * can statically initialize dld's ioctls in-line here; there's no 1001 * need for it to call dld_ioc_register() itself. 1002 */ 1003 static dld_ioc_modentry_t dld_ioc_modtable[] = { 1004 {DLD_IOC, "dld", drv_ioc_list, DLDIOCCNT(drv_ioc_list)}, 1005 {AGGR_IOC, "aggr", NULL, 0}, 1006 {VNIC_IOC, "vnic", NULL, 0} 1007 }; 1008 #define DLDIOC_CNT \ 1009 (sizeof (dld_ioc_modtable) / sizeof (dld_ioc_modentry_t)) 1010 1011 static dld_ioc_modentry_t * 1012 dld_ioc_findmod(uint16_t modid) 1013 { 1014 int i; 1015 1016 for (i = 0; i < DLDIOC_CNT; i++) { 1017 if (modid == dld_ioc_modtable[i].dim_modid) 1018 return (&dld_ioc_modtable[i]); 1019 } 1020 return (NULL); 1021 } 1022 1023 int 1024 dld_ioc_register(uint16_t modid, dld_ioc_info_t *list, uint_t count) 1025 { 1026 dld_ioc_modentry_t *dim = dld_ioc_findmod(modid); 1027 1028 if (dim == NULL) 1029 return (ENOENT); 1030 1031 dim->dim_list = list; 1032 dim->dim_count = count; 1033 return (0); 1034 } 1035 1036 void 1037 dld_ioc_unregister(uint16_t modid) 1038 { 1039 VERIFY(dld_ioc_register(modid, NULL, 0) == 0); 1040 } 1041 1042 /* 1043 * The general design with GLDv3 ioctls is that all ioctls issued 1044 * through /dev/dld go through this drv_ioctl() function. This 1045 * function handles all ioctls on behalf of modules listed in 1046 * dld_ioc_modtable. 1047 * 1048 * When an ioctl is received, this function looks for the associated 1049 * module-id-specific ioctl information using dld_ioc_findmod(). The 1050 * call to ddi_hold_devi_by_instance() on the associated device will 1051 * cause the kernel module responsible for the ioctl to be loaded if 1052 * it's not already loaded, which should result in that module calling 1053 * dld_ioc_register(), thereby filling in the dim_list containing the 1054 * details for the ioctl being processed. 1055 * 1056 * This function can then perform operations such as copyin() data and 1057 * do credential checks based on the registered ioctl information, 1058 * then issue the callback function di_func() registered by the 1059 * responsible module. Upon return, the appropriate copyout() 1060 * operation can be performed and the operation completes. 1061 */ 1062 /* ARGSUSED */ 1063 static int 1064 drv_ioctl(dev_t dev, int cmd, intptr_t arg, int mode, cred_t *cred, int *rvalp) 1065 { 1066 dld_ioc_modentry_t *dim; 1067 dld_ioc_info_t *info; 1068 dev_info_t *dip = NULL; 1069 void *buf = NULL; 1070 size_t sz; 1071 int i, err; 1072 1073 if ((dim = dld_ioc_findmod(DLD_IOC_MODID(cmd))) == NULL) 1074 return (ENOTSUP); 1075 1076 dip = ddi_hold_devi_by_instance(ddi_name_to_major(dim->dim_modname), 1077 0, 0); 1078 if (dip == NULL || dim->dim_list == NULL) { 1079 err = ENODEV; 1080 goto done; 1081 } 1082 1083 for (i = 0; i < dim->dim_count; i++) { 1084 if (cmd == dim->dim_list[i].di_cmd) 1085 break; 1086 } 1087 if (i == dim->dim_count) { 1088 err = ENOTSUP; 1089 goto done; 1090 } 1091 1092 info = &dim->dim_list[i]; 1093 1094 if ((info->di_flags & DLDDLCONFIG) && secpolicy_dl_config(cred) != 0) { 1095 err = EPERM; 1096 goto done; 1097 } 1098 1099 sz = info->di_argsize; 1100 if ((buf = kmem_zalloc(sz, KM_NOSLEEP)) == NULL) { 1101 err = ENOMEM; 1102 goto done; 1103 } 1104 1105 if ((info->di_flags & DLDCOPYIN) && 1106 ddi_copyin((void *)arg, buf, sz, mode) != 0) { 1107 err = EFAULT; 1108 goto done; 1109 } 1110 1111 err = info->di_func(buf, arg, mode, cred); 1112 1113 if ((info->di_flags & DLDCOPYOUT) && 1114 ddi_copyout(buf, (void *)arg, sz, mode) != 0 && err == 0) 1115 err = EFAULT; 1116 1117 done: 1118 if (buf != NULL) 1119 kmem_free(buf, sz); 1120 if (dip != NULL) 1121 ddi_release_devi(dip); 1122 return (err); 1123 } 1124