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