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/dld_ioc.h> 36 #include <sys/dls_impl.h> 37 #include <sys/softmac.h> 38 #include <sys/mac.h> 39 #include <sys/mac_ether.h> 40 #include <sys/mac_client.h> 41 #include <sys/mac_client_impl.h> 42 #include <sys/mac_client_priv.h> 43 #include <inet/common.h> 44 #include <sys/policy.h> 45 #include <sys/priv_names.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 * DLDIOC_ATTR 318 */ 319 /* ARGSUSED */ 320 static int 321 drv_ioc_attr(void *karg, intptr_t arg, int mode, cred_t *cred, int *rvalp) 322 { 323 dld_ioc_attr_t *diap = karg; 324 dls_dl_handle_t dlh; 325 dls_link_t *dlp; 326 int err; 327 mac_perim_handle_t mph; 328 329 if ((err = dls_devnet_hold_tmp(diap->dia_linkid, &dlh)) != 0) 330 return (err); 331 332 if ((err = mac_perim_enter_by_macname( 333 dls_devnet_mac(dlh), &mph)) != 0) { 334 dls_devnet_rele_tmp(dlh); 335 return (err); 336 } 337 338 if ((err = dls_link_hold(dls_devnet_mac(dlh), &dlp)) != 0) { 339 mac_perim_exit(mph); 340 dls_devnet_rele_tmp(dlh); 341 return (err); 342 } 343 344 mac_sdu_get(dlp->dl_mh, NULL, &diap->dia_max_sdu); 345 346 dls_link_rele(dlp); 347 mac_perim_exit(mph); 348 dls_devnet_rele_tmp(dlh); 349 350 return (0); 351 } 352 353 /* 354 * DLDIOC_PHYS_ATTR 355 */ 356 /* ARGSUSED */ 357 static int 358 drv_ioc_phys_attr(void *karg, intptr_t arg, int mode, cred_t *cred, int *rvalp) 359 { 360 dld_ioc_phys_attr_t *dipp = karg; 361 int err; 362 dls_dl_handle_t dlh; 363 dls_dev_handle_t ddh; 364 dev_t phydev; 365 366 /* 367 * Every physical link should have its physical dev_t kept in the 368 * daemon. If not, it is not a valid physical link. 369 */ 370 if (dls_mgmt_get_phydev(dipp->dip_linkid, &phydev) != 0) 371 return (EINVAL); 372 373 /* 374 * Although this is a valid physical link, it might already be removed 375 * by DR or during system shutdown. softmac_hold_device() would return 376 * ENOENT in this case. 377 */ 378 if ((err = softmac_hold_device(phydev, &ddh)) != 0) 379 return (err); 380 381 if (dls_devnet_hold_tmp(dipp->dip_linkid, &dlh) != 0) { 382 /* 383 * Although this is an active physical link, its link type is 384 * not supported by GLDv3, and therefore it does not have 385 * vanity naming support. 386 */ 387 dipp->dip_novanity = B_TRUE; 388 } else { 389 dipp->dip_novanity = B_FALSE; 390 dls_devnet_rele_tmp(dlh); 391 } 392 /* 393 * Get the physical device name from the major number and the instance 394 * number derived from phydev. 395 */ 396 (void) snprintf(dipp->dip_dev, MAXLINKNAMELEN, "%s%d", 397 ddi_major_to_name(getmajor(phydev)), getminor(phydev) - 1); 398 399 softmac_rele_device(ddh); 400 return (0); 401 } 402 403 /* ARGSUSED */ 404 static int 405 drv_ioc_hwgrpget(void *karg, intptr_t arg, int mode, cred_t *cred, int *rvalp) 406 { 407 dld_ioc_hwgrpget_t *hwgrpp = karg; 408 dld_hwgrpinfo_t hwgrp, *hip; 409 mac_handle_t mh = NULL; 410 int i, err, grpnum; 411 uint_t bytes_left; 412 413 hwgrpp->dih_n_groups = 0; 414 err = mac_open_by_linkid(hwgrpp->dih_linkid, &mh); 415 if (err != 0) 416 goto done; 417 418 hip = (dld_hwgrpinfo_t *) 419 ((uchar_t *)arg + sizeof (dld_ioc_hwgrpget_t)); 420 bytes_left = hwgrpp->dih_size; 421 grpnum = mac_hwgrp_num(mh); 422 for (i = 0; i < grpnum; i++) { 423 if (sizeof (dld_hwgrpinfo_t) > bytes_left) { 424 err = ENOSPC; 425 goto done; 426 } 427 428 bzero(&hwgrp, sizeof (hwgrp)); 429 bcopy(mac_name(mh), hwgrp.dhi_link_name, 430 sizeof (hwgrp.dhi_link_name)); 431 mac_get_hwgrp_info(mh, i, &hwgrp.dhi_grp_num, 432 &hwgrp.dhi_n_rings, &hwgrp.dhi_grp_type, 433 &hwgrp.dhi_n_clnts, hwgrp.dhi_clnts); 434 if (copyout(&hwgrp, hip, sizeof (hwgrp)) != 0) { 435 err = EFAULT; 436 goto done; 437 } 438 439 hip++; 440 bytes_left -= sizeof (dld_hwgrpinfo_t); 441 } 442 443 done: 444 if (mh != NULL) 445 dld_mac_close(mh); 446 if (err == 0) 447 hwgrpp->dih_n_groups = grpnum; 448 return (err); 449 } 450 451 /* ARGSUSED */ 452 static int 453 drv_ioc_macaddrget(void *karg, intptr_t arg, int mode, cred_t *cred, int *rvalp) 454 { 455 dld_ioc_macaddrget_t *magp = karg; 456 dld_macaddrinfo_t mai, *maip; 457 mac_handle_t mh = NULL; 458 int i, err; 459 uint_t bytes_left; 460 boolean_t is_used; 461 462 magp->dig_count = 0; 463 err = mac_open_by_linkid(magp->dig_linkid, &mh); 464 if (err != 0) 465 goto done; 466 467 maip = (dld_macaddrinfo_t *) 468 ((uchar_t *)arg + sizeof (dld_ioc_macaddrget_t)); 469 bytes_left = magp->dig_size; 470 471 for (i = 0; i < mac_addr_factory_num(mh) + 1; i++) { 472 if (sizeof (dld_macaddrinfo_t) > bytes_left) { 473 err = ENOSPC; 474 goto done; 475 } 476 477 bzero(&mai, sizeof (mai)); 478 479 if (i == 0) { 480 /* primary MAC address */ 481 mac_unicast_primary_get(mh, mai.dmi_addr); 482 mai.dmi_addrlen = mac_addr_len(mh); 483 mac_unicast_primary_info(mh, mai.dmi_client_name, 484 &is_used); 485 } else { 486 /* factory MAC address slot */ 487 mac_addr_factory_value(mh, i, mai.dmi_addr, 488 &mai.dmi_addrlen, mai.dmi_client_name, &is_used); 489 } 490 491 mai.dmi_slot = i; 492 if (is_used) 493 mai.dmi_flags |= DLDIOCMACADDR_USED; 494 495 if (copyout(&mai, maip, sizeof (mai)) != 0) { 496 err = EFAULT; 497 goto done; 498 } 499 500 maip++; 501 bytes_left -= sizeof (dld_macaddrinfo_t); 502 } 503 504 done: 505 if (mh != NULL) 506 dld_mac_close(mh); 507 if (err == 0) 508 magp->dig_count = mac_addr_factory_num(mh) + 1; 509 return (err); 510 } 511 512 /* 513 * DLDIOC_SET/GETPROP 514 */ 515 static int 516 drv_ioc_prop_common(dld_ioc_macprop_t *prop, intptr_t arg, boolean_t set, 517 int mode) 518 { 519 int err = EINVAL; 520 dls_dl_handle_t dlh = NULL; 521 dls_link_t *dlp = NULL; 522 mac_perim_handle_t mph = NULL; 523 mac_prop_t macprop; 524 dld_ioc_macprop_t *kprop; 525 datalink_id_t linkid; 526 uint_t dsize; 527 528 529 /* 530 * We only use pr_valsize from prop, as the caller only did a 531 * copyin() for sizeof (dld_ioc_prop_t), which doesn't cover 532 * the property data. We copyin the full dld_ioc_prop_t 533 * including the data into kprop down below. 534 */ 535 dsize = sizeof (dld_ioc_macprop_t) + prop->pr_valsize - 1; 536 if (dsize < prop->pr_valsize) 537 return (EINVAL); 538 539 /* 540 * The property data is variable size, so we need to allocate 541 * a buffer for kernel use as this data was not part of the 542 * prop allocation and copyin() done by the framework. 543 */ 544 if ((kprop = kmem_alloc(dsize, KM_NOSLEEP)) == NULL) 545 return (ENOMEM); 546 547 if (ddi_copyin((void *)arg, kprop, dsize, mode) != 0) { 548 err = EFAULT; 549 goto done; 550 } 551 552 linkid = kprop->pr_linkid; 553 if ((err = dls_devnet_hold_tmp(linkid, &dlh)) != 0) 554 goto done; 555 556 if ((err = mac_perim_enter_by_macname(dls_devnet_mac(dlh), 557 &mph)) != 0) { 558 goto done; 559 } 560 561 if ((err = dls_link_hold(dls_devnet_mac(dlh), &dlp)) != 0) 562 goto done; 563 564 switch (kprop->pr_num) { 565 case MAC_PROP_ZONE: 566 if (set) { 567 dld_ioc_zid_t *dzp = (dld_ioc_zid_t *)kprop->pr_val; 568 569 err = dls_devnet_setzid(dzp->diz_link, dzp->diz_zid); 570 } else { 571 kprop->pr_perm_flags = MAC_PROP_PERM_RW; 572 err = dls_devnet_getzid(linkid, 573 (zoneid_t *)kprop->pr_val); 574 } 575 break; 576 case MAC_PROP_AUTOPUSH: { 577 struct dlautopush *dlap = (struct dlautopush *)kprop->pr_val; 578 579 if (set) { 580 if (kprop->pr_valsize != 0) 581 err = drv_ioc_setap(linkid, dlap); 582 else 583 err = drv_ioc_clrap(linkid); 584 } else { 585 kprop->pr_perm_flags = MAC_PROP_PERM_RW; 586 err = drv_ioc_getap(linkid, dlap); 587 } 588 break; 589 } 590 case MAC_PROP_TAGMODE: 591 if (set) { 592 link_tagmode_t mode = *(link_tagmode_t *)kprop->pr_val; 593 594 if (mode != LINK_TAGMODE_VLANONLY && 595 mode != LINK_TAGMODE_NORMAL) { 596 err = EINVAL; 597 } else { 598 dlp->dl_tagmode = mode; 599 err = 0; 600 } 601 } else { 602 *(link_tagmode_t *)kprop->pr_val = dlp->dl_tagmode; 603 kprop->pr_perm_flags = MAC_PROP_PERM_RW; 604 err = 0; 605 } 606 break; 607 default: 608 macprop.mp_name = kprop->pr_name; 609 macprop.mp_id = kprop->pr_num; 610 macprop.mp_flags = kprop->pr_flags; 611 612 if (set) { 613 err = mac_set_prop(dlp->dl_mh, &macprop, kprop->pr_val, 614 kprop->pr_valsize); 615 } else { 616 kprop->pr_perm_flags = MAC_PROP_PERM_RW; 617 err = mac_get_prop(dlp->dl_mh, &macprop, kprop->pr_val, 618 kprop->pr_valsize, &kprop->pr_perm_flags); 619 } 620 } 621 622 done: 623 if (!set && 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