xref: /illumos-gate/usr/src/uts/common/io/dld/dld_drv.c (revision 24da5b34f49324ed742a340010ed5bd3d4e06625)
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 2007 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/dld.h>
38 #include	<sys/dld_impl.h>
39 #include	<sys/dls_impl.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 
71 /*
72  * The following entry points are private to dld and are used for control
73  * operations only. The entry points exported to mac drivers are defined
74  * in dld_str.c. Refer to the comment on top of dld_str.c for details.
75  */
76 static int	drv_open(queue_t *, dev_t *, int, int, cred_t *);
77 static int	drv_close(queue_t *);
78 
79 static void	drv_uw_put(queue_t *, mblk_t *);
80 static void	drv_uw_srv(queue_t *);
81 
82 dev_info_t	*dld_dip;		/* dev_info_t for the driver */
83 uint32_t	dld_opt = 0;		/* Global options */
84 static vmem_t	*dld_ctl_vmem;		/* for control minor numbers */
85 
86 static	struct	module_info	drv_info = {
87 	0,			/* mi_idnum */
88 	DLD_DRIVER_NAME,	/* mi_idname */
89 	0,			/* mi_minpsz */
90 	(64 * 1024),		/* mi_maxpsz */
91 	1,			/* mi_hiwat */
92 	0			/* mi_lowat */
93 };
94 
95 static	struct qinit		drv_ur_init = {
96 	NULL,			/* qi_putp */
97 	NULL,			/* qi_srvp */
98 	drv_open,		/* qi_qopen */
99 	drv_close,		/* qi_qclose */
100 	NULL,			/* qi_qadmin */
101 	&drv_info,		/* qi_minfo */
102 	NULL			/* qi_mstat */
103 };
104 
105 static	struct qinit		drv_uw_init = {
106 	(pfi_t)drv_uw_put,	/* qi_putp */
107 	(pfi_t)drv_uw_srv,	/* qi_srvp */
108 	NULL,			/* qi_qopen */
109 	NULL,			/* qi_qclose */
110 	NULL,			/* qi_qadmin */
111 	&drv_info,		/* qi_minfo */
112 	NULL			/* qi_mstat */
113 };
114 
115 static	struct streamtab	drv_stream = {
116 	&drv_ur_init,		/* st_rdinit */
117 	&drv_uw_init,		/* st_wrinit */
118 	NULL,			/* st_muxrinit */
119 	NULL			/* st_muxwinit */
120 };
121 
122 DDI_DEFINE_STREAM_OPS(drv_ops, nulldev, nulldev, drv_attach, drv_detach,
123     nodev, drv_getinfo, D_MP, &drv_stream);
124 
125 /*
126  * Module linkage information for the kernel.
127  */
128 
129 extern	struct mod_ops		mod_driverops;
130 
131 static	struct modldrv		drv_modldrv = {
132 	&mod_driverops,
133 	DLD_INFO,
134 	&drv_ops
135 };
136 
137 static	struct modlinkage	drv_modlinkage = {
138 	MODREV_1,
139 	&drv_modldrv,
140 	NULL
141 };
142 
143 int
144 _init(void)
145 {
146 	int	err;
147 
148 	drv_init();
149 
150 	if ((err = mod_install(&drv_modlinkage)) != 0)
151 		return (err);
152 
153 	return (0);
154 }
155 
156 int
157 _fini(void)
158 {
159 	int	err;
160 
161 	if ((err = mod_remove(&drv_modlinkage)) != 0)
162 		return (err);
163 
164 	if (drv_fini() != 0) {
165 		(void) mod_install(&drv_modlinkage);
166 		return (DDI_FAILURE);
167 	}
168 
169 	return (err);
170 }
171 
172 int
173 _info(struct modinfo *modinfop)
174 {
175 	return (mod_info(&drv_modlinkage, modinfop));
176 }
177 
178 /*
179  * Initialize component modules.
180  */
181 static void
182 drv_init(void)
183 {
184 	dld_ctl_vmem = vmem_create("dld_ctl", (void *)1, MAXMIN, 1,
185 	    NULL, NULL, NULL, 1, VM_SLEEP | VMC_IDENTIFIER);
186 	drv_secobj_init();
187 	dld_str_init();
188 }
189 
190 static int
191 drv_fini(void)
192 {
193 	int	err;
194 
195 	if ((err = dld_str_fini()) != 0)
196 		return (err);
197 
198 	drv_secobj_fini();
199 	vmem_destroy(dld_ctl_vmem);
200 	return (0);
201 }
202 
203 /*
204  * devo_getinfo: getinfo(9e)
205  */
206 /*ARGSUSED*/
207 static int
208 drv_getinfo(dev_info_t *dip, ddi_info_cmd_t cmd, void *arg, void **resp)
209 {
210 	if (dld_dip == NULL)
211 		return (DDI_FAILURE);
212 
213 	switch (cmd) {
214 	case DDI_INFO_DEVT2INSTANCE:
215 		*resp = (void *)0;
216 		break;
217 	case DDI_INFO_DEVT2DEVINFO:
218 		*resp = (void *)dld_dip;
219 		break;
220 	default:
221 		return (DDI_FAILURE);
222 	}
223 
224 	return (DDI_SUCCESS);
225 }
226 
227 /*
228  * Check properties to set options. (See dld.h for property definitions).
229  */
230 static void
231 drv_set_opt(dev_info_t *dip)
232 {
233 	if (ddi_prop_get_int(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS,
234 	    DLD_PROP_NO_FASTPATH, 0) != 0) {
235 		dld_opt |= DLD_OPT_NO_FASTPATH;
236 	}
237 
238 	if (ddi_prop_get_int(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS,
239 	    DLD_PROP_NO_POLL, 0) != 0) {
240 		dld_opt |= DLD_OPT_NO_POLL;
241 	}
242 
243 	if (ddi_prop_get_int(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS,
244 	    DLD_PROP_NO_ZEROCOPY, 0) != 0) {
245 		dld_opt |= DLD_OPT_NO_ZEROCOPY;
246 	}
247 
248 	if (ddi_prop_get_int(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS,
249 	    DLD_PROP_NO_SOFTRING, 0) != 0) {
250 		dld_opt |= DLD_OPT_NO_SOFTRING;
251 	}
252 }
253 
254 /*
255  * devo_attach: attach(9e)
256  */
257 static int
258 drv_attach(dev_info_t *dip, ddi_attach_cmd_t cmd)
259 {
260 	if (cmd != DDI_ATTACH)
261 		return (DDI_FAILURE);
262 
263 	ASSERT(ddi_get_instance(dip) == 0);
264 
265 	drv_set_opt(dip);
266 
267 	/*
268 	 * Create control node. DLPI provider nodes will be created on demand.
269 	 */
270 	if (ddi_create_minor_node(dip, DLD_CONTROL_MINOR_NAME, S_IFCHR,
271 	    DLD_CONTROL_MINOR, DDI_PSEUDO, 0) != DDI_SUCCESS)
272 		return (DDI_FAILURE);
273 
274 	dld_dip = dip;
275 
276 	/*
277 	 * Log the fact that the driver is now attached.
278 	 */
279 	ddi_report_dev(dip);
280 	return (DDI_SUCCESS);
281 }
282 
283 /*
284  * devo_detach: detach(9e)
285  */
286 static int
287 drv_detach(dev_info_t *dip, ddi_detach_cmd_t cmd)
288 {
289 	if (cmd != DDI_DETACH)
290 		return (DDI_FAILURE);
291 
292 	ASSERT(dld_dip == dip);
293 
294 	/*
295 	 * Remove the control node.
296 	 */
297 	ddi_remove_minor_node(dip, DLD_CONTROL_MINOR_NAME);
298 	dld_dip = NULL;
299 
300 	return (DDI_SUCCESS);
301 }
302 
303 /*
304  * dld control node open procedure.
305  */
306 /*ARGSUSED*/
307 static int
308 drv_open(queue_t *rq, dev_t *devp, int flag, int sflag, cred_t *credp)
309 {
310 	dld_ctl_str_t	*ctls;
311 	minor_t		minor;
312 	queue_t *oq =	OTHERQ(rq);
313 
314 	if (sflag == MODOPEN)
315 		return (ENOTSUP);
316 
317 	/*
318 	 * This is a cloning driver and therefore each queue should only
319 	 * ever get opened once.
320 	 */
321 	if (rq->q_ptr != NULL)
322 		return (EBUSY);
323 
324 	minor = (minor_t)(uintptr_t)vmem_alloc(dld_ctl_vmem, 1, VM_NOSLEEP);
325 	if (minor == 0)
326 		return (ENOMEM);
327 
328 	ctls = kmem_zalloc(sizeof (dld_ctl_str_t), KM_NOSLEEP);
329 	if (ctls == NULL) {
330 		vmem_free(dld_ctl_vmem, (void *)(uintptr_t)minor, 1);
331 		return (ENOMEM);
332 	}
333 
334 	ctls->cs_minor = minor;
335 	ctls->cs_wq = WR(rq);
336 
337 	rq->q_ptr = ctls;
338 	oq->q_ptr = ctls;
339 
340 	/*
341 	 * Enable the queue srv(9e) routine.
342 	 */
343 	qprocson(rq);
344 
345 	/*
346 	 * Construct a cloned dev_t to hand back.
347 	 */
348 	*devp = makedevice(getmajor(*devp), ctls->cs_minor);
349 	return (0);
350 }
351 
352 /*
353  * dld control node close procedure.
354  */
355 static int
356 drv_close(queue_t *rq)
357 {
358 	dld_ctl_str_t	*ctls;
359 
360 	ctls = rq->q_ptr;
361 	ASSERT(ctls != NULL);
362 
363 	/*
364 	 * Disable the queue srv(9e) routine.
365 	 */
366 	qprocsoff(rq);
367 
368 	vmem_free(dld_ctl_vmem, (void *)(uintptr_t)ctls->cs_minor, 1);
369 
370 	kmem_free(ctls, sizeof (dld_ctl_str_t));
371 
372 	return (0);
373 }
374 
375 /*
376  * DLDIOCATTR
377  */
378 static void
379 drv_ioc_attr(dld_ctl_str_t *ctls, mblk_t *mp)
380 {
381 	dld_ioc_attr_t	*diap;
382 	dls_vlan_t	*dvp = NULL;
383 	dls_link_t	*dlp = NULL;
384 	int		err;
385 	queue_t		*q = ctls->cs_wq;
386 
387 	if ((err = miocpullup(mp, sizeof (dld_ioc_attr_t))) != 0)
388 		goto failed;
389 
390 	diap = (dld_ioc_attr_t *)mp->b_cont->b_rptr;
391 	diap->dia_name[IFNAMSIZ - 1] = '\0';
392 
393 	if (dls_vlan_hold(diap->dia_name, &dvp, B_FALSE) != 0) {
394 		err = ENOENT;
395 		goto failed;
396 	}
397 
398 	dlp = dvp->dv_dlp;
399 	(void) strlcpy(diap->dia_dev, dlp->dl_name, sizeof (diap->dia_dev));
400 	diap->dia_vid = dvp->dv_id;
401 	diap->dia_max_sdu = dlp->dl_mip->mi_sdu_max;
402 
403 	dls_vlan_rele(dvp);
404 	miocack(q, mp, sizeof (dld_ioc_attr_t), 0);
405 	return;
406 
407 failed:
408 	ASSERT(err != 0);
409 	if (err == ENOENT) {
410 		char	devname[MAXNAMELEN];
411 		uint_t	instance;
412 		major_t	major;
413 
414 		/*
415 		 * Try to detect if the specified device is gldv3
416 		 * and return ENODEV if it is not.
417 		 */
418 		if (ddi_parse(diap->dia_name, devname, &instance) == 0 &&
419 		    (major = ddi_name_to_major(devname)) != (major_t)-1 &&
420 		    !GLDV3_DRV(major))
421 			err = ENODEV;
422 	}
423 	miocnak(q, mp, 0, err);
424 }
425 
426 
427 /*
428  * DLDIOCVLAN
429  */
430 typedef struct dld_ioc_vlan_state {
431 	uint_t		bytes_left;
432 	dld_ioc_vlan_t	*divp;
433 	dld_vlan_info_t	*vlanp;
434 } dld_ioc_vlan_state_t;
435 
436 static int
437 drv_ioc_vlan_info(dls_vlan_t *dvp, void *arg)
438 {
439 	dld_ioc_vlan_state_t	*statep = arg;
440 
441 	/*
442 	 * passed buffer space is limited to 65536 bytes. So
443 	 * copy only the vlans associated with the passed link.
444 	 */
445 	if (strcmp(dvp->dv_dlp->dl_name, statep->divp->div_name) == 0 &&
446 	    dvp->dv_id != 0) {
447 		if (statep->bytes_left < sizeof (dld_vlan_info_t))
448 			return (ENOSPC);
449 
450 		(void) strlcpy(statep->vlanp->dvi_name,
451 		    dvp->dv_name, IFNAMSIZ);
452 		statep->divp->div_count++;
453 		statep->bytes_left -= sizeof (dld_vlan_info_t);
454 		statep->vlanp += 1;
455 	}
456 	return (0);
457 }
458 
459 static void
460 drv_ioc_vlan(dld_ctl_str_t *ctls, mblk_t *mp)
461 {
462 	dld_ioc_vlan_t		*divp;
463 	dld_ioc_vlan_state_t	state;
464 	int			err = EINVAL;
465 	queue_t			*q = ctls->cs_wq;
466 	mblk_t			*bp;
467 
468 	if ((err = miocpullup(mp, sizeof (dld_ioc_vlan_t))) != 0)
469 		goto failed;
470 
471 	if ((bp = msgpullup(mp->b_cont, -1)) == NULL)
472 		goto failed;
473 
474 	freemsg(mp->b_cont);
475 	mp->b_cont = bp;
476 	divp = (dld_ioc_vlan_t *)bp->b_rptr;
477 	divp->div_count = 0;
478 	state.bytes_left = MBLKL(bp) - sizeof (dld_ioc_vlan_t);
479 	state.divp = divp;
480 	state.vlanp = (dld_vlan_info_t *)(divp + 1);
481 
482 	err = dls_vlan_walk(drv_ioc_vlan_info, &state);
483 	if (err != 0)
484 		goto failed;
485 
486 	miocack(q, mp, sizeof (dld_ioc_vlan_t) +
487 	    state.divp->div_count * sizeof (dld_vlan_info_t), 0);
488 	return;
489 
490 failed:
491 	ASSERT(err != 0);
492 	miocnak(q, mp, 0, err);
493 }
494 
495 /*
496  * DLDIOCHOLDVLAN
497  */
498 static void
499 drv_hold_vlan(dld_ctl_str_t *ctls, mblk_t *mp)
500 {
501 	queue_t		*q = ctls->cs_wq;
502 	dld_hold_vlan_t	*dhv;
503 	mblk_t		*nmp;
504 	int		err = EINVAL;
505 	dls_vlan_t	*dvp;
506 	char		mac[MAXNAMELEN];
507 	dev_info_t	*dip = NULL;
508 	major_t		major;
509 	uint_t		index;
510 
511 	nmp = mp->b_cont;
512 	if (nmp == NULL || MBLKL(nmp) < sizeof (dld_hold_vlan_t))
513 		goto failed;
514 
515 	dhv = (dld_hold_vlan_t *)nmp->b_rptr;
516 
517 	/*
518 	 * When a device instance without opens is detached, its
519 	 * dls_vlan_t will be destroyed. A subsequent DLDIOCHOLDVLAN
520 	 * invoked on this device instance will fail because
521 	 * dls_vlan_hold() does not create non-tagged vlans on demand.
522 	 * To handle this problem, we must force the creation of the
523 	 * dls_vlan_t (if it doesn't already exist) by calling
524 	 * ddi_hold_devi_by_instance() before calling dls_vlan_hold().
525 	 */
526 	if (ddi_parse(dhv->dhv_name, mac, &index) != DDI_SUCCESS)
527 		goto failed;
528 
529 	if (DLS_PPA2VID(index) == VLAN_ID_NONE && strcmp(mac, "aggr") != 0) {
530 		if ((major = ddi_name_to_major(mac)) == (major_t)-1 ||
531 		    (dip = ddi_hold_devi_by_instance(major,
532 		    DLS_PPA2INST(index), 0)) == NULL)
533 			goto failed;
534 	}
535 
536 	err = dls_vlan_hold(dhv->dhv_name, &dvp, B_TRUE);
537 	if (dip != NULL)
538 		ddi_release_devi(dip);
539 
540 	if (err != 0)
541 		goto failed;
542 
543 	if ((err = dls_vlan_setzoneid(dhv->dhv_name, dhv->dhv_zid,
544 	    dhv->dhv_docheck)) != 0) {
545 		dls_vlan_rele(dvp);
546 		goto failed;
547 	} else {
548 		miocack(q, mp, 0, 0);
549 		return;
550 	}
551 failed:
552 	miocnak(q, mp, 0, err);
553 }
554 
555 /*
556  * DLDIOCRELEVLAN
557  */
558 static void
559 drv_rele_vlan(dld_ctl_str_t *ctls, mblk_t *mp)
560 {
561 	queue_t		*q = ctls->cs_wq;
562 	dld_hold_vlan_t	*dhv;
563 	mblk_t		*nmp;
564 	int		err;
565 
566 	nmp = mp->b_cont;
567 	if (nmp == NULL || MBLKL(nmp) < sizeof (dld_hold_vlan_t)) {
568 		err = EINVAL;
569 		miocnak(q, mp, 0, err);
570 		return;
571 	}
572 	dhv = (dld_hold_vlan_t *)nmp->b_rptr;
573 
574 	if ((err = dls_vlan_setzoneid(dhv->dhv_name, dhv->dhv_zid,
575 	    dhv->dhv_docheck)) != 0) {
576 		miocnak(q, mp, 0, err);
577 		return;
578 	}
579 
580 	if ((err = dls_vlan_rele_by_name(dhv->dhv_name)) != 0) {
581 		miocnak(q, mp, 0, err);
582 		return;
583 	}
584 
585 	miocack(q, mp, 0, 0);
586 }
587 
588 /*
589  * DLDIOCZIDGET
590  */
591 static void
592 drv_ioc_zid_get(dld_ctl_str_t *ctls, mblk_t *mp)
593 {
594 	queue_t		*q = ctls->cs_wq;
595 	dld_hold_vlan_t	*dhv;
596 	mblk_t		*nmp;
597 	int		err;
598 
599 	nmp = mp->b_cont;
600 	if (nmp == NULL || MBLKL(nmp) < sizeof (dld_hold_vlan_t)) {
601 		err = EINVAL;
602 		miocnak(q, mp, 0, err);
603 		return;
604 	}
605 	dhv = (dld_hold_vlan_t *)nmp->b_rptr;
606 
607 	if ((err = dls_vlan_getzoneid(dhv->dhv_name, &dhv->dhv_zid)) != 0)
608 		miocnak(q, mp, 0, err);
609 	else
610 		miocack(q, mp, sizeof (dld_hold_vlan_t), 0);
611 }
612 
613 /*
614  * Process an IOCTL message received by the control node.
615  */
616 static void
617 drv_ioc(dld_ctl_str_t *ctls, mblk_t *mp)
618 {
619 	uint_t	cmd;
620 
621 	cmd = ((struct iocblk *)mp->b_rptr)->ioc_cmd;
622 	switch (cmd) {
623 	case DLDIOCATTR:
624 		drv_ioc_attr(ctls, mp);
625 		return;
626 	case DLDIOCVLAN:
627 		drv_ioc_vlan(ctls, mp);
628 		return;
629 	case DLDIOCSECOBJSET:
630 		drv_ioc_secobj_set(ctls, mp);
631 		return;
632 	case DLDIOCSECOBJGET:
633 		drv_ioc_secobj_get(ctls, mp);
634 		return;
635 	case DLDIOCSECOBJUNSET:
636 		drv_ioc_secobj_unset(ctls, mp);
637 		return;
638 	case DLDIOCHOLDVLAN:
639 		drv_hold_vlan(ctls, mp);
640 		return;
641 	case DLDIOCRELEVLAN:
642 		drv_rele_vlan(ctls, mp);
643 		return;
644 	case DLDIOCZIDGET:
645 		drv_ioc_zid_get(ctls, mp);
646 		return;
647 	default:
648 		miocnak(ctls->cs_wq, mp, 0, ENOTSUP);
649 		return;
650 	}
651 }
652 
653 /*
654  * Write side put routine of the dld control node.
655  */
656 static void
657 drv_uw_put(queue_t *q, mblk_t *mp)
658 {
659 	dld_ctl_str_t *ctls = q->q_ptr;
660 
661 	switch (mp->b_datap->db_type) {
662 	case M_IOCTL:
663 		drv_ioc(ctls, mp);
664 		break;
665 	default:
666 		freemsg(mp);
667 		break;
668 	}
669 }
670 
671 /*
672  * Write-side service procedure.
673  */
674 void
675 drv_uw_srv(queue_t *q)
676 {
677 	mblk_t *mp;
678 
679 	while (mp = getq(q))
680 		drv_uw_put(q, mp);
681 }
682 
683 /*
684  * Secure objects implementation
685  */
686 
687 /* ARGSUSED */
688 static int
689 drv_secobj_ctor(void *buf, void *arg, int kmflag)
690 {
691 	bzero(buf, sizeof (dld_secobj_t));
692 	return (0);
693 }
694 
695 static void
696 drv_secobj_init(void)
697 {
698 	rw_init(&drv_secobj_lock, NULL, RW_DEFAULT, NULL);
699 	drv_secobj_cachep = kmem_cache_create("drv_secobj_cache",
700 	    sizeof (dld_secobj_t), 0, drv_secobj_ctor, NULL,
701 	    NULL, NULL, NULL, 0);
702 	drv_secobj_hash = mod_hash_create_extended("drv_secobj_hash",
703 	    SECOBJ_WEP_HASHSZ, mod_hash_null_keydtor, mod_hash_null_valdtor,
704 	    mod_hash_bystr, NULL, mod_hash_strkey_cmp, KM_SLEEP);
705 }
706 
707 static void
708 drv_secobj_fini(void)
709 {
710 	mod_hash_destroy_hash(drv_secobj_hash);
711 	kmem_cache_destroy(drv_secobj_cachep);
712 	rw_destroy(&drv_secobj_lock);
713 }
714 
715 static void
716 drv_ioc_secobj_set(dld_ctl_str_t *ctls, mblk_t *mp)
717 {
718 	dld_ioc_secobj_set_t	*ssp;
719 	dld_secobj_t		*sobjp, *objp;
720 	int			err = EINVAL;
721 	queue_t			*q = ctls->cs_wq;
722 
723 	if ((err = miocpullup(mp, sizeof (dld_ioc_secobj_set_t))) != 0)
724 		goto failed;
725 
726 	ssp = (dld_ioc_secobj_set_t *)mp->b_cont->b_rptr;
727 	sobjp = &ssp->ss_obj;
728 
729 	if (sobjp->so_class != DLD_SECOBJ_CLASS_WEP &&
730 	    sobjp->so_class != DLD_SECOBJ_CLASS_WPA)
731 		goto failed;
732 
733 	if (sobjp->so_name[DLD_SECOBJ_NAME_MAX - 1] != '\0' ||
734 	    sobjp->so_len > DLD_SECOBJ_VAL_MAX)
735 		goto failed;
736 
737 	rw_enter(&drv_secobj_lock, RW_WRITER);
738 	err = mod_hash_find(drv_secobj_hash, (mod_hash_key_t)sobjp->so_name,
739 	    (mod_hash_val_t *)&objp);
740 	if (err == 0) {
741 		if ((ssp->ss_flags & DLD_SECOBJ_OPT_CREATE) != 0) {
742 			err = EEXIST;
743 			rw_exit(&drv_secobj_lock);
744 			goto failed;
745 		}
746 	} else {
747 		ASSERT(err == MH_ERR_NOTFOUND);
748 		if ((ssp->ss_flags & DLD_SECOBJ_OPT_CREATE) == 0) {
749 			err = ENOENT;
750 			rw_exit(&drv_secobj_lock);
751 			goto failed;
752 		}
753 		objp = kmem_cache_alloc(drv_secobj_cachep, KM_SLEEP);
754 		(void) strlcpy(objp->so_name, sobjp->so_name,
755 		    DLD_SECOBJ_NAME_MAX);
756 
757 		err = mod_hash_insert(drv_secobj_hash,
758 		    (mod_hash_key_t)objp->so_name, (mod_hash_val_t)objp);
759 		ASSERT(err == 0);
760 	}
761 	bcopy(sobjp->so_val, objp->so_val, sobjp->so_len);
762 	objp->so_len = sobjp->so_len;
763 	objp->so_class = sobjp->so_class;
764 	rw_exit(&drv_secobj_lock);
765 	miocack(q, mp, 0, 0);
766 	return;
767 
768 failed:
769 	ASSERT(err != 0);
770 	miocnak(q, mp, 0, err);
771 }
772 
773 typedef struct dld_secobj_state {
774 	uint_t		ss_free;
775 	uint_t		ss_count;
776 	int		ss_rc;
777 	dld_secobj_t	*ss_objp;
778 } dld_secobj_state_t;
779 
780 /* ARGSUSED */
781 static uint_t
782 drv_secobj_walker(mod_hash_key_t key, mod_hash_val_t *val, void *arg)
783 {
784 	dld_secobj_state_t	*statep = arg;
785 	dld_secobj_t		*sobjp = (dld_secobj_t *)val;
786 
787 	if (statep->ss_free < sizeof (dld_secobj_t)) {
788 		statep->ss_rc = ENOSPC;
789 		return (MH_WALK_TERMINATE);
790 	}
791 	bcopy(sobjp, statep->ss_objp, sizeof (dld_secobj_t));
792 	statep->ss_objp++;
793 	statep->ss_free -= sizeof (dld_secobj_t);
794 	statep->ss_count++;
795 	return (MH_WALK_CONTINUE);
796 }
797 
798 static void
799 drv_ioc_secobj_get(dld_ctl_str_t *ctls, mblk_t *mp)
800 {
801 	dld_ioc_secobj_get_t	*sgp;
802 	dld_secobj_t		*sobjp, *objp;
803 	int			err = EINVAL;
804 	uint_t			extra = 0;
805 	queue_t			*q = ctls->cs_wq;
806 	mblk_t			*bp;
807 
808 	if ((err = miocpullup(mp, sizeof (dld_ioc_secobj_get_t))) != 0)
809 		goto failed;
810 
811 	if ((bp = msgpullup(mp->b_cont, -1)) == NULL)
812 		goto failed;
813 
814 	freemsg(mp->b_cont);
815 	mp->b_cont = bp;
816 	sgp = (dld_ioc_secobj_get_t *)bp->b_rptr;
817 	sobjp = &sgp->sg_obj;
818 
819 	if (sobjp->so_name[DLD_SECOBJ_NAME_MAX - 1] != '\0')
820 		goto failed;
821 
822 	rw_enter(&drv_secobj_lock, RW_READER);
823 	if (sobjp->so_name[0] != '\0') {
824 		err = mod_hash_find(drv_secobj_hash,
825 		    (mod_hash_key_t)sobjp->so_name, (mod_hash_val_t *)&objp);
826 		if (err != 0) {
827 			ASSERT(err == MH_ERR_NOTFOUND);
828 			err = ENOENT;
829 			rw_exit(&drv_secobj_lock);
830 			goto failed;
831 		}
832 		bcopy(objp->so_val, sobjp->so_val, objp->so_len);
833 		sobjp->so_len = objp->so_len;
834 		sobjp->so_class = objp->so_class;
835 		sgp->sg_count = 1;
836 	} else {
837 		dld_secobj_state_t	state;
838 
839 		state.ss_free = MBLKL(bp) - sizeof (dld_ioc_secobj_get_t);
840 		state.ss_count = 0;
841 		state.ss_rc = 0;
842 		state.ss_objp = (dld_secobj_t *)(sgp + 1);
843 		mod_hash_walk(drv_secobj_hash, drv_secobj_walker, &state);
844 		if (state.ss_rc != 0) {
845 			err = state.ss_rc;
846 			rw_exit(&drv_secobj_lock);
847 			goto failed;
848 		}
849 		sgp->sg_count = state.ss_count;
850 		extra = state.ss_count * sizeof (dld_secobj_t);
851 	}
852 	rw_exit(&drv_secobj_lock);
853 	miocack(q, mp, sizeof (dld_ioc_secobj_get_t) + extra, 0);
854 	return;
855 
856 failed:
857 	ASSERT(err != 0);
858 	miocnak(q, mp, 0, err);
859 
860 }
861 
862 static void
863 drv_ioc_secobj_unset(dld_ctl_str_t *ctls, mblk_t *mp)
864 {
865 	dld_ioc_secobj_unset_t	*sup;
866 	dld_secobj_t		*objp;
867 	mod_hash_val_t		val;
868 	int			err = EINVAL;
869 	queue_t			*q = ctls->cs_wq;
870 
871 	if ((err = miocpullup(mp, sizeof (dld_ioc_secobj_unset_t))) != 0)
872 		goto failed;
873 
874 	sup = (dld_ioc_secobj_unset_t *)mp->b_cont->b_rptr;
875 	if (sup->su_name[DLD_SECOBJ_NAME_MAX - 1] != '\0')
876 		goto failed;
877 
878 	rw_enter(&drv_secobj_lock, RW_WRITER);
879 	err = mod_hash_find(drv_secobj_hash, (mod_hash_key_t)sup->su_name,
880 	    (mod_hash_val_t *)&objp);
881 	if (err != 0) {
882 		ASSERT(err == MH_ERR_NOTFOUND);
883 		err = ENOENT;
884 		rw_exit(&drv_secobj_lock);
885 		goto failed;
886 	}
887 	err = mod_hash_remove(drv_secobj_hash, (mod_hash_key_t)sup->su_name,
888 	    (mod_hash_val_t *)&val);
889 	ASSERT(err == 0);
890 	ASSERT(objp == (dld_secobj_t *)val);
891 
892 	kmem_cache_free(drv_secobj_cachep, objp);
893 	rw_exit(&drv_secobj_lock);
894 	miocack(q, mp, 0, 0);
895 	return;
896 
897 failed:
898 	ASSERT(err != 0);
899 	miocnak(q, mp, 0, err);
900 }
901