Home | History | Annotate | Download | only in dld
      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 2009 Sun Microsystems, Inc.  All rights reserved.
     23  * Use is subject to license terms.
     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 
     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, grpnum;
    433 	uint_t			bytes_left;
    434 	zoneid_t		zoneid = crgetzoneid(cred);
    435 
    436 	if (zoneid != GLOBAL_ZONEID &&
    437 	    zone_check_datalink(&zoneid, hwgrpp->dih_linkid) != 0)
    438 		return (ENOENT);
    439 
    440 	hwgrpp->dih_n_groups = 0;
    441 	err = mac_open_by_linkid(hwgrpp->dih_linkid, &mh);
    442 	if (err != 0)
    443 		goto done;
    444 
    445 	hip = (dld_hwgrpinfo_t *)
    446 	    ((uchar_t *)arg + sizeof (dld_ioc_hwgrpget_t));
    447 	bytes_left = hwgrpp->dih_size;
    448 	grpnum = mac_hwgrp_num(mh);
    449 	for (i = 0; i < grpnum; i++) {
    450 		if (sizeof (dld_hwgrpinfo_t) > bytes_left) {
    451 			err = ENOSPC;
    452 			goto done;
    453 		}
    454 
    455 		bzero(&hwgrp, sizeof (hwgrp));
    456 		bcopy(mac_name(mh), hwgrp.dhi_link_name,
    457 		    sizeof (hwgrp.dhi_link_name));
    458 		mac_get_hwgrp_info(mh, i, &hwgrp.dhi_grp_num,
    459 		    &hwgrp.dhi_n_rings, &hwgrp.dhi_grp_type,
    460 		    &hwgrp.dhi_n_clnts, hwgrp.dhi_clnts);
    461 		if (copyout(&hwgrp, hip, sizeof (hwgrp)) != 0) {
    462 			err = EFAULT;
    463 			goto done;
    464 		}
    465 
    466 		hip++;
    467 		bytes_left -= sizeof (dld_hwgrpinfo_t);
    468 	}
    469 
    470 done:
    471 	if (mh != NULL)
    472 		dld_mac_close(mh);
    473 	if (err == 0)
    474 		hwgrpp->dih_n_groups = grpnum;
    475 	return (err);
    476 }
    477 
    478 /* ARGSUSED */
    479 static int
    480 drv_ioc_macaddrget(void *karg, intptr_t arg, int mode, cred_t *cred, int *rvalp)
    481 {
    482 	dld_ioc_macaddrget_t	*magp = karg;
    483 	dld_macaddrinfo_t	mai, *maip;
    484 	mac_handle_t		mh = NULL;
    485 	int			i, err;
    486 	uint_t			bytes_left;
    487 	boolean_t		is_used;
    488 	zoneid_t		zoneid = crgetzoneid(cred);
    489 
    490 	if (zoneid != GLOBAL_ZONEID &&
    491 	    zone_check_datalink(&zoneid, magp->dig_linkid) != 0)
    492 		return (ENOENT);
    493 
    494 	magp->dig_count = 0;
    495 	err = mac_open_by_linkid(magp->dig_linkid, &mh);
    496 	if (err != 0)
    497 		goto done;
    498 
    499 	maip = (dld_macaddrinfo_t *)
    500 	    ((uchar_t *)arg + sizeof (dld_ioc_macaddrget_t));
    501 	bytes_left = magp->dig_size;
    502 
    503 	for (i = 0; i < mac_addr_factory_num(mh) + 1; i++) {
    504 		if (sizeof (dld_macaddrinfo_t) > bytes_left) {
    505 			err = ENOSPC;
    506 			goto done;
    507 		}
    508 
    509 		bzero(&mai, sizeof (mai));
    510 
    511 		if (i == 0) {
    512 			/* primary MAC address */
    513 			mac_unicast_primary_get(mh, mai.dmi_addr);
    514 			mai.dmi_addrlen = mac_addr_len(mh);
    515 			mac_unicast_primary_info(mh, mai.dmi_client_name,
    516 			    &is_used);
    517 		} else {
    518 			/* factory MAC address slot */
    519 			mac_addr_factory_value(mh, i, mai.dmi_addr,
    520 			    &mai.dmi_addrlen, mai.dmi_client_name, &is_used);
    521 		}
    522 
    523 		mai.dmi_slot = i;
    524 		if (is_used)
    525 			mai.dmi_flags |= DLDIOCMACADDR_USED;
    526 
    527 		if (copyout(&mai, maip, sizeof (mai)) != 0) {
    528 			err = EFAULT;
    529 			goto done;
    530 		}
    531 
    532 		maip++;
    533 		bytes_left -= sizeof (dld_macaddrinfo_t);
    534 	}
    535 
    536 done:
    537 	if (mh != NULL)
    538 		dld_mac_close(mh);
    539 	if (err == 0)
    540 		magp->dig_count = mac_addr_factory_num(mh) + 1;
    541 	return (err);
    542 }
    543 
    544 /*
    545  * DLDIOC_SET/GETPROP
    546  */
    547 static int
    548 drv_ioc_prop_common(dld_ioc_macprop_t *prop, intptr_t arg, boolean_t set,
    549     cred_t *cred, int mode)
    550 {
    551 	int			err = EINVAL;
    552 	dls_dl_handle_t 	dlh = NULL;
    553 	dls_link_t		*dlp = NULL;
    554 	mac_perim_handle_t	mph = NULL;
    555 	mac_prop_t		macprop;
    556 	dld_ioc_macprop_t	*kprop;
    557 	datalink_id_t		linkid;
    558 	datalink_class_t	class;
    559 	zoneid_t		zoneid = crgetzoneid(cred);
    560 	uint_t			dsize;
    561 
    562 	/*
    563 	 * We only use pr_valsize from prop, as the caller only did a
    564 	 * copyin() for sizeof (dld_ioc_prop_t), which doesn't cover
    565 	 * the property data.  We copyin the full dld_ioc_prop_t
    566 	 * including the data into kprop down below.
    567 	 */
    568 	dsize = sizeof (dld_ioc_macprop_t) + prop->pr_valsize - 1;
    569 	if (dsize < prop->pr_valsize)
    570 		return (EINVAL);
    571 
    572 	/*
    573 	 * The property data is variable size, so we need to allocate
    574 	 * a buffer for kernel use as this data was not part of the
    575 	 * prop allocation and copyin() done by the framework.
    576 	 */
    577 	if ((kprop = kmem_alloc(dsize, KM_NOSLEEP)) == NULL)
    578 		return (ENOMEM);
    579 
    580 	if (ddi_copyin((void *)arg, kprop, dsize, mode) != 0) {
    581 		err = EFAULT;
    582 		goto done;
    583 	}
    584 
    585 	linkid = kprop->pr_linkid;
    586 
    587 	if (set) {
    588 		if ((err = dls_mgmt_get_linkinfo(linkid, NULL, &class, NULL,
    589 		    NULL)) != 0 || (err = drv_ioc_checkprivs(class, cred)) != 0)
    590 			goto done;
    591 	}
    592 
    593 	if ((err = dls_devnet_hold_tmp(linkid, &dlh)) != 0)
    594 		goto done;
    595 	if ((err = mac_perim_enter_by_macname(dls_devnet_mac(dlh), &mph)) != 0)
    596 		goto done;
    597 	if ((err = dls_link_hold(dls_devnet_mac(dlh), &dlp)) != 0)
    598 		goto done;
    599 
    600 	/*
    601 	 * Don't allow a process to get or set properties of a link if that
    602 	 * link doesn't belong to that zone.
    603 	 */
    604 	if (zoneid != dls_devnet_getownerzid(dlh)) {
    605 		err = ENOENT;
    606 		goto done;
    607 	}
    608 
    609 	switch (kprop->pr_num) {
    610 	case MAC_PROP_ZONE:
    611 		if (set) {
    612 			dld_ioc_zid_t *dzp = (dld_ioc_zid_t *)kprop->pr_val;
    613 
    614 			if (zoneid != GLOBAL_ZONEID) {
    615 				err = EACCES;
    616 				goto done;
    617 			}
    618 			err = dls_devnet_setzid(dlh, dzp->diz_zid);
    619 		} else {
    620 			kprop->pr_perm_flags = MAC_PROP_PERM_RW;
    621 			(*(zoneid_t *)kprop->pr_val) = dls_devnet_getzid(dlh);
    622 		}
    623 		break;
    624 	case MAC_PROP_AUTOPUSH: {
    625 		struct dlautopush *dlap = (struct dlautopush *)kprop->pr_val;
    626 
    627 		if (set) {
    628 			if (kprop->pr_valsize != 0)
    629 				err = drv_ioc_setap(linkid, dlap);
    630 			else
    631 				err = drv_ioc_clrap(linkid);
    632 		} else {
    633 			kprop->pr_perm_flags = MAC_PROP_PERM_RW;
    634 			err = drv_ioc_getap(linkid, dlap);
    635 		}
    636 		break;
    637 	}
    638 	case MAC_PROP_TAGMODE:
    639 		if (set) {
    640 			link_tagmode_t mode = *(link_tagmode_t *)kprop->pr_val;
    641 
    642 			if (mode != LINK_TAGMODE_VLANONLY &&
    643 			    mode != LINK_TAGMODE_NORMAL) {
    644 				err = EINVAL;
    645 			} else {
    646 				dlp->dl_tagmode = mode;
    647 				err = 0;
    648 			}
    649 		} else {
    650 			*(link_tagmode_t *)kprop->pr_val = dlp->dl_tagmode;
    651 			kprop->pr_perm_flags = MAC_PROP_PERM_RW;
    652 			err = 0;
    653 		}
    654 		break;
    655 	default:
    656 		macprop.mp_name = kprop->pr_name;
    657 		macprop.mp_id = kprop->pr_num;
    658 		macprop.mp_flags = kprop->pr_flags;
    659 
    660 		if (set) {
    661 			err = mac_set_prop(dlp->dl_mh, &macprop, kprop->pr_val,
    662 			    kprop->pr_valsize);
    663 		} else {
    664 			kprop->pr_perm_flags = MAC_PROP_PERM_RW;
    665 			err = mac_get_prop(dlp->dl_mh, &macprop, kprop->pr_val,
    666 			    kprop->pr_valsize, &kprop->pr_perm_flags);
    667 		}
    668 	}
    669 
    670 done:
    671 	if (!set && ddi_copyout(kprop, (void *)arg, dsize, mode) != 0)
    672 		err = EFAULT;
    673 
    674 	if (dlp != NULL)
    675 		dls_link_rele(dlp);
    676 	if (mph != NULL) {
    677 		int32_t	cpuid;
    678 		void	*mdip = NULL;
    679 
    680 		if (dlp != NULL && set && err == 0) {
    681 			cpuid = mac_client_intr_cpu(dlp->dl_mch);
    682 			mdip = mac_get_devinfo(dlp->dl_mh);
    683 		}
    684 
    685 		mac_perim_exit(mph);
    686 
    687 		if (mdip != NULL)
    688 			mac_client_set_intr_cpu(mdip, dlp->dl_mch, cpuid);
    689 	}
    690 	if (dlh != NULL)
    691 		dls_devnet_rele_tmp(dlh);
    692 
    693 	if (kprop != NULL)
    694 		kmem_free(kprop, dsize);
    695 	return (err);
    696 }
    697 
    698 /* ARGSUSED */
    699 static int
    700 drv_ioc_setprop(void *karg, intptr_t arg, int mode, cred_t *cred, int *rvalp)
    701 {
    702 	return (drv_ioc_prop_common(karg, arg, B_TRUE, cred, mode));
    703 }
    704 
    705 /* ARGSUSED */
    706 static int
    707 drv_ioc_getprop(void *karg, intptr_t arg, int mode, cred_t *cred, int *rvalp)
    708 {
    709 	return (drv_ioc_prop_common(karg, arg, B_FALSE, cred, mode));
    710 }
    711 
    712 /*
    713  * DLDIOC_RENAME.
    714  *
    715  * This function handles two cases of link renaming. See more in comments above
    716  * dls_datalink_rename().
    717  */
    718 /* ARGSUSED */
    719 static int
    720 drv_ioc_rename(void *karg, intptr_t arg, int mode, cred_t *cred, int *rvalp)
    721 {
    722 	dld_ioc_rename_t	*dir = karg;
    723 	mod_hash_key_t		key;
    724 	mod_hash_val_t		val;
    725 	zoneid_t		zoneid = crgetzoneid(cred);
    726 	datalink_class_t	class;
    727 	int			err;
    728 
    729 	if (zoneid != GLOBAL_ZONEID &&
    730 	    (zone_check_datalink(&zoneid, dir->dir_linkid1) != 0 ||
    731 	    dir->dir_linkid2 != DATALINK_INVALID_LINKID &&
    732 	    zone_check_datalink(&zoneid, dir->dir_linkid2) != 0))
    733 		return (ENOENT);
    734 
    735 	if ((err = dls_mgmt_get_linkinfo(dir->dir_linkid1, NULL, &class, NULL,
    736 	    NULL)) != 0)
    737 		return (err);
    738 
    739 	if ((err = drv_ioc_checkprivs(class, cred)) != 0)
    740 		return (err);
    741 
    742 	if ((err = dls_devnet_rename(dir->dir_linkid1, dir->dir_linkid2,
    743 	    dir->dir_link)) != 0)
    744 		return (err);
    745 
    746 	if (dir->dir_linkid2 == DATALINK_INVALID_LINKID)
    747 		return (0);
    748 
    749 	/*
    750 	 * if dir_linkid2 is not DATALINK_INVALID_LINKID, it means this
    751 	 * renaming request is to rename a valid physical link (dir_linkid1)
    752 	 * to a "removed" physical link (dir_linkid2, which is removed by DR
    753 	 * or during system shutdown). In this case, the link (specified by
    754 	 * dir_linkid1) would inherit all the configuration of dir_linkid2,
    755 	 * and dir_linkid1 and its configuration would be lost.
    756 	 *
    757 	 * Remove per-link autopush configuration of dir_linkid1 in this case.
    758 	 */
    759 	key = (mod_hash_key_t)(uintptr_t)dir->dir_linkid1;
    760 	rw_enter(&dld_ap_hash_lock, RW_WRITER);
    761 	if (mod_hash_find(dld_ap_hashp, key, &val) != 0) {
    762 		rw_exit(&dld_ap_hash_lock);
    763 		return (0);
    764 	}
    765 
    766 	VERIFY(mod_hash_remove(dld_ap_hashp, key, &val) == 0);
    767 	kmem_free(val, sizeof (dld_ap_t));
    768 	rw_exit(&dld_ap_hash_lock);
    769 	return (0);
    770 }
    771 
    772 static int
    773 drv_ioc_setap(datalink_id_t linkid, struct dlautopush *dlap)
    774 {
    775 	dld_ap_t	*dap;
    776 	int		i;
    777 	mod_hash_key_t	key;
    778 
    779 	if (dlap->dap_npush == 0 || dlap->dap_npush > MAXAPUSH)
    780 		return (EINVAL);
    781 
    782 	/*
    783 	 * Validate that the specified list of modules exist.
    784 	 */
    785 	for (i = 0; i < dlap->dap_npush; i++) {
    786 		if (fmodsw_find(dlap->dap_aplist[i], FMODSW_LOAD) == NULL)
    787 			return (EINVAL);
    788 	}
    789 
    790 
    791 	key = (mod_hash_key_t)(uintptr_t)linkid;
    792 
    793 	rw_enter(&dld_ap_hash_lock, RW_WRITER);
    794 	if (mod_hash_find(dld_ap_hashp, key, (mod_hash_val_t *)&dap) != 0) {
    795 		dap = kmem_zalloc(sizeof (dld_ap_t), KM_NOSLEEP);
    796 		if (dap == NULL) {
    797 			rw_exit(&dld_ap_hash_lock);
    798 			return (ENOMEM);
    799 		}
    800 
    801 		dap->da_linkid = linkid;
    802 		VERIFY(mod_hash_insert(dld_ap_hashp, key,
    803 		    (mod_hash_val_t)dap) == 0);
    804 	}
    805 
    806 	/*
    807 	 * Update the configuration.
    808 	 */
    809 	dap->da_anchor = dlap->dap_anchor;
    810 	dap->da_npush = dlap->dap_npush;
    811 	for (i = 0; i < dlap->dap_npush; i++) {
    812 		(void) strlcpy(dap->da_aplist[i], dlap->dap_aplist[i],
    813 		    FMNAMESZ + 1);
    814 	}
    815 	rw_exit(&dld_ap_hash_lock);
    816 
    817 	return (0);
    818 }
    819 
    820 static int
    821 drv_ioc_getap(datalink_id_t linkid, struct dlautopush *dlap)
    822 {
    823 	dld_ap_t	*dap;
    824 	int		i;
    825 
    826 	rw_enter(&dld_ap_hash_lock, RW_READER);
    827 	if (mod_hash_find(dld_ap_hashp,
    828 	    (mod_hash_key_t)(uintptr_t)linkid,
    829 	    (mod_hash_val_t *)&dap) != 0) {
    830 		rw_exit(&dld_ap_hash_lock);
    831 		return (ENOENT);
    832 	}
    833 
    834 	/*
    835 	 * Retrieve the configuration.
    836 	 */
    837 	dlap->dap_anchor = dap->da_anchor;
    838 	dlap->dap_npush = dap->da_npush;
    839 	for (i = 0; i < dap->da_npush; i++) {
    840 		(void) strlcpy(dlap->dap_aplist[i], dap->da_aplist[i],
    841 		    FMNAMESZ + 1);
    842 	}
    843 	rw_exit(&dld_ap_hash_lock);
    844 
    845 	return (0);
    846 }
    847 
    848 static int
    849 drv_ioc_clrap(datalink_id_t linkid)
    850 {
    851 	mod_hash_val_t	val;
    852 	mod_hash_key_t	key;
    853 
    854 	key = (mod_hash_key_t)(uintptr_t)linkid;
    855 
    856 	rw_enter(&dld_ap_hash_lock, RW_WRITER);
    857 	if (mod_hash_find(dld_ap_hashp, key, &val) != 0) {
    858 		rw_exit(&dld_ap_hash_lock);
    859 		return (0);
    860 	}
    861 
    862 	VERIFY(mod_hash_remove(dld_ap_hashp, key, &val) == 0);
    863 	kmem_free(val, sizeof (dld_ap_t));
    864 	rw_exit(&dld_ap_hash_lock);
    865 	return (0);
    866 }
    867 
    868 /*
    869  * DLDIOC_DOORSERVER
    870  */
    871 /* ARGSUSED */
    872 static int
    873 drv_ioc_doorserver(void *karg, intptr_t arg, int mode, cred_t *cred, int *rvalp)
    874 {
    875 	dld_ioc_door_t	*did = karg;
    876 
    877 	return (dls_mgmt_door_set(did->did_start_door));
    878 }
    879 
    880 /*
    881  * DLDIOC_USAGELOG
    882  */
    883 /* ARGSUSED */
    884 static int
    885 drv_ioc_usagelog(void *karg, intptr_t arg, int mode, cred_t *cred,
    886     int *rvalp)
    887 {
    888 	dld_ioc_usagelog_t	*log_info = (dld_ioc_usagelog_t *)karg;
    889 	int			err = 0;
    890 
    891 	if (log_info->ul_type < MAC_LOGTYPE_LINK ||
    892 	    log_info->ul_type > MAC_LOGTYPE_FLOW)
    893 		return (EINVAL);
    894 
    895 	if (log_info->ul_onoff) {
    896 		err = mac_start_logusage(log_info->ul_type,
    897 		    log_info->ul_interval);
    898 	} else {
    899 		mac_stop_logusage(log_info->ul_type);
    900 	}
    901 	return (err);
    902 }
    903 
    904 /*
    905  * Process a DLDIOC_ADDFLOW request.
    906  */
    907 /* ARGSUSED */
    908 static int
    909 drv_ioc_addflow(void *karg, intptr_t arg, int mode, cred_t *cred, int *rvalp)
    910 {
    911 	dld_ioc_addflow_t	*afp = karg;
    912 
    913 	return (dld_add_flow(afp->af_linkid, afp->af_name,
    914 	    &afp->af_flow_desc, &afp->af_resource_props));
    915 }
    916 
    917 /*
    918  * Process a DLDIOC_REMOVEFLOW request.
    919  */
    920 /* ARGSUSED */
    921 static int
    922 drv_ioc_removeflow(void *karg, intptr_t arg, int mode, cred_t *cred, int *rvalp)
    923 {
    924 	dld_ioc_removeflow_t	*rfp = karg;
    925 
    926 	return (dld_remove_flow(rfp->rf_name));
    927 }
    928 
    929 /*
    930  * Process a DLDIOC_MODIFYFLOW request.
    931  */
    932 /* ARGSUSED */
    933 static int
    934 drv_ioc_modifyflow(void *karg, intptr_t arg, int mode, cred_t *cred, int *rvalp)
    935 {
    936 	dld_ioc_modifyflow_t	*mfp = karg;
    937 
    938 	return (dld_modify_flow(mfp->mf_name, &mfp->mf_resource_props));
    939 }
    940 
    941 /*
    942  * Process a DLDIOC_WALKFLOW request.
    943  */
    944 /* ARGSUSED */
    945 static int
    946 drv_ioc_walkflow(void *karg, intptr_t arg, int mode, cred_t *cred, int *rvalp)
    947 {
    948 	dld_ioc_walkflow_t	*wfp = karg;
    949 
    950 	return (dld_walk_flow(wfp, arg, cred));
    951 }
    952 
    953 /*
    954  * Check for GLDv3 autopush information.  There are three cases:
    955  *
    956  *   1. If devp points to a GLDv3 datalink and it has autopush configuration,
    957  *	fill dlap in with that information and return 0.
    958  *
    959  *   2. If devp points to a GLDv3 datalink but it doesn't have autopush
    960  *	configuration, then replace devp with the physical device (if one
    961  *	exists) and return 1.  This allows stropen() to find the old-school
    962  *	per-driver autopush configuration.  (For softmac, the result is that
    963  *	the softmac dev_t is replaced with the legacy device's dev_t).
    964  *
    965  *   3. If neither of the above apply, don't touch the args and return -1.
    966  */
    967 int
    968 dld_autopush(dev_t *devp, struct dlautopush *dlap)
    969 {
    970 	dld_ap_t	*dap;
    971 	datalink_id_t	linkid;
    972 	dev_t		phydev;
    973 
    974 	if (!GLDV3_DRV(getmajor(*devp)))
    975 		return (-1);
    976 
    977 	/*
    978 	 * Find the linkid by the link's dev_t.
    979 	 */
    980 	if (dls_devnet_dev2linkid(*devp, &linkid) != 0)
    981 		return (-1);
    982 
    983 	/*
    984 	 * Find the autopush configuration associated with the linkid.
    985 	 */
    986 	rw_enter(&dld_ap_hash_lock, RW_READER);
    987 	if (mod_hash_find(dld_ap_hashp, (mod_hash_key_t)(uintptr_t)linkid,
    988 	    (mod_hash_val_t *)&dap) == 0) {
    989 		*dlap = dap->da_ap;
    990 		rw_exit(&dld_ap_hash_lock);
    991 		return (0);
    992 	}
    993 	rw_exit(&dld_ap_hash_lock);
    994 
    995 	if (dls_devnet_phydev(linkid, &phydev) != 0)
    996 		return (-1);
    997 
    998 	*devp = phydev;
    999 	return (1);
   1000 }
   1001 
   1002 /*
   1003  * Secure objects implementation
   1004  */
   1005 
   1006 /* ARGSUSED */
   1007 static int
   1008 drv_secobj_ctor(void *buf, void *arg, int kmflag)
   1009 {
   1010 	bzero(buf, sizeof (dld_secobj_t));
   1011 	return (0);
   1012 }
   1013 
   1014 static void
   1015 drv_secobj_init(void)
   1016 {
   1017 	rw_init(&drv_secobj_lock, NULL, RW_DEFAULT, NULL);
   1018 	drv_secobj_cachep = kmem_cache_create("drv_secobj_cache",
   1019 	    sizeof (dld_secobj_t), 0, drv_secobj_ctor, NULL,
   1020 	    NULL, NULL, NULL, 0);
   1021 	drv_secobj_hash = mod_hash_create_extended("drv_secobj_hash",
   1022 	    SECOBJ_WEP_HASHSZ, mod_hash_null_keydtor, mod_hash_null_valdtor,
   1023 	    mod_hash_bystr, NULL, mod_hash_strkey_cmp, KM_SLEEP);
   1024 }
   1025 
   1026 static void
   1027 drv_secobj_fini(void)
   1028 {
   1029 	mod_hash_destroy_hash(drv_secobj_hash);
   1030 	kmem_cache_destroy(drv_secobj_cachep);
   1031 	rw_destroy(&drv_secobj_lock);
   1032 }
   1033 
   1034 /* ARGSUSED */
   1035 static int
   1036 drv_ioc_secobj_set(void *karg, intptr_t arg, int mode, cred_t *cred, int *rvalp)
   1037 {
   1038 	dld_ioc_secobj_set_t	*ssp = karg;
   1039 	dld_secobj_t		*sobjp, *objp;
   1040 	int			err;
   1041 
   1042 	sobjp = &ssp->ss_obj;
   1043 
   1044 	if (sobjp->so_class != DLD_SECOBJ_CLASS_WEP &&
   1045 	    sobjp->so_class != DLD_SECOBJ_CLASS_WPA)
   1046 		return (EINVAL);
   1047 
   1048 	if (sobjp->so_name[DLD_SECOBJ_NAME_MAX - 1] != '\0' ||
   1049 	    sobjp->so_len > DLD_SECOBJ_VAL_MAX)
   1050 		return (EINVAL);
   1051 
   1052 	rw_enter(&drv_secobj_lock, RW_WRITER);
   1053 	err = mod_hash_find(drv_secobj_hash, (mod_hash_key_t)sobjp->so_name,
   1054 	    (mod_hash_val_t *)&objp);
   1055 	if (err == 0) {
   1056 		if ((ssp->ss_flags & DLD_SECOBJ_OPT_CREATE) != 0) {
   1057 			rw_exit(&drv_secobj_lock);
   1058 			return (EEXIST);
   1059 		}
   1060 	} else {
   1061 		ASSERT(err == MH_ERR_NOTFOUND);
   1062 		if ((ssp->ss_flags & DLD_SECOBJ_OPT_CREATE) == 0) {
   1063 			rw_exit(&drv_secobj_lock);
   1064 			return (ENOENT);
   1065 		}
   1066 		objp = kmem_cache_alloc(drv_secobj_cachep, KM_SLEEP);
   1067 		(void) strlcpy(objp->so_name, sobjp->so_name,
   1068 		    DLD_SECOBJ_NAME_MAX);
   1069 
   1070 		VERIFY(mod_hash_insert(drv_secobj_hash,
   1071 		    (mod_hash_key_t)objp->so_name, (mod_hash_val_t)objp) == 0);
   1072 	}
   1073 	bcopy(sobjp->so_val, objp->so_val, sobjp->so_len);
   1074 	objp->so_len = sobjp->so_len;
   1075 	objp->so_class = sobjp->so_class;
   1076 	rw_exit(&drv_secobj_lock);
   1077 	return (0);
   1078 }
   1079 
   1080 typedef struct dld_secobj_state {
   1081 	uint_t		ss_free;
   1082 	uint_t		ss_count;
   1083 	int		ss_rc;
   1084 	int		ss_mode;
   1085 	dld_secobj_t	*ss_objp;
   1086 } dld_secobj_state_t;
   1087 
   1088 /* ARGSUSED */
   1089 static uint_t
   1090 drv_secobj_walker(mod_hash_key_t key, mod_hash_val_t *val, void *arg)
   1091 {
   1092 	dld_secobj_state_t	*statep = arg;
   1093 	dld_secobj_t		*sobjp = (dld_secobj_t *)val;
   1094 
   1095 	if (statep->ss_free < sizeof (dld_secobj_t)) {
   1096 		statep->ss_rc = ENOSPC;
   1097 		return (MH_WALK_TERMINATE);
   1098 	}
   1099 	if (ddi_copyout(sobjp, statep->ss_objp, sizeof (*sobjp),
   1100 	    statep->ss_mode) != 0) {
   1101 		statep->ss_rc = EFAULT;
   1102 		return (MH_WALK_TERMINATE);
   1103 	}
   1104 	statep->ss_objp++;
   1105 	statep->ss_free -= sizeof (dld_secobj_t);
   1106 	statep->ss_count++;
   1107 	return (MH_WALK_CONTINUE);
   1108 }
   1109 
   1110 /* ARGSUSED */
   1111 static int
   1112 drv_ioc_secobj_get(void *karg, intptr_t arg, int mode, cred_t *cred, int *rvalp)
   1113 {
   1114 	dld_ioc_secobj_get_t	*sgp = karg;
   1115 	dld_secobj_t		*sobjp, *objp;
   1116 	int			err;
   1117 
   1118 	sobjp = &sgp->sg_obj;
   1119 	if (sobjp->so_name[DLD_SECOBJ_NAME_MAX - 1] != '\0')
   1120 		return (EINVAL);
   1121 
   1122 	rw_enter(&drv_secobj_lock, RW_READER);
   1123 	if (sobjp->so_name[0] != '\0') {
   1124 		err = mod_hash_find(drv_secobj_hash,
   1125 		    (mod_hash_key_t)sobjp->so_name, (mod_hash_val_t *)&objp);
   1126 		if (err != 0) {
   1127 			ASSERT(err == MH_ERR_NOTFOUND);
   1128 			rw_exit(&drv_secobj_lock);
   1129 			return (ENOENT);
   1130 		}
   1131 		bcopy(objp->so_val, sobjp->so_val, objp->so_len);
   1132 		sobjp->so_len = objp->so_len;
   1133 		sobjp->so_class = objp->so_class;
   1134 		sgp->sg_count = 1;
   1135 	} else {
   1136 		dld_secobj_state_t	state;
   1137 
   1138 		state.ss_free = sgp->sg_size - sizeof (dld_ioc_secobj_get_t);
   1139 		state.ss_count = 0;
   1140 		state.ss_rc = 0;
   1141 		state.ss_mode = mode;
   1142 		state.ss_objp = (dld_secobj_t *)((uchar_t *)arg +
   1143 		    sizeof (dld_ioc_secobj_get_t));
   1144 
   1145 		mod_hash_walk(drv_secobj_hash, drv_secobj_walker, &state);
   1146 		if (state.ss_rc != 0) {
   1147 			rw_exit(&drv_secobj_lock);
   1148 			return (state.ss_rc);
   1149 		}
   1150 		sgp->sg_count = state.ss_count;
   1151 	}
   1152 	rw_exit(&drv_secobj_lock);
   1153 	return (0);
   1154 }
   1155 
   1156 /* ARGSUSED */
   1157 static int
   1158 drv_ioc_secobj_unset(void *karg, intptr_t arg, int mode, cred_t *cred,
   1159     int *rvalp)
   1160 {
   1161 	dld_ioc_secobj_unset_t	*sup = karg;
   1162 	dld_secobj_t		*objp;
   1163 	mod_hash_val_t		val;
   1164 	int			err;
   1165 
   1166 	if (sup->su_name[DLD_SECOBJ_NAME_MAX - 1] != '\0')
   1167 		return (EINVAL);
   1168 
   1169 	rw_enter(&drv_secobj_lock, RW_WRITER);
   1170 	err = mod_hash_find(drv_secobj_hash, (mod_hash_key_t)sup->su_name,
   1171 	    (mod_hash_val_t *)&objp);
   1172 	if (err != 0) {
   1173 		ASSERT(err == MH_ERR_NOTFOUND);
   1174 		rw_exit(&drv_secobj_lock);
   1175 		return (ENOENT);
   1176 	}
   1177 	VERIFY(mod_hash_remove(drv_secobj_hash, (mod_hash_key_t)sup->su_name,
   1178 	    (mod_hash_val_t *)&val) == 0);
   1179 	ASSERT(objp == (dld_secobj_t *)val);
   1180 
   1181 	kmem_cache_free(drv_secobj_cachep, objp);
   1182 	rw_exit(&drv_secobj_lock);
   1183 	return (0);
   1184 }
   1185 
   1186 /*
   1187  * Note that ioctls that modify links have a NULL di_priv_func(), as
   1188  * privileges can only be checked after we know the class of the link being
   1189  * modified (due to class-specific fine-grained privileges such as
   1190  * sys_iptun_config).
   1191  */
   1192 static dld_ioc_info_t drv_ioc_list[] = {
   1193 	{DLDIOC_ATTR, DLDCOPYINOUT, sizeof (dld_ioc_attr_t),
   1194 	    drv_ioc_attr, NULL},
   1195 	{DLDIOC_PHYS_ATTR, DLDCOPYINOUT, sizeof (dld_ioc_phys_attr_t),
   1196 	    drv_ioc_phys_attr, NULL},
   1197 	{DLDIOC_SECOBJ_SET, DLDCOPYIN, sizeof (dld_ioc_secobj_set_t),
   1198 	    drv_ioc_secobj_set, secpolicy_dl_config},
   1199 	{DLDIOC_SECOBJ_GET, DLDCOPYINOUT, sizeof (dld_ioc_secobj_get_t),
   1200 	    drv_ioc_secobj_get, secpolicy_dl_config},
   1201 	{DLDIOC_SECOBJ_UNSET, DLDCOPYIN, sizeof (dld_ioc_secobj_unset_t),
   1202 	    drv_ioc_secobj_unset, secpolicy_dl_config},
   1203 	{DLDIOC_DOORSERVER, DLDCOPYIN, sizeof (dld_ioc_door_t),
   1204 	    drv_ioc_doorserver, secpolicy_dl_config},
   1205 	{DLDIOC_RENAME, DLDCOPYIN, sizeof (dld_ioc_rename_t),
   1206 	    drv_ioc_rename, NULL},
   1207 	{DLDIOC_MACADDRGET, DLDCOPYINOUT, sizeof (dld_ioc_macaddrget_t),
   1208 	    drv_ioc_macaddrget, NULL},
   1209 	{DLDIOC_ADDFLOW, DLDCOPYIN, sizeof (dld_ioc_addflow_t),
   1210 	    drv_ioc_addflow, secpolicy_dl_config},
   1211 	{DLDIOC_REMOVEFLOW, DLDCOPYIN, sizeof (dld_ioc_removeflow_t),
   1212 	    drv_ioc_removeflow, secpolicy_dl_config},
   1213 	{DLDIOC_MODIFYFLOW, DLDCOPYIN, sizeof (dld_ioc_modifyflow_t),
   1214 	    drv_ioc_modifyflow, secpolicy_dl_config},
   1215 	{DLDIOC_WALKFLOW, DLDCOPYINOUT, sizeof (dld_ioc_walkflow_t),
   1216 	    drv_ioc_walkflow, NULL},
   1217 	{DLDIOC_USAGELOG, DLDCOPYIN, sizeof (dld_ioc_usagelog_t),
   1218 	    drv_ioc_usagelog, secpolicy_dl_config},
   1219 	{DLDIOC_SETMACPROP, DLDCOPYIN, sizeof (dld_ioc_macprop_t),
   1220 	    drv_ioc_setprop, NULL},
   1221 	{DLDIOC_GETMACPROP, DLDCOPYIN, sizeof (dld_ioc_macprop_t),
   1222 	    drv_ioc_getprop, NULL},
   1223 	{DLDIOC_GETHWGRP, DLDCOPYINOUT, sizeof (dld_ioc_hwgrpget_t),
   1224 	    drv_ioc_hwgrpget, secpolicy_dl_config},
   1225 };
   1226 
   1227 typedef struct dld_ioc_modentry {
   1228 	uint16_t	dim_modid;	/* Top 16 bits of ioctl command */
   1229 	char		*dim_modname;	/* Module to be loaded */
   1230 	dld_ioc_info_t	*dim_list;	/* array of ioctl structures */
   1231 	uint_t		dim_count;	/* number of elements in dim_list */
   1232 } dld_ioc_modentry_t;
   1233 
   1234 /*
   1235  * For all modules except for dld, dim_list and dim_count are assigned
   1236  * when the modules register their ioctls in dld_ioc_register().  We
   1237  * can statically initialize dld's ioctls in-line here; there's no
   1238  * need for it to call dld_ioc_register() itself.
   1239  */
   1240 static dld_ioc_modentry_t dld_ioc_modtable[] = {
   1241 	{DLD_IOC,	"dld",	drv_ioc_list,	DLDIOCCNT(drv_ioc_list)},
   1242 	{AGGR_IOC,	"aggr",	NULL, 0},
   1243 	{VNIC_IOC,	"vnic",	NULL, 0},
   1244 	{SIMNET_IOC,	"simnet", NULL, 0},
   1245 	{BRIDGE_IOC,	"bridge", NULL, 0},
   1246 	{IPTUN_IOC,	"iptun", NULL, 0}
   1247 };
   1248 #define	DLDIOC_CNT	\
   1249 	(sizeof (dld_ioc_modtable) / sizeof (dld_ioc_modentry_t))
   1250 
   1251 static dld_ioc_modentry_t *
   1252 dld_ioc_findmod(uint16_t modid)
   1253 {
   1254 	int	i;
   1255 
   1256 	for (i = 0; i < DLDIOC_CNT; i++) {
   1257 		if (modid == dld_ioc_modtable[i].dim_modid)
   1258 			return (&dld_ioc_modtable[i]);
   1259 	}
   1260 	return (NULL);
   1261 }
   1262 
   1263 int
   1264 dld_ioc_register(uint16_t modid, dld_ioc_info_t *list, uint_t count)
   1265 {
   1266 	dld_ioc_modentry_t *dim = dld_ioc_findmod(modid);
   1267 
   1268 	if (dim == NULL)
   1269 		return (ENOENT);
   1270 
   1271 	dim->dim_list = list;
   1272 	dim->dim_count = count;
   1273 	return (0);
   1274 }
   1275 
   1276 void
   1277 dld_ioc_unregister(uint16_t modid)
   1278 {
   1279 	VERIFY(dld_ioc_register(modid, NULL, 0) == 0);
   1280 }
   1281 
   1282 /*
   1283  * The general design with GLDv3 ioctls is that all ioctls issued
   1284  * through /dev/dld go through this drv_ioctl() function.  This
   1285  * function handles all ioctls on behalf of modules listed in
   1286  * dld_ioc_modtable.
   1287  *
   1288  * When an ioctl is received, this function looks for the associated
   1289  * module-id-specific ioctl information using dld_ioc_findmod().  The
   1290  * call to ddi_hold_devi_by_instance() on the associated device will
   1291  * cause the kernel module responsible for the ioctl to be loaded if
   1292  * it's not already loaded, which should result in that module calling
   1293  * dld_ioc_register(), thereby filling in the dim_list containing the
   1294  * details for the ioctl being processed.
   1295  *
   1296  * This function can then perform operations such as copyin() data and
   1297  * do credential checks based on the registered ioctl information,
   1298  * then issue the callback function di_func() registered by the
   1299  * responsible module.  Upon return, the appropriate copyout()
   1300  * operation can be performed and the operation completes.
   1301  */
   1302 /* ARGSUSED */
   1303 static int
   1304 drv_ioctl(dev_t dev, int cmd, intptr_t arg, int mode, cred_t *cred, int *rvalp)
   1305 {
   1306 	dld_ioc_modentry_t *dim;
   1307 	dld_ioc_info_t	*info;
   1308 	dev_info_t	*dip = NULL;
   1309 	void		*buf = NULL;
   1310 	size_t		sz;
   1311 	int		i, err;
   1312 
   1313 	if ((dim = dld_ioc_findmod(DLD_IOC_MODID(cmd))) == NULL)
   1314 		return (ENOTSUP);
   1315 
   1316 	dip = ddi_hold_devi_by_instance(ddi_name_to_major(dim->dim_modname),
   1317 	    0, 0);
   1318 	if (dip == NULL || dim->dim_list == NULL) {
   1319 		err = ENODEV;
   1320 		goto done;
   1321 	}
   1322 
   1323 	for (i = 0; i < dim->dim_count; i++) {
   1324 		if (cmd == dim->dim_list[i].di_cmd)
   1325 			break;
   1326 	}
   1327 	if (i == dim->dim_count) {
   1328 		err = ENOTSUP;
   1329 		goto done;
   1330 	}
   1331 
   1332 	info = &dim->dim_list[i];
   1333 
   1334 	if (info->di_priv_func != NULL &&
   1335 	    (err = info->di_priv_func(cred)) != 0)
   1336 		goto done;
   1337 
   1338 	sz = info->di_argsize;
   1339 	if ((buf = kmem_zalloc(sz, KM_NOSLEEP)) == NULL) {
   1340 		err = ENOMEM;
   1341 		goto done;
   1342 	}
   1343 
   1344 	if ((info->di_flags & DLDCOPYIN) &&
   1345 	    ddi_copyin((void *)arg, buf, sz, mode) != 0) {
   1346 		err = EFAULT;
   1347 		goto done;
   1348 	}
   1349 
   1350 	err = info->di_func(buf, arg, mode, cred, rvalp);
   1351 
   1352 	if ((info->di_flags & DLDCOPYOUT) &&
   1353 	    ddi_copyout(buf, (void *)arg, sz, mode) != 0 && err == 0)
   1354 		err = EFAULT;
   1355 
   1356 done:
   1357 	if (buf != NULL)
   1358 		kmem_free(buf, sz);
   1359 	if (dip != NULL)
   1360 		ddi_release_devi(dip);
   1361 	return (err);
   1362 }
   1363