xref: /titanic_44/usr/src/uts/common/io/dld/dld_drv.c (revision 7ff178cd8db129d385d3177eb20744d3b6efc59b)
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  */
24 
25 /*
26  * Data-Link Driver
27  */
28 
29 #include	<sys/conf.h>
30 #include	<sys/mkdev.h>
31 #include	<sys/modctl.h>
32 #include	<sys/stat.h>
33 #include	<sys/dld_impl.h>
34 #include	<sys/dld_ioc.h>
35 #include	<sys/dls_impl.h>
36 #include	<sys/softmac.h>
37 #include	<sys/mac.h>
38 #include	<sys/mac_ether.h>
39 #include	<sys/mac_client.h>
40 #include	<sys/mac_client_impl.h>
41 #include	<sys/mac_client_priv.h>
42 #include	<inet/common.h>
43 #include	<sys/policy.h>
44 #include	<sys/priv_names.h>
45 #include	<sys/zone.h>
46 
47 static void	drv_init(void);
48 static int	drv_fini(void);
49 
50 static int	drv_getinfo(dev_info_t	*, ddi_info_cmd_t, void *, void **);
51 static int	drv_attach(dev_info_t *, ddi_attach_cmd_t);
52 static int	drv_detach(dev_info_t *, ddi_detach_cmd_t);
53 
54 /*
55  * Secure objects declarations
56  */
57 #define	SECOBJ_WEP_HASHSZ	67
58 static krwlock_t	drv_secobj_lock;
59 static kmem_cache_t	*drv_secobj_cachep;
60 static mod_hash_t	*drv_secobj_hash;
61 static void		drv_secobj_init(void);
62 static void		drv_secobj_fini(void);
63 static int		drv_ioc_setap(datalink_id_t, struct dlautopush *);
64 static int		drv_ioc_getap(datalink_id_t, struct dlautopush *);
65 static int		drv_ioc_clrap(datalink_id_t);
66 
67 
68 /*
69  * The following entry points are private to dld and are used for control
70  * operations only. The entry points exported to mac drivers are defined
71  * in dld_str.c. Refer to the comment on top of dld_str.c for details.
72  */
73 static int	drv_open(dev_t *, int, int, cred_t *);
74 static int	drv_ioctl(dev_t, int, intptr_t, int, cred_t *, int *);
75 
76 static dev_info_t	*dld_dip;	/* dev_info_t for the driver */
77 uint32_t		dld_opt = 0;	/* Global options */
78 
79 #define	NAUTOPUSH 32
80 static mod_hash_t *dld_ap_hashp;
81 static krwlock_t dld_ap_hash_lock;
82 
83 static struct cb_ops drv_cb_ops = {
84 	drv_open,		/* open */
85 	nulldev,		/* close */
86 	nulldev,		/* strategy */
87 	nulldev,		/* print */
88 	nodev,			/* dump */
89 	nodev,			/* read */
90 	nodev,			/* write */
91 	drv_ioctl,		/* ioctl */
92 	nodev,			/* devmap */
93 	nodev,			/* mmap */
94 	nodev,			/* segmap */
95 	nochpoll,		/* poll */
96 	ddi_prop_op,		/* cb_prop_op */
97 	0,			/* streamtab  */
98 	D_MP			/* Driver compatibility flag */
99 };
100 
101 static struct dev_ops drv_ops = {
102 	DEVO_REV,		/* devo_rev */
103 	0,			/* refcnt */
104 	drv_getinfo,		/* get_dev_info */
105 	nulldev,		/* identify */
106 	nulldev,		/* probe */
107 	drv_attach,		/* attach */
108 	drv_detach,		/* detach */
109 	nodev,			/* reset */
110 	&drv_cb_ops,		/* driver operations */
111 	NULL,			/* bus operations */
112 	nodev,			/* dev power */
113 	ddi_quiesce_not_supported,	/* dev quiesce */
114 };
115 
116 /*
117  * Module linkage information for the kernel.
118  */
119 static	struct modldrv		drv_modldrv = {
120 	&mod_driverops,
121 	DLD_INFO,
122 	&drv_ops
123 };
124 
125 static	struct modlinkage	drv_modlinkage = {
126 	MODREV_1,
127 	&drv_modldrv,
128 	NULL
129 };
130 
131 int
132 _init(void)
133 {
134 	return (mod_install(&drv_modlinkage));
135 }
136 
137 int
138 _fini(void)
139 {
140 	return (mod_remove(&drv_modlinkage));
141 }
142 
143 int
144 _info(struct modinfo *modinfop)
145 {
146 	return (mod_info(&drv_modlinkage, modinfop));
147 }
148 
149 /*
150  * Initialize component modules.
151  */
152 static void
153 drv_init(void)
154 {
155 	drv_secobj_init();
156 	dld_str_init();
157 
158 	/*
159 	 * Create a hash table for autopush configuration.
160 	 */
161 	dld_ap_hashp = mod_hash_create_idhash("dld_autopush_hash",
162 	    NAUTOPUSH, mod_hash_null_valdtor);
163 
164 	ASSERT(dld_ap_hashp != NULL);
165 	rw_init(&dld_ap_hash_lock, NULL, RW_DRIVER, NULL);
166 }
167 
168 /* ARGSUSED */
169 static uint_t
170 drv_ap_exist(mod_hash_key_t key, mod_hash_val_t *val, void *arg)
171 {
172 	boolean_t *pexist = arg;
173 
174 	*pexist = B_TRUE;
175 	return (MH_WALK_TERMINATE);
176 }
177 
178 static int
179 drv_fini(void)
180 {
181 	int		err;
182 	boolean_t	exist = B_FALSE;
183 
184 	rw_enter(&dld_ap_hash_lock, RW_READER);
185 	mod_hash_walk(dld_ap_hashp, drv_ap_exist, &exist);
186 	rw_exit(&dld_ap_hash_lock);
187 	if (exist)
188 		return (EBUSY);
189 
190 	if ((err = dld_str_fini()) != 0)
191 		return (err);
192 
193 	drv_secobj_fini();
194 	mod_hash_destroy_idhash(dld_ap_hashp);
195 	rw_destroy(&dld_ap_hash_lock);
196 	return (0);
197 }
198 
199 /*
200  * devo_getinfo: getinfo(9e)
201  */
202 /*ARGSUSED*/
203 static int
204 drv_getinfo(dev_info_t *dip, ddi_info_cmd_t cmd, void *arg, void **resp)
205 {
206 	if (dld_dip == NULL)
207 		return (DDI_FAILURE);
208 
209 	switch (cmd) {
210 	case DDI_INFO_DEVT2INSTANCE:
211 		*resp = 0;
212 		break;
213 	case DDI_INFO_DEVT2DEVINFO:
214 		*resp = dld_dip;
215 		break;
216 	default:
217 		return (DDI_FAILURE);
218 	}
219 
220 	return (DDI_SUCCESS);
221 }
222 
223 /*
224  * Check properties to set options. (See dld.h for property definitions).
225  */
226 static void
227 drv_set_opt(dev_info_t *dip)
228 {
229 	if (ddi_prop_get_int(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS,
230 	    DLD_PROP_NO_FASTPATH, 0) != 0) {
231 		dld_opt |= DLD_OPT_NO_FASTPATH;
232 	}
233 
234 	if (ddi_prop_get_int(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS,
235 	    DLD_PROP_NO_POLL, 0) != 0) {
236 		dld_opt |= DLD_OPT_NO_POLL;
237 	}
238 
239 	if (ddi_prop_get_int(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS,
240 	    DLD_PROP_NO_ZEROCOPY, 0) != 0) {
241 		dld_opt |= DLD_OPT_NO_ZEROCOPY;
242 	}
243 
244 	if (ddi_prop_get_int(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS,
245 	    DLD_PROP_NO_SOFTRING, 0) != 0) {
246 		dld_opt |= DLD_OPT_NO_SOFTRING;
247 	}
248 }
249 
250 /*
251  * devo_attach: attach(9e)
252  */
253 static int
254 drv_attach(dev_info_t *dip, ddi_attach_cmd_t cmd)
255 {
256 	if (cmd != DDI_ATTACH)
257 		return (DDI_FAILURE);
258 
259 	ASSERT(ddi_get_instance(dip) == 0);
260 	drv_init();
261 	drv_set_opt(dip);
262 
263 	/*
264 	 * Create control node. DLPI provider nodes will be created on demand.
265 	 */
266 	if (ddi_create_minor_node(dip, DLD_CONTROL_MINOR_NAME, S_IFCHR,
267 	    DLD_CONTROL_MINOR, DDI_PSEUDO, 0) != DDI_SUCCESS)
268 		return (DDI_FAILURE);
269 
270 	dld_dip = dip;
271 
272 	/*
273 	 * Log the fact that the driver is now attached.
274 	 */
275 	ddi_report_dev(dip);
276 	return (DDI_SUCCESS);
277 }
278 
279 /*
280  * devo_detach: detach(9e)
281  */
282 static int
283 drv_detach(dev_info_t *dip, ddi_detach_cmd_t cmd)
284 {
285 	if (cmd != DDI_DETACH)
286 		return (DDI_FAILURE);
287 
288 	ASSERT(dld_dip == dip);
289 	if (drv_fini() != 0)
290 		return (DDI_FAILURE);
291 
292 	/*
293 	 * Remove the control node.
294 	 */
295 	ddi_remove_minor_node(dip, DLD_CONTROL_MINOR_NAME);
296 	dld_dip = NULL;
297 
298 	return (DDI_SUCCESS);
299 }
300 
301 /*
302  * dld control node open procedure.
303  */
304 /*ARGSUSED*/
305 static int
306 drv_open(dev_t *devp, int flag, int sflag, cred_t *credp)
307 {
308 	/*
309 	 * Only the control node can be opened.
310 	 */
311 	if (getminor(*devp) != DLD_CONTROL_MINOR)
312 		return (ENODEV);
313 	return (0);
314 }
315 
316 /*
317  * Verify if the caller is allowed to modify a link of the given class.
318  */
319 static int
320 drv_ioc_checkprivs(datalink_class_t class, cred_t *cred)
321 {
322 	if (class == DATALINK_CLASS_IPTUN)
323 		return (secpolicy_iptun_config(cred));
324 	return (secpolicy_dl_config(cred));
325 }
326 
327 /*
328  * DLDIOC_ATTR
329  */
330 /* ARGSUSED */
331 static int
332 drv_ioc_attr(void *karg, intptr_t arg, int mode, cred_t *cred, int *rvalp)
333 {
334 	dld_ioc_attr_t		*diap = karg;
335 	dls_dl_handle_t		dlh;
336 	dls_link_t		*dlp;
337 	zoneid_t		zoneid = crgetzoneid(cred);
338 	int			err;
339 	mac_perim_handle_t	mph;
340 
341 	if (zoneid != GLOBAL_ZONEID &&
342 	    zone_check_datalink(&zoneid, diap->dia_linkid) != 0)
343 		return (ENOENT);
344 
345 	if ((err = dls_devnet_hold_tmp(diap->dia_linkid, &dlh)) != 0)
346 		return (err);
347 
348 	if ((err = mac_perim_enter_by_macname(
349 	    dls_devnet_mac(dlh), &mph)) != 0) {
350 		dls_devnet_rele_tmp(dlh);
351 		return (err);
352 	}
353 
354 	if ((err = dls_link_hold(dls_devnet_mac(dlh), &dlp)) != 0) {
355 		mac_perim_exit(mph);
356 		dls_devnet_rele_tmp(dlh);
357 		return (err);
358 	}
359 
360 	mac_sdu_get(dlp->dl_mh, NULL, &diap->dia_max_sdu);
361 
362 	dls_link_rele(dlp);
363 	mac_perim_exit(mph);
364 	dls_devnet_rele_tmp(dlh);
365 
366 	return (0);
367 }
368 
369 /*
370  * DLDIOC_PHYS_ATTR
371  */
372 /* ARGSUSED */
373 static int
374 drv_ioc_phys_attr(void *karg, intptr_t arg, int mode, cred_t *cred, int *rvalp)
375 {
376 	dld_ioc_phys_attr_t	*dipp = karg;
377 	int			err;
378 	dls_dl_handle_t		dlh;
379 	dls_dev_handle_t	ddh;
380 	dev_t			phydev;
381 	zoneid_t		zoneid = crgetzoneid(cred);
382 
383 	if (zoneid != GLOBAL_ZONEID &&
384 	    zone_check_datalink(&zoneid, dipp->dip_linkid) != 0)
385 		return (ENOENT);
386 
387 	/*
388 	 * Every physical link should have its physical dev_t kept in the
389 	 * daemon. If not, it is not a valid physical link.
390 	 */
391 	if (dls_mgmt_get_phydev(dipp->dip_linkid, &phydev) != 0)
392 		return (EINVAL);
393 
394 	/*
395 	 * Although this is a valid physical link, it might already be removed
396 	 * by DR or during system shutdown. softmac_hold_device() would return
397 	 * ENOENT in this case.
398 	 */
399 	if ((err = softmac_hold_device(phydev, &ddh)) != 0)
400 		return (err);
401 
402 	if (dls_devnet_hold_tmp(dipp->dip_linkid, &dlh) != 0) {
403 		/*
404 		 * Although this is an active physical link, its link type is
405 		 * not supported by GLDv3, and therefore it does not have
406 		 * vanity naming support.
407 		 */
408 		dipp->dip_novanity = B_TRUE;
409 	} else {
410 		dipp->dip_novanity = B_FALSE;
411 		dls_devnet_rele_tmp(dlh);
412 	}
413 	/*
414 	 * Get the physical device name from the major number and the instance
415 	 * number derived from phydev.
416 	 */
417 	(void) snprintf(dipp->dip_dev, MAXLINKNAMELEN, "%s%d",
418 	    ddi_major_to_name(getmajor(phydev)), getminor(phydev) - 1);
419 
420 	softmac_rele_device(ddh);
421 	return (0);
422 }
423 
424 /* ARGSUSED */
425 static int
426 drv_ioc_hwgrpget(void *karg, intptr_t arg, int mode, cred_t *cred, int *rvalp)
427 {
428 	dld_ioc_hwgrpget_t	*hwgrpp = karg;
429 	dld_hwgrpinfo_t		hwgrp, *hip;
430 	mac_handle_t		mh = NULL;
431 	int			i, err, rgrpnum, tgrpnum;
432 	uint_t			bytes_left;
433 	int			totgrps = 0;
434 	zoneid_t		zoneid = crgetzoneid(cred);
435 
436 	if (zoneid != GLOBAL_ZONEID &&
437 	    zone_check_datalink(&zoneid, hwgrpp->dih_linkid) != 0)
438 		return (ENOENT);
439 
440 	hwgrpp->dih_n_groups = 0;
441 	err = mac_open_by_linkid(hwgrpp->dih_linkid, &mh);
442 	if (err != 0)
443 		goto done;
444 
445 	hip = (dld_hwgrpinfo_t *)
446 	    ((uchar_t *)arg + sizeof (dld_ioc_hwgrpget_t));
447 	bytes_left = hwgrpp->dih_size;
448 
449 	rgrpnum = mac_hwgrp_num(mh, MAC_RING_TYPE_RX);
450 	/* display the default group information first */
451 	if (rgrpnum > 0) {
452 		if (sizeof (dld_hwgrpinfo_t) > bytes_left) {
453 			err = ENOSPC;
454 			goto done;
455 		}
456 
457 		bzero(&hwgrp, sizeof (hwgrp));
458 		bcopy(mac_name(mh), hwgrp.dhi_link_name,
459 		    sizeof (hwgrp.dhi_link_name));
460 		mac_get_hwrxgrp_info(mh, 0, &hwgrp.dhi_grp_num,
461 		    &hwgrp.dhi_n_rings, hwgrp.dhi_rings, &hwgrp.dhi_grp_type,
462 		    &hwgrp.dhi_n_clnts, hwgrp.dhi_clnts);
463 		if (hwgrp.dhi_n_rings != 0) {
464 			if (copyout(&hwgrp, hip, sizeof (hwgrp)) != 0) {
465 				err = EFAULT;
466 				goto done;
467 			}
468 		}
469 		hip++;
470 		totgrps++;
471 		bytes_left -= sizeof (dld_hwgrpinfo_t);
472 	}
473 
474 	tgrpnum = mac_hwgrp_num(mh, MAC_RING_TYPE_TX);
475 	/* display the default group information first */
476 	if (tgrpnum > 0) {
477 		if (sizeof (dld_hwgrpinfo_t) > bytes_left) {
478 			err = ENOSPC;
479 			goto done;
480 		}
481 
482 		bzero(&hwgrp, sizeof (hwgrp));
483 		bcopy(mac_name(mh), hwgrp.dhi_link_name,
484 		    sizeof (hwgrp.dhi_link_name));
485 		mac_get_hwtxgrp_info(mh, tgrpnum - 1, &hwgrp.dhi_grp_num,
486 		    &hwgrp.dhi_n_rings, hwgrp.dhi_rings, &hwgrp.dhi_grp_type,
487 		    &hwgrp.dhi_n_clnts, hwgrp.dhi_clnts);
488 		if (hwgrp.dhi_n_rings != 0) {
489 			if (copyout(&hwgrp, hip, sizeof (hwgrp)) != 0) {
490 				err = EFAULT;
491 				goto done;
492 			}
493 		}
494 		hip++;
495 		totgrps++;
496 		bytes_left -= sizeof (dld_hwgrpinfo_t);
497 	}
498 
499 	/* Rest of the rx groups */
500 	for (i = 1; i < rgrpnum; i++) {
501 		if (sizeof (dld_hwgrpinfo_t) > bytes_left) {
502 			err = ENOSPC;
503 			goto done;
504 		}
505 
506 		bzero(&hwgrp, sizeof (hwgrp));
507 		bcopy(mac_name(mh), hwgrp.dhi_link_name,
508 		    sizeof (hwgrp.dhi_link_name));
509 		mac_get_hwrxgrp_info(mh, i, &hwgrp.dhi_grp_num,
510 		    &hwgrp.dhi_n_rings, hwgrp.dhi_rings, &hwgrp.dhi_grp_type,
511 		    &hwgrp.dhi_n_clnts, hwgrp.dhi_clnts);
512 		if (hwgrp.dhi_n_rings == 0)
513 			continue;
514 		if (copyout(&hwgrp, hip, sizeof (hwgrp)) != 0) {
515 			err = EFAULT;
516 			goto done;
517 		}
518 
519 		hip++;
520 		totgrps++;
521 		bytes_left -= sizeof (dld_hwgrpinfo_t);
522 	}
523 
524 	/* Rest of the tx group */
525 	tgrpnum = mac_hwgrp_num(mh, MAC_RING_TYPE_TX);
526 	for (i = 0; i < tgrpnum - 1; i++) {
527 		if (sizeof (dld_hwgrpinfo_t) > bytes_left) {
528 			err = ENOSPC;
529 			goto done;
530 		}
531 
532 		bzero(&hwgrp, sizeof (hwgrp));
533 		bcopy(mac_name(mh), hwgrp.dhi_link_name,
534 		    sizeof (hwgrp.dhi_link_name));
535 		mac_get_hwtxgrp_info(mh, i, &hwgrp.dhi_grp_num,
536 		    &hwgrp.dhi_n_rings, hwgrp.dhi_rings, &hwgrp.dhi_grp_type,
537 		    &hwgrp.dhi_n_clnts, hwgrp.dhi_clnts);
538 		if (hwgrp.dhi_n_rings == 0)
539 			continue;
540 		if (copyout(&hwgrp, hip, sizeof (hwgrp)) != 0) {
541 			err = EFAULT;
542 			goto done;
543 		}
544 
545 		hip++;
546 		totgrps++;
547 		bytes_left -= sizeof (dld_hwgrpinfo_t);
548 	}
549 
550 done:
551 	if (mh != NULL)
552 		dld_mac_close(mh);
553 	if (err == 0)
554 		hwgrpp->dih_n_groups = totgrps;
555 	return (err);
556 }
557 
558 /* ARGSUSED */
559 static int
560 drv_ioc_macaddrget(void *karg, intptr_t arg, int mode, cred_t *cred, int *rvalp)
561 {
562 	dld_ioc_macaddrget_t	*magp = karg;
563 	dld_macaddrinfo_t	mai, *maip;
564 	mac_handle_t		mh = NULL;
565 	int			i, err;
566 	uint_t			bytes_left;
567 	boolean_t		is_used;
568 	zoneid_t		zoneid = crgetzoneid(cred);
569 
570 	if (zoneid != GLOBAL_ZONEID &&
571 	    zone_check_datalink(&zoneid, magp->dig_linkid) != 0)
572 		return (ENOENT);
573 
574 	magp->dig_count = 0;
575 	err = mac_open_by_linkid(magp->dig_linkid, &mh);
576 	if (err != 0)
577 		goto done;
578 
579 	maip = (dld_macaddrinfo_t *)
580 	    ((uchar_t *)arg + sizeof (dld_ioc_macaddrget_t));
581 	bytes_left = magp->dig_size;
582 
583 	for (i = 0; i < mac_addr_factory_num(mh) + 1; i++) {
584 		if (sizeof (dld_macaddrinfo_t) > bytes_left) {
585 			err = ENOSPC;
586 			goto done;
587 		}
588 
589 		bzero(&mai, sizeof (mai));
590 
591 		if (i == 0) {
592 			/* primary MAC address */
593 			mac_unicast_primary_get(mh, mai.dmi_addr);
594 			mai.dmi_addrlen = mac_addr_len(mh);
595 			mac_unicast_primary_info(mh, mai.dmi_client_name,
596 			    &is_used);
597 		} else {
598 			/* factory MAC address slot */
599 			mac_addr_factory_value(mh, i, mai.dmi_addr,
600 			    &mai.dmi_addrlen, mai.dmi_client_name, &is_used);
601 		}
602 
603 		mai.dmi_slot = i;
604 		if (is_used)
605 			mai.dmi_flags |= DLDIOCMACADDR_USED;
606 
607 		if (copyout(&mai, maip, sizeof (mai)) != 0) {
608 			err = EFAULT;
609 			goto done;
610 		}
611 
612 		maip++;
613 		bytes_left -= sizeof (dld_macaddrinfo_t);
614 	}
615 
616 done:
617 	if (mh != NULL)
618 		dld_mac_close(mh);
619 	if (err == 0)
620 		magp->dig_count = mac_addr_factory_num(mh) + 1;
621 	return (err);
622 }
623 
624 /*
625  * DLDIOC_SET/GETMACPROP
626  */
627 static int
628 drv_ioc_prop_common(dld_ioc_macprop_t *prop, intptr_t arg, boolean_t set,
629     cred_t *cred, int mode)
630 {
631 	int			err = EINVAL;
632 	dls_dl_handle_t 	dlh = NULL;
633 	dls_link_t		*dlp = NULL;
634 	mac_perim_handle_t	mph = NULL;
635 	dld_ioc_macprop_t	*kprop;
636 	datalink_id_t		linkid;
637 	datalink_class_t	class;
638 	zoneid_t		zoneid = crgetzoneid(cred);
639 	uint_t			dsize;
640 
641 	/*
642 	 * We only use pr_valsize from prop, as the caller only did a
643 	 * copyin() for sizeof (dld_ioc_prop_t), which doesn't cover
644 	 * the property data.  We copyin the full dld_ioc_prop_t
645 	 * including the data into kprop down below.
646 	 */
647 	dsize = sizeof (dld_ioc_macprop_t) + prop->pr_valsize - 1;
648 	if (dsize < prop->pr_valsize)
649 		return (EINVAL);
650 
651 	/*
652 	 * The property data is variable size, so we need to allocate
653 	 * a buffer for kernel use as this data was not part of the
654 	 * prop allocation and copyin() done by the framework.
655 	 */
656 	if ((kprop = kmem_alloc(dsize, KM_NOSLEEP)) == NULL)
657 		return (ENOMEM);
658 
659 	if (ddi_copyin((void *)arg, kprop, dsize, mode) != 0) {
660 		err = EFAULT;
661 		goto done;
662 	}
663 
664 	linkid = kprop->pr_linkid;
665 
666 	if (set) {
667 		if ((err = dls_mgmt_get_linkinfo(linkid, NULL, &class, NULL,
668 		    NULL)) != 0 || (err = drv_ioc_checkprivs(class, cred)) != 0)
669 			goto done;
670 	}
671 
672 	if ((err = dls_devnet_hold_tmp(linkid, &dlh)) != 0)
673 		goto done;
674 	if ((err = mac_perim_enter_by_macname(dls_devnet_mac(dlh), &mph)) != 0)
675 		goto done;
676 	if ((err = dls_link_hold(dls_devnet_mac(dlh), &dlp)) != 0)
677 		goto done;
678 
679 	/*
680 	 * Don't allow a process to get or set properties of a link if that
681 	 * link doesn't belong to that zone.
682 	 */
683 	if (zoneid != dls_devnet_getownerzid(dlh)) {
684 		err = ENOENT;
685 		goto done;
686 	}
687 
688 	if (!mac_prop_check_size(kprop->pr_num, kprop->pr_valsize,
689 	    kprop->pr_flags & DLD_PROP_POSSIBLE)) {
690 		err = ENOBUFS;
691 		goto done;
692 	}
693 
694 	switch (kprop->pr_num) {
695 	case MAC_PROP_ZONE:
696 		if (set) {
697 			dld_ioc_zid_t *dzp = (dld_ioc_zid_t *)kprop->pr_val;
698 
699 			if (zoneid != GLOBAL_ZONEID) {
700 				err = EACCES;
701 				goto done;
702 			}
703 			err = dls_devnet_setzid(dlh, dzp->diz_zid);
704 		} else {
705 			kprop->pr_perm_flags = MAC_PROP_PERM_RW;
706 			(*(zoneid_t *)kprop->pr_val) = dls_devnet_getzid(dlh);
707 		}
708 		break;
709 	case MAC_PROP_AUTOPUSH: {
710 		struct dlautopush *dlap = (struct dlautopush *)kprop->pr_val;
711 
712 		if (set) {
713 			if (kprop->pr_valsize != 0)
714 				err = drv_ioc_setap(linkid, dlap);
715 			else
716 				err = drv_ioc_clrap(linkid);
717 		} else {
718 			if (kprop->pr_valsize == 0)
719 				return (ENOBUFS);
720 
721 			kprop->pr_perm_flags = MAC_PROP_PERM_RW;
722 			err = drv_ioc_getap(linkid, dlap);
723 		}
724 		break;
725 	}
726 	case MAC_PROP_TAGMODE:
727 		if (set) {
728 			link_tagmode_t mode = *(link_tagmode_t *)kprop->pr_val;
729 
730 			if (mode != LINK_TAGMODE_VLANONLY &&
731 			    mode != LINK_TAGMODE_NORMAL) {
732 				err = EINVAL;
733 			} else {
734 				dlp->dl_tagmode = mode;
735 				err = 0;
736 			}
737 		} else {
738 			*(link_tagmode_t *)kprop->pr_val = dlp->dl_tagmode;
739 			kprop->pr_perm_flags = MAC_PROP_PERM_RW;
740 			err = 0;
741 		}
742 		break;
743 	default: {
744 		mac_propval_range_t range, *rangep = NULL;
745 		void *default_val = NULL;
746 		uint_t default_size = 0;
747 		void *val = kprop->pr_val;
748 		uint_t val_size = kprop->pr_valsize;
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 = &range;
767 		} else if (kprop->pr_flags & DLD_PROP_DEFAULT) {
768 			default_val = val;
769 			default_size = val_size;
770 		}
771 
772 		/*
773 		 * Always return the permissions, and optionally return
774 		 * the default value or possible values range.
775 		 */
776 		(void) mac_prop_info(dlp->dl_mh, kprop->pr_num, kprop->pr_name,
777 		    default_val, default_size, rangep, &kprop->pr_perm_flags);
778 		err = 0;
779 
780 		if (default_val == NULL && rangep == NULL) {
781 			err = mac_get_prop(dlp->dl_mh, kprop->pr_num,
782 			    kprop->pr_name, kprop->pr_val, kprop->pr_valsize);
783 		}
784 
785 		if (rangep != NULL)
786 			bcopy(rangep, val, sizeof (range));
787 	}
788 	}
789 
790 done:
791 	if (!set && ddi_copyout(kprop, (void *)arg, dsize, mode) != 0)
792 		err = EFAULT;
793 
794 	if (dlp != NULL)
795 		dls_link_rele(dlp);
796 
797 	if (mph != NULL) {
798 		int32_t	cpuid;
799 		void	*mdip = NULL;
800 
801 		if (dlp != NULL && set && err == 0) {
802 			cpuid = mac_client_intr_cpu(dlp->dl_mch);
803 			mdip = mac_get_devinfo(dlp->dl_mh);
804 		}
805 
806 		mac_perim_exit(mph);
807 
808 		if (mdip != NULL && cpuid != -1)
809 			mac_client_set_intr_cpu(mdip, dlp->dl_mch, cpuid);
810 	}
811 
812 	if (dlh != NULL)
813 		dls_devnet_rele_tmp(dlh);
814 
815 	if (kprop != NULL)
816 		kmem_free(kprop, dsize);
817 	return (err);
818 }
819 
820 /* ARGSUSED */
821 static int
822 drv_ioc_setprop(void *karg, intptr_t arg, int mode, cred_t *cred, int *rvalp)
823 {
824 	return (drv_ioc_prop_common(karg, arg, B_TRUE, cred, mode));
825 }
826 
827 /* ARGSUSED */
828 static int
829 drv_ioc_getprop(void *karg, intptr_t arg, int mode, cred_t *cred, int *rvalp)
830 {
831 	return (drv_ioc_prop_common(karg, arg, B_FALSE, cred, mode));
832 }
833 
834 /*
835  * DLDIOC_RENAME.
836  *
837  * This function handles two cases of link renaming. See more in comments above
838  * dls_datalink_rename().
839  */
840 /* ARGSUSED */
841 static int
842 drv_ioc_rename(void *karg, intptr_t arg, int mode, cred_t *cred, int *rvalp)
843 {
844 	dld_ioc_rename_t	*dir = karg;
845 	mod_hash_key_t		key;
846 	mod_hash_val_t		val;
847 	zoneid_t		zoneid = crgetzoneid(cred);
848 	datalink_class_t	class;
849 	int			err;
850 
851 	if (zoneid != GLOBAL_ZONEID &&
852 	    (zone_check_datalink(&zoneid, dir->dir_linkid1) != 0 ||
853 	    dir->dir_linkid2 != DATALINK_INVALID_LINKID &&
854 	    zone_check_datalink(&zoneid, dir->dir_linkid2) != 0))
855 		return (ENOENT);
856 
857 	if ((err = dls_mgmt_get_linkinfo(dir->dir_linkid1, NULL, &class, NULL,
858 	    NULL)) != 0)
859 		return (err);
860 
861 	if ((err = drv_ioc_checkprivs(class, cred)) != 0)
862 		return (err);
863 
864 	if ((err = dls_devnet_rename(dir->dir_linkid1, dir->dir_linkid2,
865 	    dir->dir_link)) != 0)
866 		return (err);
867 
868 	if (dir->dir_linkid2 == DATALINK_INVALID_LINKID)
869 		return (0);
870 
871 	/*
872 	 * if dir_linkid2 is not DATALINK_INVALID_LINKID, it means this
873 	 * renaming request is to rename a valid physical link (dir_linkid1)
874 	 * to a "removed" physical link (dir_linkid2, which is removed by DR
875 	 * or during system shutdown). In this case, the link (specified by
876 	 * dir_linkid1) would inherit all the configuration of dir_linkid2,
877 	 * and dir_linkid1 and its configuration would be lost.
878 	 *
879 	 * Remove per-link autopush configuration of dir_linkid1 in this case.
880 	 */
881 	key = (mod_hash_key_t)(uintptr_t)dir->dir_linkid1;
882 	rw_enter(&dld_ap_hash_lock, RW_WRITER);
883 	if (mod_hash_find(dld_ap_hashp, key, &val) != 0) {
884 		rw_exit(&dld_ap_hash_lock);
885 		return (0);
886 	}
887 
888 	VERIFY(mod_hash_remove(dld_ap_hashp, key, &val) == 0);
889 	kmem_free(val, sizeof (dld_ap_t));
890 	rw_exit(&dld_ap_hash_lock);
891 	return (0);
892 }
893 
894 static int
895 drv_ioc_setap(datalink_id_t linkid, struct dlautopush *dlap)
896 {
897 	dld_ap_t	*dap;
898 	int		i;
899 	mod_hash_key_t	key;
900 
901 	if (dlap->dap_npush == 0 || dlap->dap_npush > MAXAPUSH)
902 		return (EINVAL);
903 
904 	/*
905 	 * Validate that the specified list of modules exist.
906 	 */
907 	for (i = 0; i < dlap->dap_npush; i++) {
908 		if (fmodsw_find(dlap->dap_aplist[i], FMODSW_LOAD) == NULL)
909 			return (EINVAL);
910 	}
911 
912 
913 	key = (mod_hash_key_t)(uintptr_t)linkid;
914 
915 	rw_enter(&dld_ap_hash_lock, RW_WRITER);
916 	if (mod_hash_find(dld_ap_hashp, key, (mod_hash_val_t *)&dap) != 0) {
917 		dap = kmem_zalloc(sizeof (dld_ap_t), KM_NOSLEEP);
918 		if (dap == NULL) {
919 			rw_exit(&dld_ap_hash_lock);
920 			return (ENOMEM);
921 		}
922 
923 		dap->da_linkid = linkid;
924 		VERIFY(mod_hash_insert(dld_ap_hashp, key,
925 		    (mod_hash_val_t)dap) == 0);
926 	}
927 
928 	/*
929 	 * Update the configuration.
930 	 */
931 	dap->da_anchor = dlap->dap_anchor;
932 	dap->da_npush = dlap->dap_npush;
933 	for (i = 0; i < dlap->dap_npush; i++) {
934 		(void) strlcpy(dap->da_aplist[i], dlap->dap_aplist[i],
935 		    FMNAMESZ + 1);
936 	}
937 	rw_exit(&dld_ap_hash_lock);
938 
939 	return (0);
940 }
941 
942 static int
943 drv_ioc_getap(datalink_id_t linkid, struct dlautopush *dlap)
944 {
945 	dld_ap_t	*dap;
946 	int		i;
947 
948 	rw_enter(&dld_ap_hash_lock, RW_READER);
949 	if (mod_hash_find(dld_ap_hashp,
950 	    (mod_hash_key_t)(uintptr_t)linkid,
951 	    (mod_hash_val_t *)&dap) != 0) {
952 		rw_exit(&dld_ap_hash_lock);
953 		dlap->dap_npush = 0;
954 		return (0);
955 	}
956 
957 	/*
958 	 * Retrieve the configuration.
959 	 */
960 	dlap->dap_anchor = dap->da_anchor;
961 	dlap->dap_npush = dap->da_npush;
962 	for (i = 0; i < dap->da_npush; i++) {
963 		(void) strlcpy(dlap->dap_aplist[i], dap->da_aplist[i],
964 		    FMNAMESZ + 1);
965 	}
966 	rw_exit(&dld_ap_hash_lock);
967 
968 	return (0);
969 }
970 
971 static int
972 drv_ioc_clrap(datalink_id_t linkid)
973 {
974 	mod_hash_val_t	val;
975 	mod_hash_key_t	key;
976 
977 	key = (mod_hash_key_t)(uintptr_t)linkid;
978 
979 	rw_enter(&dld_ap_hash_lock, RW_WRITER);
980 	if (mod_hash_find(dld_ap_hashp, key, &val) != 0) {
981 		rw_exit(&dld_ap_hash_lock);
982 		return (0);
983 	}
984 
985 	VERIFY(mod_hash_remove(dld_ap_hashp, key, &val) == 0);
986 	kmem_free(val, sizeof (dld_ap_t));
987 	rw_exit(&dld_ap_hash_lock);
988 	return (0);
989 }
990 
991 /*
992  * DLDIOC_DOORSERVER
993  */
994 /* ARGSUSED */
995 static int
996 drv_ioc_doorserver(void *karg, intptr_t arg, int mode, cred_t *cred, int *rvalp)
997 {
998 	dld_ioc_door_t	*did = karg;
999 
1000 	return (dls_mgmt_door_set(did->did_start_door));
1001 }
1002 
1003 /*
1004  * DLDIOC_USAGELOG
1005  */
1006 /* ARGSUSED */
1007 static int
1008 drv_ioc_usagelog(void *karg, intptr_t arg, int mode, cred_t *cred,
1009     int *rvalp)
1010 {
1011 	dld_ioc_usagelog_t	*log_info = (dld_ioc_usagelog_t *)karg;
1012 	int			err = 0;
1013 
1014 	if (log_info->ul_type < MAC_LOGTYPE_LINK ||
1015 	    log_info->ul_type > MAC_LOGTYPE_FLOW)
1016 		return (EINVAL);
1017 
1018 	if (log_info->ul_onoff) {
1019 		err = mac_start_logusage(log_info->ul_type,
1020 		    log_info->ul_interval);
1021 	} else {
1022 		mac_stop_logusage(log_info->ul_type);
1023 	}
1024 	return (err);
1025 }
1026 
1027 /*
1028  * Process a DLDIOC_ADDFLOW request.
1029  */
1030 /* ARGSUSED */
1031 static int
1032 drv_ioc_addflow(void *karg, intptr_t arg, int mode, cred_t *cred, int *rvalp)
1033 {
1034 	dld_ioc_addflow_t	*afp = karg;
1035 
1036 	return (dld_add_flow(afp->af_linkid, afp->af_name,
1037 	    &afp->af_flow_desc, &afp->af_resource_props));
1038 }
1039 
1040 /*
1041  * Process a DLDIOC_REMOVEFLOW request.
1042  */
1043 /* ARGSUSED */
1044 static int
1045 drv_ioc_removeflow(void *karg, intptr_t arg, int mode, cred_t *cred, int *rvalp)
1046 {
1047 	dld_ioc_removeflow_t	*rfp = karg;
1048 
1049 	return (dld_remove_flow(rfp->rf_name));
1050 }
1051 
1052 /*
1053  * Process a DLDIOC_MODIFYFLOW request.
1054  */
1055 /* ARGSUSED */
1056 static int
1057 drv_ioc_modifyflow(void *karg, intptr_t arg, int mode, cred_t *cred, int *rvalp)
1058 {
1059 	dld_ioc_modifyflow_t	*mfp = karg;
1060 
1061 	return (dld_modify_flow(mfp->mf_name, &mfp->mf_resource_props));
1062 }
1063 
1064 /*
1065  * Process a DLDIOC_WALKFLOW request.
1066  */
1067 /* ARGSUSED */
1068 static int
1069 drv_ioc_walkflow(void *karg, intptr_t arg, int mode, cred_t *cred, int *rvalp)
1070 {
1071 	dld_ioc_walkflow_t	*wfp = karg;
1072 
1073 	return (dld_walk_flow(wfp, arg, cred));
1074 }
1075 
1076 /*
1077  * Check for GLDv3 autopush information.  There are three cases:
1078  *
1079  *   1. If devp points to a GLDv3 datalink and it has autopush configuration,
1080  *	fill dlap in with that information and return 0.
1081  *
1082  *   2. If devp points to a GLDv3 datalink but it doesn't have autopush
1083  *	configuration, then replace devp with the physical device (if one
1084  *	exists) and return 1.  This allows stropen() to find the old-school
1085  *	per-driver autopush configuration.  (For softmac, the result is that
1086  *	the softmac dev_t is replaced with the legacy device's dev_t).
1087  *
1088  *   3. If neither of the above apply, don't touch the args and return -1.
1089  */
1090 int
1091 dld_autopush(dev_t *devp, struct dlautopush *dlap)
1092 {
1093 	dld_ap_t	*dap;
1094 	datalink_id_t	linkid;
1095 	dev_t		phydev;
1096 
1097 	if (!GLDV3_DRV(getmajor(*devp)))
1098 		return (-1);
1099 
1100 	/*
1101 	 * Find the linkid by the link's dev_t.
1102 	 */
1103 	if (dls_devnet_dev2linkid(*devp, &linkid) != 0)
1104 		return (-1);
1105 
1106 	/*
1107 	 * Find the autopush configuration associated with the linkid.
1108 	 */
1109 	rw_enter(&dld_ap_hash_lock, RW_READER);
1110 	if (mod_hash_find(dld_ap_hashp, (mod_hash_key_t)(uintptr_t)linkid,
1111 	    (mod_hash_val_t *)&dap) == 0) {
1112 		*dlap = dap->da_ap;
1113 		rw_exit(&dld_ap_hash_lock);
1114 		return (0);
1115 	}
1116 	rw_exit(&dld_ap_hash_lock);
1117 
1118 	if (dls_devnet_phydev(linkid, &phydev) != 0)
1119 		return (-1);
1120 
1121 	*devp = phydev;
1122 	return (1);
1123 }
1124 
1125 /*
1126  * Secure objects implementation
1127  */
1128 
1129 /* ARGSUSED */
1130 static int
1131 drv_secobj_ctor(void *buf, void *arg, int kmflag)
1132 {
1133 	bzero(buf, sizeof (dld_secobj_t));
1134 	return (0);
1135 }
1136 
1137 static void
1138 drv_secobj_init(void)
1139 {
1140 	rw_init(&drv_secobj_lock, NULL, RW_DEFAULT, NULL);
1141 	drv_secobj_cachep = kmem_cache_create("drv_secobj_cache",
1142 	    sizeof (dld_secobj_t), 0, drv_secobj_ctor, NULL,
1143 	    NULL, NULL, NULL, 0);
1144 	drv_secobj_hash = mod_hash_create_extended("drv_secobj_hash",
1145 	    SECOBJ_WEP_HASHSZ, mod_hash_null_keydtor, mod_hash_null_valdtor,
1146 	    mod_hash_bystr, NULL, mod_hash_strkey_cmp, KM_SLEEP);
1147 }
1148 
1149 static void
1150 drv_secobj_fini(void)
1151 {
1152 	mod_hash_destroy_hash(drv_secobj_hash);
1153 	kmem_cache_destroy(drv_secobj_cachep);
1154 	rw_destroy(&drv_secobj_lock);
1155 }
1156 
1157 /* ARGSUSED */
1158 static int
1159 drv_ioc_secobj_set(void *karg, intptr_t arg, int mode, cred_t *cred, int *rvalp)
1160 {
1161 	dld_ioc_secobj_set_t	*ssp = karg;
1162 	dld_secobj_t		*sobjp, *objp;
1163 	int			err;
1164 
1165 	sobjp = &ssp->ss_obj;
1166 
1167 	if (sobjp->so_class != DLD_SECOBJ_CLASS_WEP &&
1168 	    sobjp->so_class != DLD_SECOBJ_CLASS_WPA)
1169 		return (EINVAL);
1170 
1171 	if (sobjp->so_name[DLD_SECOBJ_NAME_MAX - 1] != '\0' ||
1172 	    sobjp->so_len > DLD_SECOBJ_VAL_MAX)
1173 		return (EINVAL);
1174 
1175 	rw_enter(&drv_secobj_lock, RW_WRITER);
1176 	err = mod_hash_find(drv_secobj_hash, (mod_hash_key_t)sobjp->so_name,
1177 	    (mod_hash_val_t *)&objp);
1178 	if (err == 0) {
1179 		if ((ssp->ss_flags & DLD_SECOBJ_OPT_CREATE) != 0) {
1180 			rw_exit(&drv_secobj_lock);
1181 			return (EEXIST);
1182 		}
1183 	} else {
1184 		ASSERT(err == MH_ERR_NOTFOUND);
1185 		if ((ssp->ss_flags & DLD_SECOBJ_OPT_CREATE) == 0) {
1186 			rw_exit(&drv_secobj_lock);
1187 			return (ENOENT);
1188 		}
1189 		objp = kmem_cache_alloc(drv_secobj_cachep, KM_SLEEP);
1190 		(void) strlcpy(objp->so_name, sobjp->so_name,
1191 		    DLD_SECOBJ_NAME_MAX);
1192 
1193 		VERIFY(mod_hash_insert(drv_secobj_hash,
1194 		    (mod_hash_key_t)objp->so_name, (mod_hash_val_t)objp) == 0);
1195 	}
1196 	bcopy(sobjp->so_val, objp->so_val, sobjp->so_len);
1197 	objp->so_len = sobjp->so_len;
1198 	objp->so_class = sobjp->so_class;
1199 	rw_exit(&drv_secobj_lock);
1200 	return (0);
1201 }
1202 
1203 typedef struct dld_secobj_state {
1204 	uint_t		ss_free;
1205 	uint_t		ss_count;
1206 	int		ss_rc;
1207 	int		ss_mode;
1208 	dld_secobj_t	*ss_objp;
1209 } dld_secobj_state_t;
1210 
1211 /* ARGSUSED */
1212 static uint_t
1213 drv_secobj_walker(mod_hash_key_t key, mod_hash_val_t *val, void *arg)
1214 {
1215 	dld_secobj_state_t	*statep = arg;
1216 	dld_secobj_t		*sobjp = (dld_secobj_t *)val;
1217 
1218 	if (statep->ss_free < sizeof (dld_secobj_t)) {
1219 		statep->ss_rc = ENOSPC;
1220 		return (MH_WALK_TERMINATE);
1221 	}
1222 	if (ddi_copyout(sobjp, statep->ss_objp, sizeof (*sobjp),
1223 	    statep->ss_mode) != 0) {
1224 		statep->ss_rc = EFAULT;
1225 		return (MH_WALK_TERMINATE);
1226 	}
1227 	statep->ss_objp++;
1228 	statep->ss_free -= sizeof (dld_secobj_t);
1229 	statep->ss_count++;
1230 	return (MH_WALK_CONTINUE);
1231 }
1232 
1233 /* ARGSUSED */
1234 static int
1235 drv_ioc_secobj_get(void *karg, intptr_t arg, int mode, cred_t *cred, int *rvalp)
1236 {
1237 	dld_ioc_secobj_get_t	*sgp = karg;
1238 	dld_secobj_t		*sobjp, *objp;
1239 	int			err;
1240 
1241 	sobjp = &sgp->sg_obj;
1242 	if (sobjp->so_name[DLD_SECOBJ_NAME_MAX - 1] != '\0')
1243 		return (EINVAL);
1244 
1245 	rw_enter(&drv_secobj_lock, RW_READER);
1246 	if (sobjp->so_name[0] != '\0') {
1247 		err = mod_hash_find(drv_secobj_hash,
1248 		    (mod_hash_key_t)sobjp->so_name, (mod_hash_val_t *)&objp);
1249 		if (err != 0) {
1250 			ASSERT(err == MH_ERR_NOTFOUND);
1251 			rw_exit(&drv_secobj_lock);
1252 			return (ENOENT);
1253 		}
1254 		bcopy(objp->so_val, sobjp->so_val, objp->so_len);
1255 		sobjp->so_len = objp->so_len;
1256 		sobjp->so_class = objp->so_class;
1257 		sgp->sg_count = 1;
1258 	} else {
1259 		dld_secobj_state_t	state;
1260 
1261 		state.ss_free = sgp->sg_size - sizeof (dld_ioc_secobj_get_t);
1262 		state.ss_count = 0;
1263 		state.ss_rc = 0;
1264 		state.ss_mode = mode;
1265 		state.ss_objp = (dld_secobj_t *)((uchar_t *)arg +
1266 		    sizeof (dld_ioc_secobj_get_t));
1267 
1268 		mod_hash_walk(drv_secobj_hash, drv_secobj_walker, &state);
1269 		if (state.ss_rc != 0) {
1270 			rw_exit(&drv_secobj_lock);
1271 			return (state.ss_rc);
1272 		}
1273 		sgp->sg_count = state.ss_count;
1274 	}
1275 	rw_exit(&drv_secobj_lock);
1276 	return (0);
1277 }
1278 
1279 /* ARGSUSED */
1280 static int
1281 drv_ioc_secobj_unset(void *karg, intptr_t arg, int mode, cred_t *cred,
1282     int *rvalp)
1283 {
1284 	dld_ioc_secobj_unset_t	*sup = karg;
1285 	dld_secobj_t		*objp;
1286 	mod_hash_val_t		val;
1287 	int			err;
1288 
1289 	if (sup->su_name[DLD_SECOBJ_NAME_MAX - 1] != '\0')
1290 		return (EINVAL);
1291 
1292 	rw_enter(&drv_secobj_lock, RW_WRITER);
1293 	err = mod_hash_find(drv_secobj_hash, (mod_hash_key_t)sup->su_name,
1294 	    (mod_hash_val_t *)&objp);
1295 	if (err != 0) {
1296 		ASSERT(err == MH_ERR_NOTFOUND);
1297 		rw_exit(&drv_secobj_lock);
1298 		return (ENOENT);
1299 	}
1300 	VERIFY(mod_hash_remove(drv_secobj_hash, (mod_hash_key_t)sup->su_name,
1301 	    (mod_hash_val_t *)&val) == 0);
1302 	ASSERT(objp == (dld_secobj_t *)val);
1303 
1304 	kmem_cache_free(drv_secobj_cachep, objp);
1305 	rw_exit(&drv_secobj_lock);
1306 	return (0);
1307 }
1308 
1309 /*
1310  * Note that ioctls that modify links have a NULL di_priv_func(), as
1311  * privileges can only be checked after we know the class of the link being
1312  * modified (due to class-specific fine-grained privileges such as
1313  * sys_iptun_config).
1314  */
1315 static dld_ioc_info_t drv_ioc_list[] = {
1316 	{DLDIOC_ATTR, DLDCOPYINOUT, sizeof (dld_ioc_attr_t),
1317 	    drv_ioc_attr, NULL},
1318 	{DLDIOC_PHYS_ATTR, DLDCOPYINOUT, sizeof (dld_ioc_phys_attr_t),
1319 	    drv_ioc_phys_attr, NULL},
1320 	{DLDIOC_SECOBJ_SET, DLDCOPYIN, sizeof (dld_ioc_secobj_set_t),
1321 	    drv_ioc_secobj_set, secpolicy_dl_config},
1322 	{DLDIOC_SECOBJ_GET, DLDCOPYINOUT, sizeof (dld_ioc_secobj_get_t),
1323 	    drv_ioc_secobj_get, secpolicy_dl_config},
1324 	{DLDIOC_SECOBJ_UNSET, DLDCOPYIN, sizeof (dld_ioc_secobj_unset_t),
1325 	    drv_ioc_secobj_unset, secpolicy_dl_config},
1326 	{DLDIOC_DOORSERVER, DLDCOPYIN, sizeof (dld_ioc_door_t),
1327 	    drv_ioc_doorserver, secpolicy_dl_config},
1328 	{DLDIOC_RENAME, DLDCOPYIN, sizeof (dld_ioc_rename_t),
1329 	    drv_ioc_rename, NULL},
1330 	{DLDIOC_MACADDRGET, DLDCOPYINOUT, sizeof (dld_ioc_macaddrget_t),
1331 	    drv_ioc_macaddrget, NULL},
1332 	{DLDIOC_ADDFLOW, DLDCOPYIN, sizeof (dld_ioc_addflow_t),
1333 	    drv_ioc_addflow, secpolicy_dl_config},
1334 	{DLDIOC_REMOVEFLOW, DLDCOPYIN, sizeof (dld_ioc_removeflow_t),
1335 	    drv_ioc_removeflow, secpolicy_dl_config},
1336 	{DLDIOC_MODIFYFLOW, DLDCOPYIN, sizeof (dld_ioc_modifyflow_t),
1337 	    drv_ioc_modifyflow, secpolicy_dl_config},
1338 	{DLDIOC_WALKFLOW, DLDCOPYINOUT, sizeof (dld_ioc_walkflow_t),
1339 	    drv_ioc_walkflow, NULL},
1340 	{DLDIOC_USAGELOG, DLDCOPYIN, sizeof (dld_ioc_usagelog_t),
1341 	    drv_ioc_usagelog, secpolicy_dl_config},
1342 	{DLDIOC_SETMACPROP, DLDCOPYIN, sizeof (dld_ioc_macprop_t),
1343 	    drv_ioc_setprop, NULL},
1344 	{DLDIOC_GETMACPROP, DLDCOPYIN, sizeof (dld_ioc_macprop_t),
1345 	    drv_ioc_getprop, NULL},
1346 	{DLDIOC_GETHWGRP, DLDCOPYINOUT, sizeof (dld_ioc_hwgrpget_t),
1347 	    drv_ioc_hwgrpget, NULL},
1348 };
1349 
1350 typedef struct dld_ioc_modentry {
1351 	uint16_t	dim_modid;	/* Top 16 bits of ioctl command */
1352 	char		*dim_modname;	/* Module to be loaded */
1353 	int		ctrl_node_inst;	/* Ctrl node instance */
1354 	dld_ioc_info_t	*dim_list;	/* array of ioctl structures */
1355 	uint_t		dim_count;	/* number of elements in dim_list */
1356 } dld_ioc_modentry_t;
1357 
1358 /*
1359  * For all modules except for dld, dim_list and dim_count are assigned
1360  * when the modules register their ioctls in dld_ioc_register().  We
1361  * can statically initialize dld's ioctls in-line here; there's no
1362  * need for it to call dld_ioc_register() itself. ctrl_node_inst controls
1363  * whether an instance of the device will be held or the driver. If set to
1364  * a non-negative integer, device instance specified in ctrl_node_inst will
1365  * be held; so dld_ioc_register() _must_ be called in xxx_attach() routine of
1366  * the driver. If set to -1, driver will be held; so dld_ioc_register() _must_
1367  * be called in xxx_init() routine of the driver.
1368  */
1369 static dld_ioc_modentry_t dld_ioc_modtable[] = {
1370 	{DLD_IOC,	"dld", 0, drv_ioc_list, DLDIOCCNT(drv_ioc_list)},
1371 	{AGGR_IOC,	"aggr", 0, NULL, 0},
1372 	{VNIC_IOC,	"vnic",	0, NULL, 0},
1373 	{SIMNET_IOC,	"simnet", 0, NULL, 0},
1374 	{BRIDGE_IOC,	"bridge", 0, NULL, 0},
1375 	{IPTUN_IOC,	"iptun", 0, NULL, 0},
1376 	{IBPART_IOC,	"ibp", -1, NULL, 0}
1377 };
1378 #define	DLDIOC_CNT	\
1379 	(sizeof (dld_ioc_modtable) / sizeof (dld_ioc_modentry_t))
1380 
1381 static dld_ioc_modentry_t *
1382 dld_ioc_findmod(uint16_t modid)
1383 {
1384 	int	i;
1385 
1386 	for (i = 0; i < DLDIOC_CNT; i++) {
1387 		if (modid == dld_ioc_modtable[i].dim_modid)
1388 			return (&dld_ioc_modtable[i]);
1389 	}
1390 	return (NULL);
1391 }
1392 
1393 int
1394 dld_ioc_register(uint16_t modid, dld_ioc_info_t *list, uint_t count)
1395 {
1396 	dld_ioc_modentry_t *dim = dld_ioc_findmod(modid);
1397 
1398 	if (dim == NULL)
1399 		return (ENOENT);
1400 
1401 	dim->dim_list = list;
1402 	dim->dim_count = count;
1403 	return (0);
1404 }
1405 
1406 void
1407 dld_ioc_unregister(uint16_t modid)
1408 {
1409 	VERIFY(dld_ioc_register(modid, NULL, 0) == 0);
1410 }
1411 
1412 /*
1413  * The general design with GLDv3 ioctls is that all ioctls issued
1414  * through /dev/dld go through this drv_ioctl() function.  This
1415  * function handles all ioctls on behalf of modules listed in
1416  * dld_ioc_modtable.
1417  *
1418  * When an ioctl is received, this function looks for the associated
1419  * module-id-specific ioctl information using dld_ioc_findmod(). The
1420  * call to ddi_hold_driver() or ddi_hold_devi_by_instance() on the
1421  * associated device will cause the kernel module responsible for the
1422  * ioctl to be loaded if it's not already loaded, which should result
1423  * in that module calling dld_ioc_register(), thereby filling in the
1424  * dim_list containing the details for the ioctl being processed.
1425  *
1426  * This function can then perform operations such as copyin() data and
1427  * do credential checks based on the registered ioctl information,
1428  * then issue the callback function di_func() registered by the
1429  * responsible module.  Upon return, the appropriate copyout()
1430  * operation can be performed and the operation completes.
1431  */
1432 /* ARGSUSED */
1433 static int
1434 drv_ioctl(dev_t dev, int cmd, intptr_t arg, int mode, cred_t *cred, int *rvalp)
1435 {
1436 	dld_ioc_modentry_t *dim;
1437 	dld_ioc_info_t	*info;
1438 	dev_info_t	*dip = NULL;
1439 	struct dev_ops	*dops = NULL;
1440 	major_t		major;
1441 	void		*buf = NULL;
1442 	size_t		sz;
1443 	int		i, err;
1444 
1445 	if ((dim = dld_ioc_findmod(DLD_IOC_MODID(cmd))) == NULL)
1446 		return (ENOTSUP);
1447 
1448 	major = ddi_name_to_major(dim->dim_modname);
1449 
1450 	if (dim->ctrl_node_inst == -1) {
1451 		/*
1452 		 * No dedicated instance to process ioctls.
1453 		 * dld_ioc_register() is called in xxx_init().
1454 		 */
1455 		dops = ddi_hold_driver(major);
1456 	} else {
1457 		/*
1458 		 * Dedicated instance to handle ioctl.
1459 		 * dld_ioc_register() is called in xxx_attach().
1460 		 */
1461 		dip = ddi_hold_devi_by_instance(major, dim->ctrl_node_inst, 0);
1462 	}
1463 
1464 	if ((dip == NULL && dops == NULL) || dim->dim_list == NULL) {
1465 		err = ENODEV;
1466 		goto done;
1467 	}
1468 
1469 	for (i = 0; i < dim->dim_count; i++) {
1470 		if (cmd == dim->dim_list[i].di_cmd)
1471 			break;
1472 	}
1473 	if (i == dim->dim_count) {
1474 		err = ENOTSUP;
1475 		goto done;
1476 	}
1477 
1478 	info = &dim->dim_list[i];
1479 
1480 	if (info->di_priv_func != NULL &&
1481 	    (err = info->di_priv_func(cred)) != 0)
1482 		goto done;
1483 
1484 	sz = info->di_argsize;
1485 	if ((buf = kmem_zalloc(sz, KM_NOSLEEP)) == NULL) {
1486 		err = ENOMEM;
1487 		goto done;
1488 	}
1489 
1490 	if ((info->di_flags & DLDCOPYIN) &&
1491 	    ddi_copyin((void *)arg, buf, sz, mode) != 0) {
1492 		err = EFAULT;
1493 		goto done;
1494 	}
1495 
1496 	err = info->di_func(buf, arg, mode, cred, rvalp);
1497 
1498 	if ((info->di_flags & DLDCOPYOUT) &&
1499 	    ddi_copyout(buf, (void *)arg, sz, mode) != 0 && err == 0)
1500 		err = EFAULT;
1501 
1502 done:
1503 	if (buf != NULL)
1504 		kmem_free(buf, sz);
1505 	if (dip != NULL)
1506 		ddi_release_devi(dip);
1507 	if (dops != NULL)
1508 		ddi_rele_driver(major);
1509 	return (err);
1510 }
1511