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