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