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