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 if ((err = dls_mgmt_get_linkid(dipp->pr_linkname, &linkid)) != 0) 533 goto done; 534 535 if ((err = dls_devnet_hold_tmp(linkid, &dlh)) != 0) 536 goto done; 537 538 if ((err = dls_vlan_hold(dls_devnet_mac(dlh), 539 dls_devnet_vid(dlh), &dvp, B_FALSE, B_FALSE)) != 0) { 540 dls_devnet_rele_tmp(dlh); 541 goto done; 542 } 543 544 macprop.mp_name = dipp->pr_name; 545 macprop.mp_id = dipp->pr_num; 546 547 if (set) 548 err = mac_set_prop(dvp->dv_dlp->dl_mh, &macprop, 549 dipp->pr_val, dipp->pr_valsize); 550 else 551 err = mac_get_prop(dvp->dv_dlp->dl_mh, &macprop, 552 dipp->pr_val, dipp->pr_valsize); 553 554 dls_vlan_rele(dvp); 555 dls_devnet_rele_tmp(dlh); 556 done: 557 if (err == 0) 558 miocack(q, mp, dsize, 0); 559 else 560 miocnak(q, mp, 0, err); 561 } 562 563 static void 564 drv_ioc_setprop(dld_ctl_str_t *ctls, mblk_t *mp) 565 { 566 drv_ioc_prop_common(ctls, mp, B_TRUE); 567 } 568 569 static void 570 drv_ioc_getprop(dld_ctl_str_t *ctls, mblk_t *mp) 571 { 572 drv_ioc_prop_common(ctls, mp, B_FALSE); 573 } 574 575 /* 576 * DLDIOC_CREATE_VLAN 577 */ 578 static void 579 drv_ioc_create_vlan(dld_ctl_str_t *ctls, mblk_t *mp) 580 { 581 dld_ioc_create_vlan_t *dicp; 582 int err; 583 queue_t *q = ctls->cs_wq; 584 585 if ((err = miocpullup(mp, sizeof (dld_ioc_create_vlan_t))) != 0) 586 goto failed; 587 588 dicp = (dld_ioc_create_vlan_t *)mp->b_cont->b_rptr; 589 590 if ((err = dls_devnet_create_vlan(dicp->dic_vlanid, 591 dicp->dic_linkid, dicp->dic_vid, dicp->dic_force)) != 0) { 592 goto failed; 593 } 594 595 miocack(q, mp, 0, 0); 596 return; 597 598 failed: 599 miocnak(q, mp, 0, err); 600 } 601 602 /* 603 * DLDIOC_DELETE_VLAN 604 */ 605 static void 606 drv_ioc_delete_vlan(dld_ctl_str_t *ctls, mblk_t *mp) 607 { 608 dld_ioc_delete_vlan_t *didp; 609 int err; 610 queue_t *q = ctls->cs_wq; 611 612 if ((err = miocpullup(mp, sizeof (dld_ioc_delete_vlan_t))) != 0) 613 goto done; 614 615 didp = (dld_ioc_delete_vlan_t *)mp->b_cont->b_rptr; 616 err = dls_devnet_destroy_vlan(didp->did_linkid); 617 618 done: 619 if (err == 0) 620 miocack(q, mp, 0, 0); 621 else 622 miocnak(q, mp, 0, err); 623 } 624 625 /* 626 * DLDIOC_VLAN_ATTR 627 */ 628 static void 629 drv_ioc_vlan_attr(dld_ctl_str_t *ctls, mblk_t *mp) 630 { 631 dld_ioc_vlan_attr_t *divp; 632 dls_dl_handle_t dlh; 633 uint16_t vid; 634 dls_vlan_t *dvp; 635 int err; 636 queue_t *q = ctls->cs_wq; 637 638 if ((err = miocpullup(mp, sizeof (dld_ioc_vlan_attr_t))) != 0) 639 goto failed; 640 641 divp = (dld_ioc_vlan_attr_t *)mp->b_cont->b_rptr; 642 643 /* 644 * Hold this link to prevent it from being deleted. 645 */ 646 err = dls_devnet_hold_tmp(divp->div_vlanid, &dlh); 647 if (err != 0) 648 goto failed; 649 650 if ((vid = dls_devnet_vid(dlh)) == VLAN_ID_NONE) { 651 dls_devnet_rele_tmp(dlh); 652 err = EINVAL; 653 goto failed; 654 } 655 656 err = dls_vlan_hold(dls_devnet_mac(dlh), vid, &dvp, B_FALSE, B_FALSE); 657 if (err != 0) { 658 dls_devnet_rele_tmp(dlh); 659 err = EINVAL; 660 goto failed; 661 } 662 663 divp->div_linkid = dls_devnet_linkid(dlh); 664 divp->div_implicit = !dls_devnet_is_explicit(dlh); 665 divp->div_vid = vid; 666 divp->div_force = dvp->dv_force; 667 668 dls_vlan_rele(dvp); 669 dls_devnet_rele_tmp(dlh); 670 miocack(q, mp, sizeof (dld_ioc_vlan_attr_t), 0); 671 return; 672 673 failed: 674 miocnak(q, mp, 0, err); 675 } 676 677 /* 678 * DLDIOC_RENAME. 679 * 680 * This function handles two cases of link renaming. See more in comments above 681 * dls_datalink_rename(). 682 */ 683 static void 684 drv_ioc_rename(dld_ctl_str_t *ctls, mblk_t *mp) 685 { 686 dld_ioc_rename_t *dir; 687 mod_hash_key_t key; 688 mod_hash_val_t val; 689 int err; 690 queue_t *q = ctls->cs_wq; 691 692 if ((err = miocpullup(mp, sizeof (dld_ioc_rename_t))) != 0) 693 goto done; 694 695 dir = (dld_ioc_rename_t *)mp->b_cont->b_rptr; 696 if ((err = dls_devnet_rename(dir->dir_linkid1, dir->dir_linkid2, 697 dir->dir_link)) != 0) { 698 goto done; 699 } 700 701 if (dir->dir_linkid2 == DATALINK_INVALID_LINKID) 702 goto done; 703 704 /* 705 * if dir_linkid2 is not DATALINK_INVALID_LINKID, it means this 706 * renaming request is to rename a valid physical link (dir_linkid1) 707 * to a "removed" physical link (dir_linkid2, which is removed by DR 708 * or during system shutdown). In this case, the link (specified by 709 * dir_linkid1) would inherit all the configuration of dir_linkid2, 710 * and dir_linkid1 and its configuration would be lost. 711 * 712 * Remove per-link autopush configuration of dir_linkid1 in this case. 713 */ 714 key = (mod_hash_key_t)(uintptr_t)dir->dir_linkid1; 715 rw_enter(&dld_ap_hash_lock, RW_WRITER); 716 if (mod_hash_find(dld_ap_hashp, key, &val) != 0) { 717 rw_exit(&dld_ap_hash_lock); 718 goto done; 719 } 720 721 VERIFY(mod_hash_remove(dld_ap_hashp, key, &val) == 0); 722 kmem_free(val, sizeof (dld_ap_t)); 723 rw_exit(&dld_ap_hash_lock); 724 725 done: 726 if (err == 0) 727 miocack(q, mp, 0, 0); 728 else 729 miocnak(q, mp, 0, err); 730 } 731 732 /* 733 * DLDIOC_SETAUTOPUSH 734 */ 735 static void 736 drv_ioc_setap(dld_ctl_str_t *ctls, mblk_t *mp) 737 { 738 dld_ioc_ap_t *diap; 739 dld_ap_t *dap; 740 int i, err; 741 queue_t *q = ctls->cs_wq; 742 mod_hash_key_t key; 743 744 if ((err = miocpullup(mp, sizeof (dld_ioc_ap_t))) != 0) 745 goto failed; 746 747 diap = (dld_ioc_ap_t *)mp->b_cont->b_rptr; 748 if (diap->dia_npush == 0 || diap->dia_npush > MAXAPUSH) { 749 err = EINVAL; 750 goto failed; 751 } 752 753 /* 754 * Validate that the specified list of modules exist. 755 */ 756 for (i = 0; i < diap->dia_npush; i++) { 757 if (fmodsw_find(diap->dia_aplist[i], FMODSW_LOAD) == NULL) { 758 err = EINVAL; 759 goto failed; 760 } 761 } 762 763 key = (mod_hash_key_t)(uintptr_t)diap->dia_linkid; 764 765 rw_enter(&dld_ap_hash_lock, RW_WRITER); 766 if (mod_hash_find(dld_ap_hashp, key, (mod_hash_val_t *)&dap) != 0) { 767 dap = kmem_zalloc(sizeof (dld_ap_t), KM_NOSLEEP); 768 if (dap == NULL) { 769 rw_exit(&dld_ap_hash_lock); 770 err = ENOMEM; 771 goto failed; 772 } 773 774 dap->da_linkid = diap->dia_linkid; 775 err = mod_hash_insert(dld_ap_hashp, key, (mod_hash_val_t)dap); 776 ASSERT(err == 0); 777 } 778 779 /* 780 * Update the configuration. 781 */ 782 dap->da_anchor = diap->dia_anchor; 783 dap->da_npush = diap->dia_npush; 784 for (i = 0; i < diap->dia_npush; i++) { 785 (void) strlcpy(dap->da_aplist[i], diap->dia_aplist[i], 786 FMNAMESZ + 1); 787 } 788 rw_exit(&dld_ap_hash_lock); 789 790 miocack(q, mp, 0, 0); 791 return; 792 793 failed: 794 miocnak(q, mp, 0, err); 795 } 796 797 /* 798 * DLDIOC_GETAUTOPUSH 799 */ 800 static void 801 drv_ioc_getap(dld_ctl_str_t *ctls, mblk_t *mp) 802 { 803 dld_ioc_ap_t *diap; 804 dld_ap_t *dap; 805 int i, err; 806 queue_t *q = ctls->cs_wq; 807 808 if ((err = miocpullup(mp, sizeof (dld_ioc_ap_t))) != 0) 809 goto failed; 810 811 diap = (dld_ioc_ap_t *)mp->b_cont->b_rptr; 812 813 rw_enter(&dld_ap_hash_lock, RW_READER); 814 if (mod_hash_find(dld_ap_hashp, 815 (mod_hash_key_t)(uintptr_t)diap->dia_linkid, 816 (mod_hash_val_t *)&dap) != 0) { 817 err = ENOENT; 818 rw_exit(&dld_ap_hash_lock); 819 goto failed; 820 } 821 822 /* 823 * Retrieve the configuration. 824 */ 825 diap->dia_anchor = dap->da_anchor; 826 diap->dia_npush = dap->da_npush; 827 for (i = 0; i < dap->da_npush; i++) { 828 (void) strlcpy(diap->dia_aplist[i], dap->da_aplist[i], 829 FMNAMESZ + 1); 830 } 831 rw_exit(&dld_ap_hash_lock); 832 833 miocack(q, mp, sizeof (dld_ioc_ap_t), 0); 834 return; 835 836 failed: 837 miocnak(q, mp, 0, err); 838 } 839 840 /* 841 * DLDIOC_CLRAUTOPUSH 842 */ 843 static void 844 drv_ioc_clrap(dld_ctl_str_t *ctls, mblk_t *mp) 845 { 846 dld_ioc_ap_t *diap; 847 mod_hash_val_t val; 848 mod_hash_key_t key; 849 int err; 850 queue_t *q = ctls->cs_wq; 851 852 if ((err = miocpullup(mp, sizeof (dld_ioc_ap_t))) != 0) 853 goto done; 854 855 diap = (dld_ioc_ap_t *)mp->b_cont->b_rptr; 856 key = (mod_hash_key_t)(uintptr_t)diap->dia_linkid; 857 858 rw_enter(&dld_ap_hash_lock, RW_WRITER); 859 if (mod_hash_find(dld_ap_hashp, key, &val) != 0) { 860 rw_exit(&dld_ap_hash_lock); 861 goto done; 862 } 863 864 VERIFY(mod_hash_remove(dld_ap_hashp, key, &val) == 0); 865 kmem_free(val, sizeof (dld_ap_t)); 866 rw_exit(&dld_ap_hash_lock); 867 868 done: 869 if (err == 0) 870 miocack(q, mp, 0, 0); 871 else 872 miocnak(q, mp, 0, err); 873 } 874 875 /* 876 * DLDIOC_DOORSERVER 877 */ 878 static void 879 drv_ioc_doorserver(dld_ctl_str_t *ctls, mblk_t *mp) 880 { 881 queue_t *q = ctls->cs_wq; 882 dld_ioc_door_t *did; 883 int err; 884 885 if ((err = miocpullup(mp, sizeof (dld_ioc_door_t))) != 0) 886 goto done; 887 888 did = (dld_ioc_door_t *)mp->b_cont->b_rptr; 889 err = dls_mgmt_door_set(did->did_start_door); 890 891 done: 892 if (err == 0) 893 miocack(q, mp, 0, 0); 894 else 895 miocnak(q, mp, 0, err); 896 } 897 898 /* 899 * DLDIOC_SETZID 900 */ 901 static void 902 drv_ioc_setzid(dld_ctl_str_t *ctls, mblk_t *mp) 903 { 904 queue_t *q = ctls->cs_wq; 905 dld_ioc_setzid_t *dis; 906 int err; 907 908 if ((err = miocpullup(mp, sizeof (dld_ioc_setzid_t))) != 0) 909 goto done; 910 911 dis = (dld_ioc_setzid_t *)mp->b_cont->b_rptr; 912 err = dls_devnet_setzid(dis->dis_link, dis->dis_zid); 913 914 done: 915 if (err == 0) 916 miocack(q, mp, 0, 0); 917 else 918 miocnak(q, mp, 0, err); 919 } 920 921 /* 922 * DLDIOC_GETZID 923 */ 924 static void 925 drv_ioc_getzid(dld_ctl_str_t *ctls, mblk_t *mp) 926 { 927 queue_t *q = ctls->cs_wq; 928 dld_ioc_getzid_t *dig; 929 int err; 930 931 if ((err = miocpullup(mp, sizeof (dld_ioc_getzid_t))) != 0) 932 goto done; 933 934 dig = (dld_ioc_getzid_t *)mp->b_cont->b_rptr; 935 err = dls_devnet_getzid(dig->dig_linkid, &dig->dig_zid); 936 937 done: 938 if (err == 0) 939 miocack(q, mp, sizeof (dld_ioc_getzid_t), 0); 940 else 941 miocnak(q, mp, 0, err); 942 } 943 944 /* 945 * Process an IOCTL message received by the control node. 946 */ 947 static void 948 drv_ioc(dld_ctl_str_t *ctls, mblk_t *mp) 949 { 950 uint_t cmd; 951 952 cmd = ((struct iocblk *)mp->b_rptr)->ioc_cmd; 953 switch (cmd) { 954 case DLDIOC_ATTR: 955 drv_ioc_attr(ctls, mp); 956 return; 957 case DLDIOC_PHYS_ATTR: 958 drv_ioc_phys_attr(ctls, mp); 959 return; 960 case DLDIOC_SECOBJ_SET: 961 drv_ioc_secobj_set(ctls, mp); 962 return; 963 case DLDIOC_SECOBJ_GET: 964 drv_ioc_secobj_get(ctls, mp); 965 return; 966 case DLDIOC_SECOBJ_UNSET: 967 drv_ioc_secobj_unset(ctls, mp); 968 return; 969 case DLDIOCSETPROP: 970 drv_ioc_setprop(ctls, mp); 971 return; 972 case DLDIOCGETPROP: 973 drv_ioc_getprop(ctls, mp); 974 return; 975 case DLDIOC_CREATE_VLAN: 976 drv_ioc_create_vlan(ctls, mp); 977 return; 978 case DLDIOC_DELETE_VLAN: 979 drv_ioc_delete_vlan(ctls, mp); 980 return; 981 case DLDIOC_VLAN_ATTR: 982 drv_ioc_vlan_attr(ctls, mp); 983 return; 984 case DLDIOC_SETAUTOPUSH: 985 drv_ioc_setap(ctls, mp); 986 return; 987 case DLDIOC_GETAUTOPUSH: 988 drv_ioc_getap(ctls, mp); 989 return; 990 case DLDIOC_CLRAUTOPUSH: 991 drv_ioc_clrap(ctls, mp); 992 return; 993 case DLDIOC_DOORSERVER: 994 drv_ioc_doorserver(ctls, mp); 995 return; 996 case DLDIOC_SETZID: 997 drv_ioc_setzid(ctls, mp); 998 return; 999 case DLDIOC_GETZID: 1000 drv_ioc_getzid(ctls, mp); 1001 return; 1002 case DLDIOC_RENAME: 1003 drv_ioc_rename(ctls, mp); 1004 return; 1005 default: 1006 miocnak(ctls->cs_wq, mp, 0, ENOTSUP); 1007 return; 1008 } 1009 } 1010 1011 /* 1012 * Write side put routine of the dld control node. 1013 */ 1014 static void 1015 drv_uw_put(queue_t *q, mblk_t *mp) 1016 { 1017 dld_ctl_str_t *ctls = q->q_ptr; 1018 1019 switch (mp->b_datap->db_type) { 1020 case M_IOCTL: 1021 drv_ioc(ctls, mp); 1022 break; 1023 default: 1024 freemsg(mp); 1025 break; 1026 } 1027 } 1028 1029 /* 1030 * Write-side service procedure. 1031 */ 1032 void 1033 drv_uw_srv(queue_t *q) 1034 { 1035 mblk_t *mp; 1036 1037 while (mp = getq(q)) 1038 drv_uw_put(q, mp); 1039 } 1040 1041 /* 1042 * Check for GLDv3 autopush information. There are three cases: 1043 * 1044 * 1. If devp points to a GLDv3 datalink and it has autopush configuration, 1045 * fill dlap in with that information and return 0. 1046 * 1047 * 2. If devp points to a GLDv3 datalink but it doesn't have autopush 1048 * configuration, then replace devp with the physical device (if one 1049 * exists) and return 1. This allows stropen() to find the old-school 1050 * per-driver autopush configuration. (For softmac, the result is that 1051 * the softmac dev_t is replaced with the legacy device's dev_t). 1052 * 1053 * 3. If neither of the above apply, don't touch the args and return -1. 1054 */ 1055 int 1056 dld_autopush(dev_t *devp, struct dlautopush *dlap) 1057 { 1058 dld_ap_t *dap; 1059 datalink_id_t linkid; 1060 dev_t phydev; 1061 1062 if (!GLDV3_DRV(getmajor(*devp))) 1063 return (-1); 1064 1065 /* 1066 * Find the linkid by the link's dev_t. 1067 */ 1068 if (dls_devnet_dev2linkid(*devp, &linkid) != 0) 1069 return (-1); 1070 1071 /* 1072 * Find the autopush configuration associated with the linkid. 1073 */ 1074 rw_enter(&dld_ap_hash_lock, RW_READER); 1075 if (mod_hash_find(dld_ap_hashp, (mod_hash_key_t)(uintptr_t)linkid, 1076 (mod_hash_val_t *)&dap) == 0) { 1077 *dlap = dap->da_ap; 1078 rw_exit(&dld_ap_hash_lock); 1079 return (0); 1080 } 1081 rw_exit(&dld_ap_hash_lock); 1082 1083 if (dls_devnet_phydev(linkid, &phydev) != 0) 1084 return (-1); 1085 1086 *devp = phydev; 1087 return (1); 1088 } 1089 1090 /* 1091 * Secure objects implementation 1092 */ 1093 1094 /* ARGSUSED */ 1095 static int 1096 drv_secobj_ctor(void *buf, void *arg, int kmflag) 1097 { 1098 bzero(buf, sizeof (dld_secobj_t)); 1099 return (0); 1100 } 1101 1102 static void 1103 drv_secobj_init(void) 1104 { 1105 rw_init(&drv_secobj_lock, NULL, RW_DEFAULT, NULL); 1106 drv_secobj_cachep = kmem_cache_create("drv_secobj_cache", 1107 sizeof (dld_secobj_t), 0, drv_secobj_ctor, NULL, 1108 NULL, NULL, NULL, 0); 1109 drv_secobj_hash = mod_hash_create_extended("drv_secobj_hash", 1110 SECOBJ_WEP_HASHSZ, mod_hash_null_keydtor, mod_hash_null_valdtor, 1111 mod_hash_bystr, NULL, mod_hash_strkey_cmp, KM_SLEEP); 1112 } 1113 1114 static void 1115 drv_secobj_fini(void) 1116 { 1117 mod_hash_destroy_hash(drv_secobj_hash); 1118 kmem_cache_destroy(drv_secobj_cachep); 1119 rw_destroy(&drv_secobj_lock); 1120 } 1121 1122 static void 1123 drv_ioc_secobj_set(dld_ctl_str_t *ctls, mblk_t *mp) 1124 { 1125 dld_ioc_secobj_set_t *ssp; 1126 dld_secobj_t *sobjp, *objp; 1127 int err = EINVAL; 1128 queue_t *q = ctls->cs_wq; 1129 1130 if ((err = miocpullup(mp, sizeof (dld_ioc_secobj_set_t))) != 0) 1131 goto failed; 1132 1133 ssp = (dld_ioc_secobj_set_t *)mp->b_cont->b_rptr; 1134 sobjp = &ssp->ss_obj; 1135 1136 if (sobjp->so_class != DLD_SECOBJ_CLASS_WEP && 1137 sobjp->so_class != DLD_SECOBJ_CLASS_WPA) 1138 goto failed; 1139 1140 if (sobjp->so_name[DLD_SECOBJ_NAME_MAX - 1] != '\0' || 1141 sobjp->so_len > DLD_SECOBJ_VAL_MAX) 1142 goto failed; 1143 1144 rw_enter(&drv_secobj_lock, RW_WRITER); 1145 err = mod_hash_find(drv_secobj_hash, (mod_hash_key_t)sobjp->so_name, 1146 (mod_hash_val_t *)&objp); 1147 if (err == 0) { 1148 if ((ssp->ss_flags & DLD_SECOBJ_OPT_CREATE) != 0) { 1149 err = EEXIST; 1150 rw_exit(&drv_secobj_lock); 1151 goto failed; 1152 } 1153 } else { 1154 ASSERT(err == MH_ERR_NOTFOUND); 1155 if ((ssp->ss_flags & DLD_SECOBJ_OPT_CREATE) == 0) { 1156 err = ENOENT; 1157 rw_exit(&drv_secobj_lock); 1158 goto failed; 1159 } 1160 objp = kmem_cache_alloc(drv_secobj_cachep, KM_SLEEP); 1161 (void) strlcpy(objp->so_name, sobjp->so_name, 1162 DLD_SECOBJ_NAME_MAX); 1163 1164 err = mod_hash_insert(drv_secobj_hash, 1165 (mod_hash_key_t)objp->so_name, (mod_hash_val_t)objp); 1166 ASSERT(err == 0); 1167 } 1168 bcopy(sobjp->so_val, objp->so_val, sobjp->so_len); 1169 objp->so_len = sobjp->so_len; 1170 objp->so_class = sobjp->so_class; 1171 rw_exit(&drv_secobj_lock); 1172 miocack(q, mp, 0, 0); 1173 return; 1174 1175 failed: 1176 ASSERT(err != 0); 1177 miocnak(q, mp, 0, err); 1178 } 1179 1180 typedef struct dld_secobj_state { 1181 uint_t ss_free; 1182 uint_t ss_count; 1183 int ss_rc; 1184 dld_secobj_t *ss_objp; 1185 } dld_secobj_state_t; 1186 1187 /* ARGSUSED */ 1188 static uint_t 1189 drv_secobj_walker(mod_hash_key_t key, mod_hash_val_t *val, void *arg) 1190 { 1191 dld_secobj_state_t *statep = arg; 1192 dld_secobj_t *sobjp = (dld_secobj_t *)val; 1193 1194 if (statep->ss_free < sizeof (dld_secobj_t)) { 1195 statep->ss_rc = ENOSPC; 1196 return (MH_WALK_TERMINATE); 1197 } 1198 bcopy(sobjp, statep->ss_objp, sizeof (dld_secobj_t)); 1199 statep->ss_objp++; 1200 statep->ss_free -= sizeof (dld_secobj_t); 1201 statep->ss_count++; 1202 return (MH_WALK_CONTINUE); 1203 } 1204 1205 static void 1206 drv_ioc_secobj_get(dld_ctl_str_t *ctls, mblk_t *mp) 1207 { 1208 dld_ioc_secobj_get_t *sgp; 1209 dld_secobj_t *sobjp, *objp; 1210 int err = EINVAL; 1211 uint_t extra = 0; 1212 queue_t *q = ctls->cs_wq; 1213 mblk_t *bp; 1214 1215 if ((err = miocpullup(mp, sizeof (dld_ioc_secobj_get_t))) != 0) 1216 goto failed; 1217 1218 if ((bp = msgpullup(mp->b_cont, -1)) == NULL) 1219 goto failed; 1220 1221 freemsg(mp->b_cont); 1222 mp->b_cont = bp; 1223 sgp = (dld_ioc_secobj_get_t *)bp->b_rptr; 1224 sobjp = &sgp->sg_obj; 1225 1226 if (sobjp->so_name[DLD_SECOBJ_NAME_MAX - 1] != '\0') 1227 goto failed; 1228 1229 rw_enter(&drv_secobj_lock, RW_READER); 1230 if (sobjp->so_name[0] != '\0') { 1231 err = mod_hash_find(drv_secobj_hash, 1232 (mod_hash_key_t)sobjp->so_name, (mod_hash_val_t *)&objp); 1233 if (err != 0) { 1234 ASSERT(err == MH_ERR_NOTFOUND); 1235 err = ENOENT; 1236 rw_exit(&drv_secobj_lock); 1237 goto failed; 1238 } 1239 bcopy(objp->so_val, sobjp->so_val, objp->so_len); 1240 sobjp->so_len = objp->so_len; 1241 sobjp->so_class = objp->so_class; 1242 sgp->sg_count = 1; 1243 } else { 1244 dld_secobj_state_t state; 1245 1246 state.ss_free = MBLKL(bp) - sizeof (dld_ioc_secobj_get_t); 1247 state.ss_count = 0; 1248 state.ss_rc = 0; 1249 state.ss_objp = (dld_secobj_t *)(sgp + 1); 1250 mod_hash_walk(drv_secobj_hash, drv_secobj_walker, &state); 1251 if (state.ss_rc != 0) { 1252 err = state.ss_rc; 1253 rw_exit(&drv_secobj_lock); 1254 goto failed; 1255 } 1256 sgp->sg_count = state.ss_count; 1257 extra = state.ss_count * sizeof (dld_secobj_t); 1258 } 1259 rw_exit(&drv_secobj_lock); 1260 miocack(q, mp, sizeof (dld_ioc_secobj_get_t) + extra, 0); 1261 return; 1262 1263 failed: 1264 ASSERT(err != 0); 1265 miocnak(q, mp, 0, err); 1266 1267 } 1268 1269 static void 1270 drv_ioc_secobj_unset(dld_ctl_str_t *ctls, mblk_t *mp) 1271 { 1272 dld_ioc_secobj_unset_t *sup; 1273 dld_secobj_t *objp; 1274 mod_hash_val_t val; 1275 int err = EINVAL; 1276 queue_t *q = ctls->cs_wq; 1277 1278 if ((err = miocpullup(mp, sizeof (dld_ioc_secobj_unset_t))) != 0) 1279 goto failed; 1280 1281 sup = (dld_ioc_secobj_unset_t *)mp->b_cont->b_rptr; 1282 if (sup->su_name[DLD_SECOBJ_NAME_MAX - 1] != '\0') 1283 goto failed; 1284 1285 rw_enter(&drv_secobj_lock, RW_WRITER); 1286 err = mod_hash_find(drv_secobj_hash, (mod_hash_key_t)sup->su_name, 1287 (mod_hash_val_t *)&objp); 1288 if (err != 0) { 1289 ASSERT(err == MH_ERR_NOTFOUND); 1290 err = ENOENT; 1291 rw_exit(&drv_secobj_lock); 1292 goto failed; 1293 } 1294 err = mod_hash_remove(drv_secobj_hash, (mod_hash_key_t)sup->su_name, 1295 (mod_hash_val_t *)&val); 1296 ASSERT(err == 0); 1297 ASSERT(objp == (dld_secobj_t *)val); 1298 1299 kmem_cache_free(drv_secobj_cachep, objp); 1300 rw_exit(&drv_secobj_lock); 1301 miocack(q, mp, 0, 0); 1302 return; 1303 1304 failed: 1305 ASSERT(err != 0); 1306 miocnak(q, mp, 0, err); 1307 } 1308