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 2008 Sun Microsystems, Inc. All rights reserved. 23 * Use is subject to license terms. 24 */ 25 26 #pragma ident "%Z%%M% %I% %E% SMI" 27 28 /* 29 * Data-Link Driver 30 */ 31 32 #include <sys/conf.h> 33 #include <sys/mkdev.h> 34 #include <sys/modctl.h> 35 #include <sys/stat.h> 36 #include <sys/strsun.h> 37 #include <sys/vlan.h> 38 #include <sys/dld.h> 39 #include <sys/dld_impl.h> 40 #include <sys/dls_impl.h> 41 #include <sys/softmac.h> 42 #include <sys/vlan.h> 43 #include <inet/common.h> 44 45 /* 46 * dld control node state, one per open control node session. 47 */ 48 typedef struct dld_ctl_str_s { 49 minor_t cs_minor; 50 queue_t *cs_wq; 51 } dld_ctl_str_t; 52 53 static void drv_init(void); 54 static int drv_fini(void); 55 56 static int drv_getinfo(dev_info_t *, ddi_info_cmd_t, void *, void **); 57 static int drv_attach(dev_info_t *, ddi_attach_cmd_t); 58 static int drv_detach(dev_info_t *, ddi_detach_cmd_t); 59 60 /* 61 * Secure objects declarations 62 */ 63 #define SECOBJ_WEP_HASHSZ 67 64 static krwlock_t drv_secobj_lock; 65 static kmem_cache_t *drv_secobj_cachep; 66 static mod_hash_t *drv_secobj_hash; 67 static void drv_secobj_init(void); 68 static void drv_secobj_fini(void); 69 static void drv_ioc_secobj_set(dld_ctl_str_t *, mblk_t *); 70 static void drv_ioc_secobj_get(dld_ctl_str_t *, mblk_t *); 71 static void drv_ioc_secobj_unset(dld_ctl_str_t *, mblk_t *); 72 73 /* 74 * The following entry points are private to dld and are used for control 75 * operations only. The entry points exported to mac drivers are defined 76 * in dld_str.c. Refer to the comment on top of dld_str.c for details. 77 */ 78 static int drv_open(queue_t *, dev_t *, int, int, cred_t *); 79 static int drv_close(queue_t *); 80 81 static void drv_uw_put(queue_t *, mblk_t *); 82 static void drv_uw_srv(queue_t *); 83 84 dev_info_t *dld_dip; /* dev_info_t for the driver */ 85 uint32_t dld_opt = 0; /* Global options */ 86 static vmem_t *dld_ctl_vmem; /* for control minor numbers */ 87 88 #define NAUTOPUSH 32 89 static mod_hash_t *dld_ap_hashp; 90 static krwlock_t dld_ap_hash_lock; 91 92 static struct module_info drv_info = { 93 0, /* mi_idnum */ 94 DLD_DRIVER_NAME, /* mi_idname */ 95 0, /* mi_minpsz */ 96 (64 * 1024), /* mi_maxpsz */ 97 1, /* mi_hiwat */ 98 0 /* mi_lowat */ 99 }; 100 101 static struct qinit drv_ur_init = { 102 NULL, /* qi_putp */ 103 NULL, /* qi_srvp */ 104 drv_open, /* qi_qopen */ 105 drv_close, /* qi_qclose */ 106 NULL, /* qi_qadmin */ 107 &drv_info, /* qi_minfo */ 108 NULL /* qi_mstat */ 109 }; 110 111 static struct qinit drv_uw_init = { 112 (pfi_t)drv_uw_put, /* qi_putp */ 113 (pfi_t)drv_uw_srv, /* qi_srvp */ 114 NULL, /* qi_qopen */ 115 NULL, /* qi_qclose */ 116 NULL, /* qi_qadmin */ 117 &drv_info, /* qi_minfo */ 118 NULL /* qi_mstat */ 119 }; 120 121 static struct streamtab drv_stream = { 122 &drv_ur_init, /* st_rdinit */ 123 &drv_uw_init, /* st_wrinit */ 124 NULL, /* st_muxrinit */ 125 NULL /* st_muxwinit */ 126 }; 127 128 DDI_DEFINE_STREAM_OPS(drv_ops, nulldev, nulldev, drv_attach, drv_detach, 129 nodev, drv_getinfo, D_MP, &drv_stream); 130 131 /* 132 * Module linkage information for the kernel. 133 */ 134 135 extern struct mod_ops mod_driverops; 136 137 static struct modldrv drv_modldrv = { 138 &mod_driverops, 139 DLD_INFO, 140 &drv_ops 141 }; 142 143 static struct modlinkage drv_modlinkage = { 144 MODREV_1, 145 &drv_modldrv, 146 NULL 147 }; 148 149 int 150 _init(void) 151 { 152 int err; 153 154 drv_init(); 155 156 if ((err = mod_install(&drv_modlinkage)) != 0) 157 return (err); 158 159 return (0); 160 } 161 162 int 163 _fini(void) 164 { 165 int err; 166 167 if ((err = mod_remove(&drv_modlinkage)) != 0) 168 return (err); 169 170 if (drv_fini() != 0) { 171 (void) mod_install(&drv_modlinkage); 172 return (DDI_FAILURE); 173 } 174 175 return (err); 176 } 177 178 int 179 _info(struct modinfo *modinfop) 180 { 181 return (mod_info(&drv_modlinkage, modinfop)); 182 } 183 184 /* 185 * Initialize component modules. 186 */ 187 static void 188 drv_init(void) 189 { 190 dld_ctl_vmem = vmem_create("dld_ctl", (void *)1, MAXMIN, 1, 191 NULL, NULL, NULL, 1, VM_SLEEP | VMC_IDENTIFIER); 192 drv_secobj_init(); 193 dld_str_init(); 194 /* 195 * Create a hash table for autopush configuration. 196 */ 197 dld_ap_hashp = mod_hash_create_idhash("dld_autopush_hash", 198 NAUTOPUSH, mod_hash_null_valdtor); 199 200 ASSERT(dld_ap_hashp != NULL); 201 rw_init(&dld_ap_hash_lock, NULL, RW_DRIVER, NULL); 202 } 203 204 /* ARGSUSED */ 205 static uint_t 206 drv_ap_exist(mod_hash_key_t key, mod_hash_val_t *val, void *arg) 207 { 208 boolean_t *pexist = arg; 209 210 *pexist = B_TRUE; 211 return (MH_WALK_TERMINATE); 212 } 213 214 static int 215 drv_fini(void) 216 { 217 int err; 218 boolean_t exist = B_FALSE; 219 220 rw_enter(&dld_ap_hash_lock, RW_READER); 221 mod_hash_walk(dld_ap_hashp, drv_ap_exist, &exist); 222 rw_exit(&dld_ap_hash_lock); 223 224 if (exist) 225 return (EBUSY); 226 227 if ((err = dld_str_fini()) != 0) 228 return (err); 229 230 drv_secobj_fini(); 231 vmem_destroy(dld_ctl_vmem); 232 mod_hash_destroy_idhash(dld_ap_hashp); 233 rw_destroy(&dld_ap_hash_lock); 234 return (0); 235 } 236 237 /* 238 * devo_getinfo: getinfo(9e) 239 */ 240 /*ARGSUSED*/ 241 static int 242 drv_getinfo(dev_info_t *dip, ddi_info_cmd_t cmd, void *arg, void **resp) 243 { 244 if (dld_dip == NULL) 245 return (DDI_FAILURE); 246 247 switch (cmd) { 248 case DDI_INFO_DEVT2INSTANCE: 249 *resp = (void *)0; 250 break; 251 case DDI_INFO_DEVT2DEVINFO: 252 *resp = (void *)dld_dip; 253 break; 254 default: 255 return (DDI_FAILURE); 256 } 257 258 return (DDI_SUCCESS); 259 } 260 261 /* 262 * Check properties to set options. (See dld.h for property definitions). 263 */ 264 static void 265 drv_set_opt(dev_info_t *dip) 266 { 267 if (ddi_prop_get_int(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS, 268 DLD_PROP_NO_FASTPATH, 0) != 0) { 269 dld_opt |= DLD_OPT_NO_FASTPATH; 270 } 271 272 if (ddi_prop_get_int(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS, 273 DLD_PROP_NO_POLL, 0) != 0) { 274 dld_opt |= DLD_OPT_NO_POLL; 275 } 276 277 if (ddi_prop_get_int(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS, 278 DLD_PROP_NO_ZEROCOPY, 0) != 0) { 279 dld_opt |= DLD_OPT_NO_ZEROCOPY; 280 } 281 282 if (ddi_prop_get_int(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS, 283 DLD_PROP_NO_SOFTRING, 0) != 0) { 284 dld_opt |= DLD_OPT_NO_SOFTRING; 285 } 286 } 287 288 /* 289 * devo_attach: attach(9e) 290 */ 291 static int 292 drv_attach(dev_info_t *dip, ddi_attach_cmd_t cmd) 293 { 294 if (cmd != DDI_ATTACH) 295 return (DDI_FAILURE); 296 297 ASSERT(ddi_get_instance(dip) == 0); 298 299 drv_set_opt(dip); 300 301 /* 302 * Create control node. DLPI provider nodes will be created on demand. 303 */ 304 if (ddi_create_minor_node(dip, DLD_CONTROL_MINOR_NAME, S_IFCHR, 305 DLD_CONTROL_MINOR, DDI_PSEUDO, 0) != DDI_SUCCESS) 306 return (DDI_FAILURE); 307 308 dld_dip = dip; 309 310 /* 311 * Log the fact that the driver is now attached. 312 */ 313 ddi_report_dev(dip); 314 return (DDI_SUCCESS); 315 } 316 317 /* 318 * devo_detach: detach(9e) 319 */ 320 static int 321 drv_detach(dev_info_t *dip, ddi_detach_cmd_t cmd) 322 { 323 if (cmd != DDI_DETACH) 324 return (DDI_FAILURE); 325 326 ASSERT(dld_dip == dip); 327 328 /* 329 * Remove the control node. 330 */ 331 ddi_remove_minor_node(dip, DLD_CONTROL_MINOR_NAME); 332 dld_dip = NULL; 333 334 return (DDI_SUCCESS); 335 } 336 337 /* 338 * dld control node open procedure. 339 */ 340 /*ARGSUSED*/ 341 static int 342 drv_open(queue_t *rq, dev_t *devp, int flag, int sflag, cred_t *credp) 343 { 344 dld_ctl_str_t *ctls; 345 minor_t minor; 346 queue_t *oq = OTHERQ(rq); 347 348 if (sflag == MODOPEN) 349 return (ENOTSUP); 350 351 /* 352 * This is a cloning driver and therefore each queue should only 353 * ever get opened once. 354 */ 355 if (rq->q_ptr != NULL) 356 return (EBUSY); 357 358 minor = (minor_t)(uintptr_t)vmem_alloc(dld_ctl_vmem, 1, VM_NOSLEEP); 359 if (minor == 0) 360 return (ENOMEM); 361 362 ctls = kmem_zalloc(sizeof (dld_ctl_str_t), KM_NOSLEEP); 363 if (ctls == NULL) { 364 vmem_free(dld_ctl_vmem, (void *)(uintptr_t)minor, 1); 365 return (ENOMEM); 366 } 367 368 ctls->cs_minor = minor; 369 ctls->cs_wq = WR(rq); 370 371 rq->q_ptr = ctls; 372 oq->q_ptr = ctls; 373 374 /* 375 * Enable the queue srv(9e) routine. 376 */ 377 qprocson(rq); 378 379 /* 380 * Construct a cloned dev_t to hand back. 381 */ 382 *devp = makedevice(getmajor(*devp), ctls->cs_minor); 383 return (0); 384 } 385 386 /* 387 * dld control node close procedure. 388 */ 389 static int 390 drv_close(queue_t *rq) 391 { 392 dld_ctl_str_t *ctls; 393 394 ctls = rq->q_ptr; 395 ASSERT(ctls != NULL); 396 397 /* 398 * Disable the queue srv(9e) routine. 399 */ 400 qprocsoff(rq); 401 402 vmem_free(dld_ctl_vmem, (void *)(uintptr_t)ctls->cs_minor, 1); 403 404 kmem_free(ctls, sizeof (dld_ctl_str_t)); 405 406 return (0); 407 } 408 409 /* 410 * DLDIOC_ATTR 411 */ 412 static void 413 drv_ioc_attr(dld_ctl_str_t *ctls, mblk_t *mp) 414 { 415 dld_ioc_attr_t *diap; 416 dls_dl_handle_t dlh; 417 dls_vlan_t *dvp; 418 int err; 419 queue_t *q = ctls->cs_wq; 420 421 if ((err = miocpullup(mp, sizeof (dld_ioc_attr_t))) != 0) 422 goto failed; 423 424 diap = (dld_ioc_attr_t *)mp->b_cont->b_rptr; 425 426 if ((err = dls_devnet_hold_tmp(diap->dia_linkid, &dlh)) != 0) 427 goto failed; 428 429 if ((err = dls_vlan_hold(dls_devnet_mac(dlh), 430 dls_devnet_vid(dlh), &dvp, B_FALSE, B_FALSE)) != 0) { 431 dls_devnet_rele_tmp(dlh); 432 goto failed; 433 } 434 mac_sdu_get(dvp->dv_dlp->dl_mh, NULL, &diap->dia_max_sdu); 435 436 dls_vlan_rele(dvp); 437 dls_devnet_rele_tmp(dlh); 438 439 miocack(q, mp, sizeof (dld_ioc_attr_t), 0); 440 return; 441 442 failed: 443 ASSERT(err != 0); 444 miocnak(q, mp, 0, err); 445 } 446 447 /* 448 * DLDIOC_PHYS_ATTR 449 */ 450 static void 451 drv_ioc_phys_attr(dld_ctl_str_t *ctls, mblk_t *mp) 452 { 453 dld_ioc_phys_attr_t *dipp; 454 int err; 455 dls_dl_handle_t dlh; 456 dls_dev_handle_t ddh; 457 dev_t phydev; 458 queue_t *q = ctls->cs_wq; 459 460 if ((err = miocpullup(mp, sizeof (dld_ioc_phys_attr_t))) != 0) 461 goto failed; 462 463 dipp = (dld_ioc_phys_attr_t *)mp->b_cont->b_rptr; 464 465 /* 466 * Every physical link should have its physical dev_t kept in the 467 * daemon. If not, it is not a valid physical link. 468 */ 469 if (dls_mgmt_get_phydev(dipp->dip_linkid, &phydev) != 0) { 470 err = EINVAL; 471 goto failed; 472 } 473 474 /* 475 * Although this is a valid physical link, it might already be removed 476 * by DR or during system shutdown. softmac_hold_device() would return 477 * ENOENT in this case. 478 */ 479 if ((err = softmac_hold_device(phydev, &ddh)) != 0) 480 goto failed; 481 482 if (dls_devnet_hold_tmp(dipp->dip_linkid, &dlh) != 0) { 483 /* 484 * Although this is an active physical link, its link type is 485 * not supported by GLDv3, and therefore it does not have 486 * vanity naming support. 487 */ 488 dipp->dip_novanity = B_TRUE; 489 } else { 490 dipp->dip_novanity = B_FALSE; 491 dls_devnet_rele_tmp(dlh); 492 } 493 /* 494 * Get the physical device name from the major number and the instance 495 * number derived from phydev. 496 */ 497 (void) snprintf(dipp->dip_dev, MAXLINKNAMELEN, "%s%d", 498 ddi_major_to_name(getmajor(phydev)), getminor(phydev) - 1); 499 500 softmac_rele_device(ddh); 501 502 miocack(q, mp, sizeof (dld_ioc_phys_attr_t), 0); 503 return; 504 505 failed: 506 miocnak(q, mp, 0, err); 507 } 508 509 /* 510 * DLDIOCSETPROP 511 */ 512 static void 513 drv_ioc_prop_common(dld_ctl_str_t *ctls, mblk_t *mp, boolean_t set) 514 { 515 int err = EINVAL, dsize; 516 queue_t *q = ctls->cs_wq; 517 dld_ioc_prop_t *dipp; 518 dls_dl_handle_t dlh; 519 dls_vlan_t *dvp; 520 datalink_id_t linkid; 521 mac_prop_t macprop; 522 523 if ((err = miocpullup(mp, sizeof (dld_ioc_prop_t))) != 0) 524 goto done; 525 dipp = (dld_ioc_prop_t *)mp->b_cont->b_rptr; 526 527 dsize = sizeof (dld_ioc_prop_t) + dipp->pr_valsize - 1; 528 if ((err = miocpullup(mp, dsize)) != 0) 529 goto done; 530 dipp = (dld_ioc_prop_t *)mp->b_cont->b_rptr; 531 532 linkid = dipp->pr_linkid; 533 534 if ((err = dls_devnet_hold_tmp(linkid, &dlh)) != 0) 535 goto done; 536 537 if ((err = dls_vlan_hold(dls_devnet_mac(dlh), 538 dls_devnet_vid(dlh), &dvp, B_FALSE, B_FALSE)) != 0) { 539 dls_devnet_rele_tmp(dlh); 540 goto done; 541 } 542 543 macprop.mp_name = dipp->pr_name; 544 macprop.mp_id = dipp->pr_num; 545 546 if (set) 547 err = mac_set_prop(dvp->dv_dlp->dl_mh, &macprop, 548 dipp->pr_val, dipp->pr_valsize); 549 else 550 err = mac_get_prop(dvp->dv_dlp->dl_mh, &macprop, 551 dipp->pr_val, dipp->pr_valsize); 552 553 dls_vlan_rele(dvp); 554 dls_devnet_rele_tmp(dlh); 555 done: 556 if (err == 0) 557 miocack(q, mp, dsize, 0); 558 else 559 miocnak(q, mp, 0, err); 560 } 561 562 static void 563 drv_ioc_setprop(dld_ctl_str_t *ctls, mblk_t *mp) 564 { 565 drv_ioc_prop_common(ctls, mp, B_TRUE); 566 } 567 568 static void 569 drv_ioc_getprop(dld_ctl_str_t *ctls, mblk_t *mp) 570 { 571 drv_ioc_prop_common(ctls, mp, B_FALSE); 572 } 573 574 /* 575 * DLDIOC_CREATE_VLAN 576 */ 577 static void 578 drv_ioc_create_vlan(dld_ctl_str_t *ctls, mblk_t *mp) 579 { 580 dld_ioc_create_vlan_t *dicp; 581 int err; 582 queue_t *q = ctls->cs_wq; 583 584 if ((err = miocpullup(mp, sizeof (dld_ioc_create_vlan_t))) != 0) 585 goto failed; 586 587 dicp = (dld_ioc_create_vlan_t *)mp->b_cont->b_rptr; 588 589 if ((err = dls_devnet_create_vlan(dicp->dic_vlanid, 590 dicp->dic_linkid, dicp->dic_vid, dicp->dic_force)) != 0) { 591 goto failed; 592 } 593 594 miocack(q, mp, 0, 0); 595 return; 596 597 failed: 598 miocnak(q, mp, 0, err); 599 } 600 601 /* 602 * DLDIOC_DELETE_VLAN 603 */ 604 static void 605 drv_ioc_delete_vlan(dld_ctl_str_t *ctls, mblk_t *mp) 606 { 607 dld_ioc_delete_vlan_t *didp; 608 int err; 609 queue_t *q = ctls->cs_wq; 610 611 if ((err = miocpullup(mp, sizeof (dld_ioc_delete_vlan_t))) != 0) 612 goto done; 613 614 didp = (dld_ioc_delete_vlan_t *)mp->b_cont->b_rptr; 615 err = dls_devnet_destroy_vlan(didp->did_linkid); 616 617 done: 618 if (err == 0) 619 miocack(q, mp, 0, 0); 620 else 621 miocnak(q, mp, 0, err); 622 } 623 624 /* 625 * DLDIOC_VLAN_ATTR 626 */ 627 static void 628 drv_ioc_vlan_attr(dld_ctl_str_t *ctls, mblk_t *mp) 629 { 630 dld_ioc_vlan_attr_t *divp; 631 dls_dl_handle_t dlh; 632 uint16_t vid; 633 dls_vlan_t *dvp; 634 int err; 635 queue_t *q = ctls->cs_wq; 636 637 if ((err = miocpullup(mp, sizeof (dld_ioc_vlan_attr_t))) != 0) 638 goto failed; 639 640 divp = (dld_ioc_vlan_attr_t *)mp->b_cont->b_rptr; 641 642 /* 643 * Hold this link to prevent it from being deleted. 644 */ 645 err = dls_devnet_hold_tmp(divp->div_vlanid, &dlh); 646 if (err != 0) 647 goto failed; 648 649 if ((vid = dls_devnet_vid(dlh)) == VLAN_ID_NONE) { 650 dls_devnet_rele_tmp(dlh); 651 err = EINVAL; 652 goto failed; 653 } 654 655 err = dls_vlan_hold(dls_devnet_mac(dlh), vid, &dvp, B_FALSE, B_FALSE); 656 if (err != 0) { 657 dls_devnet_rele_tmp(dlh); 658 err = EINVAL; 659 goto failed; 660 } 661 662 divp->div_linkid = dls_devnet_linkid(dlh); 663 divp->div_implicit = !dls_devnet_is_explicit(dlh); 664 divp->div_vid = vid; 665 divp->div_force = dvp->dv_force; 666 667 dls_vlan_rele(dvp); 668 dls_devnet_rele_tmp(dlh); 669 miocack(q, mp, sizeof (dld_ioc_vlan_attr_t), 0); 670 return; 671 672 failed: 673 miocnak(q, mp, 0, err); 674 } 675 676 /* 677 * DLDIOC_RENAME. 678 * 679 * This function handles two cases of link renaming. See more in comments above 680 * dls_datalink_rename(). 681 */ 682 static void 683 drv_ioc_rename(dld_ctl_str_t *ctls, mblk_t *mp) 684 { 685 dld_ioc_rename_t *dir; 686 mod_hash_key_t key; 687 mod_hash_val_t val; 688 int err; 689 queue_t *q = ctls->cs_wq; 690 691 if ((err = miocpullup(mp, sizeof (dld_ioc_rename_t))) != 0) 692 goto done; 693 694 dir = (dld_ioc_rename_t *)mp->b_cont->b_rptr; 695 if ((err = dls_devnet_rename(dir->dir_linkid1, dir->dir_linkid2, 696 dir->dir_link)) != 0) { 697 goto done; 698 } 699 700 if (dir->dir_linkid2 == DATALINK_INVALID_LINKID) 701 goto done; 702 703 /* 704 * if dir_linkid2 is not DATALINK_INVALID_LINKID, it means this 705 * renaming request is to rename a valid physical link (dir_linkid1) 706 * to a "removed" physical link (dir_linkid2, which is removed by DR 707 * or during system shutdown). In this case, the link (specified by 708 * dir_linkid1) would inherit all the configuration of dir_linkid2, 709 * and dir_linkid1 and its configuration would be lost. 710 * 711 * Remove per-link autopush configuration of dir_linkid1 in this case. 712 */ 713 key = (mod_hash_key_t)(uintptr_t)dir->dir_linkid1; 714 rw_enter(&dld_ap_hash_lock, RW_WRITER); 715 if (mod_hash_find(dld_ap_hashp, key, &val) != 0) { 716 rw_exit(&dld_ap_hash_lock); 717 goto done; 718 } 719 720 VERIFY(mod_hash_remove(dld_ap_hashp, key, &val) == 0); 721 kmem_free(val, sizeof (dld_ap_t)); 722 rw_exit(&dld_ap_hash_lock); 723 724 done: 725 if (err == 0) 726 miocack(q, mp, 0, 0); 727 else 728 miocnak(q, mp, 0, err); 729 } 730 731 /* 732 * DLDIOC_SETAUTOPUSH 733 */ 734 static void 735 drv_ioc_setap(dld_ctl_str_t *ctls, mblk_t *mp) 736 { 737 dld_ioc_ap_t *diap; 738 dld_ap_t *dap; 739 int i, err; 740 queue_t *q = ctls->cs_wq; 741 mod_hash_key_t key; 742 743 if ((err = miocpullup(mp, sizeof (dld_ioc_ap_t))) != 0) 744 goto failed; 745 746 diap = (dld_ioc_ap_t *)mp->b_cont->b_rptr; 747 if (diap->dia_npush == 0 || diap->dia_npush > MAXAPUSH) { 748 err = EINVAL; 749 goto failed; 750 } 751 752 /* 753 * Validate that the specified list of modules exist. 754 */ 755 for (i = 0; i < diap->dia_npush; i++) { 756 if (fmodsw_find(diap->dia_aplist[i], FMODSW_LOAD) == NULL) { 757 err = EINVAL; 758 goto failed; 759 } 760 } 761 762 key = (mod_hash_key_t)(uintptr_t)diap->dia_linkid; 763 764 rw_enter(&dld_ap_hash_lock, RW_WRITER); 765 if (mod_hash_find(dld_ap_hashp, key, (mod_hash_val_t *)&dap) != 0) { 766 dap = kmem_zalloc(sizeof (dld_ap_t), KM_NOSLEEP); 767 if (dap == NULL) { 768 rw_exit(&dld_ap_hash_lock); 769 err = ENOMEM; 770 goto failed; 771 } 772 773 dap->da_linkid = diap->dia_linkid; 774 err = mod_hash_insert(dld_ap_hashp, key, (mod_hash_val_t)dap); 775 ASSERT(err == 0); 776 } 777 778 /* 779 * Update the configuration. 780 */ 781 dap->da_anchor = diap->dia_anchor; 782 dap->da_npush = diap->dia_npush; 783 for (i = 0; i < diap->dia_npush; i++) { 784 (void) strlcpy(dap->da_aplist[i], diap->dia_aplist[i], 785 FMNAMESZ + 1); 786 } 787 rw_exit(&dld_ap_hash_lock); 788 789 miocack(q, mp, 0, 0); 790 return; 791 792 failed: 793 miocnak(q, mp, 0, err); 794 } 795 796 /* 797 * DLDIOC_GETAUTOPUSH 798 */ 799 static void 800 drv_ioc_getap(dld_ctl_str_t *ctls, mblk_t *mp) 801 { 802 dld_ioc_ap_t *diap; 803 dld_ap_t *dap; 804 int i, err; 805 queue_t *q = ctls->cs_wq; 806 807 if ((err = miocpullup(mp, sizeof (dld_ioc_ap_t))) != 0) 808 goto failed; 809 810 diap = (dld_ioc_ap_t *)mp->b_cont->b_rptr; 811 812 rw_enter(&dld_ap_hash_lock, RW_READER); 813 if (mod_hash_find(dld_ap_hashp, 814 (mod_hash_key_t)(uintptr_t)diap->dia_linkid, 815 (mod_hash_val_t *)&dap) != 0) { 816 err = ENOENT; 817 rw_exit(&dld_ap_hash_lock); 818 goto failed; 819 } 820 821 /* 822 * Retrieve the configuration. 823 */ 824 diap->dia_anchor = dap->da_anchor; 825 diap->dia_npush = dap->da_npush; 826 for (i = 0; i < dap->da_npush; i++) { 827 (void) strlcpy(diap->dia_aplist[i], dap->da_aplist[i], 828 FMNAMESZ + 1); 829 } 830 rw_exit(&dld_ap_hash_lock); 831 832 miocack(q, mp, sizeof (dld_ioc_ap_t), 0); 833 return; 834 835 failed: 836 miocnak(q, mp, 0, err); 837 } 838 839 /* 840 * DLDIOC_CLRAUTOPUSH 841 */ 842 static void 843 drv_ioc_clrap(dld_ctl_str_t *ctls, mblk_t *mp) 844 { 845 dld_ioc_ap_t *diap; 846 mod_hash_val_t val; 847 mod_hash_key_t key; 848 int err; 849 queue_t *q = ctls->cs_wq; 850 851 if ((err = miocpullup(mp, sizeof (dld_ioc_ap_t))) != 0) 852 goto done; 853 854 diap = (dld_ioc_ap_t *)mp->b_cont->b_rptr; 855 key = (mod_hash_key_t)(uintptr_t)diap->dia_linkid; 856 857 rw_enter(&dld_ap_hash_lock, RW_WRITER); 858 if (mod_hash_find(dld_ap_hashp, key, &val) != 0) { 859 rw_exit(&dld_ap_hash_lock); 860 goto done; 861 } 862 863 VERIFY(mod_hash_remove(dld_ap_hashp, key, &val) == 0); 864 kmem_free(val, sizeof (dld_ap_t)); 865 rw_exit(&dld_ap_hash_lock); 866 867 done: 868 if (err == 0) 869 miocack(q, mp, 0, 0); 870 else 871 miocnak(q, mp, 0, err); 872 } 873 874 /* 875 * DLDIOC_DOORSERVER 876 */ 877 static void 878 drv_ioc_doorserver(dld_ctl_str_t *ctls, mblk_t *mp) 879 { 880 queue_t *q = ctls->cs_wq; 881 dld_ioc_door_t *did; 882 int err; 883 884 if ((err = miocpullup(mp, sizeof (dld_ioc_door_t))) != 0) 885 goto done; 886 887 did = (dld_ioc_door_t *)mp->b_cont->b_rptr; 888 err = dls_mgmt_door_set(did->did_start_door); 889 890 done: 891 if (err == 0) 892 miocack(q, mp, 0, 0); 893 else 894 miocnak(q, mp, 0, err); 895 } 896 897 /* 898 * DLDIOC_SETZID 899 */ 900 static void 901 drv_ioc_setzid(dld_ctl_str_t *ctls, mblk_t *mp) 902 { 903 queue_t *q = ctls->cs_wq; 904 dld_ioc_setzid_t *dis; 905 int err; 906 907 if ((err = miocpullup(mp, sizeof (dld_ioc_setzid_t))) != 0) 908 goto done; 909 910 dis = (dld_ioc_setzid_t *)mp->b_cont->b_rptr; 911 err = dls_devnet_setzid(dis->dis_link, dis->dis_zid); 912 913 done: 914 if (err == 0) 915 miocack(q, mp, 0, 0); 916 else 917 miocnak(q, mp, 0, err); 918 } 919 920 /* 921 * DLDIOC_GETZID 922 */ 923 static void 924 drv_ioc_getzid(dld_ctl_str_t *ctls, mblk_t *mp) 925 { 926 queue_t *q = ctls->cs_wq; 927 dld_ioc_getzid_t *dig; 928 int err; 929 930 if ((err = miocpullup(mp, sizeof (dld_ioc_getzid_t))) != 0) 931 goto done; 932 933 dig = (dld_ioc_getzid_t *)mp->b_cont->b_rptr; 934 err = dls_devnet_getzid(dig->dig_linkid, &dig->dig_zid); 935 936 done: 937 if (err == 0) 938 miocack(q, mp, sizeof (dld_ioc_getzid_t), 0); 939 else 940 miocnak(q, mp, 0, err); 941 } 942 943 /* 944 * Process an IOCTL message received by the control node. 945 */ 946 static void 947 drv_ioc(dld_ctl_str_t *ctls, mblk_t *mp) 948 { 949 uint_t cmd; 950 951 cmd = ((struct iocblk *)mp->b_rptr)->ioc_cmd; 952 switch (cmd) { 953 case DLDIOC_ATTR: 954 drv_ioc_attr(ctls, mp); 955 return; 956 case DLDIOC_PHYS_ATTR: 957 drv_ioc_phys_attr(ctls, mp); 958 return; 959 case DLDIOC_SECOBJ_SET: 960 drv_ioc_secobj_set(ctls, mp); 961 return; 962 case DLDIOC_SECOBJ_GET: 963 drv_ioc_secobj_get(ctls, mp); 964 return; 965 case DLDIOC_SECOBJ_UNSET: 966 drv_ioc_secobj_unset(ctls, mp); 967 return; 968 case DLDIOCSETPROP: 969 drv_ioc_setprop(ctls, mp); 970 return; 971 case DLDIOCGETPROP: 972 drv_ioc_getprop(ctls, mp); 973 return; 974 case DLDIOC_CREATE_VLAN: 975 drv_ioc_create_vlan(ctls, mp); 976 return; 977 case DLDIOC_DELETE_VLAN: 978 drv_ioc_delete_vlan(ctls, mp); 979 return; 980 case DLDIOC_VLAN_ATTR: 981 drv_ioc_vlan_attr(ctls, mp); 982 return; 983 case DLDIOC_SETAUTOPUSH: 984 drv_ioc_setap(ctls, mp); 985 return; 986 case DLDIOC_GETAUTOPUSH: 987 drv_ioc_getap(ctls, mp); 988 return; 989 case DLDIOC_CLRAUTOPUSH: 990 drv_ioc_clrap(ctls, mp); 991 return; 992 case DLDIOC_DOORSERVER: 993 drv_ioc_doorserver(ctls, mp); 994 return; 995 case DLDIOC_SETZID: 996 drv_ioc_setzid(ctls, mp); 997 return; 998 case DLDIOC_GETZID: 999 drv_ioc_getzid(ctls, mp); 1000 return; 1001 case DLDIOC_RENAME: 1002 drv_ioc_rename(ctls, mp); 1003 return; 1004 default: 1005 miocnak(ctls->cs_wq, mp, 0, ENOTSUP); 1006 return; 1007 } 1008 } 1009 1010 /* 1011 * Write side put routine of the dld control node. 1012 */ 1013 static void 1014 drv_uw_put(queue_t *q, mblk_t *mp) 1015 { 1016 dld_ctl_str_t *ctls = q->q_ptr; 1017 1018 switch (mp->b_datap->db_type) { 1019 case M_IOCTL: 1020 drv_ioc(ctls, mp); 1021 break; 1022 default: 1023 freemsg(mp); 1024 break; 1025 } 1026 } 1027 1028 /* 1029 * Write-side service procedure. 1030 */ 1031 void 1032 drv_uw_srv(queue_t *q) 1033 { 1034 mblk_t *mp; 1035 1036 while (mp = getq(q)) 1037 drv_uw_put(q, mp); 1038 } 1039 1040 /* 1041 * Check for GLDv3 autopush information. There are three cases: 1042 * 1043 * 1. If devp points to a GLDv3 datalink and it has autopush configuration, 1044 * fill dlap in with that information and return 0. 1045 * 1046 * 2. If devp points to a GLDv3 datalink but it doesn't have autopush 1047 * configuration, then replace devp with the physical device (if one 1048 * exists) and return 1. This allows stropen() to find the old-school 1049 * per-driver autopush configuration. (For softmac, the result is that 1050 * the softmac dev_t is replaced with the legacy device's dev_t). 1051 * 1052 * 3. If neither of the above apply, don't touch the args and return -1. 1053 */ 1054 int 1055 dld_autopush(dev_t *devp, struct dlautopush *dlap) 1056 { 1057 dld_ap_t *dap; 1058 datalink_id_t linkid; 1059 dev_t phydev; 1060 1061 if (!GLDV3_DRV(getmajor(*devp))) 1062 return (-1); 1063 1064 /* 1065 * Find the linkid by the link's dev_t. 1066 */ 1067 if (dls_devnet_dev2linkid(*devp, &linkid) != 0) 1068 return (-1); 1069 1070 /* 1071 * Find the autopush configuration associated with the linkid. 1072 */ 1073 rw_enter(&dld_ap_hash_lock, RW_READER); 1074 if (mod_hash_find(dld_ap_hashp, (mod_hash_key_t)(uintptr_t)linkid, 1075 (mod_hash_val_t *)&dap) == 0) { 1076 *dlap = dap->da_ap; 1077 rw_exit(&dld_ap_hash_lock); 1078 return (0); 1079 } 1080 rw_exit(&dld_ap_hash_lock); 1081 1082 if (dls_devnet_phydev(linkid, &phydev) != 0) 1083 return (-1); 1084 1085 *devp = phydev; 1086 return (1); 1087 } 1088 1089 /* 1090 * Secure objects implementation 1091 */ 1092 1093 /* ARGSUSED */ 1094 static int 1095 drv_secobj_ctor(void *buf, void *arg, int kmflag) 1096 { 1097 bzero(buf, sizeof (dld_secobj_t)); 1098 return (0); 1099 } 1100 1101 static void 1102 drv_secobj_init(void) 1103 { 1104 rw_init(&drv_secobj_lock, NULL, RW_DEFAULT, NULL); 1105 drv_secobj_cachep = kmem_cache_create("drv_secobj_cache", 1106 sizeof (dld_secobj_t), 0, drv_secobj_ctor, NULL, 1107 NULL, NULL, NULL, 0); 1108 drv_secobj_hash = mod_hash_create_extended("drv_secobj_hash", 1109 SECOBJ_WEP_HASHSZ, mod_hash_null_keydtor, mod_hash_null_valdtor, 1110 mod_hash_bystr, NULL, mod_hash_strkey_cmp, KM_SLEEP); 1111 } 1112 1113 static void 1114 drv_secobj_fini(void) 1115 { 1116 mod_hash_destroy_hash(drv_secobj_hash); 1117 kmem_cache_destroy(drv_secobj_cachep); 1118 rw_destroy(&drv_secobj_lock); 1119 } 1120 1121 static void 1122 drv_ioc_secobj_set(dld_ctl_str_t *ctls, mblk_t *mp) 1123 { 1124 dld_ioc_secobj_set_t *ssp; 1125 dld_secobj_t *sobjp, *objp; 1126 int err = EINVAL; 1127 queue_t *q = ctls->cs_wq; 1128 1129 if ((err = miocpullup(mp, sizeof (dld_ioc_secobj_set_t))) != 0) 1130 goto failed; 1131 1132 ssp = (dld_ioc_secobj_set_t *)mp->b_cont->b_rptr; 1133 sobjp = &ssp->ss_obj; 1134 1135 if (sobjp->so_class != DLD_SECOBJ_CLASS_WEP && 1136 sobjp->so_class != DLD_SECOBJ_CLASS_WPA) 1137 goto failed; 1138 1139 if (sobjp->so_name[DLD_SECOBJ_NAME_MAX - 1] != '\0' || 1140 sobjp->so_len > DLD_SECOBJ_VAL_MAX) 1141 goto failed; 1142 1143 rw_enter(&drv_secobj_lock, RW_WRITER); 1144 err = mod_hash_find(drv_secobj_hash, (mod_hash_key_t)sobjp->so_name, 1145 (mod_hash_val_t *)&objp); 1146 if (err == 0) { 1147 if ((ssp->ss_flags & DLD_SECOBJ_OPT_CREATE) != 0) { 1148 err = EEXIST; 1149 rw_exit(&drv_secobj_lock); 1150 goto failed; 1151 } 1152 } else { 1153 ASSERT(err == MH_ERR_NOTFOUND); 1154 if ((ssp->ss_flags & DLD_SECOBJ_OPT_CREATE) == 0) { 1155 err = ENOENT; 1156 rw_exit(&drv_secobj_lock); 1157 goto failed; 1158 } 1159 objp = kmem_cache_alloc(drv_secobj_cachep, KM_SLEEP); 1160 (void) strlcpy(objp->so_name, sobjp->so_name, 1161 DLD_SECOBJ_NAME_MAX); 1162 1163 err = mod_hash_insert(drv_secobj_hash, 1164 (mod_hash_key_t)objp->so_name, (mod_hash_val_t)objp); 1165 ASSERT(err == 0); 1166 } 1167 bcopy(sobjp->so_val, objp->so_val, sobjp->so_len); 1168 objp->so_len = sobjp->so_len; 1169 objp->so_class = sobjp->so_class; 1170 rw_exit(&drv_secobj_lock); 1171 miocack(q, mp, 0, 0); 1172 return; 1173 1174 failed: 1175 ASSERT(err != 0); 1176 miocnak(q, mp, 0, err); 1177 } 1178 1179 typedef struct dld_secobj_state { 1180 uint_t ss_free; 1181 uint_t ss_count; 1182 int ss_rc; 1183 dld_secobj_t *ss_objp; 1184 } dld_secobj_state_t; 1185 1186 /* ARGSUSED */ 1187 static uint_t 1188 drv_secobj_walker(mod_hash_key_t key, mod_hash_val_t *val, void *arg) 1189 { 1190 dld_secobj_state_t *statep = arg; 1191 dld_secobj_t *sobjp = (dld_secobj_t *)val; 1192 1193 if (statep->ss_free < sizeof (dld_secobj_t)) { 1194 statep->ss_rc = ENOSPC; 1195 return (MH_WALK_TERMINATE); 1196 } 1197 bcopy(sobjp, statep->ss_objp, sizeof (dld_secobj_t)); 1198 statep->ss_objp++; 1199 statep->ss_free -= sizeof (dld_secobj_t); 1200 statep->ss_count++; 1201 return (MH_WALK_CONTINUE); 1202 } 1203 1204 static void 1205 drv_ioc_secobj_get(dld_ctl_str_t *ctls, mblk_t *mp) 1206 { 1207 dld_ioc_secobj_get_t *sgp; 1208 dld_secobj_t *sobjp, *objp; 1209 int err = EINVAL; 1210 uint_t extra = 0; 1211 queue_t *q = ctls->cs_wq; 1212 mblk_t *bp; 1213 1214 if ((err = miocpullup(mp, sizeof (dld_ioc_secobj_get_t))) != 0) 1215 goto failed; 1216 1217 if ((bp = msgpullup(mp->b_cont, -1)) == NULL) 1218 goto failed; 1219 1220 freemsg(mp->b_cont); 1221 mp->b_cont = bp; 1222 sgp = (dld_ioc_secobj_get_t *)bp->b_rptr; 1223 sobjp = &sgp->sg_obj; 1224 1225 if (sobjp->so_name[DLD_SECOBJ_NAME_MAX - 1] != '\0') 1226 goto failed; 1227 1228 rw_enter(&drv_secobj_lock, RW_READER); 1229 if (sobjp->so_name[0] != '\0') { 1230 err = mod_hash_find(drv_secobj_hash, 1231 (mod_hash_key_t)sobjp->so_name, (mod_hash_val_t *)&objp); 1232 if (err != 0) { 1233 ASSERT(err == MH_ERR_NOTFOUND); 1234 err = ENOENT; 1235 rw_exit(&drv_secobj_lock); 1236 goto failed; 1237 } 1238 bcopy(objp->so_val, sobjp->so_val, objp->so_len); 1239 sobjp->so_len = objp->so_len; 1240 sobjp->so_class = objp->so_class; 1241 sgp->sg_count = 1; 1242 } else { 1243 dld_secobj_state_t state; 1244 1245 state.ss_free = MBLKL(bp) - sizeof (dld_ioc_secobj_get_t); 1246 state.ss_count = 0; 1247 state.ss_rc = 0; 1248 state.ss_objp = (dld_secobj_t *)(sgp + 1); 1249 mod_hash_walk(drv_secobj_hash, drv_secobj_walker, &state); 1250 if (state.ss_rc != 0) { 1251 err = state.ss_rc; 1252 rw_exit(&drv_secobj_lock); 1253 goto failed; 1254 } 1255 sgp->sg_count = state.ss_count; 1256 extra = state.ss_count * sizeof (dld_secobj_t); 1257 } 1258 rw_exit(&drv_secobj_lock); 1259 miocack(q, mp, sizeof (dld_ioc_secobj_get_t) + extra, 0); 1260 return; 1261 1262 failed: 1263 ASSERT(err != 0); 1264 miocnak(q, mp, 0, err); 1265 1266 } 1267 1268 static void 1269 drv_ioc_secobj_unset(dld_ctl_str_t *ctls, mblk_t *mp) 1270 { 1271 dld_ioc_secobj_unset_t *sup; 1272 dld_secobj_t *objp; 1273 mod_hash_val_t val; 1274 int err = EINVAL; 1275 queue_t *q = ctls->cs_wq; 1276 1277 if ((err = miocpullup(mp, sizeof (dld_ioc_secobj_unset_t))) != 0) 1278 goto failed; 1279 1280 sup = (dld_ioc_secobj_unset_t *)mp->b_cont->b_rptr; 1281 if (sup->su_name[DLD_SECOBJ_NAME_MAX - 1] != '\0') 1282 goto failed; 1283 1284 rw_enter(&drv_secobj_lock, RW_WRITER); 1285 err = mod_hash_find(drv_secobj_hash, (mod_hash_key_t)sup->su_name, 1286 (mod_hash_val_t *)&objp); 1287 if (err != 0) { 1288 ASSERT(err == MH_ERR_NOTFOUND); 1289 err = ENOENT; 1290 rw_exit(&drv_secobj_lock); 1291 goto failed; 1292 } 1293 err = mod_hash_remove(drv_secobj_hash, (mod_hash_key_t)sup->su_name, 1294 (mod_hash_val_t *)&val); 1295 ASSERT(err == 0); 1296 ASSERT(objp == (dld_secobj_t *)val); 1297 1298 kmem_cache_free(drv_secobj_cachep, objp); 1299 rw_exit(&drv_secobj_lock); 1300 miocack(q, mp, 0, 0); 1301 return; 1302 1303 failed: 1304 ASSERT(err != 0); 1305 miocnak(q, mp, 0, err); 1306 } 1307