xref: /illumos-gate/usr/src/uts/common/io/dld/dld_drv.c (revision daa7e8a345b2e0424e612017e8ead2e97b0f7f37)
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) 2017, Joyent, Inc.
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 		} else {
707 			kprop->pr_perm_flags = MAC_PROP_PERM_RW;
708 			(*(zoneid_t *)kprop->pr_val) = dls_devnet_getzid(dlh);
709 		}
710 		break;
711 	case MAC_PROP_AUTOPUSH: {
712 		struct dlautopush *dlap = (struct dlautopush *)kprop->pr_val;
713 
714 		if (set) {
715 			if (kprop->pr_valsize != 0)
716 				err = drv_ioc_setap(linkid, dlap);
717 			else
718 				err = drv_ioc_clrap(linkid);
719 		} else {
720 			/*
721 			 * You might think that the earlier call to
722 			 * mac_prop_check_size() should catch this but
723 			 * it can't. The autopush prop uses 0 as a
724 			 * sentinel value to clear the prop. This
725 			 * check ensures we don't allow a get with a
726 			 * valsize of 0.
727 			 */
728 			if (kprop->pr_valsize == 0) {
729 				err = ENOBUFS;
730 				goto done;
731 			}
732 
733 			kprop->pr_perm_flags = MAC_PROP_PERM_RW;
734 			err = drv_ioc_getap(linkid, dlap);
735 		}
736 		break;
737 	}
738 	case MAC_PROP_TAGMODE:
739 		if (set) {
740 			link_tagmode_t mode = *(link_tagmode_t *)kprop->pr_val;
741 
742 			if (mode != LINK_TAGMODE_VLANONLY &&
743 			    mode != LINK_TAGMODE_NORMAL) {
744 				err = EINVAL;
745 			} else {
746 				dlp->dl_tagmode = mode;
747 				err = 0;
748 			}
749 		} else {
750 			*(link_tagmode_t *)kprop->pr_val = dlp->dl_tagmode;
751 			kprop->pr_perm_flags = MAC_PROP_PERM_RW;
752 			err = 0;
753 		}
754 		break;
755 	default: {
756 		mac_propval_range_t *rangep = NULL;
757 		void *default_val = NULL;
758 		uint_t default_size = 0;
759 
760 		/* set a property value */
761 		if (set) {
762 			err = mac_set_prop(dlp->dl_mh, kprop->pr_num,
763 			    kprop->pr_name, kprop->pr_val, kprop->pr_valsize);
764 			break;
765 		}
766 
767 		/*
768 		 * Get the property value, default, or possible value
769 		 * depending on flags passed from the user.
770 		 */
771 
772 		/* a property has RW permissions by default */
773 		kprop->pr_perm_flags = MAC_PROP_PERM_RW;
774 
775 		if (kprop->pr_flags & DLD_PROP_POSSIBLE) {
776 			rangep = (mac_propval_range_t *)kprop->pr_val;
777 
778 			/*
779 			 * fail if rangep is not aligned to first
780 			 * member of mac_propval_range_t.
781 			 */
782 			ASSERT(IS_P2ALIGNED(rangep, sizeof (uint_t)));
783 		} else if (kprop->pr_flags & DLD_PROP_DEFAULT) {
784 			default_val = kprop->pr_val;
785 			default_size = kprop->pr_valsize;
786 		}
787 
788 		/*
789 		 * Always return the permissions, and optionally return
790 		 * the default value or possible values range.
791 		 */
792 		err = mac_prop_info(dlp->dl_mh, kprop->pr_num, kprop->pr_name,
793 		    default_val, default_size, rangep, &kprop->pr_perm_flags);
794 		if (err != 0)
795 			goto done;
796 
797 		if (default_val == NULL && rangep == NULL) {
798 			err = mac_get_prop(dlp->dl_mh, kprop->pr_num,
799 			    kprop->pr_name, kprop->pr_val, kprop->pr_valsize);
800 		}
801 	}
802 	}
803 
804 done:
805 	if (!set && ddi_copyout(kprop, (void *)arg, dsize, mode) != 0)
806 		err = EFAULT;
807 
808 	if (dlp != NULL)
809 		dls_link_rele(dlp);
810 
811 	if (mph != NULL) {
812 		int32_t	cpuid;
813 		void	*mdip = NULL;
814 
815 		if (dlp != NULL && set && err == 0) {
816 			cpuid = mac_client_intr_cpu(dlp->dl_mch);
817 			mdip = mac_get_devinfo(dlp->dl_mh);
818 		}
819 
820 		mac_perim_exit(mph);
821 
822 		if (mdip != NULL && cpuid != -1)
823 			mac_client_set_intr_cpu(mdip, dlp->dl_mch, cpuid);
824 	}
825 
826 	if (dlh != NULL)
827 		dls_devnet_rele_tmp(dlh);
828 
829 	if (kprop != NULL)
830 		kmem_free(kprop, dsize);
831 	return (err);
832 }
833 
834 /* ARGSUSED */
835 static int
836 drv_ioc_setprop(void *karg, intptr_t arg, int mode, cred_t *cred, int *rvalp)
837 {
838 	return (drv_ioc_prop_common(karg, arg, B_TRUE, cred, mode));
839 }
840 
841 /* ARGSUSED */
842 static int
843 drv_ioc_getprop(void *karg, intptr_t arg, int mode, cred_t *cred, int *rvalp)
844 {
845 	return (drv_ioc_prop_common(karg, arg, B_FALSE, cred, mode));
846 }
847 
848 /*
849  * DLDIOC_RENAME.
850  *
851  * This function handles two cases of link renaming. See more in comments above
852  * dls_datalink_rename().
853  */
854 /* ARGSUSED */
855 static int
856 drv_ioc_rename(void *karg, intptr_t arg, int mode, cred_t *cred, int *rvalp)
857 {
858 	dld_ioc_rename_t	*dir = karg;
859 	mod_hash_key_t		key;
860 	mod_hash_val_t		val;
861 	zoneid_t		zoneid = crgetzoneid(cred);
862 	datalink_class_t	class;
863 	int			err;
864 
865 	if (zoneid != GLOBAL_ZONEID &&
866 	    (zone_check_datalink(&zoneid, dir->dir_linkid1) != 0 ||
867 	    dir->dir_linkid2 != DATALINK_INVALID_LINKID &&
868 	    zone_check_datalink(&zoneid, dir->dir_linkid2) != 0))
869 		return (ENOENT);
870 
871 	if ((err = dls_mgmt_get_linkinfo(dir->dir_linkid1, NULL, &class, NULL,
872 	    NULL)) != 0)
873 		return (err);
874 
875 	if ((err = drv_ioc_checkprivs(class, cred)) != 0)
876 		return (err);
877 
878 	if ((err = dls_devnet_rename(dir->dir_linkid1, dir->dir_linkid2,
879 	    dir->dir_link)) != 0)
880 		return (err);
881 
882 	if (dir->dir_linkid2 == DATALINK_INVALID_LINKID)
883 		return (0);
884 
885 	/*
886 	 * if dir_linkid2 is not DATALINK_INVALID_LINKID, it means this
887 	 * renaming request is to rename a valid physical link (dir_linkid1)
888 	 * to a "removed" physical link (dir_linkid2, which is removed by DR
889 	 * or during system shutdown). In this case, the link (specified by
890 	 * dir_linkid1) would inherit all the configuration of dir_linkid2,
891 	 * and dir_linkid1 and its configuration would be lost.
892 	 *
893 	 * Remove per-link autopush configuration of dir_linkid1 in this case.
894 	 */
895 	key = (mod_hash_key_t)(uintptr_t)dir->dir_linkid1;
896 	rw_enter(&dld_ap_hash_lock, RW_WRITER);
897 	if (mod_hash_find(dld_ap_hashp, key, &val) != 0) {
898 		rw_exit(&dld_ap_hash_lock);
899 		return (0);
900 	}
901 
902 	VERIFY(mod_hash_remove(dld_ap_hashp, key, &val) == 0);
903 	kmem_free(val, sizeof (dld_ap_t));
904 	rw_exit(&dld_ap_hash_lock);
905 	return (0);
906 }
907 
908 static int
909 drv_ioc_setap(datalink_id_t linkid, struct dlautopush *dlap)
910 {
911 	dld_ap_t	*dap;
912 	int		i;
913 	mod_hash_key_t	key;
914 
915 	if (dlap->dap_npush == 0 || dlap->dap_npush > MAXAPUSH)
916 		return (EINVAL);
917 
918 	/*
919 	 * Validate that the specified list of modules exist.
920 	 */
921 	for (i = 0; i < dlap->dap_npush; i++) {
922 		if (fmodsw_find(dlap->dap_aplist[i], FMODSW_LOAD) == NULL)
923 			return (EINVAL);
924 	}
925 
926 
927 	key = (mod_hash_key_t)(uintptr_t)linkid;
928 
929 	rw_enter(&dld_ap_hash_lock, RW_WRITER);
930 	if (mod_hash_find(dld_ap_hashp, key, (mod_hash_val_t *)&dap) != 0) {
931 		dap = kmem_zalloc(sizeof (dld_ap_t), KM_NOSLEEP);
932 		if (dap == NULL) {
933 			rw_exit(&dld_ap_hash_lock);
934 			return (ENOMEM);
935 		}
936 
937 		dap->da_linkid = linkid;
938 		VERIFY(mod_hash_insert(dld_ap_hashp, key,
939 		    (mod_hash_val_t)dap) == 0);
940 	}
941 
942 	/*
943 	 * Update the configuration.
944 	 */
945 	dap->da_anchor = dlap->dap_anchor;
946 	dap->da_npush = dlap->dap_npush;
947 	for (i = 0; i < dlap->dap_npush; i++) {
948 		(void) strlcpy(dap->da_aplist[i], dlap->dap_aplist[i],
949 		    FMNAMESZ + 1);
950 	}
951 	rw_exit(&dld_ap_hash_lock);
952 
953 	return (0);
954 }
955 
956 static int
957 drv_ioc_getap(datalink_id_t linkid, struct dlautopush *dlap)
958 {
959 	dld_ap_t	*dap;
960 	int		i;
961 
962 	rw_enter(&dld_ap_hash_lock, RW_READER);
963 	if (mod_hash_find(dld_ap_hashp,
964 	    (mod_hash_key_t)(uintptr_t)linkid,
965 	    (mod_hash_val_t *)&dap) != 0) {
966 		rw_exit(&dld_ap_hash_lock);
967 		dlap->dap_npush = 0;
968 		return (0);
969 	}
970 
971 	/*
972 	 * Retrieve the configuration.
973 	 */
974 	dlap->dap_anchor = dap->da_anchor;
975 	dlap->dap_npush = dap->da_npush;
976 	for (i = 0; i < dap->da_npush; i++) {
977 		(void) strlcpy(dlap->dap_aplist[i], dap->da_aplist[i],
978 		    FMNAMESZ + 1);
979 	}
980 	rw_exit(&dld_ap_hash_lock);
981 
982 	return (0);
983 }
984 
985 static int
986 drv_ioc_clrap(datalink_id_t linkid)
987 {
988 	mod_hash_val_t	val;
989 	mod_hash_key_t	key;
990 
991 	key = (mod_hash_key_t)(uintptr_t)linkid;
992 
993 	rw_enter(&dld_ap_hash_lock, RW_WRITER);
994 	if (mod_hash_find(dld_ap_hashp, key, &val) != 0) {
995 		rw_exit(&dld_ap_hash_lock);
996 		return (0);
997 	}
998 
999 	VERIFY(mod_hash_remove(dld_ap_hashp, key, &val) == 0);
1000 	kmem_free(val, sizeof (dld_ap_t));
1001 	rw_exit(&dld_ap_hash_lock);
1002 	return (0);
1003 }
1004 
1005 /*
1006  * DLDIOC_DOORSERVER
1007  */
1008 /* ARGSUSED */
1009 static int
1010 drv_ioc_doorserver(void *karg, intptr_t arg, int mode, cred_t *cred, int *rvalp)
1011 {
1012 	dld_ioc_door_t	*did = karg;
1013 
1014 	return (dls_mgmt_door_set(did->did_start_door));
1015 }
1016 
1017 /*
1018  * DLDIOC_USAGELOG
1019  */
1020 /* ARGSUSED */
1021 static int
1022 drv_ioc_usagelog(void *karg, intptr_t arg, int mode, cred_t *cred,
1023     int *rvalp)
1024 {
1025 	dld_ioc_usagelog_t	*log_info = (dld_ioc_usagelog_t *)karg;
1026 	int			err = 0;
1027 
1028 	if (log_info->ul_type < MAC_LOGTYPE_LINK ||
1029 	    log_info->ul_type > MAC_LOGTYPE_FLOW)
1030 		return (EINVAL);
1031 
1032 	if (log_info->ul_onoff) {
1033 		err = mac_start_logusage(log_info->ul_type,
1034 		    log_info->ul_interval);
1035 	} else {
1036 		mac_stop_logusage(log_info->ul_type);
1037 	}
1038 	return (err);
1039 }
1040 
1041 /*
1042  * Process a DLDIOC_ADDFLOW request.
1043  */
1044 /* ARGSUSED */
1045 static int
1046 drv_ioc_addflow(void *karg, intptr_t arg, int mode, cred_t *cred, int *rvalp)
1047 {
1048 	dld_ioc_addflow_t	*afp = karg;
1049 
1050 	return (dld_add_flow(afp->af_linkid, afp->af_name,
1051 	    &afp->af_flow_desc, &afp->af_resource_props));
1052 }
1053 
1054 /*
1055  * Process a DLDIOC_REMOVEFLOW request.
1056  */
1057 /* ARGSUSED */
1058 static int
1059 drv_ioc_removeflow(void *karg, intptr_t arg, int mode, cred_t *cred, int *rvalp)
1060 {
1061 	dld_ioc_removeflow_t	*rfp = karg;
1062 
1063 	return (dld_remove_flow(rfp->rf_name));
1064 }
1065 
1066 /*
1067  * Process a DLDIOC_MODIFYFLOW request.
1068  */
1069 /* ARGSUSED */
1070 static int
1071 drv_ioc_modifyflow(void *karg, intptr_t arg, int mode, cred_t *cred, int *rvalp)
1072 {
1073 	dld_ioc_modifyflow_t	*mfp = karg;
1074 
1075 	return (dld_modify_flow(mfp->mf_name, &mfp->mf_resource_props));
1076 }
1077 
1078 /*
1079  * Process a DLDIOC_WALKFLOW request.
1080  */
1081 /* ARGSUSED */
1082 static int
1083 drv_ioc_walkflow(void *karg, intptr_t arg, int mode, cred_t *cred, int *rvalp)
1084 {
1085 	dld_ioc_walkflow_t	*wfp = karg;
1086 
1087 	return (dld_walk_flow(wfp, arg, cred));
1088 }
1089 
1090 /*
1091  * Check for GLDv3 autopush information.  There are three cases:
1092  *
1093  *   1. If devp points to a GLDv3 datalink and it has autopush configuration,
1094  *	fill dlap in with that information and return 0.
1095  *
1096  *   2. If devp points to a GLDv3 datalink but it doesn't have autopush
1097  *	configuration, then replace devp with the physical device (if one
1098  *	exists) and return 1.  This allows stropen() to find the old-school
1099  *	per-driver autopush configuration.  (For softmac, the result is that
1100  *	the softmac dev_t is replaced with the legacy device's dev_t).
1101  *
1102  *   3. If neither of the above apply, don't touch the args and return -1.
1103  */
1104 int
1105 dld_autopush(dev_t *devp, struct dlautopush *dlap)
1106 {
1107 	dld_ap_t	*dap;
1108 	datalink_id_t	linkid;
1109 	dev_t		phydev;
1110 
1111 	if (!GLDV3_DRV(getmajor(*devp)))
1112 		return (-1);
1113 
1114 	/*
1115 	 * Find the linkid by the link's dev_t.
1116 	 */
1117 	if (dls_devnet_dev2linkid(*devp, &linkid) != 0)
1118 		return (-1);
1119 
1120 	/*
1121 	 * Find the autopush configuration associated with the linkid.
1122 	 */
1123 	rw_enter(&dld_ap_hash_lock, RW_READER);
1124 	if (mod_hash_find(dld_ap_hashp, (mod_hash_key_t)(uintptr_t)linkid,
1125 	    (mod_hash_val_t *)&dap) == 0) {
1126 		*dlap = dap->da_ap;
1127 		rw_exit(&dld_ap_hash_lock);
1128 		return (0);
1129 	}
1130 	rw_exit(&dld_ap_hash_lock);
1131 
1132 	if (dls_devnet_phydev(linkid, &phydev) != 0)
1133 		return (-1);
1134 
1135 	*devp = phydev;
1136 	return (1);
1137 }
1138 
1139 /*
1140  * Secure objects implementation
1141  */
1142 
1143 /* ARGSUSED */
1144 static int
1145 drv_secobj_ctor(void *buf, void *arg, int kmflag)
1146 {
1147 	bzero(buf, sizeof (dld_secobj_t));
1148 	return (0);
1149 }
1150 
1151 static void
1152 drv_secobj_init(void)
1153 {
1154 	rw_init(&drv_secobj_lock, NULL, RW_DEFAULT, NULL);
1155 	drv_secobj_cachep = kmem_cache_create("drv_secobj_cache",
1156 	    sizeof (dld_secobj_t), 0, drv_secobj_ctor, NULL,
1157 	    NULL, NULL, NULL, 0);
1158 	drv_secobj_hash = mod_hash_create_extended("drv_secobj_hash",
1159 	    SECOBJ_WEP_HASHSZ, mod_hash_null_keydtor, mod_hash_null_valdtor,
1160 	    mod_hash_bystr, NULL, mod_hash_strkey_cmp, KM_SLEEP);
1161 }
1162 
1163 static void
1164 drv_secobj_fini(void)
1165 {
1166 	mod_hash_destroy_hash(drv_secobj_hash);
1167 	kmem_cache_destroy(drv_secobj_cachep);
1168 	rw_destroy(&drv_secobj_lock);
1169 }
1170 
1171 /* ARGSUSED */
1172 static int
1173 drv_ioc_secobj_set(void *karg, intptr_t arg, int mode, cred_t *cred, int *rvalp)
1174 {
1175 	dld_ioc_secobj_set_t	*ssp = karg;
1176 	dld_secobj_t		*sobjp, *objp;
1177 	int			err;
1178 
1179 	sobjp = &ssp->ss_obj;
1180 
1181 	if (sobjp->so_class != DLD_SECOBJ_CLASS_WEP &&
1182 	    sobjp->so_class != DLD_SECOBJ_CLASS_WPA)
1183 		return (EINVAL);
1184 
1185 	if (sobjp->so_name[DLD_SECOBJ_NAME_MAX - 1] != '\0' ||
1186 	    sobjp->so_len > DLD_SECOBJ_VAL_MAX)
1187 		return (EINVAL);
1188 
1189 	rw_enter(&drv_secobj_lock, RW_WRITER);
1190 	err = mod_hash_find(drv_secobj_hash, (mod_hash_key_t)sobjp->so_name,
1191 	    (mod_hash_val_t *)&objp);
1192 	if (err == 0) {
1193 		if ((ssp->ss_flags & DLD_SECOBJ_OPT_CREATE) != 0) {
1194 			rw_exit(&drv_secobj_lock);
1195 			return (EEXIST);
1196 		}
1197 	} else {
1198 		ASSERT(err == MH_ERR_NOTFOUND);
1199 		if ((ssp->ss_flags & DLD_SECOBJ_OPT_CREATE) == 0) {
1200 			rw_exit(&drv_secobj_lock);
1201 			return (ENOENT);
1202 		}
1203 		objp = kmem_cache_alloc(drv_secobj_cachep, KM_SLEEP);
1204 		(void) strlcpy(objp->so_name, sobjp->so_name,
1205 		    DLD_SECOBJ_NAME_MAX);
1206 
1207 		VERIFY(mod_hash_insert(drv_secobj_hash,
1208 		    (mod_hash_key_t)objp->so_name, (mod_hash_val_t)objp) == 0);
1209 	}
1210 	bcopy(sobjp->so_val, objp->so_val, sobjp->so_len);
1211 	objp->so_len = sobjp->so_len;
1212 	objp->so_class = sobjp->so_class;
1213 	rw_exit(&drv_secobj_lock);
1214 	return (0);
1215 }
1216 
1217 typedef struct dld_secobj_state {
1218 	uint_t		ss_free;
1219 	uint_t		ss_count;
1220 	int		ss_rc;
1221 	int		ss_mode;
1222 	dld_secobj_t	*ss_objp;
1223 } dld_secobj_state_t;
1224 
1225 /* ARGSUSED */
1226 static uint_t
1227 drv_secobj_walker(mod_hash_key_t key, mod_hash_val_t *val, void *arg)
1228 {
1229 	dld_secobj_state_t	*statep = arg;
1230 	dld_secobj_t		*sobjp = (dld_secobj_t *)val;
1231 
1232 	if (statep->ss_free < sizeof (dld_secobj_t)) {
1233 		statep->ss_rc = ENOSPC;
1234 		return (MH_WALK_TERMINATE);
1235 	}
1236 	if (ddi_copyout(sobjp, statep->ss_objp, sizeof (*sobjp),
1237 	    statep->ss_mode) != 0) {
1238 		statep->ss_rc = EFAULT;
1239 		return (MH_WALK_TERMINATE);
1240 	}
1241 	statep->ss_objp++;
1242 	statep->ss_free -= sizeof (dld_secobj_t);
1243 	statep->ss_count++;
1244 	return (MH_WALK_CONTINUE);
1245 }
1246 
1247 /* ARGSUSED */
1248 static int
1249 drv_ioc_secobj_get(void *karg, intptr_t arg, int mode, cred_t *cred, int *rvalp)
1250 {
1251 	dld_ioc_secobj_get_t	*sgp = karg;
1252 	dld_secobj_t		*sobjp, *objp;
1253 	int			err;
1254 
1255 	sobjp = &sgp->sg_obj;
1256 	if (sobjp->so_name[DLD_SECOBJ_NAME_MAX - 1] != '\0')
1257 		return (EINVAL);
1258 
1259 	rw_enter(&drv_secobj_lock, RW_READER);
1260 	if (sobjp->so_name[0] != '\0') {
1261 		err = mod_hash_find(drv_secobj_hash,
1262 		    (mod_hash_key_t)sobjp->so_name, (mod_hash_val_t *)&objp);
1263 		if (err != 0) {
1264 			ASSERT(err == MH_ERR_NOTFOUND);
1265 			rw_exit(&drv_secobj_lock);
1266 			return (ENOENT);
1267 		}
1268 		bcopy(objp->so_val, sobjp->so_val, objp->so_len);
1269 		sobjp->so_len = objp->so_len;
1270 		sobjp->so_class = objp->so_class;
1271 		sgp->sg_count = 1;
1272 	} else {
1273 		dld_secobj_state_t	state;
1274 
1275 		state.ss_free = sgp->sg_size - sizeof (dld_ioc_secobj_get_t);
1276 		state.ss_count = 0;
1277 		state.ss_rc = 0;
1278 		state.ss_mode = mode;
1279 		state.ss_objp = (dld_secobj_t *)((uchar_t *)arg +
1280 		    sizeof (dld_ioc_secobj_get_t));
1281 
1282 		mod_hash_walk(drv_secobj_hash, drv_secobj_walker, &state);
1283 		if (state.ss_rc != 0) {
1284 			rw_exit(&drv_secobj_lock);
1285 			return (state.ss_rc);
1286 		}
1287 		sgp->sg_count = state.ss_count;
1288 	}
1289 	rw_exit(&drv_secobj_lock);
1290 	return (0);
1291 }
1292 
1293 /* ARGSUSED */
1294 static int
1295 drv_ioc_secobj_unset(void *karg, intptr_t arg, int mode, cred_t *cred,
1296     int *rvalp)
1297 {
1298 	dld_ioc_secobj_unset_t	*sup = karg;
1299 	dld_secobj_t		*objp;
1300 	mod_hash_val_t		val;
1301 	int			err;
1302 
1303 	if (sup->su_name[DLD_SECOBJ_NAME_MAX - 1] != '\0')
1304 		return (EINVAL);
1305 
1306 	rw_enter(&drv_secobj_lock, RW_WRITER);
1307 	err = mod_hash_find(drv_secobj_hash, (mod_hash_key_t)sup->su_name,
1308 	    (mod_hash_val_t *)&objp);
1309 	if (err != 0) {
1310 		ASSERT(err == MH_ERR_NOTFOUND);
1311 		rw_exit(&drv_secobj_lock);
1312 		return (ENOENT);
1313 	}
1314 	VERIFY(mod_hash_remove(drv_secobj_hash, (mod_hash_key_t)sup->su_name,
1315 	    (mod_hash_val_t *)&val) == 0);
1316 	ASSERT(objp == (dld_secobj_t *)val);
1317 
1318 	kmem_cache_free(drv_secobj_cachep, objp);
1319 	rw_exit(&drv_secobj_lock);
1320 	return (0);
1321 }
1322 
1323 /* ARGSUSED */
1324 static int
1325 drv_ioc_gettran(void *karg, intptr_t arg, int mode, cred_t *cred,
1326     int *rvalp)
1327 {
1328 	int			ret = 0;
1329 	mac_perim_handle_t	mph = NULL;
1330 	dls_dl_handle_t 	dlh = NULL;
1331 	dls_link_t		*dlp = NULL;
1332 	dld_ioc_gettran_t	*dgt = karg;
1333 
1334 	if ((ret = mac_perim_enter_by_linkid(dgt->dgt_linkid, &mph)) != 0)
1335 		goto done;
1336 
1337 	if ((ret = dls_devnet_hold_link(dgt->dgt_linkid, &dlh, &dlp)) != 0)
1338 		goto done;
1339 
1340 	/*
1341 	 * Make sure that this link belongs to the zone.
1342 	 */
1343 	if (crgetzoneid(cred) != dls_devnet_getownerzid(dlh)) {
1344 		ret = ENOENT;
1345 		goto done;
1346 	}
1347 
1348 	if (dgt->dgt_tran_id == DLDIOC_GETTRAN_GETNTRAN) {
1349 		ret = mac_transceiver_count(dlp->dl_mh, &dgt->dgt_tran_id);
1350 	} else {
1351 		ret = mac_transceiver_info(dlp->dl_mh, dgt->dgt_tran_id,
1352 		    &dgt->dgt_present, &dgt->dgt_usable);
1353 	}
1354 
1355 done:
1356 	if (dlh != NULL && dlp != NULL) {
1357 		dls_devnet_rele_link(dlh, dlp);
1358 	}
1359 
1360 	if (mph != NULL) {
1361 		mac_perim_exit(mph);
1362 	}
1363 
1364 	return (ret);
1365 }
1366 
1367 /* ARGSUSED */
1368 static int
1369 drv_ioc_readtran(void *karg, intptr_t arg, int mode, cred_t *cred,
1370     int *rvalp)
1371 {
1372 	int			ret = 0;
1373 	mac_perim_handle_t	mph = NULL;
1374 	dls_dl_handle_t 	dlh = NULL;
1375 	dls_link_t		*dlp = NULL;
1376 	dld_ioc_tranio_t	*dti = karg;
1377 	uint8_t			buf[256];
1378 	size_t			nr;
1379 
1380 	/*
1381 	 * Be strict for the moment
1382 	 */
1383 	if (dti->dti_nbytes != 256 || dti->dti_off != 0)
1384 		return (EINVAL);
1385 
1386 	if ((ret = mac_perim_enter_by_linkid(dti->dti_linkid, &mph)) != 0)
1387 		goto done;
1388 
1389 	if ((ret = dls_devnet_hold_link(dti->dti_linkid, &dlh, &dlp)) != 0)
1390 		goto done;
1391 
1392 	/*
1393 	 * Make sure that this link belongs to the zone.
1394 	 */
1395 	if (crgetzoneid(cred) != dls_devnet_getownerzid(dlh)) {
1396 		ret = ENOENT;
1397 		goto done;
1398 	}
1399 
1400 	bzero(buf, sizeof (buf));
1401 	if ((ret = mac_transceiver_read(dlp->dl_mh, dti->dti_tran_id,
1402 	    dti->dti_page, buf, dti->dti_nbytes, dti->dti_off, &nr)) == 0) {
1403 		dti->dti_nbytes = nr;
1404 		ret = ddi_copyout(buf, (void *)(uintptr_t)dti->dti_buf,
1405 		    sizeof (buf), mode);
1406 	}
1407 
1408 done:
1409 	if (dlh != NULL && dlp != NULL) {
1410 		dls_devnet_rele_link(dlh, dlp);
1411 	}
1412 
1413 	if (mph != NULL) {
1414 		mac_perim_exit(mph);
1415 	}
1416 
1417 	return (ret);
1418 }
1419 
1420 /* ARGSUSED */
1421 static int
1422 drv_ioc_getled(void *karg, intptr_t arg, int mode, cred_t *cred,
1423     int *rvalp)
1424 {
1425 	int			ret = 0;
1426 	mac_perim_handle_t	mph = NULL;
1427 	dls_dl_handle_t 	dlh = NULL;
1428 	dls_link_t		*dlp = NULL;
1429 	dld_ioc_led_t		*dil = karg;
1430 
1431 	if ((mode & FREAD) == 0)
1432 		return (EBADF);
1433 
1434 	if ((ret = dls_devnet_hold_tmp(dil->dil_linkid, &dlh)) != 0)
1435 		goto done;
1436 
1437 	if ((ret = mac_perim_enter_by_macname(dls_devnet_mac(dlh), &mph)) != 0)
1438 		goto done;
1439 
1440 	if ((ret = dls_link_hold(dls_devnet_mac(dlh), &dlp)) != 0)
1441 		goto done;
1442 
1443 	/*
1444 	 * Make sure that this link belongs to the zone.
1445 	 */
1446 	if (crgetzoneid(cred) != dls_devnet_getownerzid(dlh)) {
1447 		ret = ENOENT;
1448 		goto done;
1449 	}
1450 
1451 	ret = mac_led_get(dlp->dl_mh, &dil->dil_supported, &dil->dil_active);
1452 
1453 done:
1454 	if (dlp != NULL)
1455 		dls_link_rele(dlp);
1456 
1457 	if (mph != NULL)
1458 		mac_perim_exit(mph);
1459 
1460 	if (dlh != NULL)
1461 		dls_devnet_rele_tmp(dlh);
1462 
1463 	return (ret);
1464 }
1465 
1466 /* ARGSUSED */
1467 static int
1468 drv_ioc_setled(void *karg, intptr_t arg, int mode, cred_t *cred,
1469     int *rvalp)
1470 {
1471 	int			ret = 0;
1472 	mac_perim_handle_t	mph = NULL;
1473 	dls_dl_handle_t 	dlh = NULL;
1474 	dls_link_t		*dlp = NULL;
1475 	dld_ioc_led_t		*dil = karg;
1476 
1477 	if ((mode & FWRITE) == 0)
1478 		return (EBADF);
1479 
1480 	if ((ret = dls_devnet_hold_tmp(dil->dil_linkid, &dlh)) != 0)
1481 		goto done;
1482 
1483 	if ((ret = mac_perim_enter_by_macname(dls_devnet_mac(dlh), &mph)) != 0)
1484 		goto done;
1485 
1486 	if ((ret = dls_link_hold(dls_devnet_mac(dlh), &dlp)) != 0)
1487 		goto done;
1488 
1489 	/*
1490 	 * Make sure that this link belongs to the zone.
1491 	 */
1492 	if (crgetzoneid(cred) != dls_devnet_getownerzid(dlh)) {
1493 		ret = ENOENT;
1494 		goto done;
1495 	}
1496 
1497 	ret = mac_led_set(dlp->dl_mh, dil->dil_active);
1498 
1499 done:
1500 	if (dlp != NULL)
1501 		dls_link_rele(dlp);
1502 
1503 	if (mph != NULL)
1504 		mac_perim_exit(mph);
1505 
1506 	if (dlh != NULL)
1507 		dls_devnet_rele_tmp(dlh);
1508 
1509 	return (ret);
1510 }
1511 
1512 
1513 /*
1514  * Note that ioctls that modify links have a NULL di_priv_func(), as
1515  * privileges can only be checked after we know the class of the link being
1516  * modified (due to class-specific fine-grained privileges such as
1517  * sys_iptun_config).
1518  */
1519 static dld_ioc_info_t drv_ioc_list[] = {
1520 	{DLDIOC_ATTR, DLDCOPYINOUT, sizeof (dld_ioc_attr_t),
1521 	    drv_ioc_attr, NULL},
1522 	{DLDIOC_PHYS_ATTR, DLDCOPYINOUT, sizeof (dld_ioc_phys_attr_t),
1523 	    drv_ioc_phys_attr, NULL},
1524 	{DLDIOC_SECOBJ_SET, DLDCOPYIN, sizeof (dld_ioc_secobj_set_t),
1525 	    drv_ioc_secobj_set, secpolicy_dl_config},
1526 	{DLDIOC_SECOBJ_GET, DLDCOPYINOUT, sizeof (dld_ioc_secobj_get_t),
1527 	    drv_ioc_secobj_get, secpolicy_dl_config},
1528 	{DLDIOC_SECOBJ_UNSET, DLDCOPYIN, sizeof (dld_ioc_secobj_unset_t),
1529 	    drv_ioc_secobj_unset, secpolicy_dl_config},
1530 	{DLDIOC_DOORSERVER, DLDCOPYIN, sizeof (dld_ioc_door_t),
1531 	    drv_ioc_doorserver, secpolicy_dl_config},
1532 	{DLDIOC_RENAME, DLDCOPYIN, sizeof (dld_ioc_rename_t),
1533 	    drv_ioc_rename, NULL},
1534 	{DLDIOC_MACADDRGET, DLDCOPYINOUT, sizeof (dld_ioc_macaddrget_t),
1535 	    drv_ioc_macaddrget, NULL},
1536 	{DLDIOC_ADDFLOW, DLDCOPYIN, sizeof (dld_ioc_addflow_t),
1537 	    drv_ioc_addflow, secpolicy_dl_config},
1538 	{DLDIOC_REMOVEFLOW, DLDCOPYIN, sizeof (dld_ioc_removeflow_t),
1539 	    drv_ioc_removeflow, secpolicy_dl_config},
1540 	{DLDIOC_MODIFYFLOW, DLDCOPYIN, sizeof (dld_ioc_modifyflow_t),
1541 	    drv_ioc_modifyflow, secpolicy_dl_config},
1542 	{DLDIOC_WALKFLOW, DLDCOPYINOUT, sizeof (dld_ioc_walkflow_t),
1543 	    drv_ioc_walkflow, NULL},
1544 	{DLDIOC_USAGELOG, DLDCOPYIN, sizeof (dld_ioc_usagelog_t),
1545 	    drv_ioc_usagelog, secpolicy_dl_config},
1546 	{DLDIOC_SETMACPROP, DLDCOPYIN, sizeof (dld_ioc_macprop_t),
1547 	    drv_ioc_setprop, NULL},
1548 	{DLDIOC_GETMACPROP, DLDCOPYIN, sizeof (dld_ioc_macprop_t),
1549 	    drv_ioc_getprop, NULL},
1550 	{DLDIOC_GETHWGRP, DLDCOPYINOUT, sizeof (dld_ioc_hwgrpget_t),
1551 	    drv_ioc_hwgrpget, NULL},
1552 	{DLDIOC_GETTRAN, DLDCOPYINOUT, sizeof (dld_ioc_gettran_t),
1553 	    drv_ioc_gettran, NULL },
1554 	{DLDIOC_READTRAN, DLDCOPYINOUT, sizeof (dld_ioc_tranio_t),
1555 	    drv_ioc_readtran, NULL },
1556 	{DLDIOC_GETLED, DLDCOPYINOUT, sizeof (dld_ioc_led_t),
1557 	    drv_ioc_getled, NULL },
1558 	{DLDIOC_SETLED, DLDCOPYIN, sizeof (dld_ioc_led_t),
1559 	    drv_ioc_setled, secpolicy_dl_config}
1560 };
1561 
1562 typedef struct dld_ioc_modentry {
1563 	uint16_t	dim_modid;	/* Top 16 bits of ioctl command */
1564 	char		*dim_modname;	/* Module to be loaded */
1565 	int		ctrl_node_inst;	/* Ctrl node instance */
1566 	dld_ioc_info_t	*dim_list;	/* array of ioctl structures */
1567 	uint_t		dim_count;	/* number of elements in dim_list */
1568 } dld_ioc_modentry_t;
1569 
1570 /*
1571  * For all modules except for dld, dim_list and dim_count are assigned
1572  * when the modules register their ioctls in dld_ioc_register().  We
1573  * can statically initialize dld's ioctls in-line here; there's no
1574  * need for it to call dld_ioc_register() itself. ctrl_node_inst controls
1575  * whether an instance of the device will be held or the driver. If set to
1576  * a non-negative integer, device instance specified in ctrl_node_inst will
1577  * be held; so dld_ioc_register() _must_ be called in xxx_attach() routine of
1578  * the driver. If set to -1, driver will be held; so dld_ioc_register() _must_
1579  * be called in xxx_init() routine of the driver.
1580  */
1581 static dld_ioc_modentry_t dld_ioc_modtable[] = {
1582 	{DLD_IOC,	"dld", 0, drv_ioc_list, DLDIOCCNT(drv_ioc_list)},
1583 	{AGGR_IOC,	"aggr", 0, NULL, 0},
1584 	{VNIC_IOC,	"vnic",	0, NULL, 0},
1585 	{SIMNET_IOC,	"simnet", 0, NULL, 0},
1586 	{BRIDGE_IOC,	"bridge", 0, NULL, 0},
1587 	{IPTUN_IOC,	"iptun", 0, NULL, 0},
1588 	{IBPART_IOC,	"ibp", -1, NULL, 0}
1589 };
1590 #define	DLDIOC_CNT	\
1591 	(sizeof (dld_ioc_modtable) / sizeof (dld_ioc_modentry_t))
1592 
1593 static dld_ioc_modentry_t *
1594 dld_ioc_findmod(uint16_t modid)
1595 {
1596 	int	i;
1597 
1598 	for (i = 0; i < DLDIOC_CNT; i++) {
1599 		if (modid == dld_ioc_modtable[i].dim_modid)
1600 			return (&dld_ioc_modtable[i]);
1601 	}
1602 	return (NULL);
1603 }
1604 
1605 int
1606 dld_ioc_register(uint16_t modid, dld_ioc_info_t *list, uint_t count)
1607 {
1608 	dld_ioc_modentry_t *dim = dld_ioc_findmod(modid);
1609 
1610 	if (dim == NULL)
1611 		return (ENOENT);
1612 
1613 	dim->dim_list = list;
1614 	dim->dim_count = count;
1615 	return (0);
1616 }
1617 
1618 void
1619 dld_ioc_unregister(uint16_t modid)
1620 {
1621 	VERIFY(dld_ioc_register(modid, NULL, 0) == 0);
1622 }
1623 
1624 /*
1625  * The general design with GLDv3 ioctls is that all ioctls issued
1626  * through /dev/dld go through this drv_ioctl() function.  This
1627  * function handles all ioctls on behalf of modules listed in
1628  * dld_ioc_modtable.
1629  *
1630  * When an ioctl is received, this function looks for the associated
1631  * module-id-specific ioctl information using dld_ioc_findmod(). The
1632  * call to ddi_hold_driver() or ddi_hold_devi_by_instance() on the
1633  * associated device will cause the kernel module responsible for the
1634  * ioctl to be loaded if it's not already loaded, which should result
1635  * in that module calling dld_ioc_register(), thereby filling in the
1636  * dim_list containing the details for the ioctl being processed.
1637  *
1638  * This function can then perform operations such as copyin() data and
1639  * do credential checks based on the registered ioctl information,
1640  * then issue the callback function di_func() registered by the
1641  * responsible module.  Upon return, the appropriate copyout()
1642  * operation can be performed and the operation completes.
1643  */
1644 /* ARGSUSED */
1645 static int
1646 drv_ioctl(dev_t dev, int cmd, intptr_t arg, int mode, cred_t *cred, int *rvalp)
1647 {
1648 	dld_ioc_modentry_t *dim;
1649 	dld_ioc_info_t	*info;
1650 	dev_info_t	*dip = NULL;
1651 	struct dev_ops	*dops = NULL;
1652 	major_t		major;
1653 	void		*buf = NULL;
1654 	size_t		sz;
1655 	int		i, err;
1656 
1657 	if ((dim = dld_ioc_findmod(DLD_IOC_MODID(cmd))) == NULL)
1658 		return (ENOTSUP);
1659 
1660 	major = ddi_name_to_major(dim->dim_modname);
1661 
1662 	if (dim->ctrl_node_inst == -1) {
1663 		/*
1664 		 * No dedicated instance to process ioctls.
1665 		 * dld_ioc_register() is called in xxx_init().
1666 		 */
1667 		dops = ddi_hold_driver(major);
1668 	} else {
1669 		/*
1670 		 * Dedicated instance to handle ioctl.
1671 		 * dld_ioc_register() is called in xxx_attach().
1672 		 */
1673 		dip = ddi_hold_devi_by_instance(major, dim->ctrl_node_inst, 0);
1674 	}
1675 
1676 	if ((dip == NULL && dops == NULL) || dim->dim_list == NULL) {
1677 		err = ENODEV;
1678 		goto done;
1679 	}
1680 
1681 	for (i = 0; i < dim->dim_count; i++) {
1682 		if (cmd == dim->dim_list[i].di_cmd)
1683 			break;
1684 	}
1685 	if (i == dim->dim_count) {
1686 		err = ENOTSUP;
1687 		goto done;
1688 	}
1689 
1690 	info = &dim->dim_list[i];
1691 
1692 	if (info->di_priv_func != NULL &&
1693 	    (err = info->di_priv_func(cred)) != 0)
1694 		goto done;
1695 
1696 	sz = info->di_argsize;
1697 	if ((buf = kmem_zalloc(sz, KM_NOSLEEP)) == NULL) {
1698 		err = ENOMEM;
1699 		goto done;
1700 	}
1701 
1702 	if ((info->di_flags & DLDCOPYIN) &&
1703 	    ddi_copyin((void *)arg, buf, sz, mode) != 0) {
1704 		err = EFAULT;
1705 		goto done;
1706 	}
1707 
1708 	err = info->di_func(buf, arg, mode, cred, rvalp);
1709 
1710 	if ((info->di_flags & DLDCOPYOUT) &&
1711 	    ddi_copyout(buf, (void *)arg, sz, mode) != 0 && err == 0)
1712 		err = EFAULT;
1713 
1714 done:
1715 	if (buf != NULL)
1716 		kmem_free(buf, sz);
1717 	if (dip != NULL)
1718 		ddi_release_devi(dip);
1719 	if (dops != NULL)
1720 		ddi_rele_driver(major);
1721 	return (err);
1722 }
1723