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 2009 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 if ((err = dls_link_hold(dls_devnet_mac(dlh), &dlp)) != 0) 561 goto done; 562 563 switch (kprop->pr_num) { 564 case MAC_PROP_ZONE: 565 if (set) { 566 dld_ioc_zid_t *dzp = (dld_ioc_zid_t *)kprop->pr_val; 567 568 err = dls_devnet_setzid(dzp->diz_link, dzp->diz_zid); 569 } else { 570 kprop->pr_perm_flags = MAC_PROP_PERM_RW; 571 err = dls_devnet_getzid(linkid, 572 (zoneid_t *)kprop->pr_val); 573 } 574 break; 575 case MAC_PROP_AUTOPUSH: { 576 struct dlautopush *dlap = (struct dlautopush *)kprop->pr_val; 577 578 if (set) { 579 if (kprop->pr_valsize != 0) 580 err = drv_ioc_setap(linkid, dlap); 581 else 582 err = drv_ioc_clrap(linkid); 583 } else { 584 kprop->pr_perm_flags = MAC_PROP_PERM_RW; 585 err = drv_ioc_getap(linkid, dlap); 586 } 587 break; 588 } 589 case MAC_PROP_TAGMODE: 590 if (set) { 591 link_tagmode_t mode = *(link_tagmode_t *)kprop->pr_val; 592 593 if (mode != LINK_TAGMODE_VLANONLY && 594 mode != LINK_TAGMODE_NORMAL) { 595 err = EINVAL; 596 } else { 597 dlp->dl_tagmode = mode; 598 err = 0; 599 } 600 } else { 601 *(link_tagmode_t *)kprop->pr_val = dlp->dl_tagmode; 602 kprop->pr_perm_flags = MAC_PROP_PERM_RW; 603 err = 0; 604 } 605 break; 606 default: 607 macprop.mp_name = kprop->pr_name; 608 macprop.mp_id = kprop->pr_num; 609 macprop.mp_flags = kprop->pr_flags; 610 611 if (set) { 612 err = mac_set_prop(dlp->dl_mh, &macprop, kprop->pr_val, 613 kprop->pr_valsize); 614 } else { 615 kprop->pr_perm_flags = MAC_PROP_PERM_RW; 616 err = mac_get_prop(dlp->dl_mh, &macprop, kprop->pr_val, 617 kprop->pr_valsize, &kprop->pr_perm_flags); 618 } 619 } 620 621 done: 622 if (!set && err == 0 && 623 ddi_copyout(kprop, (void *)arg, dsize, mode) != 0) 624 err = EFAULT; 625 626 if (dlp != NULL) 627 dls_link_rele(dlp); 628 629 if (mph != NULL) { 630 int32_t cpuid; 631 void *mdip = NULL; 632 633 if (dlp != NULL && set && err == 0) { 634 cpuid = mac_client_intr_cpu(dlp->dl_mch); 635 mdip = mac_get_devinfo(dlp->dl_mh); 636 } 637 638 mac_perim_exit(mph); 639 640 if (mdip != NULL) 641 mac_client_set_intr_cpu(mdip, dlp->dl_mch, cpuid); 642 } 643 if (dlh != NULL) 644 dls_devnet_rele_tmp(dlh); 645 646 if (kprop != NULL) 647 kmem_free(kprop, dsize); 648 return (err); 649 } 650 651 /* ARGSUSED */ 652 static int 653 drv_ioc_setprop(void *karg, intptr_t arg, int mode, cred_t *cred, int *rvalp) 654 { 655 return (drv_ioc_prop_common(karg, arg, B_TRUE, mode)); 656 } 657 658 /* ARGSUSED */ 659 static int 660 drv_ioc_getprop(void *karg, intptr_t arg, int mode, cred_t *cred, int *rvalp) 661 { 662 return (drv_ioc_prop_common(karg, arg, B_FALSE, mode)); 663 } 664 665 /* 666 * DLDIOC_RENAME. 667 * 668 * This function handles two cases of link renaming. See more in comments above 669 * dls_datalink_rename(). 670 */ 671 /* ARGSUSED */ 672 static int 673 drv_ioc_rename(void *karg, intptr_t arg, int mode, cred_t *cred, int *rvalp) 674 { 675 dld_ioc_rename_t *dir = karg; 676 mod_hash_key_t key; 677 mod_hash_val_t val; 678 int err; 679 680 if ((err = dls_devnet_rename(dir->dir_linkid1, dir->dir_linkid2, 681 dir->dir_link)) != 0) 682 return (err); 683 684 if (dir->dir_linkid2 == DATALINK_INVALID_LINKID) 685 return (0); 686 687 /* 688 * if dir_linkid2 is not DATALINK_INVALID_LINKID, it means this 689 * renaming request is to rename a valid physical link (dir_linkid1) 690 * to a "removed" physical link (dir_linkid2, which is removed by DR 691 * or during system shutdown). In this case, the link (specified by 692 * dir_linkid1) would inherit all the configuration of dir_linkid2, 693 * and dir_linkid1 and its configuration would be lost. 694 * 695 * Remove per-link autopush configuration of dir_linkid1 in this case. 696 */ 697 key = (mod_hash_key_t)(uintptr_t)dir->dir_linkid1; 698 rw_enter(&dld_ap_hash_lock, RW_WRITER); 699 if (mod_hash_find(dld_ap_hashp, key, &val) != 0) { 700 rw_exit(&dld_ap_hash_lock); 701 return (0); 702 } 703 704 VERIFY(mod_hash_remove(dld_ap_hashp, key, &val) == 0); 705 kmem_free(val, sizeof (dld_ap_t)); 706 rw_exit(&dld_ap_hash_lock); 707 return (0); 708 } 709 710 static int 711 drv_ioc_setap(datalink_id_t linkid, struct dlautopush *dlap) 712 { 713 dld_ap_t *dap; 714 int i; 715 mod_hash_key_t key; 716 717 if (dlap->dap_npush == 0 || dlap->dap_npush > MAXAPUSH) 718 return (EINVAL); 719 720 /* 721 * Validate that the specified list of modules exist. 722 */ 723 for (i = 0; i < dlap->dap_npush; i++) { 724 if (fmodsw_find(dlap->dap_aplist[i], FMODSW_LOAD) == NULL) 725 return (EINVAL); 726 } 727 728 729 key = (mod_hash_key_t)(uintptr_t)linkid; 730 731 rw_enter(&dld_ap_hash_lock, RW_WRITER); 732 if (mod_hash_find(dld_ap_hashp, key, (mod_hash_val_t *)&dap) != 0) { 733 dap = kmem_zalloc(sizeof (dld_ap_t), KM_NOSLEEP); 734 if (dap == NULL) { 735 rw_exit(&dld_ap_hash_lock); 736 return (ENOMEM); 737 } 738 739 dap->da_linkid = linkid; 740 VERIFY(mod_hash_insert(dld_ap_hashp, key, 741 (mod_hash_val_t)dap) == 0); 742 } 743 744 /* 745 * Update the configuration. 746 */ 747 dap->da_anchor = dlap->dap_anchor; 748 dap->da_npush = dlap->dap_npush; 749 for (i = 0; i < dlap->dap_npush; i++) { 750 (void) strlcpy(dap->da_aplist[i], dlap->dap_aplist[i], 751 FMNAMESZ + 1); 752 } 753 rw_exit(&dld_ap_hash_lock); 754 755 return (0); 756 } 757 758 static int 759 drv_ioc_getap(datalink_id_t linkid, struct dlautopush *dlap) 760 { 761 dld_ap_t *dap; 762 int i; 763 764 rw_enter(&dld_ap_hash_lock, RW_READER); 765 if (mod_hash_find(dld_ap_hashp, 766 (mod_hash_key_t)(uintptr_t)linkid, 767 (mod_hash_val_t *)&dap) != 0) { 768 rw_exit(&dld_ap_hash_lock); 769 return (ENOENT); 770 } 771 772 /* 773 * Retrieve the configuration. 774 */ 775 dlap->dap_anchor = dap->da_anchor; 776 dlap->dap_npush = dap->da_npush; 777 for (i = 0; i < dap->da_npush; i++) { 778 (void) strlcpy(dlap->dap_aplist[i], dap->da_aplist[i], 779 FMNAMESZ + 1); 780 } 781 rw_exit(&dld_ap_hash_lock); 782 783 return (0); 784 } 785 786 static int 787 drv_ioc_clrap(datalink_id_t linkid) 788 { 789 mod_hash_val_t val; 790 mod_hash_key_t key; 791 792 key = (mod_hash_key_t)(uintptr_t)linkid; 793 794 rw_enter(&dld_ap_hash_lock, RW_WRITER); 795 if (mod_hash_find(dld_ap_hashp, key, &val) != 0) { 796 rw_exit(&dld_ap_hash_lock); 797 return (0); 798 } 799 800 VERIFY(mod_hash_remove(dld_ap_hashp, key, &val) == 0); 801 kmem_free(val, sizeof (dld_ap_t)); 802 rw_exit(&dld_ap_hash_lock); 803 return (0); 804 } 805 806 /* 807 * DLDIOC_DOORSERVER 808 */ 809 /* ARGSUSED */ 810 static int 811 drv_ioc_doorserver(void *karg, intptr_t arg, int mode, cred_t *cred, int *rvalp) 812 { 813 dld_ioc_door_t *did = karg; 814 815 return (dls_mgmt_door_set(did->did_start_door)); 816 } 817 818 /* 819 * DLDIOC_USAGELOG 820 */ 821 /* ARGSUSED */ 822 static int 823 drv_ioc_usagelog(void *karg, intptr_t arg, int mode, cred_t *cred, 824 int *rvalp) 825 { 826 dld_ioc_usagelog_t *log_info = (dld_ioc_usagelog_t *)karg; 827 int err = 0; 828 829 if (log_info->ul_type < MAC_LOGTYPE_LINK || 830 log_info->ul_type > MAC_LOGTYPE_FLOW) 831 return (EINVAL); 832 833 if (log_info->ul_onoff) { 834 err = mac_start_logusage(log_info->ul_type, 835 log_info->ul_interval); 836 } else { 837 mac_stop_logusage(log_info->ul_type); 838 } 839 return (err); 840 } 841 842 /* 843 * Process a DLDIOC_ADDFLOW request. 844 */ 845 /* ARGSUSED */ 846 static int 847 drv_ioc_addflow(void *karg, intptr_t arg, int mode, cred_t *cred, int *rvalp) 848 { 849 dld_ioc_addflow_t *afp = karg; 850 851 return (dld_add_flow(afp->af_linkid, afp->af_name, 852 &afp->af_flow_desc, &afp->af_resource_props)); 853 } 854 855 /* 856 * Process a DLDIOC_REMOVEFLOW request. 857 */ 858 /* ARGSUSED */ 859 static int 860 drv_ioc_removeflow(void *karg, intptr_t arg, int mode, cred_t *cred, int *rvalp) 861 { 862 dld_ioc_removeflow_t *rfp = karg; 863 864 return (dld_remove_flow(rfp->rf_name)); 865 } 866 867 /* 868 * Process a DLDIOC_MODIFYFLOW request. 869 */ 870 /* ARGSUSED */ 871 static int 872 drv_ioc_modifyflow(void *karg, intptr_t arg, int mode, cred_t *cred, int *rvalp) 873 { 874 dld_ioc_modifyflow_t *mfp = karg; 875 876 return (dld_modify_flow(mfp->mf_name, &mfp->mf_resource_props)); 877 } 878 879 /* 880 * Process a DLDIOC_WALKFLOW request. 881 */ 882 /* ARGSUSED */ 883 static int 884 drv_ioc_walkflow(void *karg, intptr_t arg, int mode, cred_t *cred, int *rvalp) 885 { 886 dld_ioc_walkflow_t *wfp = karg; 887 888 return (dld_walk_flow(wfp, arg)); 889 } 890 891 /* 892 * Check for GLDv3 autopush information. There are three cases: 893 * 894 * 1. If devp points to a GLDv3 datalink and it has autopush configuration, 895 * fill dlap in with that information and return 0. 896 * 897 * 2. If devp points to a GLDv3 datalink but it doesn't have autopush 898 * configuration, then replace devp with the physical device (if one 899 * exists) and return 1. This allows stropen() to find the old-school 900 * per-driver autopush configuration. (For softmac, the result is that 901 * the softmac dev_t is replaced with the legacy device's dev_t). 902 * 903 * 3. If neither of the above apply, don't touch the args and return -1. 904 */ 905 int 906 dld_autopush(dev_t *devp, struct dlautopush *dlap) 907 { 908 dld_ap_t *dap; 909 datalink_id_t linkid; 910 dev_t phydev; 911 912 if (!GLDV3_DRV(getmajor(*devp))) 913 return (-1); 914 915 /* 916 * Find the linkid by the link's dev_t. 917 */ 918 if (dls_devnet_dev2linkid(*devp, &linkid) != 0) 919 return (-1); 920 921 /* 922 * Find the autopush configuration associated with the linkid. 923 */ 924 rw_enter(&dld_ap_hash_lock, RW_READER); 925 if (mod_hash_find(dld_ap_hashp, (mod_hash_key_t)(uintptr_t)linkid, 926 (mod_hash_val_t *)&dap) == 0) { 927 *dlap = dap->da_ap; 928 rw_exit(&dld_ap_hash_lock); 929 return (0); 930 } 931 rw_exit(&dld_ap_hash_lock); 932 933 if (dls_devnet_phydev(linkid, &phydev) != 0) 934 return (-1); 935 936 *devp = phydev; 937 return (1); 938 } 939 940 /* 941 * Secure objects implementation 942 */ 943 944 /* ARGSUSED */ 945 static int 946 drv_secobj_ctor(void *buf, void *arg, int kmflag) 947 { 948 bzero(buf, sizeof (dld_secobj_t)); 949 return (0); 950 } 951 952 static void 953 drv_secobj_init(void) 954 { 955 rw_init(&drv_secobj_lock, NULL, RW_DEFAULT, NULL); 956 drv_secobj_cachep = kmem_cache_create("drv_secobj_cache", 957 sizeof (dld_secobj_t), 0, drv_secobj_ctor, NULL, 958 NULL, NULL, NULL, 0); 959 drv_secobj_hash = mod_hash_create_extended("drv_secobj_hash", 960 SECOBJ_WEP_HASHSZ, mod_hash_null_keydtor, mod_hash_null_valdtor, 961 mod_hash_bystr, NULL, mod_hash_strkey_cmp, KM_SLEEP); 962 } 963 964 static void 965 drv_secobj_fini(void) 966 { 967 mod_hash_destroy_hash(drv_secobj_hash); 968 kmem_cache_destroy(drv_secobj_cachep); 969 rw_destroy(&drv_secobj_lock); 970 } 971 972 /* ARGSUSED */ 973 static int 974 drv_ioc_secobj_set(void *karg, intptr_t arg, int mode, cred_t *cred, int *rvalp) 975 { 976 dld_ioc_secobj_set_t *ssp = karg; 977 dld_secobj_t *sobjp, *objp; 978 int err; 979 980 sobjp = &ssp->ss_obj; 981 982 if (sobjp->so_class != DLD_SECOBJ_CLASS_WEP && 983 sobjp->so_class != DLD_SECOBJ_CLASS_WPA) 984 return (EINVAL); 985 986 if (sobjp->so_name[DLD_SECOBJ_NAME_MAX - 1] != '\0' || 987 sobjp->so_len > DLD_SECOBJ_VAL_MAX) 988 return (EINVAL); 989 990 rw_enter(&drv_secobj_lock, RW_WRITER); 991 err = mod_hash_find(drv_secobj_hash, (mod_hash_key_t)sobjp->so_name, 992 (mod_hash_val_t *)&objp); 993 if (err == 0) { 994 if ((ssp->ss_flags & DLD_SECOBJ_OPT_CREATE) != 0) { 995 rw_exit(&drv_secobj_lock); 996 return (EEXIST); 997 } 998 } else { 999 ASSERT(err == MH_ERR_NOTFOUND); 1000 if ((ssp->ss_flags & DLD_SECOBJ_OPT_CREATE) == 0) { 1001 rw_exit(&drv_secobj_lock); 1002 return (ENOENT); 1003 } 1004 objp = kmem_cache_alloc(drv_secobj_cachep, KM_SLEEP); 1005 (void) strlcpy(objp->so_name, sobjp->so_name, 1006 DLD_SECOBJ_NAME_MAX); 1007 1008 VERIFY(mod_hash_insert(drv_secobj_hash, 1009 (mod_hash_key_t)objp->so_name, (mod_hash_val_t)objp) == 0); 1010 } 1011 bcopy(sobjp->so_val, objp->so_val, sobjp->so_len); 1012 objp->so_len = sobjp->so_len; 1013 objp->so_class = sobjp->so_class; 1014 rw_exit(&drv_secobj_lock); 1015 return (0); 1016 } 1017 1018 typedef struct dld_secobj_state { 1019 uint_t ss_free; 1020 uint_t ss_count; 1021 int ss_rc; 1022 int ss_mode; 1023 dld_secobj_t *ss_objp; 1024 } dld_secobj_state_t; 1025 1026 /* ARGSUSED */ 1027 static uint_t 1028 drv_secobj_walker(mod_hash_key_t key, mod_hash_val_t *val, void *arg) 1029 { 1030 dld_secobj_state_t *statep = arg; 1031 dld_secobj_t *sobjp = (dld_secobj_t *)val; 1032 1033 if (statep->ss_free < sizeof (dld_secobj_t)) { 1034 statep->ss_rc = ENOSPC; 1035 return (MH_WALK_TERMINATE); 1036 } 1037 if (ddi_copyout(sobjp, statep->ss_objp, sizeof (*sobjp), 1038 statep->ss_mode) != 0) { 1039 statep->ss_rc = EFAULT; 1040 return (MH_WALK_TERMINATE); 1041 } 1042 statep->ss_objp++; 1043 statep->ss_free -= sizeof (dld_secobj_t); 1044 statep->ss_count++; 1045 return (MH_WALK_CONTINUE); 1046 } 1047 1048 /* ARGSUSED */ 1049 static int 1050 drv_ioc_secobj_get(void *karg, intptr_t arg, int mode, cred_t *cred, int *rvalp) 1051 { 1052 dld_ioc_secobj_get_t *sgp = karg; 1053 dld_secobj_t *sobjp, *objp; 1054 int err; 1055 1056 sobjp = &sgp->sg_obj; 1057 if (sobjp->so_name[DLD_SECOBJ_NAME_MAX - 1] != '\0') 1058 return (EINVAL); 1059 1060 rw_enter(&drv_secobj_lock, RW_READER); 1061 if (sobjp->so_name[0] != '\0') { 1062 err = mod_hash_find(drv_secobj_hash, 1063 (mod_hash_key_t)sobjp->so_name, (mod_hash_val_t *)&objp); 1064 if (err != 0) { 1065 ASSERT(err == MH_ERR_NOTFOUND); 1066 rw_exit(&drv_secobj_lock); 1067 return (ENOENT); 1068 } 1069 bcopy(objp->so_val, sobjp->so_val, objp->so_len); 1070 sobjp->so_len = objp->so_len; 1071 sobjp->so_class = objp->so_class; 1072 sgp->sg_count = 1; 1073 } else { 1074 dld_secobj_state_t state; 1075 1076 state.ss_free = sgp->sg_size - sizeof (dld_ioc_secobj_get_t); 1077 state.ss_count = 0; 1078 state.ss_rc = 0; 1079 state.ss_mode = mode; 1080 state.ss_objp = (dld_secobj_t *)((uchar_t *)arg + 1081 sizeof (dld_ioc_secobj_get_t)); 1082 1083 mod_hash_walk(drv_secobj_hash, drv_secobj_walker, &state); 1084 if (state.ss_rc != 0) { 1085 rw_exit(&drv_secobj_lock); 1086 return (state.ss_rc); 1087 } 1088 sgp->sg_count = state.ss_count; 1089 } 1090 rw_exit(&drv_secobj_lock); 1091 return (0); 1092 } 1093 1094 /* ARGSUSED */ 1095 static int 1096 drv_ioc_secobj_unset(void *karg, intptr_t arg, int mode, cred_t *cred, 1097 int *rvalp) 1098 { 1099 dld_ioc_secobj_unset_t *sup = karg; 1100 dld_secobj_t *objp; 1101 mod_hash_val_t val; 1102 int err; 1103 1104 if (sup->su_name[DLD_SECOBJ_NAME_MAX - 1] != '\0') 1105 return (EINVAL); 1106 1107 rw_enter(&drv_secobj_lock, RW_WRITER); 1108 err = mod_hash_find(drv_secobj_hash, (mod_hash_key_t)sup->su_name, 1109 (mod_hash_val_t *)&objp); 1110 if (err != 0) { 1111 ASSERT(err == MH_ERR_NOTFOUND); 1112 rw_exit(&drv_secobj_lock); 1113 return (ENOENT); 1114 } 1115 VERIFY(mod_hash_remove(drv_secobj_hash, (mod_hash_key_t)sup->su_name, 1116 (mod_hash_val_t *)&val) == 0); 1117 ASSERT(objp == (dld_secobj_t *)val); 1118 1119 kmem_cache_free(drv_secobj_cachep, objp); 1120 rw_exit(&drv_secobj_lock); 1121 return (0); 1122 } 1123 1124 static int 1125 drv_check_policy(dld_ioc_info_t *info, cred_t *cred) 1126 { 1127 int i, err = 0; 1128 1129 for (i = 0; info->di_priv[i] != NULL && i < DLD_MAX_PRIV; i++) { 1130 if ((err = secpolicy_dld_ioctl(cred, info->di_priv[i], 1131 "dld ioctl")) != 0) { 1132 break; 1133 } 1134 } 1135 if (err == 0) 1136 return (0); 1137 1138 return (secpolicy_net_config(cred, B_FALSE)); 1139 } 1140 1141 static dld_ioc_info_t drv_ioc_list[] = { 1142 {DLDIOC_ATTR, DLDCOPYINOUT, sizeof (dld_ioc_attr_t), 1143 drv_ioc_attr, {NULL}}, 1144 {DLDIOC_PHYS_ATTR, DLDCOPYINOUT, sizeof (dld_ioc_phys_attr_t), 1145 drv_ioc_phys_attr, {NULL}}, 1146 {DLDIOC_SECOBJ_SET, DLDCOPYIN, sizeof (dld_ioc_secobj_set_t), 1147 drv_ioc_secobj_set, {PRIV_SYS_DL_CONFIG}}, 1148 {DLDIOC_SECOBJ_GET, DLDCOPYINOUT, sizeof (dld_ioc_secobj_get_t), 1149 drv_ioc_secobj_get, {PRIV_SYS_DL_CONFIG}}, 1150 {DLDIOC_SECOBJ_UNSET, DLDCOPYIN, sizeof (dld_ioc_secobj_unset_t), 1151 drv_ioc_secobj_unset, {PRIV_SYS_DL_CONFIG}}, 1152 {DLDIOC_DOORSERVER, DLDCOPYIN, sizeof (dld_ioc_door_t), 1153 drv_ioc_doorserver, {PRIV_SYS_DL_CONFIG}}, 1154 {DLDIOC_RENAME, DLDCOPYIN, sizeof (dld_ioc_rename_t), 1155 drv_ioc_rename, {PRIV_SYS_DL_CONFIG}}, 1156 {DLDIOC_MACADDRGET, DLDCOPYINOUT, sizeof (dld_ioc_macaddrget_t), 1157 drv_ioc_macaddrget, {PRIV_SYS_DL_CONFIG}}, 1158 {DLDIOC_ADDFLOW, DLDCOPYIN, sizeof (dld_ioc_addflow_t), 1159 drv_ioc_addflow, {PRIV_SYS_DL_CONFIG}}, 1160 {DLDIOC_REMOVEFLOW, DLDCOPYIN, sizeof (dld_ioc_removeflow_t), 1161 drv_ioc_removeflow, {PRIV_SYS_DL_CONFIG}}, 1162 {DLDIOC_MODIFYFLOW, DLDCOPYIN, sizeof (dld_ioc_modifyflow_t), 1163 drv_ioc_modifyflow, {PRIV_SYS_DL_CONFIG}}, 1164 {DLDIOC_WALKFLOW, DLDCOPYINOUT, sizeof (dld_ioc_walkflow_t), 1165 drv_ioc_walkflow, {NULL}}, 1166 {DLDIOC_USAGELOG, DLDCOPYIN, sizeof (dld_ioc_usagelog_t), 1167 drv_ioc_usagelog, {PRIV_SYS_DL_CONFIG}}, 1168 {DLDIOC_SETMACPROP, DLDCOPYIN, sizeof (dld_ioc_macprop_t), 1169 drv_ioc_setprop, {PRIV_SYS_DL_CONFIG}}, 1170 {DLDIOC_GETMACPROP, DLDCOPYIN, sizeof (dld_ioc_macprop_t), 1171 drv_ioc_getprop, {NULL}}, 1172 {DLDIOC_GETHWGRP, DLDCOPYINOUT, sizeof (dld_ioc_hwgrpget_t), 1173 drv_ioc_hwgrpget, {PRIV_SYS_DL_CONFIG}}, 1174 }; 1175 1176 typedef struct dld_ioc_modentry { 1177 uint16_t dim_modid; /* Top 16 bits of ioctl command */ 1178 char *dim_modname; /* Module to be loaded */ 1179 dld_ioc_info_t *dim_list; /* array of ioctl structures */ 1180 uint_t dim_count; /* number of elements in dim_list */ 1181 } dld_ioc_modentry_t; 1182 1183 /* 1184 * For all modules except for dld, dim_list and dim_count are assigned 1185 * when the modules register their ioctls in dld_ioc_register(). We 1186 * can statically initialize dld's ioctls in-line here; there's no 1187 * need for it to call dld_ioc_register() itself. 1188 */ 1189 static dld_ioc_modentry_t dld_ioc_modtable[] = { 1190 {DLD_IOC, "dld", drv_ioc_list, DLDIOCCNT(drv_ioc_list)}, 1191 {AGGR_IOC, "aggr", NULL, 0}, 1192 {VNIC_IOC, "vnic", NULL, 0} 1193 }; 1194 #define DLDIOC_CNT \ 1195 (sizeof (dld_ioc_modtable) / sizeof (dld_ioc_modentry_t)) 1196 1197 static dld_ioc_modentry_t * 1198 dld_ioc_findmod(uint16_t modid) 1199 { 1200 int i; 1201 1202 for (i = 0; i < DLDIOC_CNT; i++) { 1203 if (modid == dld_ioc_modtable[i].dim_modid) 1204 return (&dld_ioc_modtable[i]); 1205 } 1206 return (NULL); 1207 } 1208 1209 int 1210 dld_ioc_register(uint16_t modid, dld_ioc_info_t *list, uint_t count) 1211 { 1212 dld_ioc_modentry_t *dim = dld_ioc_findmod(modid); 1213 1214 if (dim == NULL) 1215 return (ENOENT); 1216 1217 dim->dim_list = list; 1218 dim->dim_count = count; 1219 return (0); 1220 } 1221 1222 void 1223 dld_ioc_unregister(uint16_t modid) 1224 { 1225 VERIFY(dld_ioc_register(modid, NULL, 0) == 0); 1226 } 1227 1228 /* 1229 * The general design with GLDv3 ioctls is that all ioctls issued 1230 * through /dev/dld go through this drv_ioctl() function. This 1231 * function handles all ioctls on behalf of modules listed in 1232 * dld_ioc_modtable. 1233 * 1234 * When an ioctl is received, this function looks for the associated 1235 * module-id-specific ioctl information using dld_ioc_findmod(). The 1236 * call to ddi_hold_devi_by_instance() on the associated device will 1237 * cause the kernel module responsible for the ioctl to be loaded if 1238 * it's not already loaded, which should result in that module calling 1239 * dld_ioc_register(), thereby filling in the dim_list containing the 1240 * details for the ioctl being processed. 1241 * 1242 * This function can then perform operations such as copyin() data and 1243 * do credential checks based on the registered ioctl information, 1244 * then issue the callback function di_func() registered by the 1245 * responsible module. Upon return, the appropriate copyout() 1246 * operation can be performed and the operation completes. 1247 */ 1248 /* ARGSUSED */ 1249 static int 1250 drv_ioctl(dev_t dev, int cmd, intptr_t arg, int mode, cred_t *cred, int *rvalp) 1251 { 1252 dld_ioc_modentry_t *dim; 1253 dld_ioc_info_t *info; 1254 dev_info_t *dip = NULL; 1255 void *buf = NULL; 1256 size_t sz; 1257 int i, err; 1258 1259 if ((dim = dld_ioc_findmod(DLD_IOC_MODID(cmd))) == NULL) 1260 return (ENOTSUP); 1261 1262 dip = ddi_hold_devi_by_instance(ddi_name_to_major(dim->dim_modname), 1263 0, 0); 1264 if (dip == NULL || dim->dim_list == NULL) { 1265 err = ENODEV; 1266 goto done; 1267 } 1268 1269 for (i = 0; i < dim->dim_count; i++) { 1270 if (cmd == dim->dim_list[i].di_cmd) 1271 break; 1272 } 1273 if (i == dim->dim_count) { 1274 err = ENOTSUP; 1275 goto done; 1276 } 1277 1278 info = &dim->dim_list[i]; 1279 if ((err = drv_check_policy(info, cred)) != 0) 1280 goto done; 1281 1282 sz = info->di_argsize; 1283 if ((buf = kmem_zalloc(sz, KM_NOSLEEP)) == NULL) { 1284 err = ENOMEM; 1285 goto done; 1286 } 1287 1288 if ((info->di_flags & DLDCOPYIN) && 1289 ddi_copyin((void *)arg, buf, sz, mode) != 0) { 1290 err = EFAULT; 1291 goto done; 1292 } 1293 1294 err = info->di_func(buf, arg, mode, cred, rvalp); 1295 1296 if ((info->di_flags & DLDCOPYOUT) && 1297 ddi_copyout(buf, (void *)arg, sz, mode) != 0 && err == 0) 1298 err = EFAULT; 1299 1300 done: 1301 if (buf != NULL) 1302 kmem_free(buf, sz); 1303 if (dip != NULL) 1304 ddi_release_devi(dip); 1305 return (err); 1306 } 1307