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