xref: /illumos-gate/usr/src/uts/common/io/dld/dld_drv.c (revision 3bc21d0a9c7b31b1132c254e389a4114c23bcf00)
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 2008 Sun Microsystems, Inc.  All rights reserved.
23  * Use is subject to license terms.
24  */
25 
26 /*
27  * Data-Link Driver
28  */
29 
30 #include	<sys/conf.h>
31 #include	<sys/mkdev.h>
32 #include	<sys/modctl.h>
33 #include	<sys/stat.h>
34 #include	<sys/strsun.h>
35 #include	<sys/vlan.h>
36 #include	<sys/mac.h>
37 #include	<sys/dld_impl.h>
38 #include	<sys/dls_impl.h>
39 #include	<sys/softmac.h>
40 #include 	<sys/vlan.h>
41 #include	<inet/common.h>
42 
43 /*
44  * dld control node state, one per open control node session.
45  */
46 typedef struct dld_ctl_str_s {
47 	minor_t cs_minor;
48 	queue_t *cs_wq;
49 } dld_ctl_str_t;
50 
51 static void	drv_init(void);
52 static int	drv_fini(void);
53 
54 static int	drv_getinfo(dev_info_t	*, ddi_info_cmd_t, void *, void **);
55 static int	drv_attach(dev_info_t *, ddi_attach_cmd_t);
56 static int	drv_detach(dev_info_t *, ddi_detach_cmd_t);
57 
58 /*
59  * Secure objects declarations
60  */
61 #define	SECOBJ_WEP_HASHSZ	67
62 static krwlock_t	drv_secobj_lock;
63 static kmem_cache_t	*drv_secobj_cachep;
64 static mod_hash_t	*drv_secobj_hash;
65 static void		drv_secobj_init(void);
66 static void		drv_secobj_fini(void);
67 static void		drv_ioc_secobj_set(dld_ctl_str_t *, mblk_t *);
68 static void		drv_ioc_secobj_get(dld_ctl_str_t *, mblk_t *);
69 static void		drv_ioc_secobj_unset(dld_ctl_str_t *, mblk_t *);
70 static int		drv_ioc_setap(datalink_id_t, struct dlautopush *);
71 static int		drv_ioc_getap(datalink_id_t, struct dlautopush *);
72 static int		drv_ioc_clrap(datalink_id_t);
73 
74 /*
75  * The following entry points are private to dld and are used for control
76  * operations only. The entry points exported to mac drivers are defined
77  * in dld_str.c. Refer to the comment on top of dld_str.c for details.
78  */
79 static int	drv_open(queue_t *, dev_t *, int, int, cred_t *);
80 static int	drv_close(queue_t *);
81 
82 static void	drv_uw_put(queue_t *, mblk_t *);
83 static void	drv_uw_srv(queue_t *);
84 
85 dev_info_t	*dld_dip;		/* dev_info_t for the driver */
86 uint32_t	dld_opt = 0;		/* Global options */
87 static vmem_t	*dld_ctl_vmem;		/* for control minor numbers */
88 
89 #define	NAUTOPUSH 32
90 static mod_hash_t *dld_ap_hashp;
91 static krwlock_t dld_ap_hash_lock;
92 
93 static	struct	module_info	drv_info = {
94 	0,			/* mi_idnum */
95 	DLD_DRIVER_NAME,	/* mi_idname */
96 	0,			/* mi_minpsz */
97 	(64 * 1024),		/* mi_maxpsz */
98 	1,			/* mi_hiwat */
99 	0			/* mi_lowat */
100 };
101 
102 static	struct qinit		drv_ur_init = {
103 	NULL,			/* qi_putp */
104 	NULL,			/* qi_srvp */
105 	drv_open,		/* qi_qopen */
106 	drv_close,		/* qi_qclose */
107 	NULL,			/* qi_qadmin */
108 	&drv_info,		/* qi_minfo */
109 	NULL			/* qi_mstat */
110 };
111 
112 static	struct qinit		drv_uw_init = {
113 	(pfi_t)drv_uw_put,	/* qi_putp */
114 	(pfi_t)drv_uw_srv,	/* qi_srvp */
115 	NULL,			/* qi_qopen */
116 	NULL,			/* qi_qclose */
117 	NULL,			/* qi_qadmin */
118 	&drv_info,		/* qi_minfo */
119 	NULL			/* qi_mstat */
120 };
121 
122 static	struct streamtab	drv_stream = {
123 	&drv_ur_init,		/* st_rdinit */
124 	&drv_uw_init,		/* st_wrinit */
125 	NULL,			/* st_muxrinit */
126 	NULL			/* st_muxwinit */
127 };
128 
129 DDI_DEFINE_STREAM_OPS(drv_ops, nulldev, nulldev, drv_attach, drv_detach,
130     nodev, drv_getinfo, D_MP, &drv_stream);
131 
132 /*
133  * Module linkage information for the kernel.
134  */
135 
136 extern	struct mod_ops		mod_driverops;
137 
138 static	struct modldrv		drv_modldrv = {
139 	&mod_driverops,
140 	DLD_INFO,
141 	&drv_ops
142 };
143 
144 static	struct modlinkage	drv_modlinkage = {
145 	MODREV_1,
146 	&drv_modldrv,
147 	NULL
148 };
149 
150 int
151 _init(void)
152 {
153 	int	err;
154 
155 	drv_init();
156 
157 	if ((err = mod_install(&drv_modlinkage)) != 0)
158 		return (err);
159 
160 	return (0);
161 }
162 
163 int
164 _fini(void)
165 {
166 	int	err;
167 
168 	if ((err = mod_remove(&drv_modlinkage)) != 0)
169 		return (err);
170 
171 	if (drv_fini() != 0) {
172 		(void) mod_install(&drv_modlinkage);
173 		return (DDI_FAILURE);
174 	}
175 
176 	return (err);
177 }
178 
179 int
180 _info(struct modinfo *modinfop)
181 {
182 	return (mod_info(&drv_modlinkage, modinfop));
183 }
184 
185 /*
186  * Initialize component modules.
187  */
188 static void
189 drv_init(void)
190 {
191 	dld_ctl_vmem = vmem_create("dld_ctl", (void *)1, MAXMIN, 1,
192 	    NULL, NULL, NULL, 1, VM_SLEEP | VMC_IDENTIFIER);
193 	drv_secobj_init();
194 	dld_str_init();
195 	/*
196 	 * Create a hash table for autopush configuration.
197 	 */
198 	dld_ap_hashp = mod_hash_create_idhash("dld_autopush_hash",
199 	    NAUTOPUSH, mod_hash_null_valdtor);
200 
201 	ASSERT(dld_ap_hashp != NULL);
202 	rw_init(&dld_ap_hash_lock, NULL, RW_DRIVER, NULL);
203 }
204 
205 /* ARGSUSED */
206 static uint_t
207 drv_ap_exist(mod_hash_key_t key, mod_hash_val_t *val, void *arg)
208 {
209 	boolean_t *pexist = arg;
210 
211 	*pexist = B_TRUE;
212 	return (MH_WALK_TERMINATE);
213 }
214 
215 static int
216 drv_fini(void)
217 {
218 	int		err;
219 	boolean_t	exist = B_FALSE;
220 
221 	rw_enter(&dld_ap_hash_lock, RW_READER);
222 	mod_hash_walk(dld_ap_hashp, drv_ap_exist, &exist);
223 	rw_exit(&dld_ap_hash_lock);
224 
225 	if (exist)
226 		return (EBUSY);
227 
228 	if ((err = dld_str_fini()) != 0)
229 		return (err);
230 
231 	drv_secobj_fini();
232 	vmem_destroy(dld_ctl_vmem);
233 	mod_hash_destroy_idhash(dld_ap_hashp);
234 	rw_destroy(&dld_ap_hash_lock);
235 	return (0);
236 }
237 
238 /*
239  * devo_getinfo: getinfo(9e)
240  */
241 /*ARGSUSED*/
242 static int
243 drv_getinfo(dev_info_t *dip, ddi_info_cmd_t cmd, void *arg, void **resp)
244 {
245 	if (dld_dip == NULL)
246 		return (DDI_FAILURE);
247 
248 	switch (cmd) {
249 	case DDI_INFO_DEVT2INSTANCE:
250 		*resp = (void *)0;
251 		break;
252 	case DDI_INFO_DEVT2DEVINFO:
253 		*resp = (void *)dld_dip;
254 		break;
255 	default:
256 		return (DDI_FAILURE);
257 	}
258 
259 	return (DDI_SUCCESS);
260 }
261 
262 /*
263  * Check properties to set options. (See dld.h for property definitions).
264  */
265 static void
266 drv_set_opt(dev_info_t *dip)
267 {
268 	if (ddi_prop_get_int(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS,
269 	    DLD_PROP_NO_FASTPATH, 0) != 0) {
270 		dld_opt |= DLD_OPT_NO_FASTPATH;
271 	}
272 
273 	if (ddi_prop_get_int(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS,
274 	    DLD_PROP_NO_POLL, 0) != 0) {
275 		dld_opt |= DLD_OPT_NO_POLL;
276 	}
277 
278 	if (ddi_prop_get_int(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS,
279 	    DLD_PROP_NO_ZEROCOPY, 0) != 0) {
280 		dld_opt |= DLD_OPT_NO_ZEROCOPY;
281 	}
282 
283 	if (ddi_prop_get_int(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS,
284 	    DLD_PROP_NO_SOFTRING, 0) != 0) {
285 		dld_opt |= DLD_OPT_NO_SOFTRING;
286 	}
287 }
288 
289 /*
290  * devo_attach: attach(9e)
291  */
292 static int
293 drv_attach(dev_info_t *dip, ddi_attach_cmd_t cmd)
294 {
295 	if (cmd != DDI_ATTACH)
296 		return (DDI_FAILURE);
297 
298 	ASSERT(ddi_get_instance(dip) == 0);
299 
300 	drv_set_opt(dip);
301 
302 	/*
303 	 * Create control node. DLPI provider nodes will be created on demand.
304 	 */
305 	if (ddi_create_minor_node(dip, DLD_CONTROL_MINOR_NAME, S_IFCHR,
306 	    DLD_CONTROL_MINOR, DDI_PSEUDO, 0) != DDI_SUCCESS)
307 		return (DDI_FAILURE);
308 
309 	dld_dip = dip;
310 
311 	/*
312 	 * Log the fact that the driver is now attached.
313 	 */
314 	ddi_report_dev(dip);
315 	return (DDI_SUCCESS);
316 }
317 
318 /*
319  * devo_detach: detach(9e)
320  */
321 static int
322 drv_detach(dev_info_t *dip, ddi_detach_cmd_t cmd)
323 {
324 	if (cmd != DDI_DETACH)
325 		return (DDI_FAILURE);
326 
327 	ASSERT(dld_dip == dip);
328 
329 	/*
330 	 * Remove the control node.
331 	 */
332 	ddi_remove_minor_node(dip, DLD_CONTROL_MINOR_NAME);
333 	dld_dip = NULL;
334 
335 	return (DDI_SUCCESS);
336 }
337 
338 /*
339  * dld control node open procedure.
340  */
341 /*ARGSUSED*/
342 static int
343 drv_open(queue_t *rq, dev_t *devp, int flag, int sflag, cred_t *credp)
344 {
345 	dld_ctl_str_t	*ctls;
346 	minor_t		minor;
347 	queue_t *oq =	OTHERQ(rq);
348 
349 	if (sflag == MODOPEN)
350 		return (ENOTSUP);
351 
352 	/*
353 	 * This is a cloning driver and therefore each queue should only
354 	 * ever get opened once.
355 	 */
356 	if (rq->q_ptr != NULL)
357 		return (EBUSY);
358 
359 	minor = (minor_t)(uintptr_t)vmem_alloc(dld_ctl_vmem, 1, VM_NOSLEEP);
360 	if (minor == 0)
361 		return (ENOMEM);
362 
363 	ctls = kmem_zalloc(sizeof (dld_ctl_str_t), KM_NOSLEEP);
364 	if (ctls == NULL) {
365 		vmem_free(dld_ctl_vmem, (void *)(uintptr_t)minor, 1);
366 		return (ENOMEM);
367 	}
368 
369 	ctls->cs_minor = minor;
370 	ctls->cs_wq = WR(rq);
371 
372 	rq->q_ptr = ctls;
373 	oq->q_ptr = ctls;
374 
375 	/*
376 	 * Enable the queue srv(9e) routine.
377 	 */
378 	qprocson(rq);
379 
380 	/*
381 	 * Construct a cloned dev_t to hand back.
382 	 */
383 	*devp = makedevice(getmajor(*devp), ctls->cs_minor);
384 	return (0);
385 }
386 
387 /*
388  * dld control node close procedure.
389  */
390 static int
391 drv_close(queue_t *rq)
392 {
393 	dld_ctl_str_t	*ctls;
394 
395 	ctls = rq->q_ptr;
396 	ASSERT(ctls != NULL);
397 
398 	/*
399 	 * Disable the queue srv(9e) routine.
400 	 */
401 	qprocsoff(rq);
402 
403 	vmem_free(dld_ctl_vmem, (void *)(uintptr_t)ctls->cs_minor, 1);
404 
405 	kmem_free(ctls, sizeof (dld_ctl_str_t));
406 
407 	return (0);
408 }
409 
410 /*
411  * DLDIOC_ATTR
412  */
413 static void
414 drv_ioc_attr(dld_ctl_str_t *ctls, mblk_t *mp)
415 {
416 	dld_ioc_attr_t		*diap;
417 	dls_dl_handle_t		dlh;
418 	dls_vlan_t		*dvp;
419 	int			err;
420 	queue_t			*q = ctls->cs_wq;
421 
422 	if ((err = miocpullup(mp, sizeof (dld_ioc_attr_t))) != 0)
423 		goto failed;
424 
425 	diap = (dld_ioc_attr_t *)mp->b_cont->b_rptr;
426 
427 	if ((err = dls_devnet_hold_tmp(diap->dia_linkid, &dlh)) != 0)
428 		goto failed;
429 
430 	if ((err = dls_vlan_hold(dls_devnet_mac(dlh),
431 	    dls_devnet_vid(dlh), &dvp, B_FALSE, B_FALSE)) != 0) {
432 		dls_devnet_rele_tmp(dlh);
433 		goto failed;
434 	}
435 	mac_sdu_get(dvp->dv_dlp->dl_mh, NULL, &diap->dia_max_sdu);
436 
437 	dls_vlan_rele(dvp);
438 	dls_devnet_rele_tmp(dlh);
439 
440 	miocack(q, mp, sizeof (dld_ioc_attr_t), 0);
441 	return;
442 
443 failed:
444 	ASSERT(err != 0);
445 	miocnak(q, mp, 0, err);
446 }
447 
448 /*
449  * DLDIOC_PHYS_ATTR
450  */
451 static void
452 drv_ioc_phys_attr(dld_ctl_str_t *ctls, mblk_t *mp)
453 {
454 	dld_ioc_phys_attr_t	*dipp;
455 	int			err;
456 	dls_dl_handle_t		dlh;
457 	dls_dev_handle_t	ddh;
458 	dev_t			phydev;
459 	queue_t			*q = ctls->cs_wq;
460 
461 	if ((err = miocpullup(mp, sizeof (dld_ioc_phys_attr_t))) != 0)
462 		goto failed;
463 
464 	dipp = (dld_ioc_phys_attr_t *)mp->b_cont->b_rptr;
465 
466 	/*
467 	 * Every physical link should have its physical dev_t kept in the
468 	 * daemon. If not, it is not a valid physical link.
469 	 */
470 	if (dls_mgmt_get_phydev(dipp->dip_linkid, &phydev) != 0) {
471 		err = EINVAL;
472 		goto failed;
473 	}
474 
475 	/*
476 	 * Although this is a valid physical link, it might already be removed
477 	 * by DR or during system shutdown. softmac_hold_device() would return
478 	 * ENOENT in this case.
479 	 */
480 	if ((err = softmac_hold_device(phydev, &ddh)) != 0)
481 		goto failed;
482 
483 	if (dls_devnet_hold_tmp(dipp->dip_linkid, &dlh) != 0) {
484 		/*
485 		 * Although this is an active physical link, its link type is
486 		 * not supported by GLDv3, and therefore it does not have
487 		 * vanity naming support.
488 		 */
489 		dipp->dip_novanity = B_TRUE;
490 	} else {
491 		dipp->dip_novanity = B_FALSE;
492 		dls_devnet_rele_tmp(dlh);
493 	}
494 	/*
495 	 * Get the physical device name from the major number and the instance
496 	 * number derived from phydev.
497 	 */
498 	(void) snprintf(dipp->dip_dev, MAXLINKNAMELEN, "%s%d",
499 	    ddi_major_to_name(getmajor(phydev)), getminor(phydev) - 1);
500 
501 	softmac_rele_device(ddh);
502 
503 	miocack(q, mp, sizeof (dld_ioc_phys_attr_t), 0);
504 	return;
505 
506 failed:
507 	miocnak(q, mp, 0, err);
508 }
509 
510 /*
511  * DLDIOC_SETPROP
512  */
513 static void
514 drv_ioc_prop_common(dld_ctl_str_t *ctls, mblk_t *mp, boolean_t set)
515 {
516 	int		err = EINVAL, dsize;
517 	queue_t		*q = ctls->cs_wq;
518 	dld_ioc_macprop_t	*dipp;
519 	dls_dl_handle_t 	dlh;
520 	dls_vlan_t		*dvp;
521 	datalink_id_t 		linkid;
522 	mac_prop_t		macprop;
523 	uchar_t			*cp;
524 	struct dlautopush	*dlap;
525 	dld_ioc_zid_t		*dzp;
526 
527 	if ((err = miocpullup(mp, sizeof (dld_ioc_macprop_t) - 1)) != 0)
528 		goto done;
529 	dipp = (dld_ioc_macprop_t *)mp->b_cont->b_rptr;
530 
531 	dsize = sizeof (dld_ioc_macprop_t) + dipp->pr_valsize - 1;
532 	if ((err = miocpullup(mp, dsize)) != 0)
533 		goto done;
534 	dipp = (dld_ioc_macprop_t *)mp->b_cont->b_rptr;
535 
536 	linkid = dipp->pr_linkid;
537 
538 	switch (dipp->pr_num) {
539 	case MAC_PROP_ZONE:
540 		if (set) {
541 			dzp = (dld_ioc_zid_t *)dipp->pr_val;
542 			err = dls_devnet_setzid(dzp->diz_link, dzp->diz_zid);
543 			goto done;
544 		} else {
545 			cp = (uchar_t *)dipp->pr_val;
546 			err = dls_devnet_getzid(linkid, (zoneid_t *)cp);
547 			goto done;
548 		}
549 	case MAC_PROP_AUTOPUSH:
550 		if (set) {
551 			if (dipp->pr_valsize != 0) {
552 				dlap = (struct dlautopush *)dipp->pr_val;
553 				err = drv_ioc_setap(linkid, dlap);
554 				goto done;
555 			} else {
556 				err = drv_ioc_clrap(linkid);
557 				goto done;
558 			}
559 		} else {
560 			dlap = (struct dlautopush *)dipp->pr_val;
561 			err = drv_ioc_getap(linkid, dlap);
562 			goto done;
563 		}
564 
565 	default:
566 		break;
567 	}
568 
569 	if ((err = dls_devnet_hold_tmp(linkid, &dlh)) != 0)
570 		goto done;
571 
572 	if ((err = dls_vlan_hold(dls_devnet_mac(dlh),
573 	    dls_devnet_vid(dlh), &dvp, B_FALSE, B_FALSE)) != 0) {
574 		dls_devnet_rele_tmp(dlh);
575 		goto done;
576 	}
577 
578 	macprop.mp_name = dipp->pr_name;
579 	macprop.mp_id = dipp->pr_num;
580 	macprop.mp_flags = dipp->pr_flags;
581 
582 	if (set)
583 		err = mac_set_prop(dvp->dv_dlp->dl_mh, &macprop,
584 		    dipp->pr_val, dipp->pr_valsize);
585 	else
586 		err = mac_get_prop(dvp->dv_dlp->dl_mh, &macprop,
587 		    dipp->pr_val, dipp->pr_valsize);
588 
589 	dls_vlan_rele(dvp);
590 	dls_devnet_rele_tmp(dlh);
591 done:
592 	if (err == 0)
593 		miocack(q, mp, dsize, 0);
594 	else
595 		miocnak(q, mp, 0, err);
596 }
597 
598 static void
599 drv_ioc_setprop(dld_ctl_str_t *ctls, mblk_t *mp)
600 {
601 	drv_ioc_prop_common(ctls, mp, B_TRUE);
602 }
603 
604 static void
605 drv_ioc_getprop(dld_ctl_str_t *ctls, mblk_t *mp)
606 {
607 	drv_ioc_prop_common(ctls, mp, B_FALSE);
608 }
609 
610 /*
611  * DLDIOC_CREATE_VLAN
612  */
613 static void
614 drv_ioc_create_vlan(dld_ctl_str_t *ctls, mblk_t *mp)
615 {
616 	dld_ioc_create_vlan_t	*dicp;
617 	int			err;
618 	queue_t			*q = ctls->cs_wq;
619 
620 	if ((err = miocpullup(mp, sizeof (dld_ioc_create_vlan_t))) != 0)
621 		goto failed;
622 
623 	dicp = (dld_ioc_create_vlan_t *)mp->b_cont->b_rptr;
624 
625 	if ((err = dls_devnet_create_vlan(dicp->dic_vlanid,
626 	    dicp->dic_linkid, dicp->dic_vid, dicp->dic_force)) != 0) {
627 		goto failed;
628 	}
629 
630 	miocack(q, mp, 0, 0);
631 	return;
632 
633 failed:
634 	miocnak(q, mp, 0, err);
635 }
636 
637 /*
638  * DLDIOC_DELETE_VLAN
639  */
640 static void
641 drv_ioc_delete_vlan(dld_ctl_str_t *ctls, mblk_t *mp)
642 {
643 	dld_ioc_delete_vlan_t	*didp;
644 	int			err;
645 	queue_t			*q = ctls->cs_wq;
646 
647 	if ((err = miocpullup(mp, sizeof (dld_ioc_delete_vlan_t))) != 0)
648 		goto done;
649 
650 	didp = (dld_ioc_delete_vlan_t *)mp->b_cont->b_rptr;
651 	err = dls_devnet_destroy_vlan(didp->did_linkid);
652 
653 done:
654 	if (err == 0)
655 		miocack(q, mp, 0, 0);
656 	else
657 		miocnak(q, mp, 0, err);
658 }
659 
660 /*
661  * DLDIOC_VLAN_ATTR
662  */
663 static void
664 drv_ioc_vlan_attr(dld_ctl_str_t *ctls, mblk_t *mp)
665 {
666 	dld_ioc_vlan_attr_t	*divp;
667 	dls_dl_handle_t		dlh;
668 	uint16_t		vid;
669 	dls_vlan_t		*dvp;
670 	int			err;
671 	queue_t			*q = ctls->cs_wq;
672 
673 	if ((err = miocpullup(mp, sizeof (dld_ioc_vlan_attr_t))) != 0)
674 		goto failed;
675 
676 	divp = (dld_ioc_vlan_attr_t *)mp->b_cont->b_rptr;
677 
678 	/*
679 	 * Hold this link to prevent it from being deleted.
680 	 */
681 	err = dls_devnet_hold_tmp(divp->div_vlanid, &dlh);
682 	if (err != 0)
683 		goto failed;
684 
685 	if ((vid = dls_devnet_vid(dlh)) == VLAN_ID_NONE) {
686 		dls_devnet_rele_tmp(dlh);
687 		err = EINVAL;
688 		goto failed;
689 	}
690 
691 	err = dls_vlan_hold(dls_devnet_mac(dlh), vid, &dvp, B_FALSE, B_FALSE);
692 	if (err != 0) {
693 		dls_devnet_rele_tmp(dlh);
694 		err = EINVAL;
695 		goto failed;
696 	}
697 
698 	divp->div_linkid = dls_devnet_linkid(dlh);
699 	divp->div_implicit = !dls_devnet_is_explicit(dlh);
700 	divp->div_vid = vid;
701 	divp->div_force = dvp->dv_force;
702 
703 	dls_vlan_rele(dvp);
704 	dls_devnet_rele_tmp(dlh);
705 	miocack(q, mp, sizeof (dld_ioc_vlan_attr_t), 0);
706 	return;
707 
708 failed:
709 	miocnak(q, mp, 0, err);
710 }
711 
712 /*
713  * DLDIOC_RENAME.
714  *
715  * This function handles two cases of link renaming. See more in comments above
716  * dls_datalink_rename().
717  */
718 static void
719 drv_ioc_rename(dld_ctl_str_t *ctls, mblk_t *mp)
720 {
721 	dld_ioc_rename_t	*dir;
722 	mod_hash_key_t		key;
723 	mod_hash_val_t		val;
724 	int			err;
725 	queue_t			*q = ctls->cs_wq;
726 
727 	if ((err = miocpullup(mp, sizeof (dld_ioc_rename_t))) != 0)
728 		goto done;
729 
730 	dir = (dld_ioc_rename_t *)mp->b_cont->b_rptr;
731 	if ((err = dls_devnet_rename(dir->dir_linkid1, dir->dir_linkid2,
732 	    dir->dir_link)) != 0) {
733 		goto done;
734 	}
735 
736 	if (dir->dir_linkid2 == DATALINK_INVALID_LINKID)
737 		goto done;
738 
739 	/*
740 	 * if dir_linkid2 is not DATALINK_INVALID_LINKID, it means this
741 	 * renaming request is to rename a valid physical link (dir_linkid1)
742 	 * to a "removed" physical link (dir_linkid2, which is removed by DR
743 	 * or during system shutdown). In this case, the link (specified by
744 	 * dir_linkid1) would inherit all the configuration of dir_linkid2,
745 	 * and dir_linkid1 and its configuration would be lost.
746 	 *
747 	 * Remove per-link autopush configuration of dir_linkid1 in this case.
748 	 */
749 	key = (mod_hash_key_t)(uintptr_t)dir->dir_linkid1;
750 	rw_enter(&dld_ap_hash_lock, RW_WRITER);
751 	if (mod_hash_find(dld_ap_hashp, key, &val) != 0) {
752 		rw_exit(&dld_ap_hash_lock);
753 		goto done;
754 	}
755 
756 	VERIFY(mod_hash_remove(dld_ap_hashp, key, &val) == 0);
757 	kmem_free(val, sizeof (dld_ap_t));
758 	rw_exit(&dld_ap_hash_lock);
759 
760 done:
761 	if (err == 0)
762 		miocack(q, mp, 0, 0);
763 	else
764 		miocnak(q, mp, 0, err);
765 }
766 
767 static int
768 drv_ioc_setap(datalink_id_t linkid, struct dlautopush *dlap)
769 {
770 	dld_ap_t	*dap;
771 	int		i, err;
772 	mod_hash_key_t	key;
773 
774 	if (dlap->dap_npush == 0 || dlap->dap_npush > MAXAPUSH) {
775 		err = EINVAL;
776 		goto failed;
777 	}
778 
779 	/*
780 	 * Validate that the specified list of modules exist.
781 	 */
782 	for (i = 0; i < dlap->dap_npush; i++) {
783 		if (fmodsw_find(dlap->dap_aplist[i], FMODSW_LOAD) == NULL) {
784 			err = EINVAL;
785 			goto failed;
786 		}
787 	}
788 
789 	key = (mod_hash_key_t)(uintptr_t)linkid;
790 
791 	rw_enter(&dld_ap_hash_lock, RW_WRITER);
792 	if (mod_hash_find(dld_ap_hashp, key, (mod_hash_val_t *)&dap) != 0) {
793 		dap = kmem_zalloc(sizeof (dld_ap_t), KM_NOSLEEP);
794 		if (dap == NULL) {
795 			rw_exit(&dld_ap_hash_lock);
796 			err = ENOMEM;
797 			goto failed;
798 		}
799 
800 		dap->da_linkid = linkid;
801 		err = mod_hash_insert(dld_ap_hashp, key, (mod_hash_val_t)dap);
802 		ASSERT(err == 0);
803 	}
804 
805 	/*
806 	 * Update the configuration.
807 	 */
808 	dap->da_anchor = dlap->dap_anchor;
809 	dap->da_npush = dlap->dap_npush;
810 	for (i = 0; i < dlap->dap_npush; i++) {
811 		(void) strlcpy(dap->da_aplist[i], dlap->dap_aplist[i],
812 		    FMNAMESZ + 1);
813 	}
814 	rw_exit(&dld_ap_hash_lock);
815 
816 	return (0);
817 
818 failed:
819 	return (err);
820 }
821 
822 static int
823 drv_ioc_getap(datalink_id_t linkid, struct dlautopush *dlap)
824 {
825 	dld_ap_t	*dap;
826 	int		i, err;
827 
828 	rw_enter(&dld_ap_hash_lock, RW_READER);
829 	if (mod_hash_find(dld_ap_hashp,
830 	    (mod_hash_key_t)(uintptr_t)linkid,
831 	    (mod_hash_val_t *)&dap) != 0) {
832 		err = ENOENT;
833 		rw_exit(&dld_ap_hash_lock);
834 		goto failed;
835 	}
836 
837 	/*
838 	 * Retrieve the configuration.
839 	 */
840 	dlap->dap_anchor = dap->da_anchor;
841 	dlap->dap_npush = dap->da_npush;
842 	for (i = 0; i < dap->da_npush; i++) {
843 		(void) strlcpy(dlap->dap_aplist[i], dap->da_aplist[i],
844 		    FMNAMESZ + 1);
845 	}
846 	rw_exit(&dld_ap_hash_lock);
847 
848 	return (0);
849 
850 failed:
851 	return (err);
852 }
853 
854 static int
855 drv_ioc_clrap(datalink_id_t linkid)
856 {
857 	mod_hash_val_t	val;
858 	mod_hash_key_t	key;
859 
860 	key = (mod_hash_key_t)(uintptr_t)linkid;
861 
862 	rw_enter(&dld_ap_hash_lock, RW_WRITER);
863 	if (mod_hash_find(dld_ap_hashp, key, &val) != 0) {
864 		rw_exit(&dld_ap_hash_lock);
865 		return (0);
866 	}
867 
868 	VERIFY(mod_hash_remove(dld_ap_hashp, key, &val) == 0);
869 	kmem_free(val, sizeof (dld_ap_t));
870 	rw_exit(&dld_ap_hash_lock);
871 	return (0);
872 }
873 
874 /*
875  * DLDIOC_DOORSERVER
876  */
877 static void
878 drv_ioc_doorserver(dld_ctl_str_t *ctls, mblk_t *mp)
879 {
880 	queue_t		*q = ctls->cs_wq;
881 	dld_ioc_door_t	*did;
882 	int		err;
883 
884 	if ((err = miocpullup(mp, sizeof (dld_ioc_door_t))) != 0)
885 		goto done;
886 
887 	did = (dld_ioc_door_t *)mp->b_cont->b_rptr;
888 	err = dls_mgmt_door_set(did->did_start_door);
889 
890 done:
891 	if (err == 0)
892 		miocack(q, mp, 0, 0);
893 	else
894 		miocnak(q, mp, 0, err);
895 }
896 
897 /*
898  * Process an IOCTL message received by the control node.
899  */
900 static void
901 drv_ioc(dld_ctl_str_t *ctls, mblk_t *mp)
902 {
903 	uint_t	cmd;
904 
905 	cmd = ((struct iocblk *)mp->b_rptr)->ioc_cmd;
906 	switch (cmd) {
907 	case DLDIOC_ATTR:
908 		drv_ioc_attr(ctls, mp);
909 		return;
910 	case DLDIOC_PHYS_ATTR:
911 		drv_ioc_phys_attr(ctls, mp);
912 		return;
913 	case DLDIOC_SECOBJ_SET:
914 		drv_ioc_secobj_set(ctls, mp);
915 		return;
916 	case DLDIOC_SECOBJ_GET:
917 		drv_ioc_secobj_get(ctls, mp);
918 		return;
919 	case DLDIOC_SECOBJ_UNSET:
920 		drv_ioc_secobj_unset(ctls, mp);
921 		return;
922 	case DLDIOC_SETMACPROP:
923 		drv_ioc_setprop(ctls, mp);
924 		return;
925 	case DLDIOC_GETMACPROP:
926 		drv_ioc_getprop(ctls, mp);
927 		return;
928 	case DLDIOC_CREATE_VLAN:
929 		drv_ioc_create_vlan(ctls, mp);
930 		return;
931 	case DLDIOC_DELETE_VLAN:
932 		drv_ioc_delete_vlan(ctls, mp);
933 		return;
934 	case DLDIOC_VLAN_ATTR:
935 		drv_ioc_vlan_attr(ctls, mp);
936 		return;
937 	case DLDIOC_DOORSERVER:
938 		drv_ioc_doorserver(ctls, mp);
939 		return;
940 	case DLDIOC_RENAME:
941 		drv_ioc_rename(ctls, mp);
942 		return;
943 	default:
944 		miocnak(ctls->cs_wq, mp, 0, ENOTSUP);
945 		return;
946 	}
947 }
948 
949 /*
950  * Write side put routine of the dld control node.
951  */
952 static void
953 drv_uw_put(queue_t *q, mblk_t *mp)
954 {
955 	dld_ctl_str_t *ctls = q->q_ptr;
956 
957 	switch (mp->b_datap->db_type) {
958 	case M_IOCTL:
959 		drv_ioc(ctls, mp);
960 		break;
961 	default:
962 		freemsg(mp);
963 		break;
964 	}
965 }
966 
967 /*
968  * Write-side service procedure.
969  */
970 void
971 drv_uw_srv(queue_t *q)
972 {
973 	mblk_t *mp;
974 
975 	while (mp = getq(q))
976 		drv_uw_put(q, mp);
977 }
978 
979 /*
980  * Check for GLDv3 autopush information.  There are three cases:
981  *
982  *   1. If devp points to a GLDv3 datalink and it has autopush configuration,
983  *	fill dlap in with that information and return 0.
984  *
985  *   2. If devp points to a GLDv3 datalink but it doesn't have autopush
986  *	configuration, then replace devp with the physical device (if one
987  *	exists) and return 1.  This allows stropen() to find the old-school
988  *	per-driver autopush configuration.  (For softmac, the result is that
989  *	the softmac dev_t is replaced with the legacy device's dev_t).
990  *
991  *   3. If neither of the above apply, don't touch the args and return -1.
992  */
993 int
994 dld_autopush(dev_t *devp, struct dlautopush *dlap)
995 {
996 	dld_ap_t	*dap;
997 	datalink_id_t	linkid;
998 	dev_t		phydev;
999 
1000 	if (!GLDV3_DRV(getmajor(*devp)))
1001 		return (-1);
1002 
1003 	/*
1004 	 * Find the linkid by the link's dev_t.
1005 	 */
1006 	if (dls_devnet_dev2linkid(*devp, &linkid) != 0)
1007 		return (-1);
1008 
1009 	/*
1010 	 * Find the autopush configuration associated with the linkid.
1011 	 */
1012 	rw_enter(&dld_ap_hash_lock, RW_READER);
1013 	if (mod_hash_find(dld_ap_hashp, (mod_hash_key_t)(uintptr_t)linkid,
1014 	    (mod_hash_val_t *)&dap) == 0) {
1015 		*dlap = dap->da_ap;
1016 		rw_exit(&dld_ap_hash_lock);
1017 		return (0);
1018 	}
1019 	rw_exit(&dld_ap_hash_lock);
1020 
1021 	if (dls_devnet_phydev(linkid, &phydev) != 0)
1022 		return (-1);
1023 
1024 	*devp = phydev;
1025 	return (1);
1026 }
1027 
1028 /*
1029  * Secure objects implementation
1030  */
1031 
1032 /* ARGSUSED */
1033 static int
1034 drv_secobj_ctor(void *buf, void *arg, int kmflag)
1035 {
1036 	bzero(buf, sizeof (dld_secobj_t));
1037 	return (0);
1038 }
1039 
1040 static void
1041 drv_secobj_init(void)
1042 {
1043 	rw_init(&drv_secobj_lock, NULL, RW_DEFAULT, NULL);
1044 	drv_secobj_cachep = kmem_cache_create("drv_secobj_cache",
1045 	    sizeof (dld_secobj_t), 0, drv_secobj_ctor, NULL,
1046 	    NULL, NULL, NULL, 0);
1047 	drv_secobj_hash = mod_hash_create_extended("drv_secobj_hash",
1048 	    SECOBJ_WEP_HASHSZ, mod_hash_null_keydtor, mod_hash_null_valdtor,
1049 	    mod_hash_bystr, NULL, mod_hash_strkey_cmp, KM_SLEEP);
1050 }
1051 
1052 static void
1053 drv_secobj_fini(void)
1054 {
1055 	mod_hash_destroy_hash(drv_secobj_hash);
1056 	kmem_cache_destroy(drv_secobj_cachep);
1057 	rw_destroy(&drv_secobj_lock);
1058 }
1059 
1060 static void
1061 drv_ioc_secobj_set(dld_ctl_str_t *ctls, mblk_t *mp)
1062 {
1063 	dld_ioc_secobj_set_t	*ssp;
1064 	dld_secobj_t		*sobjp, *objp;
1065 	int			err = EINVAL;
1066 	queue_t			*q = ctls->cs_wq;
1067 
1068 	if ((err = miocpullup(mp, sizeof (dld_ioc_secobj_set_t))) != 0)
1069 		goto failed;
1070 
1071 	ssp = (dld_ioc_secobj_set_t *)mp->b_cont->b_rptr;
1072 	sobjp = &ssp->ss_obj;
1073 
1074 	if (sobjp->so_class != DLD_SECOBJ_CLASS_WEP &&
1075 	    sobjp->so_class != DLD_SECOBJ_CLASS_WPA)
1076 		goto failed;
1077 
1078 	if (sobjp->so_name[DLD_SECOBJ_NAME_MAX - 1] != '\0' ||
1079 	    sobjp->so_len > DLD_SECOBJ_VAL_MAX)
1080 		goto failed;
1081 
1082 	rw_enter(&drv_secobj_lock, RW_WRITER);
1083 	err = mod_hash_find(drv_secobj_hash, (mod_hash_key_t)sobjp->so_name,
1084 	    (mod_hash_val_t *)&objp);
1085 	if (err == 0) {
1086 		if ((ssp->ss_flags & DLD_SECOBJ_OPT_CREATE) != 0) {
1087 			err = EEXIST;
1088 			rw_exit(&drv_secobj_lock);
1089 			goto failed;
1090 		}
1091 	} else {
1092 		ASSERT(err == MH_ERR_NOTFOUND);
1093 		if ((ssp->ss_flags & DLD_SECOBJ_OPT_CREATE) == 0) {
1094 			err = ENOENT;
1095 			rw_exit(&drv_secobj_lock);
1096 			goto failed;
1097 		}
1098 		objp = kmem_cache_alloc(drv_secobj_cachep, KM_SLEEP);
1099 		(void) strlcpy(objp->so_name, sobjp->so_name,
1100 		    DLD_SECOBJ_NAME_MAX);
1101 
1102 		err = mod_hash_insert(drv_secobj_hash,
1103 		    (mod_hash_key_t)objp->so_name, (mod_hash_val_t)objp);
1104 		ASSERT(err == 0);
1105 	}
1106 	bcopy(sobjp->so_val, objp->so_val, sobjp->so_len);
1107 	objp->so_len = sobjp->so_len;
1108 	objp->so_class = sobjp->so_class;
1109 	rw_exit(&drv_secobj_lock);
1110 	miocack(q, mp, 0, 0);
1111 	return;
1112 
1113 failed:
1114 	ASSERT(err != 0);
1115 	miocnak(q, mp, 0, err);
1116 }
1117 
1118 typedef struct dld_secobj_state {
1119 	uint_t		ss_free;
1120 	uint_t		ss_count;
1121 	int		ss_rc;
1122 	dld_secobj_t	*ss_objp;
1123 } dld_secobj_state_t;
1124 
1125 /* ARGSUSED */
1126 static uint_t
1127 drv_secobj_walker(mod_hash_key_t key, mod_hash_val_t *val, void *arg)
1128 {
1129 	dld_secobj_state_t	*statep = arg;
1130 	dld_secobj_t		*sobjp = (dld_secobj_t *)val;
1131 
1132 	if (statep->ss_free < sizeof (dld_secobj_t)) {
1133 		statep->ss_rc = ENOSPC;
1134 		return (MH_WALK_TERMINATE);
1135 	}
1136 	bcopy(sobjp, statep->ss_objp, sizeof (dld_secobj_t));
1137 	statep->ss_objp++;
1138 	statep->ss_free -= sizeof (dld_secobj_t);
1139 	statep->ss_count++;
1140 	return (MH_WALK_CONTINUE);
1141 }
1142 
1143 static void
1144 drv_ioc_secobj_get(dld_ctl_str_t *ctls, mblk_t *mp)
1145 {
1146 	dld_ioc_secobj_get_t	*sgp;
1147 	dld_secobj_t		*sobjp, *objp;
1148 	int			err = EINVAL;
1149 	uint_t			extra = 0;
1150 	queue_t			*q = ctls->cs_wq;
1151 	mblk_t			*bp;
1152 
1153 	if ((err = miocpullup(mp, sizeof (dld_ioc_secobj_get_t))) != 0)
1154 		goto failed;
1155 
1156 	if ((bp = msgpullup(mp->b_cont, -1)) == NULL)
1157 		goto failed;
1158 
1159 	freemsg(mp->b_cont);
1160 	mp->b_cont = bp;
1161 	sgp = (dld_ioc_secobj_get_t *)bp->b_rptr;
1162 	sobjp = &sgp->sg_obj;
1163 
1164 	if (sobjp->so_name[DLD_SECOBJ_NAME_MAX - 1] != '\0')
1165 		goto failed;
1166 
1167 	rw_enter(&drv_secobj_lock, RW_READER);
1168 	if (sobjp->so_name[0] != '\0') {
1169 		err = mod_hash_find(drv_secobj_hash,
1170 		    (mod_hash_key_t)sobjp->so_name, (mod_hash_val_t *)&objp);
1171 		if (err != 0) {
1172 			ASSERT(err == MH_ERR_NOTFOUND);
1173 			err = ENOENT;
1174 			rw_exit(&drv_secobj_lock);
1175 			goto failed;
1176 		}
1177 		bcopy(objp->so_val, sobjp->so_val, objp->so_len);
1178 		sobjp->so_len = objp->so_len;
1179 		sobjp->so_class = objp->so_class;
1180 		sgp->sg_count = 1;
1181 	} else {
1182 		dld_secobj_state_t	state;
1183 
1184 		state.ss_free = MBLKL(bp) - sizeof (dld_ioc_secobj_get_t);
1185 		state.ss_count = 0;
1186 		state.ss_rc = 0;
1187 		state.ss_objp = (dld_secobj_t *)(sgp + 1);
1188 		mod_hash_walk(drv_secobj_hash, drv_secobj_walker, &state);
1189 		if (state.ss_rc != 0) {
1190 			err = state.ss_rc;
1191 			rw_exit(&drv_secobj_lock);
1192 			goto failed;
1193 		}
1194 		sgp->sg_count = state.ss_count;
1195 		extra = state.ss_count * sizeof (dld_secobj_t);
1196 	}
1197 	rw_exit(&drv_secobj_lock);
1198 	miocack(q, mp, sizeof (dld_ioc_secobj_get_t) + extra, 0);
1199 	return;
1200 
1201 failed:
1202 	ASSERT(err != 0);
1203 	miocnak(q, mp, 0, err);
1204 
1205 }
1206 
1207 static void
1208 drv_ioc_secobj_unset(dld_ctl_str_t *ctls, mblk_t *mp)
1209 {
1210 	dld_ioc_secobj_unset_t	*sup;
1211 	dld_secobj_t		*objp;
1212 	mod_hash_val_t		val;
1213 	int			err = EINVAL;
1214 	queue_t			*q = ctls->cs_wq;
1215 
1216 	if ((err = miocpullup(mp, sizeof (dld_ioc_secobj_unset_t))) != 0)
1217 		goto failed;
1218 
1219 	sup = (dld_ioc_secobj_unset_t *)mp->b_cont->b_rptr;
1220 	if (sup->su_name[DLD_SECOBJ_NAME_MAX - 1] != '\0')
1221 		goto failed;
1222 
1223 	rw_enter(&drv_secobj_lock, RW_WRITER);
1224 	err = mod_hash_find(drv_secobj_hash, (mod_hash_key_t)sup->su_name,
1225 	    (mod_hash_val_t *)&objp);
1226 	if (err != 0) {
1227 		ASSERT(err == MH_ERR_NOTFOUND);
1228 		err = ENOENT;
1229 		rw_exit(&drv_secobj_lock);
1230 		goto failed;
1231 	}
1232 	err = mod_hash_remove(drv_secobj_hash, (mod_hash_key_t)sup->su_name,
1233 	    (mod_hash_val_t *)&val);
1234 	ASSERT(err == 0);
1235 	ASSERT(objp == (dld_secobj_t *)val);
1236 
1237 	kmem_cache_free(drv_secobj_cachep, objp);
1238 	rw_exit(&drv_secobj_lock);
1239 	miocack(q, mp, 0, 0);
1240 	return;
1241 
1242 failed:
1243 	ASSERT(err != 0);
1244 	miocnak(q, mp, 0, err);
1245 }
1246