xref: /illumos-gate/usr/src/uts/common/io/dld/dld_drv.c (revision dd72704bd9e794056c558153663c739e2012d721)
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(
352 	    dls_devnet_mac(dlh), &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 
365 	dls_link_rele(dlp);
366 	mac_perim_exit(mph);
367 	dls_devnet_rele_tmp(dlh);
368 
369 	return (0);
370 }
371 
372 /*
373  * DLDIOC_PHYS_ATTR
374  */
375 /* ARGSUSED */
376 static int
377 drv_ioc_phys_attr(void *karg, intptr_t arg, int mode, cred_t *cred, int *rvalp)
378 {
379 	dld_ioc_phys_attr_t	*dipp = karg;
380 	int			err;
381 	dls_dl_handle_t		dlh;
382 	dls_dev_handle_t	ddh;
383 	dev_t			phydev;
384 	zoneid_t		zoneid = crgetzoneid(cred);
385 
386 	if (zoneid != GLOBAL_ZONEID &&
387 	    zone_check_datalink(&zoneid, dipp->dip_linkid) != 0)
388 		return (ENOENT);
389 
390 	/*
391 	 * Every physical link should have its physical dev_t kept in the
392 	 * daemon. If not, it is not a valid physical link.
393 	 */
394 	if (dls_mgmt_get_phydev(dipp->dip_linkid, &phydev) != 0)
395 		return (EINVAL);
396 
397 	/*
398 	 * Although this is a valid physical link, it might already be removed
399 	 * by DR or during system shutdown. softmac_hold_device() would return
400 	 * ENOENT in this case.
401 	 */
402 	if ((err = softmac_hold_device(phydev, &ddh)) != 0)
403 		return (err);
404 
405 	if (dls_devnet_hold_tmp(dipp->dip_linkid, &dlh) != 0) {
406 		/*
407 		 * Although this is an active physical link, its link type is
408 		 * not supported by GLDv3, and therefore it does not have
409 		 * vanity naming support.
410 		 */
411 		dipp->dip_novanity = B_TRUE;
412 	} else {
413 		dipp->dip_novanity = B_FALSE;
414 		dls_devnet_rele_tmp(dlh);
415 	}
416 	/*
417 	 * Get the physical device name from the major number and the instance
418 	 * number derived from phydev.
419 	 */
420 	(void) snprintf(dipp->dip_dev, MAXLINKNAMELEN, "%s%d",
421 	    ddi_major_to_name(getmajor(phydev)), getminor(phydev) - 1);
422 
423 	softmac_rele_device(ddh);
424 	return (0);
425 }
426 
427 /* ARGSUSED */
428 static int
429 drv_ioc_hwgrpget(void *karg, intptr_t arg, int mode, cred_t *cred, int *rvalp)
430 {
431 	dld_ioc_hwgrpget_t	*hwgrpp = karg;
432 	dld_hwgrpinfo_t		hwgrp, *hip;
433 	mac_handle_t		mh = NULL;
434 	int			i, err, rgrpnum, tgrpnum;
435 	uint_t			bytes_left;
436 	int			totgrps = 0;
437 	zoneid_t		zoneid = crgetzoneid(cred);
438 
439 	if (zoneid != GLOBAL_ZONEID &&
440 	    zone_check_datalink(&zoneid, hwgrpp->dih_linkid) != 0)
441 		return (ENOENT);
442 
443 	hwgrpp->dih_n_groups = 0;
444 	err = mac_open_by_linkid(hwgrpp->dih_linkid, &mh);
445 	if (err != 0)
446 		goto done;
447 
448 	hip = (dld_hwgrpinfo_t *)
449 	    ((uchar_t *)arg + sizeof (dld_ioc_hwgrpget_t));
450 	bytes_left = hwgrpp->dih_size;
451 
452 	rgrpnum = mac_hwgrp_num(mh, MAC_RING_TYPE_RX);
453 	/* display the default group information first */
454 	if (rgrpnum > 0) {
455 		if (sizeof (dld_hwgrpinfo_t) > bytes_left) {
456 			err = ENOSPC;
457 			goto done;
458 		}
459 
460 		bzero(&hwgrp, sizeof (hwgrp));
461 		bcopy(mac_name(mh), hwgrp.dhi_link_name,
462 		    sizeof (hwgrp.dhi_link_name));
463 		mac_get_hwrxgrp_info(mh, 0, &hwgrp.dhi_grp_num,
464 		    &hwgrp.dhi_n_rings, hwgrp.dhi_rings, &hwgrp.dhi_grp_type,
465 		    &hwgrp.dhi_n_clnts, hwgrp.dhi_clnts);
466 		if (hwgrp.dhi_n_rings != 0) {
467 			if (copyout(&hwgrp, hip, sizeof (hwgrp)) != 0) {
468 				err = EFAULT;
469 				goto done;
470 			}
471 		}
472 		hip++;
473 		totgrps++;
474 		bytes_left -= sizeof (dld_hwgrpinfo_t);
475 	}
476 
477 	tgrpnum = mac_hwgrp_num(mh, MAC_RING_TYPE_TX);
478 	/* display the default group information first */
479 	if (tgrpnum > 0) {
480 		if (sizeof (dld_hwgrpinfo_t) > bytes_left) {
481 			err = ENOSPC;
482 			goto done;
483 		}
484 
485 		bzero(&hwgrp, sizeof (hwgrp));
486 		bcopy(mac_name(mh), hwgrp.dhi_link_name,
487 		    sizeof (hwgrp.dhi_link_name));
488 		mac_get_hwtxgrp_info(mh, tgrpnum - 1, &hwgrp.dhi_grp_num,
489 		    &hwgrp.dhi_n_rings, hwgrp.dhi_rings, &hwgrp.dhi_grp_type,
490 		    &hwgrp.dhi_n_clnts, hwgrp.dhi_clnts);
491 		if (hwgrp.dhi_n_rings != 0) {
492 			if (copyout(&hwgrp, hip, sizeof (hwgrp)) != 0) {
493 				err = EFAULT;
494 				goto done;
495 			}
496 		}
497 		hip++;
498 		totgrps++;
499 		bytes_left -= sizeof (dld_hwgrpinfo_t);
500 	}
501 
502 	/* Rest of the rx groups */
503 	for (i = 1; i < rgrpnum; i++) {
504 		if (sizeof (dld_hwgrpinfo_t) > bytes_left) {
505 			err = ENOSPC;
506 			goto done;
507 		}
508 
509 		bzero(&hwgrp, sizeof (hwgrp));
510 		bcopy(mac_name(mh), hwgrp.dhi_link_name,
511 		    sizeof (hwgrp.dhi_link_name));
512 		mac_get_hwrxgrp_info(mh, i, &hwgrp.dhi_grp_num,
513 		    &hwgrp.dhi_n_rings, hwgrp.dhi_rings, &hwgrp.dhi_grp_type,
514 		    &hwgrp.dhi_n_clnts, hwgrp.dhi_clnts);
515 		if (hwgrp.dhi_n_rings == 0)
516 			continue;
517 		if (copyout(&hwgrp, hip, sizeof (hwgrp)) != 0) {
518 			err = EFAULT;
519 			goto done;
520 		}
521 
522 		hip++;
523 		totgrps++;
524 		bytes_left -= sizeof (dld_hwgrpinfo_t);
525 	}
526 
527 	/* Rest of the tx group */
528 	tgrpnum = mac_hwgrp_num(mh, MAC_RING_TYPE_TX);
529 	for (i = 0; i < tgrpnum - 1; i++) {
530 		if (sizeof (dld_hwgrpinfo_t) > bytes_left) {
531 			err = ENOSPC;
532 			goto done;
533 		}
534 
535 		bzero(&hwgrp, sizeof (hwgrp));
536 		bcopy(mac_name(mh), hwgrp.dhi_link_name,
537 		    sizeof (hwgrp.dhi_link_name));
538 		mac_get_hwtxgrp_info(mh, i, &hwgrp.dhi_grp_num,
539 		    &hwgrp.dhi_n_rings, hwgrp.dhi_rings, &hwgrp.dhi_grp_type,
540 		    &hwgrp.dhi_n_clnts, hwgrp.dhi_clnts);
541 		if (hwgrp.dhi_n_rings == 0)
542 			continue;
543 		if (copyout(&hwgrp, hip, sizeof (hwgrp)) != 0) {
544 			err = EFAULT;
545 			goto done;
546 		}
547 
548 		hip++;
549 		totgrps++;
550 		bytes_left -= sizeof (dld_hwgrpinfo_t);
551 	}
552 
553 done:
554 	if (mh != NULL)
555 		dld_mac_close(mh);
556 	if (err == 0)
557 		hwgrpp->dih_n_groups = totgrps;
558 	return (err);
559 }
560 
561 /* ARGSUSED */
562 static int
563 drv_ioc_macaddrget(void *karg, intptr_t arg, int mode, cred_t *cred, int *rvalp)
564 {
565 	dld_ioc_macaddrget_t	*magp = karg;
566 	dld_macaddrinfo_t	mai, *maip;
567 	mac_handle_t		mh = NULL;
568 	int			i, err;
569 	uint_t			bytes_left;
570 	boolean_t		is_used;
571 	zoneid_t		zoneid = crgetzoneid(cred);
572 
573 	if (zoneid != GLOBAL_ZONEID &&
574 	    zone_check_datalink(&zoneid, magp->dig_linkid) != 0)
575 		return (ENOENT);
576 
577 	magp->dig_count = 0;
578 	err = mac_open_by_linkid(magp->dig_linkid, &mh);
579 	if (err != 0)
580 		goto done;
581 
582 	maip = (dld_macaddrinfo_t *)
583 	    ((uchar_t *)arg + sizeof (dld_ioc_macaddrget_t));
584 	bytes_left = magp->dig_size;
585 
586 	for (i = 0; i < mac_addr_factory_num(mh) + 1; i++) {
587 		if (sizeof (dld_macaddrinfo_t) > bytes_left) {
588 			err = ENOSPC;
589 			goto done;
590 		}
591 
592 		bzero(&mai, sizeof (mai));
593 
594 		if (i == 0) {
595 			/* primary MAC address */
596 			mac_unicast_primary_get(mh, mai.dmi_addr);
597 			mai.dmi_addrlen = mac_addr_len(mh);
598 			mac_unicast_primary_info(mh, mai.dmi_client_name,
599 			    &is_used);
600 		} else {
601 			/* factory MAC address slot */
602 			mac_addr_factory_value(mh, i, mai.dmi_addr,
603 			    &mai.dmi_addrlen, mai.dmi_client_name, &is_used);
604 		}
605 
606 		mai.dmi_slot = i;
607 		if (is_used)
608 			mai.dmi_flags |= DLDIOCMACADDR_USED;
609 
610 		if (copyout(&mai, maip, sizeof (mai)) != 0) {
611 			err = EFAULT;
612 			goto done;
613 		}
614 
615 		maip++;
616 		bytes_left -= sizeof (dld_macaddrinfo_t);
617 	}
618 
619 done:
620 	if (mh != NULL)
621 		dld_mac_close(mh);
622 	if (err == 0)
623 		magp->dig_count = mac_addr_factory_num(mh) + 1;
624 	return (err);
625 }
626 
627 /*
628  * DLDIOC_SET/GETMACPROP
629  */
630 static int
631 drv_ioc_prop_common(dld_ioc_macprop_t *prop, intptr_t arg, boolean_t set,
632     cred_t *cred, int mode)
633 {
634 	int			err = EINVAL;
635 	dls_dl_handle_t		dlh = NULL;
636 	dls_link_t		*dlp = NULL;
637 	mac_perim_handle_t	mph = NULL;
638 	dld_ioc_macprop_t	*kprop;
639 	datalink_id_t		linkid;
640 	datalink_class_t	class;
641 	zoneid_t		zoneid = crgetzoneid(cred);
642 	uint_t			dsize;
643 
644 	/*
645 	 * We only use pr_valsize from prop, as the caller only did a
646 	 * copyin() for sizeof (dld_ioc_prop_t), which doesn't cover
647 	 * the property data.  We copyin the full dld_ioc_prop_t
648 	 * including the data into kprop down below.
649 	 */
650 	dsize = sizeof (dld_ioc_macprop_t) + prop->pr_valsize - 1;
651 	if (dsize < prop->pr_valsize)
652 		return (EINVAL);
653 
654 	/*
655 	 * The property data is variable size, so we need to allocate
656 	 * a buffer for kernel use as this data was not part of the
657 	 * prop allocation and copyin() done by the framework.
658 	 */
659 	if ((kprop = kmem_alloc(dsize, KM_NOSLEEP)) == NULL)
660 		return (ENOMEM);
661 
662 	if (ddi_copyin((void *)arg, kprop, dsize, mode) != 0) {
663 		err = EFAULT;
664 		goto done;
665 	}
666 
667 	linkid = kprop->pr_linkid;
668 
669 	if (set) {
670 		if ((err = dls_mgmt_get_linkinfo(linkid, NULL, &class, NULL,
671 		    NULL)) != 0 || (err = drv_ioc_checkprivs(class, cred)) != 0)
672 			goto done;
673 	}
674 
675 	if ((err = dls_devnet_hold_tmp(linkid, &dlh)) != 0)
676 		goto done;
677 	if ((err = mac_perim_enter_by_macname(dls_devnet_mac(dlh), &mph)) != 0)
678 		goto done;
679 	if ((err = dls_link_hold(dls_devnet_mac(dlh), &dlp)) != 0)
680 		goto done;
681 
682 	/*
683 	 * Don't allow a process to get or set properties of a link if that
684 	 * link doesn't belong to that zone.
685 	 */
686 	if (zoneid != dls_devnet_getownerzid(dlh)) {
687 		err = ENOENT;
688 		goto done;
689 	}
690 
691 	if (!mac_prop_check_size(kprop->pr_num, kprop->pr_valsize,
692 	    kprop->pr_flags & DLD_PROP_POSSIBLE)) {
693 		err = ENOBUFS;
694 		goto done;
695 	}
696 
697 	switch (kprop->pr_num) {
698 	case MAC_PROP_ZONE:
699 		if (set) {
700 			dld_ioc_zid_t *dzp = (dld_ioc_zid_t *)kprop->pr_val;
701 
702 			if (zoneid != GLOBAL_ZONEID) {
703 				err = EACCES;
704 				goto done;
705 			}
706 			err = dls_devnet_setzid(dlh, dzp->diz_zid);
707 		} else {
708 			kprop->pr_perm_flags = MAC_PROP_PERM_RW;
709 			(*(zoneid_t *)kprop->pr_val) = dls_devnet_getzid(dlh);
710 		}
711 		break;
712 	case MAC_PROP_AUTOPUSH: {
713 		struct dlautopush *dlap = (struct dlautopush *)kprop->pr_val;
714 
715 		if (set) {
716 			if (kprop->pr_valsize != 0)
717 				err = drv_ioc_setap(linkid, dlap);
718 			else
719 				err = drv_ioc_clrap(linkid);
720 		} else {
721 			/*
722 			 * You might think that the earlier call to
723 			 * mac_prop_check_size() should catch this but
724 			 * it can't. The autopush prop uses 0 as a
725 			 * sentinel value to clear the prop. This
726 			 * check ensures we don't allow a get with a
727 			 * valsize of 0.
728 			 */
729 			if (kprop->pr_valsize == 0) {
730 				err = ENOBUFS;
731 				goto done;
732 			}
733 
734 			kprop->pr_perm_flags = MAC_PROP_PERM_RW;
735 			err = drv_ioc_getap(linkid, dlap);
736 		}
737 		break;
738 	}
739 	case MAC_PROP_TAGMODE:
740 		if (set) {
741 			link_tagmode_t mode = *(link_tagmode_t *)kprop->pr_val;
742 
743 			if (mode != LINK_TAGMODE_VLANONLY &&
744 			    mode != LINK_TAGMODE_NORMAL) {
745 				err = EINVAL;
746 			} else {
747 				dlp->dl_tagmode = mode;
748 				err = 0;
749 			}
750 		} else {
751 			*(link_tagmode_t *)kprop->pr_val = dlp->dl_tagmode;
752 			kprop->pr_perm_flags = MAC_PROP_PERM_RW;
753 			err = 0;
754 		}
755 		break;
756 	default: {
757 		mac_propval_range_t *rangep = NULL;
758 		void *default_val = NULL;
759 		uint_t default_size = 0;
760 
761 		/* set a property value */
762 		if (set) {
763 			err = mac_set_prop(dlp->dl_mh, kprop->pr_num,
764 			    kprop->pr_name, kprop->pr_val, kprop->pr_valsize);
765 			break;
766 		}
767 
768 		/*
769 		 * Get the property value, default, or possible value
770 		 * depending on flags passed from the user.
771 		 */
772 
773 		/* a property has RW permissions by default */
774 		kprop->pr_perm_flags = MAC_PROP_PERM_RW;
775 
776 		if (kprop->pr_flags & DLD_PROP_POSSIBLE) {
777 			rangep = (mac_propval_range_t *)kprop->pr_val;
778 
779 			/*
780 			 * fail if rangep is not aligned to first
781 			 * member of mac_propval_range_t.
782 			 */
783 			ASSERT(IS_P2ALIGNED(rangep, sizeof (uint_t)));
784 		} else if (kprop->pr_flags & DLD_PROP_DEFAULT) {
785 			default_val = kprop->pr_val;
786 			default_size = kprop->pr_valsize;
787 		}
788 
789 		/*
790 		 * Always return the permissions, and optionally return
791 		 * the default value or possible values range.
792 		 */
793 		err = mac_prop_info(dlp->dl_mh, kprop->pr_num, kprop->pr_name,
794 		    default_val, default_size, rangep, &kprop->pr_perm_flags);
795 		if (err != 0)
796 			goto done;
797 
798 		if (default_val == NULL && rangep == NULL) {
799 			err = mac_get_prop(dlp->dl_mh, kprop->pr_num,
800 			    kprop->pr_name, kprop->pr_val, kprop->pr_valsize);
801 		}
802 	}
803 	}
804 
805 done:
806 	if (!set && ddi_copyout(kprop, (void *)arg, dsize, mode) != 0)
807 		err = EFAULT;
808 
809 	if (dlp != NULL)
810 		dls_link_rele(dlp);
811 
812 	if (mph != NULL) {
813 		int32_t	cpuid;
814 		void	*mdip = NULL;
815 
816 		if (dlp != NULL && set && err == 0) {
817 			cpuid = mac_client_intr_cpu(dlp->dl_mch);
818 			mdip = mac_get_devinfo(dlp->dl_mh);
819 		}
820 
821 		mac_perim_exit(mph);
822 
823 		if (mdip != NULL && cpuid != -1)
824 			mac_client_set_intr_cpu(mdip, dlp->dl_mch, cpuid);
825 	}
826 
827 	if (dlh != NULL)
828 		dls_devnet_rele_tmp(dlh);
829 
830 	if (kprop != NULL)
831 		kmem_free(kprop, dsize);
832 	return (err);
833 }
834 
835 /* ARGSUSED */
836 static int
837 drv_ioc_setprop(void *karg, intptr_t arg, int mode, cred_t *cred, int *rvalp)
838 {
839 	return (drv_ioc_prop_common(karg, arg, B_TRUE, cred, mode));
840 }
841 
842 /* ARGSUSED */
843 static int
844 drv_ioc_getprop(void *karg, intptr_t arg, int mode, cred_t *cred, int *rvalp)
845 {
846 	return (drv_ioc_prop_common(karg, arg, B_FALSE, cred, mode));
847 }
848 
849 /*
850  * DLDIOC_RENAME.
851  *
852  * This function handles two cases of link renaming. See more in comments above
853  * dls_datalink_rename().
854  */
855 /* ARGSUSED */
856 static int
857 drv_ioc_rename(void *karg, intptr_t arg, int mode, cred_t *cred, int *rvalp)
858 {
859 	dld_ioc_rename_t	*dir = karg;
860 	mod_hash_key_t		key;
861 	mod_hash_val_t		val;
862 	zoneid_t		zoneid = crgetzoneid(cred);
863 	datalink_class_t	class;
864 	int			err;
865 
866 	if (zoneid != GLOBAL_ZONEID &&
867 	    (zone_check_datalink(&zoneid, dir->dir_linkid1) != 0 ||
868 	    dir->dir_linkid2 != DATALINK_INVALID_LINKID &&
869 	    zone_check_datalink(&zoneid, dir->dir_linkid2) != 0))
870 		return (ENOENT);
871 
872 	if ((err = dls_mgmt_get_linkinfo(dir->dir_linkid1, NULL, &class, NULL,
873 	    NULL)) != 0)
874 		return (err);
875 
876 	if ((err = drv_ioc_checkprivs(class, cred)) != 0)
877 		return (err);
878 
879 	if ((err = dls_devnet_rename(dir->dir_linkid1, dir->dir_linkid2,
880 	    dir->dir_link)) != 0)
881 		return (err);
882 
883 	if (dir->dir_linkid2 == DATALINK_INVALID_LINKID)
884 		return (0);
885 
886 	/*
887 	 * if dir_linkid2 is not DATALINK_INVALID_LINKID, it means this
888 	 * renaming request is to rename a valid physical link (dir_linkid1)
889 	 * to a "removed" physical link (dir_linkid2, which is removed by DR
890 	 * or during system shutdown). In this case, the link (specified by
891 	 * dir_linkid1) would inherit all the configuration of dir_linkid2,
892 	 * and dir_linkid1 and its configuration would be lost.
893 	 *
894 	 * Remove per-link autopush configuration of dir_linkid1 in this case.
895 	 */
896 	key = (mod_hash_key_t)(uintptr_t)dir->dir_linkid1;
897 	rw_enter(&dld_ap_hash_lock, RW_WRITER);
898 	if (mod_hash_find(dld_ap_hashp, key, &val) != 0) {
899 		rw_exit(&dld_ap_hash_lock);
900 		return (0);
901 	}
902 
903 	VERIFY(mod_hash_remove(dld_ap_hashp, key, &val) == 0);
904 	kmem_free(val, sizeof (dld_ap_t));
905 	rw_exit(&dld_ap_hash_lock);
906 	return (0);
907 }
908 
909 static int
910 drv_ioc_setap(datalink_id_t linkid, struct dlautopush *dlap)
911 {
912 	dld_ap_t	*dap;
913 	int		i;
914 	mod_hash_key_t	key;
915 
916 	if (dlap->dap_npush == 0 || dlap->dap_npush > MAXAPUSH)
917 		return (EINVAL);
918 
919 	/*
920 	 * Validate that the specified list of modules exist.
921 	 */
922 	for (i = 0; i < dlap->dap_npush; i++) {
923 		if (fmodsw_find(dlap->dap_aplist[i], FMODSW_LOAD) == NULL)
924 			return (EINVAL);
925 	}
926 
927 
928 	key = (mod_hash_key_t)(uintptr_t)linkid;
929 
930 	rw_enter(&dld_ap_hash_lock, RW_WRITER);
931 	if (mod_hash_find(dld_ap_hashp, key, (mod_hash_val_t *)&dap) != 0) {
932 		dap = kmem_zalloc(sizeof (dld_ap_t), KM_NOSLEEP);
933 		if (dap == NULL) {
934 			rw_exit(&dld_ap_hash_lock);
935 			return (ENOMEM);
936 		}
937 
938 		dap->da_linkid = linkid;
939 		VERIFY(mod_hash_insert(dld_ap_hashp, key,
940 		    (mod_hash_val_t)dap) == 0);
941 	}
942 
943 	/*
944 	 * Update the configuration.
945 	 */
946 	dap->da_anchor = dlap->dap_anchor;
947 	dap->da_npush = dlap->dap_npush;
948 	for (i = 0; i < dlap->dap_npush; i++) {
949 		(void) strlcpy(dap->da_aplist[i], dlap->dap_aplist[i],
950 		    FMNAMESZ + 1);
951 	}
952 	rw_exit(&dld_ap_hash_lock);
953 
954 	return (0);
955 }
956 
957 static int
958 drv_ioc_getap(datalink_id_t linkid, struct dlautopush *dlap)
959 {
960 	dld_ap_t	*dap;
961 	int		i;
962 
963 	rw_enter(&dld_ap_hash_lock, RW_READER);
964 	if (mod_hash_find(dld_ap_hashp,
965 	    (mod_hash_key_t)(uintptr_t)linkid,
966 	    (mod_hash_val_t *)&dap) != 0) {
967 		rw_exit(&dld_ap_hash_lock);
968 		dlap->dap_npush = 0;
969 		return (0);
970 	}
971 
972 	/*
973 	 * Retrieve the configuration.
974 	 */
975 	dlap->dap_anchor = dap->da_anchor;
976 	dlap->dap_npush = dap->da_npush;
977 	for (i = 0; i < dap->da_npush; i++) {
978 		(void) strlcpy(dlap->dap_aplist[i], dap->da_aplist[i],
979 		    FMNAMESZ + 1);
980 	}
981 	rw_exit(&dld_ap_hash_lock);
982 
983 	return (0);
984 }
985 
986 static int
987 drv_ioc_clrap(datalink_id_t linkid)
988 {
989 	mod_hash_val_t	val;
990 	mod_hash_key_t	key;
991 
992 	key = (mod_hash_key_t)(uintptr_t)linkid;
993 
994 	rw_enter(&dld_ap_hash_lock, RW_WRITER);
995 	if (mod_hash_find(dld_ap_hashp, key, &val) != 0) {
996 		rw_exit(&dld_ap_hash_lock);
997 		return (0);
998 	}
999 
1000 	VERIFY(mod_hash_remove(dld_ap_hashp, key, &val) == 0);
1001 	kmem_free(val, sizeof (dld_ap_t));
1002 	rw_exit(&dld_ap_hash_lock);
1003 	return (0);
1004 }
1005 
1006 /*
1007  * DLDIOC_DOORSERVER
1008  */
1009 /* ARGSUSED */
1010 static int
1011 drv_ioc_doorserver(void *karg, intptr_t arg, int mode, cred_t *cred, int *rvalp)
1012 {
1013 	dld_ioc_door_t	*did = karg;
1014 
1015 	return (dls_mgmt_door_set(did->did_start_door));
1016 }
1017 
1018 /*
1019  * DLDIOC_USAGELOG
1020  */
1021 /* ARGSUSED */
1022 static int
1023 drv_ioc_usagelog(void *karg, intptr_t arg, int mode, cred_t *cred,
1024     int *rvalp)
1025 {
1026 	dld_ioc_usagelog_t	*log_info = (dld_ioc_usagelog_t *)karg;
1027 	int			err = 0;
1028 
1029 	if (log_info->ul_type < MAC_LOGTYPE_LINK ||
1030 	    log_info->ul_type > MAC_LOGTYPE_FLOW)
1031 		return (EINVAL);
1032 
1033 	if (log_info->ul_onoff) {
1034 		err = mac_start_logusage(log_info->ul_type,
1035 		    log_info->ul_interval);
1036 	} else {
1037 		mac_stop_logusage(log_info->ul_type);
1038 	}
1039 	return (err);
1040 }
1041 
1042 /*
1043  * Process a DLDIOC_ADDFLOW request.
1044  */
1045 /* ARGSUSED */
1046 static int
1047 drv_ioc_addflow(void *karg, intptr_t arg, int mode, cred_t *cred, int *rvalp)
1048 {
1049 	dld_ioc_addflow_t	*afp = karg;
1050 
1051 	return (dld_add_flow(afp->af_linkid, afp->af_name,
1052 	    &afp->af_flow_desc, &afp->af_resource_props));
1053 }
1054 
1055 /*
1056  * Process a DLDIOC_REMOVEFLOW request.
1057  */
1058 /* ARGSUSED */
1059 static int
1060 drv_ioc_removeflow(void *karg, intptr_t arg, int mode, cred_t *cred, int *rvalp)
1061 {
1062 	dld_ioc_removeflow_t	*rfp = karg;
1063 
1064 	return (dld_remove_flow(rfp->rf_name));
1065 }
1066 
1067 /*
1068  * Process a DLDIOC_MODIFYFLOW request.
1069  */
1070 /* ARGSUSED */
1071 static int
1072 drv_ioc_modifyflow(void *karg, intptr_t arg, int mode, cred_t *cred, int *rvalp)
1073 {
1074 	dld_ioc_modifyflow_t	*mfp = karg;
1075 
1076 	return (dld_modify_flow(mfp->mf_name, &mfp->mf_resource_props));
1077 }
1078 
1079 /*
1080  * Process a DLDIOC_WALKFLOW request.
1081  */
1082 /* ARGSUSED */
1083 static int
1084 drv_ioc_walkflow(void *karg, intptr_t arg, int mode, cred_t *cred, int *rvalp)
1085 {
1086 	dld_ioc_walkflow_t	*wfp = karg;
1087 
1088 	return (dld_walk_flow(wfp, arg, cred));
1089 }
1090 
1091 /*
1092  * Check for GLDv3 autopush information.  There are three cases:
1093  *
1094  *   1. If devp points to a GLDv3 datalink and it has autopush configuration,
1095  *	fill dlap in with that information and return 0.
1096  *
1097  *   2. If devp points to a GLDv3 datalink but it doesn't have autopush
1098  *	configuration, then replace devp with the physical device (if one
1099  *	exists) and return 1.  This allows stropen() to find the old-school
1100  *	per-driver autopush configuration.  (For softmac, the result is that
1101  *	the softmac dev_t is replaced with the legacy device's dev_t).
1102  *
1103  *   3. If neither of the above apply, don't touch the args and return -1.
1104  */
1105 int
1106 dld_autopush(dev_t *devp, struct dlautopush *dlap)
1107 {
1108 	dld_ap_t	*dap;
1109 	datalink_id_t	linkid;
1110 	dev_t		phydev;
1111 
1112 	if (!GLDV3_DRV(getmajor(*devp)))
1113 		return (-1);
1114 
1115 	/*
1116 	 * Find the linkid by the link's dev_t.
1117 	 */
1118 	if (dls_devnet_dev2linkid(*devp, &linkid) != 0)
1119 		return (-1);
1120 
1121 	/*
1122 	 * Find the autopush configuration associated with the linkid.
1123 	 */
1124 	rw_enter(&dld_ap_hash_lock, RW_READER);
1125 	if (mod_hash_find(dld_ap_hashp, (mod_hash_key_t)(uintptr_t)linkid,
1126 	    (mod_hash_val_t *)&dap) == 0) {
1127 		*dlap = dap->da_ap;
1128 		rw_exit(&dld_ap_hash_lock);
1129 		return (0);
1130 	}
1131 	rw_exit(&dld_ap_hash_lock);
1132 
1133 	if (dls_devnet_phydev(linkid, &phydev) != 0)
1134 		return (-1);
1135 
1136 	*devp = phydev;
1137 	return (1);
1138 }
1139 
1140 /*
1141  * Secure objects implementation
1142  */
1143 
1144 /* ARGSUSED */
1145 static int
1146 drv_secobj_ctor(void *buf, void *arg, int kmflag)
1147 {
1148 	bzero(buf, sizeof (dld_secobj_t));
1149 	return (0);
1150 }
1151 
1152 static void
1153 drv_secobj_init(void)
1154 {
1155 	rw_init(&drv_secobj_lock, NULL, RW_DEFAULT, NULL);
1156 	drv_secobj_cachep = kmem_cache_create("drv_secobj_cache",
1157 	    sizeof (dld_secobj_t), 0, drv_secobj_ctor, NULL,
1158 	    NULL, NULL, NULL, 0);
1159 	drv_secobj_hash = mod_hash_create_extended("drv_secobj_hash",
1160 	    SECOBJ_WEP_HASHSZ, mod_hash_null_keydtor, mod_hash_null_valdtor,
1161 	    mod_hash_bystr, NULL, mod_hash_strkey_cmp, KM_SLEEP);
1162 }
1163 
1164 static void
1165 drv_secobj_fini(void)
1166 {
1167 	mod_hash_destroy_hash(drv_secobj_hash);
1168 	kmem_cache_destroy(drv_secobj_cachep);
1169 	rw_destroy(&drv_secobj_lock);
1170 }
1171 
1172 /* ARGSUSED */
1173 static int
1174 drv_ioc_secobj_set(void *karg, intptr_t arg, int mode, cred_t *cred, int *rvalp)
1175 {
1176 	dld_ioc_secobj_set_t	*ssp = karg;
1177 	dld_secobj_t		*sobjp, *objp;
1178 	int			err;
1179 
1180 	sobjp = &ssp->ss_obj;
1181 
1182 	if (sobjp->so_class != DLD_SECOBJ_CLASS_WEP &&
1183 	    sobjp->so_class != DLD_SECOBJ_CLASS_WPA)
1184 		return (EINVAL);
1185 
1186 	if (sobjp->so_name[DLD_SECOBJ_NAME_MAX - 1] != '\0' ||
1187 	    sobjp->so_len > DLD_SECOBJ_VAL_MAX)
1188 		return (EINVAL);
1189 
1190 	rw_enter(&drv_secobj_lock, RW_WRITER);
1191 	err = mod_hash_find(drv_secobj_hash, (mod_hash_key_t)sobjp->so_name,
1192 	    (mod_hash_val_t *)&objp);
1193 	if (err == 0) {
1194 		if ((ssp->ss_flags & DLD_SECOBJ_OPT_CREATE) != 0) {
1195 			rw_exit(&drv_secobj_lock);
1196 			return (EEXIST);
1197 		}
1198 	} else {
1199 		ASSERT(err == MH_ERR_NOTFOUND);
1200 		if ((ssp->ss_flags & DLD_SECOBJ_OPT_CREATE) == 0) {
1201 			rw_exit(&drv_secobj_lock);
1202 			return (ENOENT);
1203 		}
1204 		objp = kmem_cache_alloc(drv_secobj_cachep, KM_SLEEP);
1205 		(void) strlcpy(objp->so_name, sobjp->so_name,
1206 		    DLD_SECOBJ_NAME_MAX);
1207 
1208 		VERIFY(mod_hash_insert(drv_secobj_hash,
1209 		    (mod_hash_key_t)objp->so_name, (mod_hash_val_t)objp) == 0);
1210 	}
1211 	bcopy(sobjp->so_val, objp->so_val, sobjp->so_len);
1212 	objp->so_len = sobjp->so_len;
1213 	objp->so_class = sobjp->so_class;
1214 	rw_exit(&drv_secobj_lock);
1215 	return (0);
1216 }
1217 
1218 typedef struct dld_secobj_state {
1219 	uint_t		ss_free;
1220 	uint_t		ss_count;
1221 	int		ss_rc;
1222 	int		ss_mode;
1223 	dld_secobj_t	*ss_objp;
1224 } dld_secobj_state_t;
1225 
1226 /* ARGSUSED */
1227 static uint_t
1228 drv_secobj_walker(mod_hash_key_t key, mod_hash_val_t *val, void *arg)
1229 {
1230 	dld_secobj_state_t	*statep = arg;
1231 	dld_secobj_t		*sobjp = (dld_secobj_t *)val;
1232 
1233 	if (statep->ss_free < sizeof (dld_secobj_t)) {
1234 		statep->ss_rc = ENOSPC;
1235 		return (MH_WALK_TERMINATE);
1236 	}
1237 	if (ddi_copyout(sobjp, statep->ss_objp, sizeof (*sobjp),
1238 	    statep->ss_mode) != 0) {
1239 		statep->ss_rc = EFAULT;
1240 		return (MH_WALK_TERMINATE);
1241 	}
1242 	statep->ss_objp++;
1243 	statep->ss_free -= sizeof (dld_secobj_t);
1244 	statep->ss_count++;
1245 	return (MH_WALK_CONTINUE);
1246 }
1247 
1248 /* ARGSUSED */
1249 static int
1250 drv_ioc_secobj_get(void *karg, intptr_t arg, int mode, cred_t *cred, int *rvalp)
1251 {
1252 	dld_ioc_secobj_get_t	*sgp = karg;
1253 	dld_secobj_t		*sobjp, *objp;
1254 	int			err;
1255 
1256 	sobjp = &sgp->sg_obj;
1257 	if (sobjp->so_name[DLD_SECOBJ_NAME_MAX - 1] != '\0')
1258 		return (EINVAL);
1259 
1260 	rw_enter(&drv_secobj_lock, RW_READER);
1261 	if (sobjp->so_name[0] != '\0') {
1262 		err = mod_hash_find(drv_secobj_hash,
1263 		    (mod_hash_key_t)sobjp->so_name, (mod_hash_val_t *)&objp);
1264 		if (err != 0) {
1265 			ASSERT(err == MH_ERR_NOTFOUND);
1266 			rw_exit(&drv_secobj_lock);
1267 			return (ENOENT);
1268 		}
1269 		bcopy(objp->so_val, sobjp->so_val, objp->so_len);
1270 		sobjp->so_len = objp->so_len;
1271 		sobjp->so_class = objp->so_class;
1272 		sgp->sg_count = 1;
1273 	} else {
1274 		dld_secobj_state_t	state;
1275 
1276 		state.ss_free = sgp->sg_size - sizeof (dld_ioc_secobj_get_t);
1277 		state.ss_count = 0;
1278 		state.ss_rc = 0;
1279 		state.ss_mode = mode;
1280 		state.ss_objp = (dld_secobj_t *)((uchar_t *)arg +
1281 		    sizeof (dld_ioc_secobj_get_t));
1282 
1283 		mod_hash_walk(drv_secobj_hash, drv_secobj_walker, &state);
1284 		if (state.ss_rc != 0) {
1285 			rw_exit(&drv_secobj_lock);
1286 			return (state.ss_rc);
1287 		}
1288 		sgp->sg_count = state.ss_count;
1289 	}
1290 	rw_exit(&drv_secobj_lock);
1291 	return (0);
1292 }
1293 
1294 /* ARGSUSED */
1295 static int
1296 drv_ioc_secobj_unset(void *karg, intptr_t arg, int mode, cred_t *cred,
1297     int *rvalp)
1298 {
1299 	dld_ioc_secobj_unset_t	*sup = karg;
1300 	dld_secobj_t		*objp;
1301 	mod_hash_val_t		val;
1302 	int			err;
1303 
1304 	if (sup->su_name[DLD_SECOBJ_NAME_MAX - 1] != '\0')
1305 		return (EINVAL);
1306 
1307 	rw_enter(&drv_secobj_lock, RW_WRITER);
1308 	err = mod_hash_find(drv_secobj_hash, (mod_hash_key_t)sup->su_name,
1309 	    (mod_hash_val_t *)&objp);
1310 	if (err != 0) {
1311 		ASSERT(err == MH_ERR_NOTFOUND);
1312 		rw_exit(&drv_secobj_lock);
1313 		return (ENOENT);
1314 	}
1315 	VERIFY(mod_hash_remove(drv_secobj_hash, (mod_hash_key_t)sup->su_name,
1316 	    (mod_hash_val_t *)&val) == 0);
1317 	ASSERT(objp == (dld_secobj_t *)val);
1318 
1319 	kmem_cache_free(drv_secobj_cachep, objp);
1320 	rw_exit(&drv_secobj_lock);
1321 	return (0);
1322 }
1323 
1324 /* ARGSUSED */
1325 static int
1326 drv_ioc_gettran(void *karg, intptr_t arg, int mode, cred_t *cred,
1327     int *rvalp)
1328 {
1329 	int			ret = 0;
1330 	mac_perim_handle_t	mph = NULL;
1331 	dls_dl_handle_t		dlh = NULL;
1332 	dls_link_t		*dlp = NULL;
1333 	dld_ioc_gettran_t	*dgt = karg;
1334 
1335 	if ((ret = mac_perim_enter_by_linkid(dgt->dgt_linkid, &mph)) != 0)
1336 		goto done;
1337 
1338 	if ((ret = dls_devnet_hold_link(dgt->dgt_linkid, &dlh, &dlp)) != 0)
1339 		goto done;
1340 
1341 	/*
1342 	 * Make sure that this link belongs to the zone.
1343 	 */
1344 	if (crgetzoneid(cred) != dls_devnet_getownerzid(dlh)) {
1345 		ret = ENOENT;
1346 		goto done;
1347 	}
1348 
1349 	if (dgt->dgt_tran_id == DLDIOC_GETTRAN_GETNTRAN) {
1350 		ret = mac_transceiver_count(dlp->dl_mh, &dgt->dgt_tran_id);
1351 	} else {
1352 		ret = mac_transceiver_info(dlp->dl_mh, dgt->dgt_tran_id,
1353 		    &dgt->dgt_present, &dgt->dgt_usable);
1354 	}
1355 
1356 done:
1357 	if (dlh != NULL && dlp != NULL) {
1358 		dls_devnet_rele_link(dlh, dlp);
1359 	}
1360 
1361 	if (mph != NULL) {
1362 		mac_perim_exit(mph);
1363 	}
1364 
1365 	return (ret);
1366 }
1367 
1368 /* ARGSUSED */
1369 static int
1370 drv_ioc_readtran(void *karg, intptr_t arg, int mode, cred_t *cred,
1371     int *rvalp)
1372 {
1373 	int			ret = 0;
1374 	mac_perim_handle_t	mph = NULL;
1375 	dls_dl_handle_t		dlh = NULL;
1376 	dls_link_t		*dlp = NULL;
1377 	dld_ioc_tranio_t	*dti = karg;
1378 	uint8_t			buf[256];
1379 	size_t			nr;
1380 
1381 	/*
1382 	 * Be strict for the moment
1383 	 */
1384 	if (dti->dti_nbytes != 256 || dti->dti_off != 0)
1385 		return (EINVAL);
1386 
1387 	if ((ret = mac_perim_enter_by_linkid(dti->dti_linkid, &mph)) != 0)
1388 		goto done;
1389 
1390 	if ((ret = dls_devnet_hold_link(dti->dti_linkid, &dlh, &dlp)) != 0)
1391 		goto done;
1392 
1393 	/*
1394 	 * Make sure that this link belongs to the zone.
1395 	 */
1396 	if (crgetzoneid(cred) != dls_devnet_getownerzid(dlh)) {
1397 		ret = ENOENT;
1398 		goto done;
1399 	}
1400 
1401 	bzero(buf, sizeof (buf));
1402 	if ((ret = mac_transceiver_read(dlp->dl_mh, dti->dti_tran_id,
1403 	    dti->dti_page, buf, dti->dti_nbytes, dti->dti_off, &nr)) == 0) {
1404 		dti->dti_nbytes = nr;
1405 		ret = ddi_copyout(buf, (void *)(uintptr_t)dti->dti_buf,
1406 		    sizeof (buf), mode);
1407 	}
1408 
1409 done:
1410 	if (dlh != NULL && dlp != NULL) {
1411 		dls_devnet_rele_link(dlh, dlp);
1412 	}
1413 
1414 	if (mph != NULL) {
1415 		mac_perim_exit(mph);
1416 	}
1417 
1418 	return (ret);
1419 }
1420 
1421 /* ARGSUSED */
1422 static int
1423 drv_ioc_getled(void *karg, intptr_t arg, int mode, cred_t *cred,
1424     int *rvalp)
1425 {
1426 	int			ret = 0;
1427 	mac_perim_handle_t	mph = NULL;
1428 	dls_dl_handle_t		dlh = NULL;
1429 	dls_link_t		*dlp = NULL;
1430 	dld_ioc_led_t		*dil = karg;
1431 
1432 	if ((mode & FREAD) == 0)
1433 		return (EBADF);
1434 
1435 	if ((ret = dls_devnet_hold_tmp(dil->dil_linkid, &dlh)) != 0)
1436 		goto done;
1437 
1438 	if ((ret = mac_perim_enter_by_macname(dls_devnet_mac(dlh), &mph)) != 0)
1439 		goto done;
1440 
1441 	if ((ret = dls_link_hold(dls_devnet_mac(dlh), &dlp)) != 0)
1442 		goto done;
1443 
1444 	/*
1445 	 * Make sure that this link belongs to the zone.
1446 	 */
1447 	if (crgetzoneid(cred) != dls_devnet_getownerzid(dlh)) {
1448 		ret = ENOENT;
1449 		goto done;
1450 	}
1451 
1452 	ret = mac_led_get(dlp->dl_mh, &dil->dil_supported, &dil->dil_active);
1453 
1454 done:
1455 	if (dlp != NULL)
1456 		dls_link_rele(dlp);
1457 
1458 	if (mph != NULL)
1459 		mac_perim_exit(mph);
1460 
1461 	if (dlh != NULL)
1462 		dls_devnet_rele_tmp(dlh);
1463 
1464 	return (ret);
1465 }
1466 
1467 /* ARGSUSED */
1468 static int
1469 drv_ioc_setled(void *karg, intptr_t arg, int mode, cred_t *cred,
1470     int *rvalp)
1471 {
1472 	int			ret = 0;
1473 	mac_perim_handle_t	mph = NULL;
1474 	dls_dl_handle_t		dlh = NULL;
1475 	dls_link_t		*dlp = NULL;
1476 	dld_ioc_led_t		*dil = karg;
1477 
1478 	if ((mode & FWRITE) == 0)
1479 		return (EBADF);
1480 
1481 	if ((ret = dls_devnet_hold_tmp(dil->dil_linkid, &dlh)) != 0)
1482 		goto done;
1483 
1484 	if ((ret = mac_perim_enter_by_macname(dls_devnet_mac(dlh), &mph)) != 0)
1485 		goto done;
1486 
1487 	if ((ret = dls_link_hold(dls_devnet_mac(dlh), &dlp)) != 0)
1488 		goto done;
1489 
1490 	/*
1491 	 * Make sure that this link belongs to the zone.
1492 	 */
1493 	if (crgetzoneid(cred) != dls_devnet_getownerzid(dlh)) {
1494 		ret = ENOENT;
1495 		goto done;
1496 	}
1497 
1498 	ret = mac_led_set(dlp->dl_mh, dil->dil_active);
1499 
1500 done:
1501 	if (dlp != NULL)
1502 		dls_link_rele(dlp);
1503 
1504 	if (mph != NULL)
1505 		mac_perim_exit(mph);
1506 
1507 	if (dlh != NULL)
1508 		dls_devnet_rele_tmp(dlh);
1509 
1510 	return (ret);
1511 }
1512 
1513 
1514 /*
1515  * Note that ioctls that modify links have a NULL di_priv_func(), as
1516  * privileges can only be checked after we know the class of the link being
1517  * modified (due to class-specific fine-grained privileges such as
1518  * sys_iptun_config).
1519  */
1520 static dld_ioc_info_t drv_ioc_list[] = {
1521 	{DLDIOC_ATTR, DLDCOPYINOUT, sizeof (dld_ioc_attr_t),
1522 	    drv_ioc_attr, NULL},
1523 	{DLDIOC_PHYS_ATTR, DLDCOPYINOUT, sizeof (dld_ioc_phys_attr_t),
1524 	    drv_ioc_phys_attr, NULL},
1525 	{DLDIOC_SECOBJ_SET, DLDCOPYIN, sizeof (dld_ioc_secobj_set_t),
1526 	    drv_ioc_secobj_set, secpolicy_dl_config},
1527 	{DLDIOC_SECOBJ_GET, DLDCOPYINOUT, sizeof (dld_ioc_secobj_get_t),
1528 	    drv_ioc_secobj_get, secpolicy_dl_config},
1529 	{DLDIOC_SECOBJ_UNSET, DLDCOPYIN, sizeof (dld_ioc_secobj_unset_t),
1530 	    drv_ioc_secobj_unset, secpolicy_dl_config},
1531 	{DLDIOC_DOORSERVER, DLDCOPYIN, sizeof (dld_ioc_door_t),
1532 	    drv_ioc_doorserver, secpolicy_dl_config},
1533 	{DLDIOC_RENAME, DLDCOPYIN, sizeof (dld_ioc_rename_t),
1534 	    drv_ioc_rename, NULL},
1535 	{DLDIOC_MACADDRGET, DLDCOPYINOUT, sizeof (dld_ioc_macaddrget_t),
1536 	    drv_ioc_macaddrget, NULL},
1537 	{DLDIOC_ADDFLOW, DLDCOPYIN, sizeof (dld_ioc_addflow_t),
1538 	    drv_ioc_addflow, secpolicy_dl_config},
1539 	{DLDIOC_REMOVEFLOW, DLDCOPYIN, sizeof (dld_ioc_removeflow_t),
1540 	    drv_ioc_removeflow, secpolicy_dl_config},
1541 	{DLDIOC_MODIFYFLOW, DLDCOPYIN, sizeof (dld_ioc_modifyflow_t),
1542 	    drv_ioc_modifyflow, secpolicy_dl_config},
1543 	{DLDIOC_WALKFLOW, DLDCOPYINOUT, sizeof (dld_ioc_walkflow_t),
1544 	    drv_ioc_walkflow, NULL},
1545 	{DLDIOC_USAGELOG, DLDCOPYIN, sizeof (dld_ioc_usagelog_t),
1546 	    drv_ioc_usagelog, secpolicy_dl_config},
1547 	{DLDIOC_SETMACPROP, DLDCOPYIN, sizeof (dld_ioc_macprop_t),
1548 	    drv_ioc_setprop, NULL},
1549 	{DLDIOC_GETMACPROP, DLDCOPYIN, sizeof (dld_ioc_macprop_t),
1550 	    drv_ioc_getprop, NULL},
1551 	{DLDIOC_GETHWGRP, DLDCOPYINOUT, sizeof (dld_ioc_hwgrpget_t),
1552 	    drv_ioc_hwgrpget, NULL},
1553 	{DLDIOC_GETTRAN, DLDCOPYINOUT, sizeof (dld_ioc_gettran_t),
1554 	    drv_ioc_gettran, NULL },
1555 	{DLDIOC_READTRAN, DLDCOPYINOUT, sizeof (dld_ioc_tranio_t),
1556 	    drv_ioc_readtran, NULL },
1557 	{DLDIOC_GETLED, DLDCOPYINOUT, sizeof (dld_ioc_led_t),
1558 	    drv_ioc_getled, NULL },
1559 	{DLDIOC_SETLED, DLDCOPYIN, sizeof (dld_ioc_led_t),
1560 	    drv_ioc_setled, secpolicy_dl_config}
1561 };
1562 
1563 typedef struct dld_ioc_modentry {
1564 	uint16_t	dim_modid;	/* Top 16 bits of ioctl command */
1565 	char		*dim_modname;	/* Module to be loaded */
1566 	int		ctrl_node_inst;	/* Ctrl node instance */
1567 	dld_ioc_info_t	*dim_list;	/* array of ioctl structures */
1568 	uint_t		dim_count;	/* number of elements in dim_list */
1569 } dld_ioc_modentry_t;
1570 
1571 /*
1572  * For all modules except for dld, dim_list and dim_count are assigned
1573  * when the modules register their ioctls in dld_ioc_register().  We
1574  * can statically initialize dld's ioctls in-line here; there's no
1575  * need for it to call dld_ioc_register() itself. ctrl_node_inst controls
1576  * whether an instance of the device will be held or the driver. If set to
1577  * a non-negative integer, device instance specified in ctrl_node_inst will
1578  * be held; so dld_ioc_register() _must_ be called in xxx_attach() routine of
1579  * the driver. If set to -1, driver will be held; so dld_ioc_register() _must_
1580  * be called in xxx_init() routine of the driver.
1581  */
1582 static dld_ioc_modentry_t dld_ioc_modtable[] = {
1583 	{DLD_IOC,	"dld", 0, drv_ioc_list, DLDIOCCNT(drv_ioc_list)},
1584 	{AGGR_IOC,	"aggr", 0, NULL, 0},
1585 	{VNIC_IOC,	"vnic",	0, NULL, 0},
1586 	{SIMNET_IOC,	"simnet", 0, NULL, 0},
1587 	{BRIDGE_IOC,	"bridge", 0, NULL, 0},
1588 	{IPTUN_IOC,	"iptun", 0, NULL, 0},
1589 	{IBPART_IOC,	"ibp", -1, NULL, 0},
1590 	{OVERLAY_IOC,	"overlay", 0, NULL, 0}
1591 };
1592 #define	DLDIOC_CNT	\
1593 	(sizeof (dld_ioc_modtable) / sizeof (dld_ioc_modentry_t))
1594 
1595 static dld_ioc_modentry_t *
1596 dld_ioc_findmod(uint16_t modid)
1597 {
1598 	int	i;
1599 
1600 	for (i = 0; i < DLDIOC_CNT; i++) {
1601 		if (modid == dld_ioc_modtable[i].dim_modid)
1602 			return (&dld_ioc_modtable[i]);
1603 	}
1604 	return (NULL);
1605 }
1606 
1607 int
1608 dld_ioc_register(uint16_t modid, dld_ioc_info_t *list, uint_t count)
1609 {
1610 	dld_ioc_modentry_t *dim = dld_ioc_findmod(modid);
1611 
1612 	if (dim == NULL)
1613 		return (ENOENT);
1614 
1615 	dim->dim_list = list;
1616 	dim->dim_count = count;
1617 	return (0);
1618 }
1619 
1620 void
1621 dld_ioc_unregister(uint16_t modid)
1622 {
1623 	VERIFY(dld_ioc_register(modid, NULL, 0) == 0);
1624 }
1625 
1626 /*
1627  * The general design with GLDv3 ioctls is that all ioctls issued
1628  * through /dev/dld go through this drv_ioctl() function.  This
1629  * function handles all ioctls on behalf of modules listed in
1630  * dld_ioc_modtable.
1631  *
1632  * When an ioctl is received, this function looks for the associated
1633  * module-id-specific ioctl information using dld_ioc_findmod(). The
1634  * call to ddi_hold_driver() or ddi_hold_devi_by_instance() on the
1635  * associated device will cause the kernel module responsible for the
1636  * ioctl to be loaded if it's not already loaded, which should result
1637  * in that module calling dld_ioc_register(), thereby filling in the
1638  * dim_list containing the details for the ioctl being processed.
1639  *
1640  * This function can then perform operations such as copyin() data and
1641  * do credential checks based on the registered ioctl information,
1642  * then issue the callback function di_func() registered by the
1643  * responsible module.  Upon return, the appropriate copyout()
1644  * operation can be performed and the operation completes.
1645  */
1646 /* ARGSUSED */
1647 static int
1648 drv_ioctl(dev_t dev, int cmd, intptr_t arg, int mode, cred_t *cred, int *rvalp)
1649 {
1650 	dld_ioc_modentry_t *dim;
1651 	dld_ioc_info_t	*info;
1652 	dev_info_t	*dip = NULL;
1653 	struct dev_ops	*dops = NULL;
1654 	major_t		major;
1655 	void		*buf = NULL;
1656 	size_t		sz;
1657 	int		i, err;
1658 
1659 	if ((dim = dld_ioc_findmod(DLD_IOC_MODID(cmd))) == NULL)
1660 		return (ENOTSUP);
1661 
1662 	major = ddi_name_to_major(dim->dim_modname);
1663 
1664 	if (dim->ctrl_node_inst == -1) {
1665 		/*
1666 		 * No dedicated instance to process ioctls.
1667 		 * dld_ioc_register() is called in xxx_init().
1668 		 */
1669 		dops = ddi_hold_driver(major);
1670 	} else {
1671 		/*
1672 		 * Dedicated instance to handle ioctl.
1673 		 * dld_ioc_register() is called in xxx_attach().
1674 		 */
1675 		dip = ddi_hold_devi_by_instance(major, dim->ctrl_node_inst, 0);
1676 	}
1677 
1678 	if ((dip == NULL && dops == NULL) || dim->dim_list == NULL) {
1679 		err = ENODEV;
1680 		goto done;
1681 	}
1682 
1683 	for (i = 0; i < dim->dim_count; i++) {
1684 		if (cmd == dim->dim_list[i].di_cmd)
1685 			break;
1686 	}
1687 	if (i == dim->dim_count) {
1688 		err = ENOTSUP;
1689 		goto done;
1690 	}
1691 
1692 	info = &dim->dim_list[i];
1693 
1694 	if (info->di_priv_func != NULL &&
1695 	    (err = info->di_priv_func(cred)) != 0)
1696 		goto done;
1697 
1698 	sz = info->di_argsize;
1699 	if ((buf = kmem_zalloc(sz, KM_NOSLEEP)) == NULL) {
1700 		err = ENOMEM;
1701 		goto done;
1702 	}
1703 
1704 	if ((info->di_flags & DLDCOPYIN) &&
1705 	    ddi_copyin((void *)arg, buf, sz, mode) != 0) {
1706 		err = EFAULT;
1707 		goto done;
1708 	}
1709 
1710 	err = info->di_func(buf, arg, mode, cred, rvalp);
1711 
1712 	if ((info->di_flags & DLDCOPYOUT) &&
1713 	    ddi_copyout(buf, (void *)arg, sz, mode) != 0 && err == 0)
1714 		err = EFAULT;
1715 
1716 done:
1717 	if (buf != NULL)
1718 		kmem_free(buf, sz);
1719 	if (dip != NULL)
1720 		ddi_release_devi(dip);
1721 	if (dops != NULL)
1722 		ddi_rele_driver(major);
1723 	return (err);
1724 }
1725