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