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