xref: /illumos-gate/usr/src/uts/common/io/dld/dld_drv.c (revision fc910014e8a32a65612105835a10995f2c13d942)
1 /*
2  * CDDL HEADER START
3  *
4  * The contents of this file are subject to the terms of the
5  * Common Development and Distribution License (the "License").
6  * You may not use this file except in compliance with the License.
7  *
8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9  * or http://www.opensolaris.org/os/licensing.
10  * See the License for the specific language governing permissions
11  * and limitations under the License.
12  *
13  * When distributing Covered Code, include this CDDL HEADER in each
14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15  * If applicable, add the following below this CDDL HEADER, with the
16  * fields enclosed by brackets "[]" replaced with your own identifying
17  * information: Portions Copyright [yyyy] [name of copyright owner]
18  *
19  * CDDL HEADER END
20  */
21 /*
22  * Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved.
23  * Copyright 2015, Joyent Inc.
24  * Copyright (c) 2017, Joyent, Inc.
25  */
26 
27 /*
28  * Data-Link Driver
29  */
30 
31 #include	<sys/conf.h>
32 #include	<sys/mkdev.h>
33 #include	<sys/modctl.h>
34 #include	<sys/stat.h>
35 #include	<sys/dld_impl.h>
36 #include	<sys/dld_ioc.h>
37 #include	<sys/dls_impl.h>
38 #include	<sys/softmac.h>
39 #include	<sys/mac.h>
40 #include	<sys/mac_ether.h>
41 #include	<sys/mac_client.h>
42 #include	<sys/mac_client_impl.h>
43 #include	<sys/mac_client_priv.h>
44 #include	<inet/common.h>
45 #include	<sys/policy.h>
46 #include	<sys/priv_names.h>
47 #include	<sys/zone.h>
48 #include	<sys/sysmacros.h>
49 
50 static void	drv_init(void);
51 static int	drv_fini(void);
52 
53 static int	drv_getinfo(dev_info_t	*, ddi_info_cmd_t, void *, void **);
54 static int	drv_attach(dev_info_t *, ddi_attach_cmd_t);
55 static int	drv_detach(dev_info_t *, ddi_detach_cmd_t);
56 
57 /*
58  * Secure objects declarations
59  */
60 #define	SECOBJ_WEP_HASHSZ	67
61 static krwlock_t	drv_secobj_lock;
62 static kmem_cache_t	*drv_secobj_cachep;
63 static mod_hash_t	*drv_secobj_hash;
64 static void		drv_secobj_init(void);
65 static void		drv_secobj_fini(void);
66 static int		drv_ioc_setap(datalink_id_t, struct dlautopush *);
67 static int		drv_ioc_getap(datalink_id_t, struct dlautopush *);
68 static int		drv_ioc_clrap(datalink_id_t);
69 
70 
71 /*
72  * The following entry points are private to dld and are used for control
73  * operations only. The entry points exported to mac drivers are defined
74  * in dld_str.c. Refer to the comment on top of dld_str.c for details.
75  */
76 static int	drv_open(dev_t *, int, int, cred_t *);
77 static int	drv_ioctl(dev_t, int, intptr_t, int, cred_t *, int *);
78 
79 static dev_info_t	*dld_dip;	/* dev_info_t for the driver */
80 uint32_t		dld_opt = 0;	/* Global options */
81 
82 #define	NAUTOPUSH 32
83 static mod_hash_t *dld_ap_hashp;
84 static krwlock_t dld_ap_hash_lock;
85 
86 static struct cb_ops drv_cb_ops = {
87 	drv_open,		/* open */
88 	nulldev,		/* close */
89 	nulldev,		/* strategy */
90 	nulldev,		/* print */
91 	nodev,			/* dump */
92 	nodev,			/* read */
93 	nodev,			/* write */
94 	drv_ioctl,		/* ioctl */
95 	nodev,			/* devmap */
96 	nodev,			/* mmap */
97 	nodev,			/* segmap */
98 	nochpoll,		/* poll */
99 	ddi_prop_op,		/* cb_prop_op */
100 	0,			/* streamtab  */
101 	D_MP			/* Driver compatibility flag */
102 };
103 
104 static struct dev_ops drv_ops = {
105 	DEVO_REV,		/* devo_rev */
106 	0,			/* refcnt */
107 	drv_getinfo,		/* get_dev_info */
108 	nulldev,		/* identify */
109 	nulldev,		/* probe */
110 	drv_attach,		/* attach */
111 	drv_detach,		/* detach */
112 	nodev,			/* reset */
113 	&drv_cb_ops,		/* driver operations */
114 	NULL,			/* bus operations */
115 	nodev,			/* dev power */
116 	ddi_quiesce_not_supported,	/* dev quiesce */
117 };
118 
119 /*
120  * Module linkage information for the kernel.
121  */
122 static	struct modldrv		drv_modldrv = {
123 	&mod_driverops,
124 	DLD_INFO,
125 	&drv_ops
126 };
127 
128 static	struct modlinkage	drv_modlinkage = {
129 	MODREV_1,
130 	&drv_modldrv,
131 	NULL
132 };
133 
134 int
135 _init(void)
136 {
137 	return (mod_install(&drv_modlinkage));
138 }
139 
140 int
141 _fini(void)
142 {
143 	return (mod_remove(&drv_modlinkage));
144 }
145 
146 int
147 _info(struct modinfo *modinfop)
148 {
149 	return (mod_info(&drv_modlinkage, modinfop));
150 }
151 
152 /*
153  * Initialize component modules.
154  */
155 static void
156 drv_init(void)
157 {
158 	drv_secobj_init();
159 	dld_str_init();
160 
161 	/*
162 	 * Create a hash table for autopush configuration.
163 	 */
164 	dld_ap_hashp = mod_hash_create_idhash("dld_autopush_hash",
165 	    NAUTOPUSH, mod_hash_null_valdtor);
166 
167 	ASSERT(dld_ap_hashp != NULL);
168 	rw_init(&dld_ap_hash_lock, NULL, RW_DRIVER, NULL);
169 }
170 
171 /* ARGSUSED */
172 static uint_t
173 drv_ap_exist(mod_hash_key_t key, mod_hash_val_t *val, void *arg)
174 {
175 	boolean_t *pexist = arg;
176 
177 	*pexist = B_TRUE;
178 	return (MH_WALK_TERMINATE);
179 }
180 
181 static int
182 drv_fini(void)
183 {
184 	int		err;
185 	boolean_t	exist = B_FALSE;
186 
187 	rw_enter(&dld_ap_hash_lock, RW_READER);
188 	mod_hash_walk(dld_ap_hashp, drv_ap_exist, &exist);
189 	rw_exit(&dld_ap_hash_lock);
190 	if (exist)
191 		return (EBUSY);
192 
193 	if ((err = dld_str_fini()) != 0)
194 		return (err);
195 
196 	drv_secobj_fini();
197 	mod_hash_destroy_idhash(dld_ap_hashp);
198 	rw_destroy(&dld_ap_hash_lock);
199 	return (0);
200 }
201 
202 /*
203  * devo_getinfo: getinfo(9e)
204  */
205 /*ARGSUSED*/
206 static int
207 drv_getinfo(dev_info_t *dip, ddi_info_cmd_t cmd, void *arg, void **resp)
208 {
209 	if (dld_dip == NULL)
210 		return (DDI_FAILURE);
211 
212 	switch (cmd) {
213 	case DDI_INFO_DEVT2INSTANCE:
214 		*resp = 0;
215 		break;
216 	case DDI_INFO_DEVT2DEVINFO:
217 		*resp = dld_dip;
218 		break;
219 	default:
220 		return (DDI_FAILURE);
221 	}
222 
223 	return (DDI_SUCCESS);
224 }
225 
226 /*
227  * Check properties to set options. (See dld.h for property definitions).
228  */
229 static void
230 drv_set_opt(dev_info_t *dip)
231 {
232 	if (ddi_prop_get_int(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS,
233 	    DLD_PROP_NO_FASTPATH, 0) != 0) {
234 		dld_opt |= DLD_OPT_NO_FASTPATH;
235 	}
236 
237 	if (ddi_prop_get_int(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS,
238 	    DLD_PROP_NO_POLL, 0) != 0) {
239 		dld_opt |= DLD_OPT_NO_POLL;
240 	}
241 
242 	if (ddi_prop_get_int(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS,
243 	    DLD_PROP_NO_ZEROCOPY, 0) != 0) {
244 		dld_opt |= DLD_OPT_NO_ZEROCOPY;
245 	}
246 
247 	if (ddi_prop_get_int(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS,
248 	    DLD_PROP_NO_SOFTRING, 0) != 0) {
249 		dld_opt |= DLD_OPT_NO_SOFTRING;
250 	}
251 }
252 
253 /*
254  * devo_attach: attach(9e)
255  */
256 static int
257 drv_attach(dev_info_t *dip, ddi_attach_cmd_t cmd)
258 {
259 	if (cmd != DDI_ATTACH)
260 		return (DDI_FAILURE);
261 
262 	ASSERT(ddi_get_instance(dip) == 0);
263 	drv_init();
264 	drv_set_opt(dip);
265 
266 	/*
267 	 * Create control node. DLPI provider nodes will be created on demand.
268 	 */
269 	if (ddi_create_minor_node(dip, DLD_CONTROL_MINOR_NAME, S_IFCHR,
270 	    DLD_CONTROL_MINOR, DDI_PSEUDO, 0) != DDI_SUCCESS)
271 		return (DDI_FAILURE);
272 
273 	dld_dip = dip;
274 
275 	/*
276 	 * Log the fact that the driver is now attached.
277 	 */
278 	ddi_report_dev(dip);
279 	return (DDI_SUCCESS);
280 }
281 
282 /*
283  * devo_detach: detach(9e)
284  */
285 static int
286 drv_detach(dev_info_t *dip, ddi_detach_cmd_t cmd)
287 {
288 	if (cmd != DDI_DETACH)
289 		return (DDI_FAILURE);
290 
291 	ASSERT(dld_dip == dip);
292 	if (drv_fini() != 0)
293 		return (DDI_FAILURE);
294 
295 	/*
296 	 * Remove the control node.
297 	 */
298 	ddi_remove_minor_node(dip, DLD_CONTROL_MINOR_NAME);
299 	dld_dip = NULL;
300 
301 	return (DDI_SUCCESS);
302 }
303 
304 /*
305  * dld control node open procedure.
306  */
307 /*ARGSUSED*/
308 static int
309 drv_open(dev_t *devp, int flag, int sflag, cred_t *credp)
310 {
311 	/*
312 	 * Only the control node can be opened.
313 	 */
314 	if (getminor(*devp) != DLD_CONTROL_MINOR)
315 		return (ENODEV);
316 	return (0);
317 }
318 
319 /*
320  * Verify if the caller is allowed to modify a link of the given class.
321  */
322 static int
323 drv_ioc_checkprivs(datalink_class_t class, cred_t *cred)
324 {
325 	if (class == DATALINK_CLASS_IPTUN)
326 		return (secpolicy_iptun_config(cred));
327 	return (secpolicy_dl_config(cred));
328 }
329 
330 /*
331  * DLDIOC_ATTR
332  */
333 /* ARGSUSED */
334 static int
335 drv_ioc_attr(void *karg, intptr_t arg, int mode, cred_t *cred, int *rvalp)
336 {
337 	dld_ioc_attr_t		*diap = karg;
338 	dls_dl_handle_t		dlh;
339 	dls_link_t		*dlp;
340 	zoneid_t		zoneid = crgetzoneid(cred);
341 	int			err;
342 	mac_perim_handle_t	mph;
343 
344 	if (zoneid != GLOBAL_ZONEID &&
345 	    zone_check_datalink(&zoneid, diap->dia_linkid) != 0)
346 		return (ENOENT);
347 
348 	if ((err = dls_devnet_hold_tmp(diap->dia_linkid, &dlh)) != 0)
349 		return (err);
350 
351 	if ((err = mac_perim_enter_by_macname(dls_devnet_mac(dlh),
352 	    &mph)) != 0) {
353 		dls_devnet_rele_tmp(dlh);
354 		return (err);
355 	}
356 
357 	if ((err = dls_link_hold(dls_devnet_mac(dlh), &dlp)) != 0) {
358 		mac_perim_exit(mph);
359 		dls_devnet_rele_tmp(dlh);
360 		return (err);
361 	}
362 
363 	mac_sdu_get(dlp->dl_mh, NULL, &diap->dia_max_sdu);
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 = dls_devnet_hold_tmp(dgt->dgt_linkid, &dlh)) != 0)
1335 		goto done;
1336 
1337 	if ((ret = mac_perim_enter_by_macname(dls_devnet_mac(dlh), &mph)) != 0)
1338 		goto done;
1339 
1340 	if ((ret = dls_link_hold(dls_devnet_mac(dlh), &dlp)) != 0)
1341 		goto done;
1342 
1343 	/*
1344 	 * Make sure that this link belongs to the zone.
1345 	 */
1346 	if (crgetzoneid(cred) != dls_devnet_getownerzid(dlh)) {
1347 		ret = ENOENT;
1348 		goto done;
1349 	}
1350 
1351 	if (dgt->dgt_tran_id == DLDIOC_GETTRAN_GETNTRAN) {
1352 		ret = mac_transceiver_count(dlp->dl_mh, &dgt->dgt_tran_id);
1353 	} else {
1354 		ret = mac_transceiver_info(dlp->dl_mh, dgt->dgt_tran_id,
1355 		    &dgt->dgt_present, &dgt->dgt_usable);
1356 	}
1357 
1358 done:
1359 	if (dlp != NULL)
1360 		dls_link_rele(dlp);
1361 
1362 	if (mph != NULL)
1363 		mac_perim_exit(mph);
1364 
1365 	if (dlh != NULL)
1366 		dls_devnet_rele_tmp(dlh);
1367 
1368 	return (ret);
1369 }
1370 
1371 /* ARGSUSED */
1372 static int
1373 drv_ioc_readtran(void *karg, intptr_t arg, int mode, cred_t *cred,
1374     int *rvalp)
1375 {
1376 	int			ret = 0;
1377 	mac_perim_handle_t	mph = NULL;
1378 	dls_dl_handle_t		dlh = NULL;
1379 	dls_link_t		*dlp = NULL;
1380 	dld_ioc_tranio_t	*dti = karg;
1381 	uint8_t			buf[256];
1382 	size_t			nr;
1383 
1384 	/*
1385 	 * Be strict for the moment
1386 	 */
1387 	if (dti->dti_nbytes != 256 || dti->dti_off != 0)
1388 		return (EINVAL);
1389 
1390 	if ((ret = dls_devnet_hold_tmp(dti->dti_linkid, &dlh)) != 0)
1391 		goto done;
1392 
1393 	if ((ret = mac_perim_enter_by_macname(dls_devnet_mac(dlh), &mph)) != 0)
1394 		goto done;
1395 
1396 	if ((ret = dls_link_hold(dls_devnet_mac(dlh), &dlp)) != 0)
1397 		goto done;
1398 
1399 	/*
1400 	 * Make sure that this link belongs to the zone.
1401 	 */
1402 	if (crgetzoneid(cred) != dls_devnet_getownerzid(dlh)) {
1403 		ret = ENOENT;
1404 		goto done;
1405 	}
1406 
1407 	bzero(buf, sizeof (buf));
1408 	if ((ret = mac_transceiver_read(dlp->dl_mh, dti->dti_tran_id,
1409 	    dti->dti_page, buf, dti->dti_nbytes, dti->dti_off, &nr)) == 0) {
1410 		dti->dti_nbytes = nr;
1411 		ret = ddi_copyout(buf, (void *)(uintptr_t)dti->dti_buf,
1412 		    sizeof (buf), mode);
1413 	}
1414 
1415 done:
1416 	if (dlp != NULL)
1417 		dls_link_rele(dlp);
1418 
1419 	if (mph != NULL)
1420 		mac_perim_exit(mph);
1421 
1422 	if (dlh != NULL)
1423 		dls_devnet_rele_tmp(dlh);
1424 
1425 	return (ret);
1426 }
1427 
1428 /* ARGSUSED */
1429 static int
1430 drv_ioc_getled(void *karg, intptr_t arg, int mode, cred_t *cred,
1431     int *rvalp)
1432 {
1433 	int			ret = 0;
1434 	mac_perim_handle_t	mph = NULL;
1435 	dls_dl_handle_t		dlh = NULL;
1436 	dls_link_t		*dlp = NULL;
1437 	dld_ioc_led_t		*dil = karg;
1438 
1439 	if ((mode & FREAD) == 0)
1440 		return (EBADF);
1441 
1442 	if ((ret = dls_devnet_hold_tmp(dil->dil_linkid, &dlh)) != 0)
1443 		goto done;
1444 
1445 	if ((ret = mac_perim_enter_by_macname(dls_devnet_mac(dlh), &mph)) != 0)
1446 		goto done;
1447 
1448 	if ((ret = dls_link_hold(dls_devnet_mac(dlh), &dlp)) != 0)
1449 		goto done;
1450 
1451 	/*
1452 	 * Make sure that this link belongs to the zone.
1453 	 */
1454 	if (crgetzoneid(cred) != dls_devnet_getownerzid(dlh)) {
1455 		ret = ENOENT;
1456 		goto done;
1457 	}
1458 
1459 	ret = mac_led_get(dlp->dl_mh, &dil->dil_supported, &dil->dil_active);
1460 
1461 done:
1462 	if (dlp != NULL)
1463 		dls_link_rele(dlp);
1464 
1465 	if (mph != NULL)
1466 		mac_perim_exit(mph);
1467 
1468 	if (dlh != NULL)
1469 		dls_devnet_rele_tmp(dlh);
1470 
1471 	return (ret);
1472 }
1473 
1474 /* ARGSUSED */
1475 static int
1476 drv_ioc_setled(void *karg, intptr_t arg, int mode, cred_t *cred,
1477     int *rvalp)
1478 {
1479 	int			ret = 0;
1480 	mac_perim_handle_t	mph = NULL;
1481 	dls_dl_handle_t		dlh = NULL;
1482 	dls_link_t		*dlp = NULL;
1483 	dld_ioc_led_t		*dil = karg;
1484 
1485 	if ((mode & FWRITE) == 0)
1486 		return (EBADF);
1487 
1488 	if ((ret = dls_devnet_hold_tmp(dil->dil_linkid, &dlh)) != 0)
1489 		goto done;
1490 
1491 	if ((ret = mac_perim_enter_by_macname(dls_devnet_mac(dlh), &mph)) != 0)
1492 		goto done;
1493 
1494 	if ((ret = dls_link_hold(dls_devnet_mac(dlh), &dlp)) != 0)
1495 		goto done;
1496 
1497 	/*
1498 	 * Make sure that this link belongs to the zone.
1499 	 */
1500 	if (crgetzoneid(cred) != dls_devnet_getownerzid(dlh)) {
1501 		ret = ENOENT;
1502 		goto done;
1503 	}
1504 
1505 	ret = mac_led_set(dlp->dl_mh, dil->dil_active);
1506 
1507 done:
1508 	if (dlp != NULL)
1509 		dls_link_rele(dlp);
1510 
1511 	if (mph != NULL)
1512 		mac_perim_exit(mph);
1513 
1514 	if (dlh != NULL)
1515 		dls_devnet_rele_tmp(dlh);
1516 
1517 	return (ret);
1518 }
1519 
1520 
1521 /*
1522  * Note that ioctls that modify links have a NULL di_priv_func(), as
1523  * privileges can only be checked after we know the class of the link being
1524  * modified (due to class-specific fine-grained privileges such as
1525  * sys_iptun_config).
1526  */
1527 static dld_ioc_info_t drv_ioc_list[] = {
1528 	{DLDIOC_ATTR, DLDCOPYINOUT, sizeof (dld_ioc_attr_t),
1529 	    drv_ioc_attr, NULL},
1530 	{DLDIOC_PHYS_ATTR, DLDCOPYINOUT, sizeof (dld_ioc_phys_attr_t),
1531 	    drv_ioc_phys_attr, NULL},
1532 	{DLDIOC_SECOBJ_SET, DLDCOPYIN, sizeof (dld_ioc_secobj_set_t),
1533 	    drv_ioc_secobj_set, secpolicy_dl_config},
1534 	{DLDIOC_SECOBJ_GET, DLDCOPYINOUT, sizeof (dld_ioc_secobj_get_t),
1535 	    drv_ioc_secobj_get, secpolicy_dl_config},
1536 	{DLDIOC_SECOBJ_UNSET, DLDCOPYIN, sizeof (dld_ioc_secobj_unset_t),
1537 	    drv_ioc_secobj_unset, secpolicy_dl_config},
1538 	{DLDIOC_DOORSERVER, DLDCOPYIN, sizeof (dld_ioc_door_t),
1539 	    drv_ioc_doorserver, secpolicy_dl_config},
1540 	{DLDIOC_RENAME, DLDCOPYIN, sizeof (dld_ioc_rename_t),
1541 	    drv_ioc_rename, NULL},
1542 	{DLDIOC_MACADDRGET, DLDCOPYINOUT, sizeof (dld_ioc_macaddrget_t),
1543 	    drv_ioc_macaddrget, NULL},
1544 	{DLDIOC_ADDFLOW, DLDCOPYIN, sizeof (dld_ioc_addflow_t),
1545 	    drv_ioc_addflow, secpolicy_dl_config},
1546 	{DLDIOC_REMOVEFLOW, DLDCOPYIN, sizeof (dld_ioc_removeflow_t),
1547 	    drv_ioc_removeflow, secpolicy_dl_config},
1548 	{DLDIOC_MODIFYFLOW, DLDCOPYIN, sizeof (dld_ioc_modifyflow_t),
1549 	    drv_ioc_modifyflow, secpolicy_dl_config},
1550 	{DLDIOC_WALKFLOW, DLDCOPYINOUT, sizeof (dld_ioc_walkflow_t),
1551 	    drv_ioc_walkflow, NULL},
1552 	{DLDIOC_USAGELOG, DLDCOPYIN, sizeof (dld_ioc_usagelog_t),
1553 	    drv_ioc_usagelog, secpolicy_dl_config},
1554 	{DLDIOC_SETMACPROP, DLDCOPYIN, sizeof (dld_ioc_macprop_t),
1555 	    drv_ioc_setprop, NULL},
1556 	{DLDIOC_GETMACPROP, DLDCOPYIN, sizeof (dld_ioc_macprop_t),
1557 	    drv_ioc_getprop, NULL},
1558 	{DLDIOC_GETHWGRP, DLDCOPYINOUT, sizeof (dld_ioc_hwgrpget_t),
1559 	    drv_ioc_hwgrpget, NULL},
1560 	{DLDIOC_GETTRAN, DLDCOPYINOUT, sizeof (dld_ioc_gettran_t),
1561 	    drv_ioc_gettran, NULL },
1562 	{DLDIOC_READTRAN, DLDCOPYINOUT, sizeof (dld_ioc_tranio_t),
1563 	    drv_ioc_readtran, NULL },
1564 	{DLDIOC_GETLED, DLDCOPYINOUT, sizeof (dld_ioc_led_t),
1565 	    drv_ioc_getled, NULL },
1566 	{DLDIOC_SETLED, DLDCOPYIN, sizeof (dld_ioc_led_t),
1567 	    drv_ioc_setled, secpolicy_dl_config}
1568 };
1569 
1570 typedef struct dld_ioc_modentry {
1571 	uint16_t	dim_modid;	/* Top 16 bits of ioctl command */
1572 	char		*dim_modname;	/* Module to be loaded */
1573 	int		ctrl_node_inst;	/* Ctrl node instance */
1574 	dld_ioc_info_t	*dim_list;	/* array of ioctl structures */
1575 	uint_t		dim_count;	/* number of elements in dim_list */
1576 } dld_ioc_modentry_t;
1577 
1578 /*
1579  * For all modules except for dld, dim_list and dim_count are assigned
1580  * when the modules register their ioctls in dld_ioc_register().  We
1581  * can statically initialize dld's ioctls in-line here; there's no
1582  * need for it to call dld_ioc_register() itself. ctrl_node_inst controls
1583  * whether an instance of the device will be held or the driver. If set to
1584  * a non-negative integer, device instance specified in ctrl_node_inst will
1585  * be held; so dld_ioc_register() _must_ be called in xxx_attach() routine of
1586  * the driver. If set to -1, driver will be held; so dld_ioc_register() _must_
1587  * be called in xxx_init() routine of the driver.
1588  */
1589 static dld_ioc_modentry_t dld_ioc_modtable[] = {
1590 	{DLD_IOC,	"dld", 0, drv_ioc_list, DLDIOCCNT(drv_ioc_list)},
1591 	{AGGR_IOC,	"aggr", 0, NULL, 0},
1592 	{VNIC_IOC,	"vnic",	0, NULL, 0},
1593 	{SIMNET_IOC,	"simnet", 0, NULL, 0},
1594 	{BRIDGE_IOC,	"bridge", 0, NULL, 0},
1595 	{IPTUN_IOC,	"iptun", 0, NULL, 0},
1596 	{IBPART_IOC,	"ibp", -1, NULL, 0},
1597 	{OVERLAY_IOC,	"overlay", 0, NULL, 0}
1598 };
1599 #define	DLDIOC_CNT	\
1600 	(sizeof (dld_ioc_modtable) / sizeof (dld_ioc_modentry_t))
1601 
1602 static dld_ioc_modentry_t *
1603 dld_ioc_findmod(uint16_t modid)
1604 {
1605 	int	i;
1606 
1607 	for (i = 0; i < DLDIOC_CNT; i++) {
1608 		if (modid == dld_ioc_modtable[i].dim_modid)
1609 			return (&dld_ioc_modtable[i]);
1610 	}
1611 	return (NULL);
1612 }
1613 
1614 int
1615 dld_ioc_register(uint16_t modid, dld_ioc_info_t *list, uint_t count)
1616 {
1617 	dld_ioc_modentry_t *dim = dld_ioc_findmod(modid);
1618 
1619 	if (dim == NULL)
1620 		return (ENOENT);
1621 
1622 	dim->dim_list = list;
1623 	dim->dim_count = count;
1624 	return (0);
1625 }
1626 
1627 void
1628 dld_ioc_unregister(uint16_t modid)
1629 {
1630 	VERIFY(dld_ioc_register(modid, NULL, 0) == 0);
1631 }
1632 
1633 /*
1634  * The general design with GLDv3 ioctls is that all ioctls issued
1635  * through /dev/dld go through this drv_ioctl() function.  This
1636  * function handles all ioctls on behalf of modules listed in
1637  * dld_ioc_modtable.
1638  *
1639  * When an ioctl is received, this function looks for the associated
1640  * module-id-specific ioctl information using dld_ioc_findmod(). The
1641  * call to ddi_hold_driver() or ddi_hold_devi_by_instance() on the
1642  * associated device will cause the kernel module responsible for the
1643  * ioctl to be loaded if it's not already loaded, which should result
1644  * in that module calling dld_ioc_register(), thereby filling in the
1645  * dim_list containing the details for the ioctl being processed.
1646  *
1647  * This function can then perform operations such as copyin() data and
1648  * do credential checks based on the registered ioctl information,
1649  * then issue the callback function di_func() registered by the
1650  * responsible module.  Upon return, the appropriate copyout()
1651  * operation can be performed and the operation completes.
1652  */
1653 /* ARGSUSED */
1654 static int
1655 drv_ioctl(dev_t dev, int cmd, intptr_t arg, int mode, cred_t *cred, int *rvalp)
1656 {
1657 	dld_ioc_modentry_t *dim;
1658 	dld_ioc_info_t	*info;
1659 	dev_info_t	*dip = NULL;
1660 	struct dev_ops	*dops = NULL;
1661 	major_t		major;
1662 	void		*buf = NULL;
1663 	size_t		sz;
1664 	int		i, err;
1665 
1666 	if ((dim = dld_ioc_findmod(DLD_IOC_MODID(cmd))) == NULL)
1667 		return (ENOTSUP);
1668 
1669 	major = ddi_name_to_major(dim->dim_modname);
1670 
1671 	if (dim->ctrl_node_inst == -1) {
1672 		/*
1673 		 * No dedicated instance to process ioctls.
1674 		 * dld_ioc_register() is called in xxx_init().
1675 		 */
1676 		dops = ddi_hold_driver(major);
1677 	} else {
1678 		/*
1679 		 * Dedicated instance to handle ioctl.
1680 		 * dld_ioc_register() is called in xxx_attach().
1681 		 */
1682 		dip = ddi_hold_devi_by_instance(major, dim->ctrl_node_inst, 0);
1683 	}
1684 
1685 	if ((dip == NULL && dops == NULL) || dim->dim_list == NULL) {
1686 		err = ENODEV;
1687 		goto done;
1688 	}
1689 
1690 	for (i = 0; i < dim->dim_count; i++) {
1691 		if (cmd == dim->dim_list[i].di_cmd)
1692 			break;
1693 	}
1694 	if (i == dim->dim_count) {
1695 		err = ENOTSUP;
1696 		goto done;
1697 	}
1698 
1699 	info = &dim->dim_list[i];
1700 
1701 	if (info->di_priv_func != NULL &&
1702 	    (err = info->di_priv_func(cred)) != 0)
1703 		goto done;
1704 
1705 	sz = info->di_argsize;
1706 	if ((buf = kmem_zalloc(sz, KM_NOSLEEP)) == NULL) {
1707 		err = ENOMEM;
1708 		goto done;
1709 	}
1710 
1711 	if ((info->di_flags & DLDCOPYIN) &&
1712 	    ddi_copyin((void *)arg, buf, sz, mode) != 0) {
1713 		err = EFAULT;
1714 		goto done;
1715 	}
1716 
1717 	err = info->di_func(buf, arg, mode, cred, rvalp);
1718 
1719 	if ((info->di_flags & DLDCOPYOUT) &&
1720 	    ddi_copyout(buf, (void *)arg, sz, mode) != 0 && err == 0)
1721 		err = EFAULT;
1722 
1723 done:
1724 	if (buf != NULL)
1725 		kmem_free(buf, sz);
1726 	if (dip != NULL)
1727 		ddi_release_devi(dip);
1728 	if (dops != NULL)
1729 		ddi_rele_driver(major);
1730 	return (err);
1731 }
1732