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