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 (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved.
  23  * Copyright 2015, Joyent Inc.
  24  * Copyright (c) 2017, Joyent, Inc.
  25  */
  26 
  27 /*
  28  * Data-Link Driver
  29  */
  30 
  31 #include        <sys/conf.h>
  32 #include        <sys/mkdev.h>
  33 #include        <sys/modctl.h>
  34 #include        <sys/stat.h>
  35 #include        <sys/dld_impl.h>
  36 #include        <sys/dld_ioc.h>
  37 #include        <sys/dls_impl.h>
  38 #include        <sys/softmac.h>
  39 #include        <sys/mac.h>
  40 #include        <sys/mac_ether.h>
  41 #include        <sys/mac_client.h>
  42 #include        <sys/mac_client_impl.h>
  43 #include        <sys/mac_client_priv.h>
  44 #include        <inet/common.h>
  45 #include        <sys/policy.h>
  46 #include        <sys/priv_names.h>
  47 #include        <sys/zone.h>
  48 #include        <sys/sysmacros.h>
  49 
  50 static void     drv_init(void);
  51 static int      drv_fini(void);
  52 
  53 static int      drv_getinfo(dev_info_t  *, ddi_info_cmd_t, void *, void **);
  54 static int      drv_attach(dev_info_t *, ddi_attach_cmd_t);
  55 static int      drv_detach(dev_info_t *, ddi_detach_cmd_t);
  56 
  57 /*
  58  * Secure objects declarations
  59  */
  60 #define SECOBJ_WEP_HASHSZ       67
  61 static krwlock_t        drv_secobj_lock;
  62 static kmem_cache_t     *drv_secobj_cachep;
  63 static mod_hash_t       *drv_secobj_hash;
  64 static void             drv_secobj_init(void);
  65 static void             drv_secobj_fini(void);
  66 static int              drv_ioc_setap(datalink_id_t, struct dlautopush *);
  67 static int              drv_ioc_getap(datalink_id_t, struct dlautopush *);
  68 static int              drv_ioc_clrap(datalink_id_t);
  69 
  70 
  71 /*
  72  * The following entry points are private to dld and are used for control
  73  * operations only. The entry points exported to mac drivers are defined
  74  * in dld_str.c. Refer to the comment on top of dld_str.c for details.
  75  */
  76 static int      drv_open(dev_t *, int, int, cred_t *);
  77 static int      drv_ioctl(dev_t, int, intptr_t, int, cred_t *, int *);
  78 
  79 static dev_info_t       *dld_dip;       /* dev_info_t for the driver */
  80 uint32_t                dld_opt = 0;    /* Global options */
  81 
  82 #define NAUTOPUSH 32
  83 static mod_hash_t *dld_ap_hashp;
  84 static krwlock_t dld_ap_hash_lock;
  85 
  86 static struct cb_ops drv_cb_ops = {
  87         drv_open,               /* open */
  88         nulldev,                /* close */
  89         nulldev,                /* strategy */
  90         nulldev,                /* print */
  91         nodev,                  /* dump */
  92         nodev,                  /* read */
  93         nodev,                  /* write */
  94         drv_ioctl,              /* ioctl */
  95         nodev,                  /* devmap */
  96         nodev,                  /* mmap */
  97         nodev,                  /* segmap */
  98         nochpoll,               /* poll */
  99         ddi_prop_op,            /* cb_prop_op */
 100         0,                      /* streamtab  */
 101         D_MP                    /* Driver compatibility flag */
 102 };
 103 
 104 static struct dev_ops drv_ops = {
 105         DEVO_REV,               /* devo_rev */
 106         0,                      /* refcnt */
 107         drv_getinfo,            /* get_dev_info */
 108         nulldev,                /* identify */
 109         nulldev,                /* probe */
 110         drv_attach,             /* attach */
 111         drv_detach,             /* detach */
 112         nodev,                  /* reset */
 113         &drv_cb_ops,                /* driver operations */
 114         NULL,                   /* bus operations */
 115         nodev,                  /* dev power */
 116         ddi_quiesce_not_supported,      /* dev quiesce */
 117 };
 118 
 119 /*
 120  * Module linkage information for the kernel.
 121  */
 122 static  struct modldrv          drv_modldrv = {
 123         &mod_driverops,
 124         DLD_INFO,
 125         &drv_ops
 126 };
 127 
 128 static  struct modlinkage       drv_modlinkage = {
 129         MODREV_1,
 130         &drv_modldrv,
 131         NULL
 132 };
 133 
 134 int
 135 _init(void)
 136 {
 137         return (mod_install(&drv_modlinkage));
 138 }
 139 
 140 int
 141 _fini(void)
 142 {
 143         return (mod_remove(&drv_modlinkage));
 144 }
 145 
 146 int
 147 _info(struct modinfo *modinfop)
 148 {
 149         return (mod_info(&drv_modlinkage, modinfop));
 150 }
 151 
 152 /*
 153  * Initialize component modules.
 154  */
 155 static void
 156 drv_init(void)
 157 {
 158         drv_secobj_init();
 159         dld_str_init();
 160 
 161         /*
 162          * Create a hash table for autopush configuration.
 163          */
 164         dld_ap_hashp = mod_hash_create_idhash("dld_autopush_hash",
 165             NAUTOPUSH, mod_hash_null_valdtor);
 166 
 167         ASSERT(dld_ap_hashp != NULL);
 168         rw_init(&dld_ap_hash_lock, NULL, RW_DRIVER, NULL);
 169 }
 170 
 171 /* ARGSUSED */
 172 static uint_t
 173 drv_ap_exist(mod_hash_key_t key, mod_hash_val_t *val, void *arg)
 174 {
 175         boolean_t *pexist = arg;
 176 
 177         *pexist = B_TRUE;
 178         return (MH_WALK_TERMINATE);
 179 }
 180 
 181 static int
 182 drv_fini(void)
 183 {
 184         int             err;
 185         boolean_t       exist = B_FALSE;
 186 
 187         rw_enter(&dld_ap_hash_lock, RW_READER);
 188         mod_hash_walk(dld_ap_hashp, drv_ap_exist, &exist);
 189         rw_exit(&dld_ap_hash_lock);
 190         if (exist)
 191                 return (EBUSY);
 192 
 193         if ((err = dld_str_fini()) != 0)
 194                 return (err);
 195 
 196         drv_secobj_fini();
 197         mod_hash_destroy_idhash(dld_ap_hashp);
 198         rw_destroy(&dld_ap_hash_lock);
 199         return (0);
 200 }
 201 
 202 /*
 203  * devo_getinfo: getinfo(9e)
 204  */
 205 /*ARGSUSED*/
 206 static int
 207 drv_getinfo(dev_info_t *dip, ddi_info_cmd_t cmd, void *arg, void **resp)
 208 {
 209         if (dld_dip == NULL)
 210                 return (DDI_FAILURE);
 211 
 212         switch (cmd) {
 213         case DDI_INFO_DEVT2INSTANCE:
 214                 *resp = 0;
 215                 break;
 216         case DDI_INFO_DEVT2DEVINFO:
 217                 *resp = dld_dip;
 218                 break;
 219         default:
 220                 return (DDI_FAILURE);
 221         }
 222 
 223         return (DDI_SUCCESS);
 224 }
 225 
 226 /*
 227  * Check properties to set options. (See dld.h for property definitions).
 228  */
 229 static void
 230 drv_set_opt(dev_info_t *dip)
 231 {
 232         if (ddi_prop_get_int(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS,
 233             DLD_PROP_NO_FASTPATH, 0) != 0) {
 234                 dld_opt |= DLD_OPT_NO_FASTPATH;
 235         }
 236 
 237         if (ddi_prop_get_int(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS,
 238             DLD_PROP_NO_POLL, 0) != 0) {
 239                 dld_opt |= DLD_OPT_NO_POLL;
 240         }
 241 
 242         if (ddi_prop_get_int(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS,
 243             DLD_PROP_NO_ZEROCOPY, 0) != 0) {
 244                 dld_opt |= DLD_OPT_NO_ZEROCOPY;
 245         }
 246 
 247         if (ddi_prop_get_int(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS,
 248             DLD_PROP_NO_SOFTRING, 0) != 0) {
 249                 dld_opt |= DLD_OPT_NO_SOFTRING;
 250         }
 251 }
 252 
 253 /*
 254  * devo_attach: attach(9e)
 255  */
 256 static int
 257 drv_attach(dev_info_t *dip, ddi_attach_cmd_t cmd)
 258 {
 259         if (cmd != DDI_ATTACH)
 260                 return (DDI_FAILURE);
 261 
 262         ASSERT(ddi_get_instance(dip) == 0);
 263         drv_init();
 264         drv_set_opt(dip);
 265 
 266         /*
 267          * Create control node. DLPI provider nodes will be created on demand.
 268          */
 269         if (ddi_create_minor_node(dip, DLD_CONTROL_MINOR_NAME, S_IFCHR,
 270             DLD_CONTROL_MINOR, DDI_PSEUDO, 0) != DDI_SUCCESS)
 271                 return (DDI_FAILURE);
 272 
 273         dld_dip = dip;
 274 
 275         /*
 276          * Log the fact that the driver is now attached.
 277          */
 278         ddi_report_dev(dip);
 279         return (DDI_SUCCESS);
 280 }
 281 
 282 /*
 283  * devo_detach: detach(9e)
 284  */
 285 static int
 286 drv_detach(dev_info_t *dip, ddi_detach_cmd_t cmd)
 287 {
 288         if (cmd != DDI_DETACH)
 289                 return (DDI_FAILURE);
 290 
 291         ASSERT(dld_dip == dip);
 292         if (drv_fini() != 0)
 293                 return (DDI_FAILURE);
 294 
 295         /*
 296          * Remove the control node.
 297          */
 298         ddi_remove_minor_node(dip, DLD_CONTROL_MINOR_NAME);
 299         dld_dip = NULL;
 300 
 301         return (DDI_SUCCESS);
 302 }
 303 
 304 /*
 305  * dld control node open procedure.
 306  */
 307 /*ARGSUSED*/
 308 static int
 309 drv_open(dev_t *devp, int flag, int sflag, cred_t *credp)
 310 {
 311         /*
 312          * Only the control node can be opened.
 313          */
 314         if (getminor(*devp) != DLD_CONTROL_MINOR)
 315                 return (ENODEV);
 316         return (0);
 317 }
 318 
 319 /*
 320  * Verify if the caller is allowed to modify a link of the given class.
 321  */
 322 static int
 323 drv_ioc_checkprivs(datalink_class_t class, cred_t *cred)
 324 {
 325         if (class == DATALINK_CLASS_IPTUN)
 326                 return (secpolicy_iptun_config(cred));
 327         return (secpolicy_dl_config(cred));
 328 }
 329 
 330 /*
 331  * DLDIOC_ATTR
 332  */
 333 /* ARGSUSED */
 334 static int
 335 drv_ioc_attr(void *karg, intptr_t arg, int mode, cred_t *cred, int *rvalp)
 336 {
 337         dld_ioc_attr_t          *diap = karg;
 338         dls_dl_handle_t         dlh;
 339         dls_link_t              *dlp;
 340         zoneid_t                zoneid = crgetzoneid(cred);
 341         int                     err;
 342         mac_perim_handle_t      mph;
 343 
 344         if (zoneid != GLOBAL_ZONEID &&
 345             zone_check_datalink(&zoneid, diap->dia_linkid) != 0)
 346                 return (ENOENT);
 347 
 348         if ((err = dls_devnet_hold_tmp(diap->dia_linkid, &dlh)) != 0)
 349                 return (err);
 350 
 351         if ((err = mac_perim_enter_by_macname(dls_devnet_mac(dlh),
 352             &mph)) != 0) {
 353                 dls_devnet_rele_tmp(dlh);
 354                 return (err);
 355         }
 356 
 357         if ((err = dls_link_hold(dls_devnet_mac(dlh), &dlp)) != 0) {
 358                 mac_perim_exit(mph);
 359                 dls_devnet_rele_tmp(dlh);
 360                 return (err);
 361         }
 362 
 363         mac_sdu_get(dlp->dl_mh, NULL, &diap->dia_max_sdu);
 364         dls_link_rele(dlp);
 365         mac_perim_exit(mph);
 366         dls_devnet_rele_tmp(dlh);
 367 
 368         return (0);
 369 }
 370 
 371 /*
 372  * DLDIOC_PHYS_ATTR
 373  */
 374 /* ARGSUSED */
 375 static int
 376 drv_ioc_phys_attr(void *karg, intptr_t arg, int mode, cred_t *cred, int *rvalp)
 377 {
 378         dld_ioc_phys_attr_t     *dipp = karg;
 379         int                     err;
 380         dls_dl_handle_t         dlh;
 381         dls_dev_handle_t        ddh;
 382         dev_t                   phydev;
 383         zoneid_t                zoneid = crgetzoneid(cred);
 384 
 385         if (zoneid != GLOBAL_ZONEID &&
 386             zone_check_datalink(&zoneid, dipp->dip_linkid) != 0)
 387                 return (ENOENT);
 388 
 389         /*
 390          * Every physical link should have its physical dev_t kept in the
 391          * daemon. If not, it is not a valid physical link.
 392          */
 393         if (dls_mgmt_get_phydev(dipp->dip_linkid, &phydev) != 0)
 394                 return (EINVAL);
 395 
 396         /*
 397          * Although this is a valid physical link, it might already be removed
 398          * by DR or during system shutdown. softmac_hold_device() would return
 399          * ENOENT in this case.
 400          */
 401         if ((err = softmac_hold_device(phydev, &ddh)) != 0)
 402                 return (err);
 403 
 404         if (dls_devnet_hold_tmp(dipp->dip_linkid, &dlh) != 0) {
 405                 /*
 406                  * Although this is an active physical link, its link type is
 407                  * not supported by GLDv3, and therefore it does not have
 408                  * vanity naming support.
 409                  */
 410                 dipp->dip_novanity = B_TRUE;
 411         } else {
 412                 dipp->dip_novanity = B_FALSE;
 413                 dls_devnet_rele_tmp(dlh);
 414         }
 415         /*
 416          * Get the physical device name from the major number and the instance
 417          * number derived from phydev.
 418          */
 419         (void) snprintf(dipp->dip_dev, MAXLINKNAMELEN, "%s%d",
 420             ddi_major_to_name(getmajor(phydev)), getminor(phydev) - 1);
 421 
 422         softmac_rele_device(ddh);
 423         return (0);
 424 }
 425 
 426 /* ARGSUSED */
 427 static int
 428 drv_ioc_hwgrpget(void *karg, intptr_t arg, int mode, cred_t *cred, int *rvalp)
 429 {
 430         dld_ioc_hwgrpget_t      *hwgrpp = karg;
 431         dld_hwgrpinfo_t         hwgrp, *hip;
 432         mac_handle_t            mh = NULL;
 433         int                     i, err, rgrpnum, tgrpnum;
 434         uint_t                  bytes_left;
 435         int                     totgrps = 0;
 436         zoneid_t                zoneid = crgetzoneid(cred);
 437 
 438         if (zoneid != GLOBAL_ZONEID &&
 439             zone_check_datalink(&zoneid, hwgrpp->dih_linkid) != 0)
 440                 return (ENOENT);
 441 
 442         hwgrpp->dih_n_groups = 0;
 443         err = mac_open_by_linkid(hwgrpp->dih_linkid, &mh);
 444         if (err != 0)
 445                 goto done;
 446 
 447         hip = (dld_hwgrpinfo_t *)
 448             ((uchar_t *)arg + sizeof (dld_ioc_hwgrpget_t));
 449         bytes_left = hwgrpp->dih_size;
 450 
 451         rgrpnum = mac_hwgrp_num(mh, MAC_RING_TYPE_RX);
 452         /* display the default group information first */
 453         if (rgrpnum > 0) {
 454                 if (sizeof (dld_hwgrpinfo_t) > bytes_left) {
 455                         err = ENOSPC;
 456                         goto done;
 457                 }
 458 
 459                 bzero(&hwgrp, sizeof (hwgrp));
 460                 bcopy(mac_name(mh), hwgrp.dhi_link_name,
 461                     sizeof (hwgrp.dhi_link_name));
 462                 mac_get_hwrxgrp_info(mh, 0, &hwgrp.dhi_grp_num,
 463                     &hwgrp.dhi_n_rings, hwgrp.dhi_rings, &hwgrp.dhi_grp_type,
 464                     &hwgrp.dhi_n_clnts, hwgrp.dhi_clnts);
 465                 if (hwgrp.dhi_n_rings != 0) {
 466                         if (copyout(&hwgrp, hip, sizeof (hwgrp)) != 0) {
 467                                 err = EFAULT;
 468                                 goto done;
 469                         }
 470                 }
 471                 hip++;
 472                 totgrps++;
 473                 bytes_left -= sizeof (dld_hwgrpinfo_t);
 474         }
 475 
 476         tgrpnum = mac_hwgrp_num(mh, MAC_RING_TYPE_TX);
 477         /* display the default group information first */
 478         if (tgrpnum > 0) {
 479                 if (sizeof (dld_hwgrpinfo_t) > bytes_left) {
 480                         err = ENOSPC;
 481                         goto done;
 482                 }
 483 
 484                 bzero(&hwgrp, sizeof (hwgrp));
 485                 bcopy(mac_name(mh), hwgrp.dhi_link_name,
 486                     sizeof (hwgrp.dhi_link_name));
 487                 mac_get_hwtxgrp_info(mh, tgrpnum - 1, &hwgrp.dhi_grp_num,
 488                     &hwgrp.dhi_n_rings, hwgrp.dhi_rings, &hwgrp.dhi_grp_type,
 489                     &hwgrp.dhi_n_clnts, hwgrp.dhi_clnts);
 490                 if (hwgrp.dhi_n_rings != 0) {
 491                         if (copyout(&hwgrp, hip, sizeof (hwgrp)) != 0) {
 492                                 err = EFAULT;
 493                                 goto done;
 494                         }
 495                 }
 496                 hip++;
 497                 totgrps++;
 498                 bytes_left -= sizeof (dld_hwgrpinfo_t);
 499         }
 500 
 501         /* Rest of the rx groups */
 502         for (i = 1; i < rgrpnum; i++) {
 503                 if (sizeof (dld_hwgrpinfo_t) > bytes_left) {
 504                         err = ENOSPC;
 505                         goto done;
 506                 }
 507 
 508                 bzero(&hwgrp, sizeof (hwgrp));
 509                 bcopy(mac_name(mh), hwgrp.dhi_link_name,
 510                     sizeof (hwgrp.dhi_link_name));
 511                 mac_get_hwrxgrp_info(mh, i, &hwgrp.dhi_grp_num,
 512                     &hwgrp.dhi_n_rings, hwgrp.dhi_rings, &hwgrp.dhi_grp_type,
 513                     &hwgrp.dhi_n_clnts, hwgrp.dhi_clnts);
 514                 if (hwgrp.dhi_n_rings == 0)
 515                         continue;
 516                 if (copyout(&hwgrp, hip, sizeof (hwgrp)) != 0) {
 517                         err = EFAULT;
 518                         goto done;
 519                 }
 520 
 521                 hip++;
 522                 totgrps++;
 523                 bytes_left -= sizeof (dld_hwgrpinfo_t);
 524         }
 525 
 526         /* Rest of the tx group */
 527         tgrpnum = mac_hwgrp_num(mh, MAC_RING_TYPE_TX);
 528         for (i = 0; i < tgrpnum - 1; i++) {
 529                 if (sizeof (dld_hwgrpinfo_t) > bytes_left) {
 530                         err = ENOSPC;
 531                         goto done;
 532                 }
 533 
 534                 bzero(&hwgrp, sizeof (hwgrp));
 535                 bcopy(mac_name(mh), hwgrp.dhi_link_name,
 536                     sizeof (hwgrp.dhi_link_name));
 537                 mac_get_hwtxgrp_info(mh, i, &hwgrp.dhi_grp_num,
 538                     &hwgrp.dhi_n_rings, hwgrp.dhi_rings, &hwgrp.dhi_grp_type,
 539                     &hwgrp.dhi_n_clnts, hwgrp.dhi_clnts);
 540                 if (hwgrp.dhi_n_rings == 0)
 541                         continue;
 542                 if (copyout(&hwgrp, hip, sizeof (hwgrp)) != 0) {
 543                         err = EFAULT;
 544                         goto done;
 545                 }
 546 
 547                 hip++;
 548                 totgrps++;
 549                 bytes_left -= sizeof (dld_hwgrpinfo_t);
 550         }
 551 
 552 done:
 553         if (mh != NULL)
 554                 dld_mac_close(mh);
 555         if (err == 0)
 556                 hwgrpp->dih_n_groups = totgrps;
 557         return (err);
 558 }
 559 
 560 /* ARGSUSED */
 561 static int
 562 drv_ioc_macaddrget(void *karg, intptr_t arg, int mode, cred_t *cred, int *rvalp)
 563 {
 564         dld_ioc_macaddrget_t    *magp = karg;
 565         dld_macaddrinfo_t       mai, *maip;
 566         mac_handle_t            mh = NULL;
 567         int                     i, err;
 568         uint_t                  bytes_left;
 569         boolean_t               is_used;
 570         zoneid_t                zoneid = crgetzoneid(cred);
 571 
 572         if (zoneid != GLOBAL_ZONEID &&
 573             zone_check_datalink(&zoneid, magp->dig_linkid) != 0)
 574                 return (ENOENT);
 575 
 576         magp->dig_count = 0;
 577         err = mac_open_by_linkid(magp->dig_linkid, &mh);
 578         if (err != 0)
 579                 goto done;
 580 
 581         maip = (dld_macaddrinfo_t *)
 582             ((uchar_t *)arg + sizeof (dld_ioc_macaddrget_t));
 583         bytes_left = magp->dig_size;
 584 
 585         for (i = 0; i < mac_addr_factory_num(mh) + 1; i++) {
 586                 if (sizeof (dld_macaddrinfo_t) > bytes_left) {
 587                         err = ENOSPC;
 588                         goto done;
 589                 }
 590 
 591                 bzero(&mai, sizeof (mai));
 592 
 593                 if (i == 0) {
 594                         /* primary MAC address */
 595                         mac_unicast_primary_get(mh, mai.dmi_addr);
 596                         mai.dmi_addrlen = mac_addr_len(mh);
 597                         mac_unicast_primary_info(mh, mai.dmi_client_name,
 598                             &is_used);
 599                 } else {
 600                         /* factory MAC address slot */
 601                         mac_addr_factory_value(mh, i, mai.dmi_addr,
 602                             &mai.dmi_addrlen, mai.dmi_client_name, &is_used);
 603                 }
 604 
 605                 mai.dmi_slot = i;
 606                 if (is_used)
 607                         mai.dmi_flags |= DLDIOCMACADDR_USED;
 608 
 609                 if (copyout(&mai, maip, sizeof (mai)) != 0) {
 610                         err = EFAULT;
 611                         goto done;
 612                 }
 613 
 614                 maip++;
 615                 bytes_left -= sizeof (dld_macaddrinfo_t);
 616         }
 617 
 618 done:
 619         if (mh != NULL)
 620                 dld_mac_close(mh);
 621         if (err == 0)
 622                 magp->dig_count = mac_addr_factory_num(mh) + 1;
 623         return (err);
 624 }
 625 
 626 /*
 627  * DLDIOC_SET/GETMACPROP
 628  */
 629 static int
 630 drv_ioc_prop_common(dld_ioc_macprop_t *prop, intptr_t arg, boolean_t set,
 631     cred_t *cred, int mode)
 632 {
 633         int                     err = EINVAL;
 634         dls_dl_handle_t         dlh = NULL;
 635         dls_link_t              *dlp = NULL;
 636         mac_perim_handle_t      mph = NULL;
 637         dld_ioc_macprop_t       *kprop;
 638         datalink_id_t           linkid;
 639         datalink_class_t        class;
 640         zoneid_t                zoneid = crgetzoneid(cred);
 641         uint_t                  dsize;
 642 
 643         /*
 644          * We only use pr_valsize from prop, as the caller only did a
 645          * copyin() for sizeof (dld_ioc_prop_t), which doesn't cover
 646          * the property data.  We copyin the full dld_ioc_prop_t
 647          * including the data into kprop down below.
 648          */
 649         dsize = sizeof (dld_ioc_macprop_t) + prop->pr_valsize - 1;
 650         if (dsize < prop->pr_valsize)
 651                 return (EINVAL);
 652 
 653         /*
 654          * The property data is variable size, so we need to allocate
 655          * a buffer for kernel use as this data was not part of the
 656          * prop allocation and copyin() done by the framework.
 657          */
 658         if ((kprop = kmem_alloc(dsize, KM_NOSLEEP)) == NULL)
 659                 return (ENOMEM);
 660 
 661         if (ddi_copyin((void *)arg, kprop, dsize, mode) != 0) {
 662                 err = EFAULT;
 663                 goto done;
 664         }
 665 
 666         linkid = kprop->pr_linkid;
 667 
 668         if (set) {
 669                 if ((err = dls_mgmt_get_linkinfo(linkid, NULL, &class, NULL,
 670                     NULL)) != 0 || (err = drv_ioc_checkprivs(class, cred)) != 0)
 671                         goto done;
 672         }
 673 
 674         if ((err = dls_devnet_hold_tmp(linkid, &dlh)) != 0)
 675                 goto done;
 676         if ((err = mac_perim_enter_by_macname(dls_devnet_mac(dlh), &mph)) != 0)
 677                 goto done;
 678         if ((err = dls_link_hold(dls_devnet_mac(dlh), &dlp)) != 0)
 679                 goto done;
 680 
 681         /*
 682          * Don't allow a process to get or set properties of a link if that
 683          * link doesn't belong to that zone.
 684          */
 685         if (zoneid != dls_devnet_getownerzid(dlh)) {
 686                 err = ENOENT;
 687                 goto done;
 688         }
 689 
 690         if (!mac_prop_check_size(kprop->pr_num, kprop->pr_valsize,
 691             kprop->pr_flags & DLD_PROP_POSSIBLE)) {
 692                 err = ENOBUFS;
 693                 goto done;
 694         }
 695 
 696         switch (kprop->pr_num) {
 697         case MAC_PROP_ZONE:
 698                 if (set) {
 699                         dld_ioc_zid_t *dzp = (dld_ioc_zid_t *)kprop->pr_val;
 700 
 701                         if (zoneid != GLOBAL_ZONEID) {
 702                                 err = EACCES;
 703                                 goto done;
 704                         }
 705                         err = dls_devnet_setzid(dlh, dzp->diz_zid);
 706                 } else {
 707                         kprop->pr_perm_flags = MAC_PROP_PERM_RW;
 708                         (*(zoneid_t *)kprop->pr_val) = dls_devnet_getzid(dlh);
 709                 }
 710                 break;
 711         case MAC_PROP_AUTOPUSH: {
 712                 struct dlautopush *dlap = (struct dlautopush *)kprop->pr_val;
 713 
 714                 if (set) {
 715                         if (kprop->pr_valsize != 0)
 716                                 err = drv_ioc_setap(linkid, dlap);
 717                         else
 718                                 err = drv_ioc_clrap(linkid);
 719                 } else {
 720                         /*
 721                          * You might think that the earlier call to
 722                          * mac_prop_check_size() should catch this but
 723                          * it can't. The autopush prop uses 0 as a
 724                          * sentinel value to clear the prop. This
 725                          * check ensures we don't allow a get with a
 726                          * valsize of 0.
 727                          */
 728                         if (kprop->pr_valsize == 0) {
 729                                 err = ENOBUFS;
 730                                 goto done;
 731                         }
 732 
 733                         kprop->pr_perm_flags = MAC_PROP_PERM_RW;
 734                         err = drv_ioc_getap(linkid, dlap);
 735                 }
 736                 break;
 737         }
 738         case MAC_PROP_TAGMODE:
 739                 if (set) {
 740                         link_tagmode_t mode = *(link_tagmode_t *)kprop->pr_val;
 741 
 742                         if (mode != LINK_TAGMODE_VLANONLY &&
 743                             mode != LINK_TAGMODE_NORMAL) {
 744                                 err = EINVAL;
 745                         } else {
 746                                 dlp->dl_tagmode = mode;
 747                                 err = 0;
 748                         }
 749                 } else {
 750                         *(link_tagmode_t *)kprop->pr_val = dlp->dl_tagmode;
 751                         kprop->pr_perm_flags = MAC_PROP_PERM_RW;
 752                         err = 0;
 753                 }
 754                 break;
 755         default: {
 756                 mac_propval_range_t *rangep = NULL;
 757                 void *default_val = NULL;
 758                 uint_t default_size = 0;
 759 
 760                 /* set a property value */
 761                 if (set) {
 762                         err = mac_set_prop(dlp->dl_mh, kprop->pr_num,
 763                             kprop->pr_name, kprop->pr_val, kprop->pr_valsize);
 764                         break;
 765                 }
 766 
 767                 /*
 768                  * Get the property value, default, or possible value
 769                  * depending on flags passed from the user.
 770                  */
 771 
 772                 /* a property has RW permissions by default */
 773                 kprop->pr_perm_flags = MAC_PROP_PERM_RW;
 774 
 775                 if (kprop->pr_flags & DLD_PROP_POSSIBLE) {
 776                         rangep = (mac_propval_range_t *)kprop->pr_val;
 777 
 778                         /*
 779                          * fail if rangep is not aligned to first
 780                          * member of mac_propval_range_t.
 781                          */
 782                         ASSERT(IS_P2ALIGNED(rangep, sizeof (uint_t)));
 783                 } else if (kprop->pr_flags & DLD_PROP_DEFAULT) {
 784                         default_val = kprop->pr_val;
 785                         default_size = kprop->pr_valsize;
 786                 }
 787 
 788                 /*
 789                  * Always return the permissions, and optionally return
 790                  * the default value or possible values range.
 791                  */
 792                 err = mac_prop_info(dlp->dl_mh, kprop->pr_num, kprop->pr_name,
 793                     default_val, default_size, rangep, &kprop->pr_perm_flags);
 794                 if (err != 0)
 795                         goto done;
 796 
 797                 if (default_val == NULL && rangep == NULL) {
 798                         err = mac_get_prop(dlp->dl_mh, kprop->pr_num,
 799                             kprop->pr_name, kprop->pr_val, kprop->pr_valsize);
 800                 }
 801         }
 802         }
 803 
 804 done:
 805         if (!set && ddi_copyout(kprop, (void *)arg, dsize, mode) != 0)
 806                 err = EFAULT;
 807 
 808         if (dlp != NULL)
 809                 dls_link_rele(dlp);
 810 
 811         if (mph != NULL) {
 812                 int32_t cpuid;
 813                 void    *mdip = NULL;
 814 
 815                 if (dlp != NULL && set && err == 0) {
 816                         cpuid = mac_client_intr_cpu(dlp->dl_mch);
 817                         mdip = mac_get_devinfo(dlp->dl_mh);
 818                 }
 819 
 820                 mac_perim_exit(mph);
 821 
 822                 if (mdip != NULL && cpuid != -1)
 823                         mac_client_set_intr_cpu(mdip, dlp->dl_mch, cpuid);
 824         }
 825 
 826         if (dlh != NULL)
 827                 dls_devnet_rele_tmp(dlh);
 828 
 829         if (kprop != NULL)
 830                 kmem_free(kprop, dsize);
 831         return (err);
 832 }
 833 
 834 /* ARGSUSED */
 835 static int
 836 drv_ioc_setprop(void *karg, intptr_t arg, int mode, cred_t *cred, int *rvalp)
 837 {
 838         return (drv_ioc_prop_common(karg, arg, B_TRUE, cred, mode));
 839 }
 840 
 841 /* ARGSUSED */
 842 static int
 843 drv_ioc_getprop(void *karg, intptr_t arg, int mode, cred_t *cred, int *rvalp)
 844 {
 845         return (drv_ioc_prop_common(karg, arg, B_FALSE, cred, mode));
 846 }
 847 
 848 /*
 849  * DLDIOC_RENAME.
 850  *
 851  * This function handles two cases of link renaming. See more in comments above
 852  * dls_datalink_rename().
 853  */
 854 /* ARGSUSED */
 855 static int
 856 drv_ioc_rename(void *karg, intptr_t arg, int mode, cred_t *cred, int *rvalp)
 857 {
 858         dld_ioc_rename_t        *dir = karg;
 859         mod_hash_key_t          key;
 860         mod_hash_val_t          val;
 861         zoneid_t                zoneid = crgetzoneid(cred);
 862         datalink_class_t        class;
 863         int                     err;
 864 
 865         if (zoneid != GLOBAL_ZONEID &&
 866             (zone_check_datalink(&zoneid, dir->dir_linkid1) != 0 ||
 867             dir->dir_linkid2 != DATALINK_INVALID_LINKID &&
 868             zone_check_datalink(&zoneid, dir->dir_linkid2) != 0))
 869                 return (ENOENT);
 870 
 871         if ((err = dls_mgmt_get_linkinfo(dir->dir_linkid1, NULL, &class, NULL,
 872             NULL)) != 0)
 873                 return (err);
 874 
 875         if ((err = drv_ioc_checkprivs(class, cred)) != 0)
 876                 return (err);
 877 
 878         if ((err = dls_devnet_rename(dir->dir_linkid1, dir->dir_linkid2,
 879             dir->dir_link)) != 0)
 880                 return (err);
 881 
 882         if (dir->dir_linkid2 == DATALINK_INVALID_LINKID)
 883                 return (0);
 884 
 885         /*
 886          * if dir_linkid2 is not DATALINK_INVALID_LINKID, it means this
 887          * renaming request is to rename a valid physical link (dir_linkid1)
 888          * to a "removed" physical link (dir_linkid2, which is removed by DR
 889          * or during system shutdown). In this case, the link (specified by
 890          * dir_linkid1) would inherit all the configuration of dir_linkid2,
 891          * and dir_linkid1 and its configuration would be lost.
 892          *
 893          * Remove per-link autopush configuration of dir_linkid1 in this case.
 894          */
 895         key = (mod_hash_key_t)(uintptr_t)dir->dir_linkid1;
 896         rw_enter(&dld_ap_hash_lock, RW_WRITER);
 897         if (mod_hash_find(dld_ap_hashp, key, &val) != 0) {
 898                 rw_exit(&dld_ap_hash_lock);
 899                 return (0);
 900         }
 901 
 902         VERIFY(mod_hash_remove(dld_ap_hashp, key, &val) == 0);
 903         kmem_free(val, sizeof (dld_ap_t));
 904         rw_exit(&dld_ap_hash_lock);
 905         return (0);
 906 }
 907 
 908 static int
 909 drv_ioc_setap(datalink_id_t linkid, struct dlautopush *dlap)
 910 {
 911         dld_ap_t        *dap;
 912         int             i;
 913         mod_hash_key_t  key;
 914 
 915         if (dlap->dap_npush == 0 || dlap->dap_npush > MAXAPUSH)
 916                 return (EINVAL);
 917 
 918         /*
 919          * Validate that the specified list of modules exist.
 920          */
 921         for (i = 0; i < dlap->dap_npush; i++) {
 922                 if (fmodsw_find(dlap->dap_aplist[i], FMODSW_LOAD) == NULL)
 923                         return (EINVAL);
 924         }
 925 
 926 
 927         key = (mod_hash_key_t)(uintptr_t)linkid;
 928 
 929         rw_enter(&dld_ap_hash_lock, RW_WRITER);
 930         if (mod_hash_find(dld_ap_hashp, key, (mod_hash_val_t *)&dap) != 0) {
 931                 dap = kmem_zalloc(sizeof (dld_ap_t), KM_NOSLEEP);
 932                 if (dap == NULL) {
 933                         rw_exit(&dld_ap_hash_lock);
 934                         return (ENOMEM);
 935                 }
 936 
 937                 dap->da_linkid = linkid;
 938                 VERIFY(mod_hash_insert(dld_ap_hashp, key,
 939                     (mod_hash_val_t)dap) == 0);
 940         }
 941 
 942         /*
 943          * Update the configuration.
 944          */
 945         dap->da_anchor = dlap->dap_anchor;
 946         dap->da_npush = dlap->dap_npush;
 947         for (i = 0; i < dlap->dap_npush; i++) {
 948                 (void) strlcpy(dap->da_aplist[i], dlap->dap_aplist[i],
 949                     FMNAMESZ + 1);
 950         }
 951         rw_exit(&dld_ap_hash_lock);
 952 
 953         return (0);
 954 }
 955 
 956 static int
 957 drv_ioc_getap(datalink_id_t linkid, struct dlautopush *dlap)
 958 {
 959         dld_ap_t        *dap;
 960         int             i;
 961 
 962         rw_enter(&dld_ap_hash_lock, RW_READER);
 963         if (mod_hash_find(dld_ap_hashp,
 964             (mod_hash_key_t)(uintptr_t)linkid,
 965             (mod_hash_val_t *)&dap) != 0) {
 966                 rw_exit(&dld_ap_hash_lock);
 967                 dlap->dap_npush = 0;
 968                 return (0);
 969         }
 970 
 971         /*
 972          * Retrieve the configuration.
 973          */
 974         dlap->dap_anchor = dap->da_anchor;
 975         dlap->dap_npush = dap->da_npush;
 976         for (i = 0; i < dap->da_npush; i++) {
 977                 (void) strlcpy(dlap->dap_aplist[i], dap->da_aplist[i],
 978                     FMNAMESZ + 1);
 979         }
 980         rw_exit(&dld_ap_hash_lock);
 981 
 982         return (0);
 983 }
 984 
 985 static int
 986 drv_ioc_clrap(datalink_id_t linkid)
 987 {
 988         mod_hash_val_t  val;
 989         mod_hash_key_t  key;
 990 
 991         key = (mod_hash_key_t)(uintptr_t)linkid;
 992 
 993         rw_enter(&dld_ap_hash_lock, RW_WRITER);
 994         if (mod_hash_find(dld_ap_hashp, key, &val) != 0) {
 995                 rw_exit(&dld_ap_hash_lock);
 996                 return (0);
 997         }
 998 
 999         VERIFY(mod_hash_remove(dld_ap_hashp, key, &val) == 0);
1000         kmem_free(val, sizeof (dld_ap_t));
1001         rw_exit(&dld_ap_hash_lock);
1002         return (0);
1003 }
1004 
1005 /*
1006  * DLDIOC_DOORSERVER
1007  */
1008 /* ARGSUSED */
1009 static int
1010 drv_ioc_doorserver(void *karg, intptr_t arg, int mode, cred_t *cred, int *rvalp)
1011 {
1012         dld_ioc_door_t  *did = karg;
1013 
1014         return (dls_mgmt_door_set(did->did_start_door));
1015 }
1016 
1017 /*
1018  * DLDIOC_USAGELOG
1019  */
1020 /* ARGSUSED */
1021 static int
1022 drv_ioc_usagelog(void *karg, intptr_t arg, int mode, cred_t *cred,
1023     int *rvalp)
1024 {
1025         dld_ioc_usagelog_t      *log_info = (dld_ioc_usagelog_t *)karg;
1026         int                     err = 0;
1027 
1028         if (log_info->ul_type < MAC_LOGTYPE_LINK ||
1029             log_info->ul_type > MAC_LOGTYPE_FLOW)
1030                 return (EINVAL);
1031 
1032         if (log_info->ul_onoff) {
1033                 err = mac_start_logusage(log_info->ul_type,
1034                     log_info->ul_interval);
1035         } else {
1036                 mac_stop_logusage(log_info->ul_type);
1037         }
1038         return (err);
1039 }
1040 
1041 /*
1042  * Process a DLDIOC_ADDFLOW request.
1043  */
1044 /* ARGSUSED */
1045 static int
1046 drv_ioc_addflow(void *karg, intptr_t arg, int mode, cred_t *cred, int *rvalp)
1047 {
1048         dld_ioc_addflow_t       *afp = karg;
1049 
1050         return (dld_add_flow(afp->af_linkid, afp->af_name,
1051             &afp->af_flow_desc, &afp->af_resource_props));
1052 }
1053 
1054 /*
1055  * Process a DLDIOC_REMOVEFLOW request.
1056  */
1057 /* ARGSUSED */
1058 static int
1059 drv_ioc_removeflow(void *karg, intptr_t arg, int mode, cred_t *cred, int *rvalp)
1060 {
1061         dld_ioc_removeflow_t    *rfp = karg;
1062 
1063         return (dld_remove_flow(rfp->rf_name));
1064 }
1065 
1066 /*
1067  * Process a DLDIOC_MODIFYFLOW request.
1068  */
1069 /* ARGSUSED */
1070 static int
1071 drv_ioc_modifyflow(void *karg, intptr_t arg, int mode, cred_t *cred, int *rvalp)
1072 {
1073         dld_ioc_modifyflow_t    *mfp = karg;
1074 
1075         return (dld_modify_flow(mfp->mf_name, &mfp->mf_resource_props));
1076 }
1077 
1078 /*
1079  * Process a DLDIOC_WALKFLOW request.
1080  */
1081 /* ARGSUSED */
1082 static int
1083 drv_ioc_walkflow(void *karg, intptr_t arg, int mode, cred_t *cred, int *rvalp)
1084 {
1085         dld_ioc_walkflow_t      *wfp = karg;
1086 
1087         return (dld_walk_flow(wfp, arg, cred));
1088 }
1089 
1090 /*
1091  * Check for GLDv3 autopush information.  There are three cases:
1092  *
1093  *   1. If devp points to a GLDv3 datalink and it has autopush configuration,
1094  *      fill dlap in with that information and return 0.
1095  *
1096  *   2. If devp points to a GLDv3 datalink but it doesn't have autopush
1097  *      configuration, then replace devp with the physical device (if one
1098  *      exists) and return 1.  This allows stropen() to find the old-school
1099  *      per-driver autopush configuration.  (For softmac, the result is that
1100  *      the softmac dev_t is replaced with the legacy device's dev_t).
1101  *
1102  *   3. If neither of the above apply, don't touch the args and return -1.
1103  */
1104 int
1105 dld_autopush(dev_t *devp, struct dlautopush *dlap)
1106 {
1107         dld_ap_t        *dap;
1108         datalink_id_t   linkid;
1109         dev_t           phydev;
1110 
1111         if (!GLDV3_DRV(getmajor(*devp)))
1112                 return (-1);
1113 
1114         /*
1115          * Find the linkid by the link's dev_t.
1116          */
1117         if (dls_devnet_dev2linkid(*devp, &linkid) != 0)
1118                 return (-1);
1119 
1120         /*
1121          * Find the autopush configuration associated with the linkid.
1122          */
1123         rw_enter(&dld_ap_hash_lock, RW_READER);
1124         if (mod_hash_find(dld_ap_hashp, (mod_hash_key_t)(uintptr_t)linkid,
1125             (mod_hash_val_t *)&dap) == 0) {
1126                 *dlap = dap->da_ap;
1127                 rw_exit(&dld_ap_hash_lock);
1128                 return (0);
1129         }
1130         rw_exit(&dld_ap_hash_lock);
1131 
1132         if (dls_devnet_phydev(linkid, &phydev) != 0)
1133                 return (-1);
1134 
1135         *devp = phydev;
1136         return (1);
1137 }
1138 
1139 /*
1140  * Secure objects implementation
1141  */
1142 
1143 /* ARGSUSED */
1144 static int
1145 drv_secobj_ctor(void *buf, void *arg, int kmflag)
1146 {
1147         bzero(buf, sizeof (dld_secobj_t));
1148         return (0);
1149 }
1150 
1151 static void
1152 drv_secobj_init(void)
1153 {
1154         rw_init(&drv_secobj_lock, NULL, RW_DEFAULT, NULL);
1155         drv_secobj_cachep = kmem_cache_create("drv_secobj_cache",
1156             sizeof (dld_secobj_t), 0, drv_secobj_ctor, NULL,
1157             NULL, NULL, NULL, 0);
1158         drv_secobj_hash = mod_hash_create_extended("drv_secobj_hash",
1159             SECOBJ_WEP_HASHSZ, mod_hash_null_keydtor, mod_hash_null_valdtor,
1160             mod_hash_bystr, NULL, mod_hash_strkey_cmp, KM_SLEEP);
1161 }
1162 
1163 static void
1164 drv_secobj_fini(void)
1165 {
1166         mod_hash_destroy_hash(drv_secobj_hash);
1167         kmem_cache_destroy(drv_secobj_cachep);
1168         rw_destroy(&drv_secobj_lock);
1169 }
1170 
1171 /* ARGSUSED */
1172 static int
1173 drv_ioc_secobj_set(void *karg, intptr_t arg, int mode, cred_t *cred, int *rvalp)
1174 {
1175         dld_ioc_secobj_set_t    *ssp = karg;
1176         dld_secobj_t            *sobjp, *objp;
1177         int                     err;
1178 
1179         sobjp = &ssp->ss_obj;
1180 
1181         if (sobjp->so_class != DLD_SECOBJ_CLASS_WEP &&
1182             sobjp->so_class != DLD_SECOBJ_CLASS_WPA)
1183                 return (EINVAL);
1184 
1185         if (sobjp->so_name[DLD_SECOBJ_NAME_MAX - 1] != '\0' ||
1186             sobjp->so_len > DLD_SECOBJ_VAL_MAX)
1187                 return (EINVAL);
1188 
1189         rw_enter(&drv_secobj_lock, RW_WRITER);
1190         err = mod_hash_find(drv_secobj_hash, (mod_hash_key_t)sobjp->so_name,
1191             (mod_hash_val_t *)&objp);
1192         if (err == 0) {
1193                 if ((ssp->ss_flags & DLD_SECOBJ_OPT_CREATE) != 0) {
1194                         rw_exit(&drv_secobj_lock);
1195                         return (EEXIST);
1196                 }
1197         } else {
1198                 ASSERT(err == MH_ERR_NOTFOUND);
1199                 if ((ssp->ss_flags & DLD_SECOBJ_OPT_CREATE) == 0) {
1200                         rw_exit(&drv_secobj_lock);
1201                         return (ENOENT);
1202                 }
1203                 objp = kmem_cache_alloc(drv_secobj_cachep, KM_SLEEP);
1204                 (void) strlcpy(objp->so_name, sobjp->so_name,
1205                     DLD_SECOBJ_NAME_MAX);
1206 
1207                 VERIFY(mod_hash_insert(drv_secobj_hash,
1208                     (mod_hash_key_t)objp->so_name, (mod_hash_val_t)objp) == 0);
1209         }
1210         bcopy(sobjp->so_val, objp->so_val, sobjp->so_len);
1211         objp->so_len = sobjp->so_len;
1212         objp->so_class = sobjp->so_class;
1213         rw_exit(&drv_secobj_lock);
1214         return (0);
1215 }
1216 
1217 typedef struct dld_secobj_state {
1218         uint_t          ss_free;
1219         uint_t          ss_count;
1220         int             ss_rc;
1221         int             ss_mode;
1222         dld_secobj_t    *ss_objp;
1223 } dld_secobj_state_t;
1224 
1225 /* ARGSUSED */
1226 static uint_t
1227 drv_secobj_walker(mod_hash_key_t key, mod_hash_val_t *val, void *arg)
1228 {
1229         dld_secobj_state_t      *statep = arg;
1230         dld_secobj_t            *sobjp = (dld_secobj_t *)val;
1231 
1232         if (statep->ss_free < sizeof (dld_secobj_t)) {
1233                 statep->ss_rc = ENOSPC;
1234                 return (MH_WALK_TERMINATE);
1235         }
1236         if (ddi_copyout(sobjp, statep->ss_objp, sizeof (*sobjp),
1237             statep->ss_mode) != 0) {
1238                 statep->ss_rc = EFAULT;
1239                 return (MH_WALK_TERMINATE);
1240         }
1241         statep->ss_objp++;
1242         statep->ss_free -= sizeof (dld_secobj_t);
1243         statep->ss_count++;
1244         return (MH_WALK_CONTINUE);
1245 }
1246 
1247 /* ARGSUSED */
1248 static int
1249 drv_ioc_secobj_get(void *karg, intptr_t arg, int mode, cred_t *cred, int *rvalp)
1250 {
1251         dld_ioc_secobj_get_t    *sgp = karg;
1252         dld_secobj_t            *sobjp, *objp;
1253         int                     err;
1254 
1255         sobjp = &sgp->sg_obj;
1256         if (sobjp->so_name[DLD_SECOBJ_NAME_MAX - 1] != '\0')
1257                 return (EINVAL);
1258 
1259         rw_enter(&drv_secobj_lock, RW_READER);
1260         if (sobjp->so_name[0] != '\0') {
1261                 err = mod_hash_find(drv_secobj_hash,
1262                     (mod_hash_key_t)sobjp->so_name, (mod_hash_val_t *)&objp);
1263                 if (err != 0) {
1264                         ASSERT(err == MH_ERR_NOTFOUND);
1265                         rw_exit(&drv_secobj_lock);
1266                         return (ENOENT);
1267                 }
1268                 bcopy(objp->so_val, sobjp->so_val, objp->so_len);
1269                 sobjp->so_len = objp->so_len;
1270                 sobjp->so_class = objp->so_class;
1271                 sgp->sg_count = 1;
1272         } else {
1273                 dld_secobj_state_t      state;
1274 
1275                 state.ss_free = sgp->sg_size - sizeof (dld_ioc_secobj_get_t);
1276                 state.ss_count = 0;
1277                 state.ss_rc = 0;
1278                 state.ss_mode = mode;
1279                 state.ss_objp = (dld_secobj_t *)((uchar_t *)arg +
1280                     sizeof (dld_ioc_secobj_get_t));
1281 
1282                 mod_hash_walk(drv_secobj_hash, drv_secobj_walker, &state);
1283                 if (state.ss_rc != 0) {
1284                         rw_exit(&drv_secobj_lock);
1285                         return (state.ss_rc);
1286                 }
1287                 sgp->sg_count = state.ss_count;
1288         }
1289         rw_exit(&drv_secobj_lock);
1290         return (0);
1291 }
1292 
1293 /* ARGSUSED */
1294 static int
1295 drv_ioc_secobj_unset(void *karg, intptr_t arg, int mode, cred_t *cred,
1296     int *rvalp)
1297 {
1298         dld_ioc_secobj_unset_t  *sup = karg;
1299         dld_secobj_t            *objp;
1300         mod_hash_val_t          val;
1301         int                     err;
1302 
1303         if (sup->su_name[DLD_SECOBJ_NAME_MAX - 1] != '\0')
1304                 return (EINVAL);
1305 
1306         rw_enter(&drv_secobj_lock, RW_WRITER);
1307         err = mod_hash_find(drv_secobj_hash, (mod_hash_key_t)sup->su_name,
1308             (mod_hash_val_t *)&objp);
1309         if (err != 0) {
1310                 ASSERT(err == MH_ERR_NOTFOUND);
1311                 rw_exit(&drv_secobj_lock);
1312                 return (ENOENT);
1313         }
1314         VERIFY(mod_hash_remove(drv_secobj_hash, (mod_hash_key_t)sup->su_name,
1315             (mod_hash_val_t *)&val) == 0);
1316         ASSERT(objp == (dld_secobj_t *)val);
1317 
1318         kmem_cache_free(drv_secobj_cachep, objp);
1319         rw_exit(&drv_secobj_lock);
1320         return (0);
1321 }
1322 
1323 /* ARGSUSED */
1324 static int
1325 drv_ioc_gettran(void *karg, intptr_t arg, int mode, cred_t *cred,
1326     int *rvalp)
1327 {
1328         int                     ret = 0;
1329         mac_perim_handle_t      mph = NULL;
1330         dls_dl_handle_t         dlh = NULL;
1331         dls_link_t              *dlp = NULL;
1332         dld_ioc_gettran_t       *dgt = karg;
1333 
1334         if ((ret = dls_devnet_hold_tmp(dgt->dgt_linkid, &dlh)) != 0)
1335                 goto done;
1336 
1337         if ((ret = mac_perim_enter_by_macname(dls_devnet_mac(dlh), &mph)) != 0)
1338                 goto done;
1339 
1340         if ((ret = dls_link_hold(dls_devnet_mac(dlh), &dlp)) != 0)
1341                 goto done;
1342 
1343         /*
1344          * Make sure that this link belongs to the zone.
1345          */
1346         if (crgetzoneid(cred) != dls_devnet_getownerzid(dlh)) {
1347                 ret = ENOENT;
1348                 goto done;
1349         }
1350 
1351         if (dgt->dgt_tran_id == DLDIOC_GETTRAN_GETNTRAN) {
1352                 ret = mac_transceiver_count(dlp->dl_mh, &dgt->dgt_tran_id);
1353         } else {
1354                 ret = mac_transceiver_info(dlp->dl_mh, dgt->dgt_tran_id,
1355                     &dgt->dgt_present, &dgt->dgt_usable);
1356         }
1357 
1358 done:
1359         if (dlp != NULL)
1360                 dls_link_rele(dlp);
1361 
1362         if (mph != NULL)
1363                 mac_perim_exit(mph);
1364 
1365         if (dlh != NULL)
1366                 dls_devnet_rele_tmp(dlh);
1367 
1368         return (ret);
1369 }
1370 
1371 /* ARGSUSED */
1372 static int
1373 drv_ioc_readtran(void *karg, intptr_t arg, int mode, cred_t *cred,
1374     int *rvalp)
1375 {
1376         int                     ret = 0;
1377         mac_perim_handle_t      mph = NULL;
1378         dls_dl_handle_t         dlh = NULL;
1379         dls_link_t              *dlp = NULL;
1380         dld_ioc_tranio_t        *dti = karg;
1381         uint8_t                 buf[256];
1382         size_t                  nr;
1383 
1384         /*
1385          * Be strict for the moment
1386          */
1387         if (dti->dti_nbytes != 256 || dti->dti_off != 0)
1388                 return (EINVAL);
1389 
1390         if ((ret = dls_devnet_hold_tmp(dti->dti_linkid, &dlh)) != 0)
1391                 goto done;
1392 
1393         if ((ret = mac_perim_enter_by_macname(dls_devnet_mac(dlh), &mph)) != 0)
1394                 goto done;
1395 
1396         if ((ret = dls_link_hold(dls_devnet_mac(dlh), &dlp)) != 0)
1397                 goto done;
1398 
1399         /*
1400          * Make sure that this link belongs to the zone.
1401          */
1402         if (crgetzoneid(cred) != dls_devnet_getownerzid(dlh)) {
1403                 ret = ENOENT;
1404                 goto done;
1405         }
1406 
1407         bzero(buf, sizeof (buf));
1408         if ((ret = mac_transceiver_read(dlp->dl_mh, dti->dti_tran_id,
1409             dti->dti_page, buf, dti->dti_nbytes, dti->dti_off, &nr)) == 0) {
1410                 dti->dti_nbytes = nr;
1411                 ret = ddi_copyout(buf, (void *)(uintptr_t)dti->dti_buf,
1412                     sizeof (buf), mode);
1413         }
1414 
1415 done:
1416         if (dlp != NULL)
1417                 dls_link_rele(dlp);
1418 
1419         if (mph != NULL)
1420                 mac_perim_exit(mph);
1421 
1422         if (dlh != NULL)
1423                 dls_devnet_rele_tmp(dlh);
1424 
1425         return (ret);
1426 }
1427 
1428 /* ARGSUSED */
1429 static int
1430 drv_ioc_getled(void *karg, intptr_t arg, int mode, cred_t *cred,
1431     int *rvalp)
1432 {
1433         int                     ret = 0;
1434         mac_perim_handle_t      mph = NULL;
1435         dls_dl_handle_t         dlh = NULL;
1436         dls_link_t              *dlp = NULL;
1437         dld_ioc_led_t           *dil = karg;
1438 
1439         if ((mode & FREAD) == 0)
1440                 return (EBADF);
1441 
1442         if ((ret = dls_devnet_hold_tmp(dil->dil_linkid, &dlh)) != 0)
1443                 goto done;
1444 
1445         if ((ret = mac_perim_enter_by_macname(dls_devnet_mac(dlh), &mph)) != 0)
1446                 goto done;
1447 
1448         if ((ret = dls_link_hold(dls_devnet_mac(dlh), &dlp)) != 0)
1449                 goto done;
1450 
1451         /*
1452          * Make sure that this link belongs to the zone.
1453          */
1454         if (crgetzoneid(cred) != dls_devnet_getownerzid(dlh)) {
1455                 ret = ENOENT;
1456                 goto done;
1457         }
1458 
1459         ret = mac_led_get(dlp->dl_mh, &dil->dil_supported, &dil->dil_active);
1460 
1461 done:
1462         if (dlp != NULL)
1463                 dls_link_rele(dlp);
1464 
1465         if (mph != NULL)
1466                 mac_perim_exit(mph);
1467 
1468         if (dlh != NULL)
1469                 dls_devnet_rele_tmp(dlh);
1470 
1471         return (ret);
1472 }
1473 
1474 /* ARGSUSED */
1475 static int
1476 drv_ioc_setled(void *karg, intptr_t arg, int mode, cred_t *cred,
1477     int *rvalp)
1478 {
1479         int                     ret = 0;
1480         mac_perim_handle_t      mph = NULL;
1481         dls_dl_handle_t         dlh = NULL;
1482         dls_link_t              *dlp = NULL;
1483         dld_ioc_led_t           *dil = karg;
1484 
1485         if ((mode & FWRITE) == 0)
1486                 return (EBADF);
1487 
1488         if ((ret = dls_devnet_hold_tmp(dil->dil_linkid, &dlh)) != 0)
1489                 goto done;
1490 
1491         if ((ret = mac_perim_enter_by_macname(dls_devnet_mac(dlh), &mph)) != 0)
1492                 goto done;
1493 
1494         if ((ret = dls_link_hold(dls_devnet_mac(dlh), &dlp)) != 0)
1495                 goto done;
1496 
1497         /*
1498          * Make sure that this link belongs to the zone.
1499          */
1500         if (crgetzoneid(cred) != dls_devnet_getownerzid(dlh)) {
1501                 ret = ENOENT;
1502                 goto done;
1503         }
1504 
1505         ret = mac_led_set(dlp->dl_mh, dil->dil_active);
1506 
1507 done:
1508         if (dlp != NULL)
1509                 dls_link_rele(dlp);
1510 
1511         if (mph != NULL)
1512                 mac_perim_exit(mph);
1513 
1514         if (dlh != NULL)
1515                 dls_devnet_rele_tmp(dlh);
1516 
1517         return (ret);
1518 }
1519 
1520 
1521 /*
1522  * Note that ioctls that modify links have a NULL di_priv_func(), as
1523  * privileges can only be checked after we know the class of the link being
1524  * modified (due to class-specific fine-grained privileges such as
1525  * sys_iptun_config).
1526  */
1527 static dld_ioc_info_t drv_ioc_list[] = {
1528         {DLDIOC_ATTR, DLDCOPYINOUT, sizeof (dld_ioc_attr_t),
1529             drv_ioc_attr, NULL},
1530         {DLDIOC_PHYS_ATTR, DLDCOPYINOUT, sizeof (dld_ioc_phys_attr_t),
1531             drv_ioc_phys_attr, NULL},
1532         {DLDIOC_SECOBJ_SET, DLDCOPYIN, sizeof (dld_ioc_secobj_set_t),
1533             drv_ioc_secobj_set, secpolicy_dl_config},
1534         {DLDIOC_SECOBJ_GET, DLDCOPYINOUT, sizeof (dld_ioc_secobj_get_t),
1535             drv_ioc_secobj_get, secpolicy_dl_config},
1536         {DLDIOC_SECOBJ_UNSET, DLDCOPYIN, sizeof (dld_ioc_secobj_unset_t),
1537             drv_ioc_secobj_unset, secpolicy_dl_config},
1538         {DLDIOC_DOORSERVER, DLDCOPYIN, sizeof (dld_ioc_door_t),
1539             drv_ioc_doorserver, secpolicy_dl_config},
1540         {DLDIOC_RENAME, DLDCOPYIN, sizeof (dld_ioc_rename_t),
1541             drv_ioc_rename, NULL},
1542         {DLDIOC_MACADDRGET, DLDCOPYINOUT, sizeof (dld_ioc_macaddrget_t),
1543             drv_ioc_macaddrget, NULL},
1544         {DLDIOC_ADDFLOW, DLDCOPYIN, sizeof (dld_ioc_addflow_t),
1545             drv_ioc_addflow, secpolicy_dl_config},
1546         {DLDIOC_REMOVEFLOW, DLDCOPYIN, sizeof (dld_ioc_removeflow_t),
1547             drv_ioc_removeflow, secpolicy_dl_config},
1548         {DLDIOC_MODIFYFLOW, DLDCOPYIN, sizeof (dld_ioc_modifyflow_t),
1549             drv_ioc_modifyflow, secpolicy_dl_config},
1550         {DLDIOC_WALKFLOW, DLDCOPYINOUT, sizeof (dld_ioc_walkflow_t),
1551             drv_ioc_walkflow, NULL},
1552         {DLDIOC_USAGELOG, DLDCOPYIN, sizeof (dld_ioc_usagelog_t),
1553             drv_ioc_usagelog, secpolicy_dl_config},
1554         {DLDIOC_SETMACPROP, DLDCOPYIN, sizeof (dld_ioc_macprop_t),
1555             drv_ioc_setprop, NULL},
1556         {DLDIOC_GETMACPROP, DLDCOPYIN, sizeof (dld_ioc_macprop_t),
1557             drv_ioc_getprop, NULL},
1558         {DLDIOC_GETHWGRP, DLDCOPYINOUT, sizeof (dld_ioc_hwgrpget_t),
1559             drv_ioc_hwgrpget, NULL},
1560         {DLDIOC_GETTRAN, DLDCOPYINOUT, sizeof (dld_ioc_gettran_t),
1561             drv_ioc_gettran, NULL },
1562         {DLDIOC_READTRAN, DLDCOPYINOUT, sizeof (dld_ioc_tranio_t),
1563             drv_ioc_readtran, NULL },
1564         {DLDIOC_GETLED, DLDCOPYINOUT, sizeof (dld_ioc_led_t),
1565             drv_ioc_getled, NULL },
1566         {DLDIOC_SETLED, DLDCOPYIN, sizeof (dld_ioc_led_t),
1567             drv_ioc_setled, secpolicy_dl_config}
1568 };
1569 
1570 typedef struct dld_ioc_modentry {
1571         uint16_t        dim_modid;      /* Top 16 bits of ioctl command */
1572         char            *dim_modname;   /* Module to be loaded */
1573         int             ctrl_node_inst; /* Ctrl node instance */
1574         dld_ioc_info_t  *dim_list;      /* array of ioctl structures */
1575         uint_t          dim_count;      /* number of elements in dim_list */
1576 } dld_ioc_modentry_t;
1577 
1578 /*
1579  * For all modules except for dld, dim_list and dim_count are assigned
1580  * when the modules register their ioctls in dld_ioc_register().  We
1581  * can statically initialize dld's ioctls in-line here; there's no
1582  * need for it to call dld_ioc_register() itself. ctrl_node_inst controls
1583  * whether an instance of the device will be held or the driver. If set to
1584  * a non-negative integer, device instance specified in ctrl_node_inst will
1585  * be held; so dld_ioc_register() _must_ be called in xxx_attach() routine of
1586  * the driver. If set to -1, driver will be held; so dld_ioc_register() _must_
1587  * be called in xxx_init() routine of the driver.
1588  */
1589 static dld_ioc_modentry_t dld_ioc_modtable[] = {
1590         {DLD_IOC,       "dld", 0, drv_ioc_list, DLDIOCCNT(drv_ioc_list)},
1591         {AGGR_IOC,      "aggr", 0, NULL, 0},
1592         {VNIC_IOC,      "vnic", 0, NULL, 0},
1593         {SIMNET_IOC,    "simnet", 0, NULL, 0},
1594         {BRIDGE_IOC,    "bridge", 0, NULL, 0},
1595         {IPTUN_IOC,     "iptun", 0, NULL, 0},
1596         {IBPART_IOC,    "ibp", -1, NULL, 0},
1597         {OVERLAY_IOC,   "overlay", 0, NULL, 0}
1598 };
1599 #define DLDIOC_CNT      \
1600         (sizeof (dld_ioc_modtable) / sizeof (dld_ioc_modentry_t))
1601 
1602 static dld_ioc_modentry_t *
1603 dld_ioc_findmod(uint16_t modid)
1604 {
1605         int     i;
1606 
1607         for (i = 0; i < DLDIOC_CNT; i++) {
1608                 if (modid == dld_ioc_modtable[i].dim_modid)
1609                         return (&dld_ioc_modtable[i]);
1610         }
1611         return (NULL);
1612 }
1613 
1614 int
1615 dld_ioc_register(uint16_t modid, dld_ioc_info_t *list, uint_t count)
1616 {
1617         dld_ioc_modentry_t *dim = dld_ioc_findmod(modid);
1618 
1619         if (dim == NULL)
1620                 return (ENOENT);
1621 
1622         dim->dim_list = list;
1623         dim->dim_count = count;
1624         return (0);
1625 }
1626 
1627 void
1628 dld_ioc_unregister(uint16_t modid)
1629 {
1630         VERIFY(dld_ioc_register(modid, NULL, 0) == 0);
1631 }
1632 
1633 /*
1634  * The general design with GLDv3 ioctls is that all ioctls issued
1635  * through /dev/dld go through this drv_ioctl() function.  This
1636  * function handles all ioctls on behalf of modules listed in
1637  * dld_ioc_modtable.
1638  *
1639  * When an ioctl is received, this function looks for the associated
1640  * module-id-specific ioctl information using dld_ioc_findmod(). The
1641  * call to ddi_hold_driver() or ddi_hold_devi_by_instance() on the
1642  * associated device will cause the kernel module responsible for the
1643  * ioctl to be loaded if it's not already loaded, which should result
1644  * in that module calling dld_ioc_register(), thereby filling in the
1645  * dim_list containing the details for the ioctl being processed.
1646  *
1647  * This function can then perform operations such as copyin() data and
1648  * do credential checks based on the registered ioctl information,
1649  * then issue the callback function di_func() registered by the
1650  * responsible module.  Upon return, the appropriate copyout()
1651  * operation can be performed and the operation completes.
1652  */
1653 /* ARGSUSED */
1654 static int
1655 drv_ioctl(dev_t dev, int cmd, intptr_t arg, int mode, cred_t *cred, int *rvalp)
1656 {
1657         dld_ioc_modentry_t *dim;
1658         dld_ioc_info_t  *info;
1659         dev_info_t      *dip = NULL;
1660         struct dev_ops  *dops = NULL;
1661         major_t         major;
1662         void            *buf = NULL;
1663         size_t          sz;
1664         int             i, err;
1665 
1666         if ((dim = dld_ioc_findmod(DLD_IOC_MODID(cmd))) == NULL)
1667                 return (ENOTSUP);
1668 
1669         major = ddi_name_to_major(dim->dim_modname);
1670 
1671         if (dim->ctrl_node_inst == -1) {
1672                 /*
1673                  * No dedicated instance to process ioctls.
1674                  * dld_ioc_register() is called in xxx_init().
1675                  */
1676                 dops = ddi_hold_driver(major);
1677         } else {
1678                 /*
1679                  * Dedicated instance to handle ioctl.
1680                  * dld_ioc_register() is called in xxx_attach().
1681                  */
1682                 dip = ddi_hold_devi_by_instance(major, dim->ctrl_node_inst, 0);
1683         }
1684 
1685         if ((dip == NULL && dops == NULL) || dim->dim_list == NULL) {
1686                 err = ENODEV;
1687                 goto done;
1688         }
1689 
1690         for (i = 0; i < dim->dim_count; i++) {
1691                 if (cmd == dim->dim_list[i].di_cmd)
1692                         break;
1693         }
1694         if (i == dim->dim_count) {
1695                 err = ENOTSUP;
1696                 goto done;
1697         }
1698 
1699         info = &dim->dim_list[i];
1700 
1701         if (info->di_priv_func != NULL &&
1702             (err = info->di_priv_func(cred)) != 0)
1703                 goto done;
1704 
1705         sz = info->di_argsize;
1706         if ((buf = kmem_zalloc(sz, KM_NOSLEEP)) == NULL) {
1707                 err = ENOMEM;
1708                 goto done;
1709         }
1710 
1711         if ((info->di_flags & DLDCOPYIN) &&
1712             ddi_copyin((void *)arg, buf, sz, mode) != 0) {
1713                 err = EFAULT;
1714                 goto done;
1715         }
1716 
1717         err = info->di_func(buf, arg, mode, cred, rvalp);
1718 
1719         if ((info->di_flags & DLDCOPYOUT) &&
1720             ddi_copyout(buf, (void *)arg, sz, mode) != 0 && err == 0)
1721                 err = EFAULT;
1722 
1723 done:
1724         if (buf != NULL)
1725                 kmem_free(buf, sz);
1726         if (dip != NULL)
1727                 ddi_release_devi(dip);
1728         if (dops != NULL)
1729                 ddi_rele_driver(major);
1730         return (err);
1731 }