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