xref: /titanic_52/usr/src/uts/common/fs/sockfs/sockparams.c (revision 8de5c4f463386063e184a851437d58080c6c626c)
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 /*
23  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
24  * Use is subject to license terms.
25  */
26 
27 #include <sys/types.h>
28 #include <sys/t_lock.h>
29 #include <sys/param.h>
30 #include <sys/systm.h>
31 #include <sys/sysmacros.h>
32 #include <sys/cmn_err.h>
33 #include <sys/list.h>
34 
35 #include <sys/stropts.h>
36 #include <sys/socket.h>
37 #include <sys/socketvar.h>
38 
39 #include <fs/sockfs/sockcommon.h>
40 #include <fs/sockfs/socktpi.h>
41 
42 /*
43  * Socket Parameters
44  *
45  * Socket parameter (struct sockparams) entries represent the socket types
46  * available on the system.
47  *
48  * Flags (sp_flags):
49  *
50  * SOCKPARAMS_EPHEMERAL: A temporary sockparams entry that will be deleted
51  * as soon as its' ref count drops to zero. In addition, ephemeral entries will
52  * never be hooked onto the global sockparams list. Ephemeral entries are
53  * created when application requests to create a socket using an application
54  * supplied device path, or when a socket is falling back to TPI.
55  *
56  * Lock order:
57  *   The lock order is splist_lock -> sp_lock.
58  *   The lock order is sp_ephem_lock -> sp_lock.
59  */
60 extern int 	kobj_path_exists(char *, int);
61 extern void	nl7c_init(void);
62 extern int	sockfs_defer_nl7c_init;
63 
64 static int 	sockparams_sdev_init(struct sockparams *, char *, int);
65 static void 	sockparams_sdev_fini(struct sockparams *);
66 
67 /*
68  * Global sockparams list (populated via soconfig(1M)).
69  */
70 static list_t sphead;
71 static krwlock_t splist_lock;
72 
73 /*
74  * List of ephemeral sockparams.
75  */
76 static list_t sp_ephem_list;
77 static krwlock_t sp_ephem_lock;
78 
79 /* Global kstats for sockparams */
80 typedef struct sockparams_g_stats {
81 	kstat_named_t spgs_ephem_nalloc;
82 	kstat_named_t spgs_ephem_nreuse;
83 } sockparams_g_stats_t;
84 
85 static sockparams_g_stats_t sp_g_stats;
86 static kstat_t *sp_g_kstat;
87 
88 
89 void
90 sockparams_init(void)
91 {
92 	list_create(&sphead, sizeof (struct sockparams),
93 	    offsetof(struct sockparams, sp_node));
94 	list_create(&sp_ephem_list, sizeof (struct sockparams),
95 	    offsetof(struct sockparams, sp_node));
96 
97 	rw_init(&splist_lock, NULL, RW_DEFAULT, NULL);
98 	rw_init(&sp_ephem_lock, NULL, RW_DEFAULT, NULL);
99 
100 	kstat_named_init(&sp_g_stats.spgs_ephem_nalloc, "ephemeral_nalloc",
101 	    KSTAT_DATA_UINT64);
102 	kstat_named_init(&sp_g_stats.spgs_ephem_nreuse, "ephemeral_nreuse",
103 	    KSTAT_DATA_UINT64);
104 
105 	sp_g_kstat = kstat_create("sockfs", 0, "sockparams", "misc",
106 	    KSTAT_TYPE_NAMED, sizeof (sp_g_stats) / sizeof (kstat_named_t),
107 	    KSTAT_FLAG_VIRTUAL);
108 	if (sp_g_kstat == NULL)
109 		return;
110 
111 	sp_g_kstat->ks_data = &sp_g_stats;
112 
113 	kstat_install(sp_g_kstat);
114 }
115 
116 static int
117 sockparams_kstat_update(kstat_t *ksp, int rw)
118 {
119 	struct sockparams *sp = ksp->ks_private;
120 	sockparams_stats_t *sps = ksp->ks_data;
121 
122 	if (rw == KSTAT_WRITE)
123 		return (EACCES);
124 
125 	sps->sps_nactive.value.ui64 = sp->sp_refcnt;
126 
127 	return (0);
128 }
129 
130 /*
131  * Setup kstats for the given sockparams entry.
132  */
133 static void
134 sockparams_kstat_init(struct sockparams *sp)
135 {
136 	char name[KSTAT_STRLEN];
137 
138 	(void) snprintf(name, KSTAT_STRLEN, "socket_%d_%d_%d", sp->sp_family,
139 	    sp->sp_type, sp->sp_protocol);
140 
141 	sp->sp_kstat = kstat_create("sockfs", 0, name, "misc", KSTAT_TYPE_NAMED,
142 	    sizeof (sockparams_stats_t) / sizeof (kstat_named_t),
143 	    KSTAT_FLAG_VIRTUAL);
144 
145 	if (sp->sp_kstat == NULL)
146 		return;
147 
148 	sp->sp_kstat->ks_data = &sp->sp_stats;
149 	sp->sp_kstat->ks_update = sockparams_kstat_update;
150 	sp->sp_kstat->ks_private = sp;
151 	kstat_install(sp->sp_kstat);
152 }
153 
154 static void
155 sockparams_kstat_fini(struct sockparams *sp)
156 {
157 	if (sp->sp_kstat != NULL) {
158 		kstat_delete(sp->sp_kstat);
159 		sp->sp_kstat = NULL;
160 	}
161 }
162 
163 /*
164  * sockparams_create(int family, int type, int protocol, char *modname,
165  *     char *devpath, int devpathlen, int flags, int kmflags, int *errorp)
166  *
167  * Create a new sockparams entry.
168  *
169  * Arguments:
170  *   family, type, protocol: specifies the socket type
171  *   modname: Name of the module associated with the socket type. The
172  *            module can be NULL if a device path is given, in which
173  *            case the TPI module is used.
174  *   devpath: Path to the STREAMS device. May be NULL for non-STREAMS
175  *            based transports, or those transports that do not provide
176  *            the capability to fallback to STREAMS.
177  *   devpathlen: Length of the devpath string. The argument can be 0,
178  *            indicating that devpath was allocated statically, and should
179  *            not be freed when the sockparams entry is destroyed.
180  *
181  *   flags  : SOCKPARAMS_EPHEMERAL is the only flag that is allowed.
182  *   kmflags: KM_{NO,}SLEEP
183  *   errorp : Value-return argument, set when an error occurs.
184  *
185  * Returns:
186  *   On success a new sockparams entry is returned, and *errorp is set
187  *   to 0. On failure NULL is returned and *errorp is set to indicate the
188  *   type of error that occured.
189  *
190  * Notes:
191  *   devpath and modname are freed upon failure.
192  */
193 struct sockparams *
194 sockparams_create(int family, int type, int protocol, char *modname,
195     char *devpath, int devpathlen, int flags, int kmflags, int *errorp)
196 {
197 	struct sockparams *sp = NULL;
198 	size_t size;
199 
200 	ASSERT((flags & ~SOCKPARAMS_EPHEMERAL) == 0);
201 	if (flags & ~SOCKPARAMS_EPHEMERAL) {
202 		*errorp = EINVAL;
203 		goto error;
204 	}
205 
206 	/* either a module or device must be given */
207 	if (modname == NULL && devpath == NULL) {
208 		*errorp = EINVAL;
209 		goto error;
210 	}
211 
212 	sp = kmem_zalloc(sizeof (*sp), kmflags);
213 	if (sp == NULL) {
214 		*errorp = ENOMEM;
215 		goto error;
216 	}
217 	sp->sp_family = family;
218 	sp->sp_type = type;
219 	sp->sp_protocol = protocol;
220 	sp->sp_refcnt = 0;
221 	sp->sp_flags = flags;
222 
223 	kstat_named_init(&sp->sp_stats.sps_nfallback, "nfallback",
224 	    KSTAT_DATA_UINT64);
225 	kstat_named_init(&sp->sp_stats.sps_nactive, "nactive",
226 	    KSTAT_DATA_UINT64);
227 	kstat_named_init(&sp->sp_stats.sps_ncreate, "ncreate",
228 	    KSTAT_DATA_UINT64);
229 
230 	/*
231 	 * Track how many ephemeral entries we have created.
232 	 */
233 	if (sp->sp_flags & SOCKPARAMS_EPHEMERAL)
234 		sp_g_stats.spgs_ephem_nalloc.value.ui64++;
235 
236 	if (modname != NULL) {
237 		sp->sp_smod_name = modname;
238 	} else {
239 		size = strlen(SOTPI_SMOD_NAME) + 1;
240 		modname = kmem_zalloc(size, kmflags);
241 		if (modname == NULL) {
242 			*errorp = ENOMEM;
243 			goto error;
244 		}
245 		sp->sp_smod_name = modname;
246 		(void) sprintf(sp->sp_smod_name, "%s", SOTPI_SMOD_NAME);
247 	}
248 
249 	if (devpath != NULL) {
250 		/* Set up the device entry. */
251 		*errorp = sockparams_sdev_init(sp, devpath, devpathlen);
252 		if (*errorp != 0)
253 			goto error;
254 	}
255 
256 	mutex_init(&sp->sp_lock, NULL, MUTEX_DEFAULT, NULL);
257 	*errorp = 0;
258 	return (sp);
259 error:
260 	ASSERT(*errorp != 0);
261 	if (modname != NULL)
262 		kmem_free(modname, strlen(modname) + 1);
263 	if (devpathlen != 0)
264 		kmem_free(devpath, devpathlen);
265 	if (sp != NULL)
266 		kmem_free(sp, sizeof (*sp));
267 	return (NULL);
268 }
269 
270 /*
271  * Initialize the STREAMS device aspect of the sockparams entry.
272  */
273 static int
274 sockparams_sdev_init(struct sockparams *sp, char *devpath, int devpathlen)
275 {
276 	vnode_t *vp = NULL;
277 	int error;
278 
279 	ASSERT(devpath != NULL);
280 
281 	if ((error = sogetvp(devpath, &vp, UIO_SYSSPACE)) != 0) {
282 		dprint(0, ("sockparams_sdev_init: vp %s failed with %d\n",
283 		    devpath, error));
284 		return (error);
285 	}
286 
287 	ASSERT(vp != NULL);
288 	sp->sp_sdev_info.sd_vnode = vp;
289 	sp->sp_sdev_info.sd_devpath = devpath;
290 	sp->sp_sdev_info.sd_devpathlen = devpathlen;
291 
292 	return (0);
293 }
294 
295 /*
296  * sockparams_destroy(struct sockparams *sp)
297  *
298  * Releases all the resources associated with the sockparams entry,
299  * and frees the sockparams entry.
300  *
301  * Arguments:
302  *   sp: the sockparams entry to destroy.
303  *
304  * Returns:
305  *   Nothing.
306  *
307  * Locking:
308  *   The sp_lock of the entry can not be held.
309  */
310 void
311 sockparams_destroy(struct sockparams *sp)
312 {
313 	ASSERT(sp->sp_refcnt == 0);
314 	ASSERT(!list_link_active(&sp->sp_node));
315 
316 	sockparams_sdev_fini(sp);
317 
318 	if (sp->sp_smod_info != NULL)
319 		SMOD_DEC_REF(sp, sp->sp_smod_info);
320 	kmem_free(sp->sp_smod_name, strlen(sp->sp_smod_name) + 1);
321 	sp->sp_smod_name = NULL;
322 	sp->sp_smod_info = NULL;
323 	mutex_destroy(&sp->sp_lock);
324 	sockparams_kstat_fini(sp);
325 
326 	kmem_free(sp, sizeof (*sp));
327 }
328 
329 /*
330  * Clean up the STREAMS device part of the sockparams entry.
331  */
332 static void
333 sockparams_sdev_fini(struct sockparams *sp)
334 {
335 	sdev_info_t sd;
336 
337 	/*
338 	 * if the entry does not have a STREAMS device, then there
339 	 * is nothing to do.
340 	 */
341 	if (!SOCKPARAMS_HAS_DEVICE(sp))
342 		return;
343 
344 	sd = sp->sp_sdev_info;
345 	if (sd.sd_vnode != NULL)
346 		VN_RELE(sd.sd_vnode);
347 	if (sd.sd_devpathlen != 0)
348 		kmem_free(sd.sd_devpath, sd.sd_devpathlen);
349 
350 	sp->sp_sdev_info.sd_vnode = NULL;
351 	sp->sp_sdev_info.sd_devpath = NULL;
352 }
353 
354 /*
355  * Look for a matching sockparams entry on the given list.
356  * The caller must hold the associated list lock.
357  */
358 static struct sockparams *
359 sockparams_find(list_t *list, int family, int type, int protocol,
360     boolean_t by_devpath, const char *name)
361 {
362 	struct sockparams *sp;
363 
364 	for (sp = list_head(list); sp != NULL; sp = list_next(list, sp)) {
365 		if (sp->sp_family == family && sp->sp_type == type) {
366 			if (sp->sp_protocol == protocol) {
367 				if (name == NULL)
368 					break;
369 				else if (by_devpath &&
370 				    sp->sp_sdev_info.sd_devpath != NULL &&
371 				    strcmp(sp->sp_sdev_info.sd_devpath,
372 				    name) == 0)
373 					break;
374 				else if (strcmp(sp->sp_smod_name, name) == 0)
375 					break;
376 			}
377 		}
378 	}
379 	return (sp);
380 }
381 
382 /*
383  * sockparams_hold_ephemeral()
384  *
385  * Returns an ephemeral sockparams entry of the requested family, type and
386  * protocol. The entry is returned held, and the caller is responsible for
387  * dropping the reference using SOCKPARAMS_DEC_REF() once done.
388  *
389  * All ephemeral entries are on list (sp_ephem_list). If there is an
390  * entry on the list that match the search criteria, then a reference is
391  * placed on that entry. Otherwise, a new entry is created and inserted
392  * in the list. The entry is removed from the list when the last reference
393  * is dropped.
394  *
395  * The tpi flag is used to determine whether name refers to a device or
396  * module name.
397  */
398 static struct sockparams *
399 sockparams_hold_ephemeral(int family, int type, int protocol,
400     const char *name, boolean_t by_devpath, int kmflag, int *errorp)
401 {
402 	struct sockparams *sp = NULL;
403 	*errorp = 0;
404 
405 	/*
406 	 * First look for an existing entry
407 	 */
408 	rw_enter(&sp_ephem_lock, RW_READER);
409 	sp = sockparams_find(&sp_ephem_list, family, type, protocol,
410 	    by_devpath, name);
411 	if (sp != NULL) {
412 		SOCKPARAMS_INC_REF(sp);
413 		rw_exit(&sp_ephem_lock);
414 		sp_g_stats.spgs_ephem_nreuse.value.ui64++;
415 
416 		return (sp);
417 	} else {
418 		struct sockparams *newsp = NULL;
419 		char *namebuf = NULL;
420 		int namelen = 0;
421 
422 		rw_exit(&sp_ephem_lock);
423 
424 		namelen = strlen(name) + 1;
425 		namebuf = kmem_alloc(namelen, kmflag);
426 		if (namebuf == NULL) {
427 			*errorp = ENOMEM;
428 			return (NULL);
429 		}
430 
431 		(void *)strncpy(namebuf, name, namelen);
432 		if (by_devpath) {
433 			newsp = sockparams_create(family, type,
434 			    protocol, NULL, namebuf, namelen,
435 			    SOCKPARAMS_EPHEMERAL, kmflag, errorp);
436 		} else {
437 			newsp = sockparams_create(family, type,
438 			    protocol, namebuf, NULL, 0,
439 			    SOCKPARAMS_EPHEMERAL, kmflag, errorp);
440 		}
441 
442 		if (newsp == NULL) {
443 			ASSERT(*errorp != 0);
444 			return (NULL);
445 		}
446 
447 		/*
448 		 * Time to load the socket module.
449 		 */
450 		ASSERT(newsp->sp_smod_info == NULL);
451 		newsp->sp_smod_info =
452 		    smod_lookup_byname(newsp->sp_smod_name);
453 		if (newsp->sp_smod_info == NULL) {
454 			/* Failed to load */
455 			sockparams_destroy(newsp);
456 			*errorp = ENXIO;
457 			return (NULL);
458 		}
459 
460 		/*
461 		 * The sockparams entry was created, now try to add it
462 		 * to the list. We need to hold the lock as a WRITER.
463 		 */
464 		rw_enter(&sp_ephem_lock, RW_WRITER);
465 		sp = sockparams_find(&sp_ephem_list, family, type, protocol,
466 		    by_devpath, name);
467 		if (sp != NULL) {
468 			/*
469 			 * Someone has requested a matching entry, so just
470 			 * place a hold on it and release the entry we alloc'ed.
471 			 */
472 			SOCKPARAMS_INC_REF(sp);
473 			rw_exit(&sp_ephem_lock);
474 
475 			sockparams_destroy(newsp);
476 		} else {
477 			SOCKPARAMS_INC_REF(newsp);
478 			list_insert_tail(&sp_ephem_list, newsp);
479 			rw_exit(&sp_ephem_lock);
480 
481 			sp = newsp;
482 		}
483 		ASSERT(*errorp == 0);
484 
485 		return (sp);
486 	}
487 }
488 
489 struct sockparams *
490 sockparams_hold_ephemeral_bydev(int family, int type, int protocol,
491     const char *dev, int kmflag, int *errorp)
492 {
493 	return (sockparams_hold_ephemeral(family, type, protocol, dev, B_TRUE,
494 	    kmflag, errorp));
495 }
496 
497 struct sockparams *
498 sockparams_hold_ephemeral_bymod(int family, int type, int protocol,
499     const char *mod, int kmflag, int *errorp)
500 {
501 	return (sockparams_hold_ephemeral(family, type, protocol, mod, B_FALSE,
502 	    kmflag, errorp));
503 }
504 
505 /*
506  * Called when the last socket using the ephemeral entry is dropping
507  * its' reference. To maintain lock order we must drop the sockparams
508  * lock before calling this function. As a result, a new reference
509  * might be placed on the entry, in which case there is nothing to
510  * do. However, if ref count goes to zero, we delete the entry.
511  */
512 void
513 sockparams_ephemeral_drop_last_ref(struct sockparams *sp)
514 {
515 	ASSERT(sp->sp_flags & SOCKPARAMS_EPHEMERAL);
516 	ASSERT(MUTEX_NOT_HELD(&sp->sp_lock));
517 
518 	rw_enter(&sp_ephem_lock, RW_WRITER);
519 	mutex_enter(&sp->sp_lock);
520 
521 	if (--sp->sp_refcnt == 0) {
522 		list_remove(&sp_ephem_list, sp);
523 		mutex_exit(&sp->sp_lock);
524 		rw_exit(&sp_ephem_lock);
525 
526 		sockparams_destroy(sp);
527 	} else {
528 		mutex_exit(&sp->sp_lock);
529 		rw_exit(&sp_ephem_lock);
530 	}
531 }
532 
533 /*
534  * sockparams_add(struct sockparams *sp)
535  *
536  * Tries to add the given sockparams entry to the global list.
537  *
538  * Arguments:
539  *   sp: the sockparms entry to add
540  *
541  * Returns:
542  *   On success 0, but if an entry already exists, then EEXIST
543  *   is returned.
544  *
545  * Locking:
546  *   The caller can not be holding splist_lock.
547  */
548 static int
549 sockparams_add(struct sockparams *sp)
550 {
551 	ASSERT(!(sp->sp_flags & SOCKPARAMS_EPHEMERAL));
552 
553 	rw_enter(&splist_lock, RW_WRITER);
554 	if (sockparams_find(&sphead, sp->sp_family, sp->sp_type,
555 	    sp->sp_protocol, B_TRUE, NULL) != 0) {
556 		rw_exit(&splist_lock);
557 		return (EEXIST);
558 	} else {
559 		list_insert_tail(&sphead, sp);
560 		rw_exit(&splist_lock);
561 		return (0);
562 	}
563 }
564 
565 /*
566  * sockparams_delete(int family, int type, int protocol)
567  *
568  * Marks the sockparams entry for a specific family, type and protocol
569  * for deletion. The entry is removed from the list and destroyed
570  * if no one is holding a reference to it.
571  *
572  * Arguments:
573  *   family, type, protocol: the socket type that should be removed.
574  *
575  * Returns:
576  *   On success 0, otherwise ENXIO.
577  *
578  * Locking:
579  *   Caller can not be holding splist_lock or the sp_lock of
580  *   any sockparams entry.
581  */
582 static int
583 sockparams_delete(int family, int type, int protocol)
584 {
585 	struct sockparams *sp;
586 
587 	rw_enter(&splist_lock, RW_WRITER);
588 	sp = sockparams_find(&sphead, family, type, protocol, B_TRUE, NULL);
589 
590 	if (sp != NULL) {
591 		/*
592 		 * If no one is holding a reference to the entry, then
593 		 * we go ahead and remove it from the list and then
594 		 * destroy it.
595 		 */
596 		mutex_enter(&sp->sp_lock);
597 		if (sp->sp_refcnt != 0) {
598 			mutex_exit(&sp->sp_lock);
599 			rw_exit(&splist_lock);
600 			return (EBUSY);
601 		}
602 		mutex_exit(&sp->sp_lock);
603 		/* Delete the sockparams entry. */
604 		list_remove(&sphead, sp);
605 		rw_exit(&splist_lock);
606 
607 		sockparams_destroy(sp);
608 		return (0);
609 	} else {
610 		rw_exit(&splist_lock);
611 		return (ENXIO);
612 	}
613 }
614 
615 /*
616  * soconfig(int family, int type, int protocol,
617  *     char *devpath, int devpathlen, char *module)
618  *
619  * Add or delete an entry to the sockparams table.
620  * When devpath and module both are NULL, it will delete an entry.
621  *
622  * Arguments:
623  *   family, type, protocol: the tuple in question
624  *   devpath: STREAMS device path. Can be NULL for module based sockets.
625  *   module : Name of the socket module. Can be NULL for STREAMS
626  *            based sockets.
627  *   devpathlen: length of the devpath string, or 0 if devpath
628  *            was statically allocated.
629  *
630  * Note:
631  *   This routine assumes that the caller has kmem_alloced
632  *   devpath (if devpathlen > 0) and module for this routine to
633  *   consume.
634  */
635 int
636 soconfig(int family, int type, int protocol,
637     char *devpath, int devpathlen, char *module)
638 {
639 	struct sockparams *sp;
640 	int error = 0;
641 
642 	dprint(0, ("soconfig(%d,%d,%d,%s,%d,%s)\n",
643 	    family, type, protocol, devpath, devpathlen,
644 	    module == NULL ? "NULL" : module));
645 
646 	if (sockfs_defer_nl7c_init) {
647 		nl7c_init();
648 		sockfs_defer_nl7c_init = 0;
649 	}
650 
651 	if (devpath == NULL && module == NULL) {
652 		/*
653 		 * Delete existing entry,
654 		 * both socket module and STEAMS device.
655 		 */
656 		ASSERT(module == NULL);
657 		error = sockparams_delete(family, type, protocol);
658 	} else {
659 		/*
660 		 * Adding an entry
661 		 * sockparams_create frees mod name and devpath upon failure.
662 		 */
663 		sp = sockparams_create(family, type, protocol, module,
664 		    devpath, devpathlen, 0, KM_SLEEP, &error);
665 
666 		if (sp != NULL) {
667 			/*
668 			 * The sockparams entry becomes globally visible once
669 			 * we call sockparams_add(). So we add a reference so
670 			 * we do not have to worry about the entry being
671 			 * immediately deleted.
672 			 */
673 			SOCKPARAMS_INC_REF(sp);
674 			error = sockparams_add(sp);
675 			if (error != 0) {
676 				SOCKPARAMS_DEC_REF(sp);
677 				sockparams_destroy(sp);
678 			} else {
679 				/*
680 				 * Unique sockparams entry, so init the kstats.
681 				 */
682 				sockparams_kstat_init(sp);
683 				SOCKPARAMS_DEC_REF(sp);
684 			}
685 		}
686 	}
687 
688 	return (error);
689 }
690 
691 /*
692  * solookup(int family, int type, int protocol, struct sockparams **spp)
693  *
694  * Lookup an entry in the sockparams list based on the triple. The returned
695  * entry either exactly match the given tuple, or it is the 'default' entry
696  * for the given <family, type>. A default entry is on with a protocol
697  * value of zero.
698  *
699  * Arguments:
700  *   family, type, protocol: tuple to search for
701  *   spp: Value-return argument
702  *
703  * Returns:
704  *   If an entry is found, 0 is returned and *spp is set to point to the
705  *   entry. In case an entry is not found, *spp is set to NULL, and an
706  *   error code is returned. The errors are (in decreasing precedence):
707  *	EAFNOSUPPORT - address family not in list
708  *	EPROTONOSUPPORT - address family supported but not protocol.
709  *	EPROTOTYPE - address family and protocol supported but not socket type.
710  *
711  * TODO: should use ddi_modopen()/ddi_modclose()
712  */
713 int
714 solookup(int family, int type, int protocol, struct sockparams **spp)
715 {
716 	struct sockparams *sp = NULL;
717 	int error = 0;
718 
719 	*spp = NULL;
720 	rw_enter(&splist_lock, RW_READER);
721 
722 	/*
723 	 * Search the sockparams list for an appropiate entry.
724 	 * Hopefully we find an entry that match the exact family,
725 	 * type and protocol specified by the user, in which case
726 	 * we return that entry. However, we also keep track of
727 	 * the default entry for a specific family and type, the
728 	 * entry of which would have a protocol value of 0.
729 	 */
730 	sp = sockparams_find(&sphead, family, type, protocol, B_TRUE, NULL);
731 
732 	if (sp == NULL) {
733 		int found = 0;
734 
735 		/* Determine correct error code */
736 		for (sp = list_head(&sphead); sp != NULL;
737 		    sp = list_next(&sphead, sp)) {
738 			if (sp->sp_family == family && found < 1)
739 				found = 1;
740 			if (sp->sp_family == family &&
741 			    sp->sp_protocol == protocol && found < 2)
742 				found = 2;
743 		}
744 		rw_exit(&splist_lock);
745 		switch (found) {
746 		case 0:
747 			error = EAFNOSUPPORT;
748 			break;
749 		case 1:
750 			error = EPROTONOSUPPORT;
751 			break;
752 		case 2:
753 			error = EPROTOTYPE;
754 			break;
755 		}
756 		return (error);
757 	}
758 
759 	/*
760 	 * An entry was found.
761 	 *
762 	 * We put a hold on the entry early on, so if the
763 	 * sockmod is not loaded, and we have to exit
764 	 * splist_lock to call modload(), we know that the
765 	 * sockparams entry wont go away. That way we don't
766 	 * have to look up the entry once we come back from
767 	 * modload().
768 	 */
769 	SOCKPARAMS_INC_REF(sp);
770 	rw_exit(&splist_lock);
771 
772 	if (sp->sp_smod_info == NULL) {
773 		sp->sp_smod_info = smod_lookup_byname(sp->sp_smod_name);
774 		if (sp->sp_smod_info == NULL) {
775 			/*
776 			 * We put a hold on the sockparams entry
777 			 * earlier, hoping everything would work out.
778 			 * That obviously did not happen, so release
779 			 * the hold here.
780 			 */
781 			SOCKPARAMS_DEC_REF(sp);
782 			/*
783 			 * We should probably mark the sockparams as
784 			 * "bad", and redo the lookup skipping the
785 			 * "bad" entries. I.e., sp->sp_mod_state |= BAD,
786 			 * return (solookup(...))
787 			 */
788 			return (ENXIO);
789 		}
790 	}
791 
792 	/*
793 	 * Alright, we have a valid sockparams entry.
794 	 */
795 	*spp = sp;
796 	return (0);
797 }
798