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