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