blob: 50df715aaf5681169b572000b972fe3c9917140b [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
Anders Persson7d64f412009-02-11 15:38:45 -080079/* Global kstats for sockparams */
80typedef struct sockparams_g_stats {
81 kstat_named_t spgs_ephem_nalloc;
82 kstat_named_t spgs_ephem_nreuse;
83} sockparams_g_stats_t;
84
85static sockparams_g_stats_t sp_g_stats;
86static kstat_t *sp_g_kstat;
87
88
Yu Xiangning0f1702c2008-12-11 20:04:13 -080089void
90sockparams_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);
Anders Persson7d64f412009-02-11 15:38:45 -080099
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
116static int
117sockparams_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 */
133static void
134sockparams_kstat_init(struct sockparams *sp)
135{
136 char name[KSTAT_STRLEN];
137
138 kstat_named_init(&sp->sp_stats.sps_nfallback, "nfallback",
139 KSTAT_DATA_UINT64);
140 kstat_named_init(&sp->sp_stats.sps_nactive, "nactive",
141 KSTAT_DATA_UINT64);
142 kstat_named_init(&sp->sp_stats.sps_ncreate, "ncreate",
143 KSTAT_DATA_UINT64);
144
145 (void) snprintf(name, KSTAT_STRLEN, "socket_%d_%d_%d", sp->sp_family,
146 sp->sp_type, sp->sp_protocol);
147
148 sp->sp_kstat = kstat_create("sockfs", 0, name, "misc", KSTAT_TYPE_NAMED,
149 sizeof (sockparams_stats_t) / sizeof (kstat_named_t),
150 KSTAT_FLAG_VIRTUAL);
151
152 if (sp->sp_kstat == NULL)
153 return;
154
155 sp->sp_kstat->ks_data = &sp->sp_stats;
156 sp->sp_kstat->ks_update = sockparams_kstat_update;
157 sp->sp_kstat->ks_private = sp;
158 kstat_install(sp->sp_kstat);
159}
160
161static void
162sockparams_kstat_fini(struct sockparams *sp)
163{
164 if (sp->sp_kstat != NULL) {
165 kstat_delete(sp->sp_kstat);
166 sp->sp_kstat = NULL;
167 }
Yu Xiangning0f1702c2008-12-11 20:04:13 -0800168}
169
170/*
171 * sockparams_create(int family, int type, int protocol, char *modname,
172 * char *devpath, int devpathlen, int flags, int kmflags, int *errorp)
173 *
174 * Create a new sockparams entry.
175 *
176 * Arguments:
177 * family, type, protocol: specifies the socket type
178 * modname: Name of the module associated with the socket type. The
179 * module can be NULL if a device path is given, in which
180 * case the TPI module is used.
181 * devpath: Path to the STREAMS device. May be NULL for non-STREAMS
182 * based transports, or those transports that do not provide
183 * the capability to fallback to STREAMS.
184 * devpathlen: Length of the devpath string. The argument can be 0,
185 * indicating that devpath was allocated statically, and should
186 * not be freed when the sockparams entry is destroyed.
187 *
188 * flags : SOCKPARAMS_EPHEMERAL is the only flag that is allowed.
189 * kmflags: KM_{NO,}SLEEP
190 * errorp : Value-return argument, set when an error occurs.
191 *
192 * Returns:
193 * On success a new sockparams entry is returned, and *errorp is set
194 * to 0. On failure NULL is returned and *errorp is set to indicate the
195 * type of error that occured.
196 *
197 * Notes:
198 * devpath and modname are freed upon failure.
199 */
200struct sockparams *
201sockparams_create(int family, int type, int protocol, char *modname,
202 char *devpath, int devpathlen, int flags, int kmflags, int *errorp)
203{
204 struct sockparams *sp = NULL;
205 size_t size;
206
207 ASSERT((flags & ~SOCKPARAMS_EPHEMERAL) == 0);
208 if (flags & ~SOCKPARAMS_EPHEMERAL) {
209 *errorp = EINVAL;
210 goto error;
211 }
212
213 /* either a module or device must be given */
214 if (modname == NULL && devpath == NULL) {
215 *errorp = EINVAL;
216 goto error;
217 }
218
219 sp = kmem_zalloc(sizeof (*sp), kmflags);
220 if (sp == NULL) {
221 *errorp = ENOMEM;
222 goto error;
223 }
224 sp->sp_family = family;
225 sp->sp_type = type;
226 sp->sp_protocol = protocol;
227 sp->sp_refcnt = 0;
228 sp->sp_flags = flags;
229
Anders Persson7d64f412009-02-11 15:38:45 -0800230 /*
231 * We do not create kstats for ephemeral entries, but we do keep
232 * track how many we have created.
233 */
234 if (sp->sp_flags & SOCKPARAMS_EPHEMERAL)
235 sp_g_stats.spgs_ephem_nalloc.value.ui64++;
236 else
237 sockparams_kstat_init(sp);
238
Yu Xiangning0f1702c2008-12-11 20:04:13 -0800239 if (modname != NULL) {
240 sp->sp_smod_name = modname;
241 } else {
242 size = strlen(SOTPI_SMOD_NAME) + 1;
243 modname = kmem_zalloc(size, kmflags);
244 if (modname == NULL) {
245 *errorp = ENOMEM;
246 goto error;
247 }
248 sp->sp_smod_name = modname;
249 (void) sprintf(sp->sp_smod_name, "%s", SOTPI_SMOD_NAME);
250 }
251
252 if (devpath != NULL) {
253 /* Set up the device entry. */
254 *errorp = sockparams_sdev_init(sp, devpath, devpathlen);
255 if (*errorp != 0)
256 goto error;
257 }
258
259 mutex_init(&sp->sp_lock, NULL, MUTEX_DEFAULT, NULL);
260 *errorp = 0;
261 return (sp);
262error:
263 ASSERT(*errorp != 0);
264 if (modname != NULL)
265 kmem_free(modname, strlen(modname) + 1);
266 if (devpathlen != 0)
267 kmem_free(devpath, devpathlen);
Anders Persson7d64f412009-02-11 15:38:45 -0800268 if (sp != NULL) {
269 sockparams_kstat_fini(sp);
Yu Xiangning0f1702c2008-12-11 20:04:13 -0800270 kmem_free(sp, sizeof (*sp));
Anders Persson7d64f412009-02-11 15:38:45 -0800271 }
Yu Xiangning0f1702c2008-12-11 20:04:13 -0800272 return (NULL);
273}
274
275/*
276 * Initialize the STREAMS device aspect of the sockparams entry.
277 */
278static int
279sockparams_sdev_init(struct sockparams *sp, char *devpath, int devpathlen)
280{
281 vnode_t *vp = NULL;
282 int error;
283
284 ASSERT(devpath != NULL);
285
286 if ((error = sogetvp(devpath, &vp, UIO_SYSSPACE)) != 0) {
287 dprint(0, ("sockparams_sdev_init: vp %s failed with %d\n",
288 devpath, error));
289 return (error);
290 }
291
292 ASSERT(vp != NULL);
293 sp->sp_sdev_info.sd_vnode = vp;
294 sp->sp_sdev_info.sd_devpath = devpath;
295 sp->sp_sdev_info.sd_devpathlen = devpathlen;
296
297 return (0);
298}
299
300/*
301 * sockparams_destroy(struct sockparams *sp)
302 *
303 * Releases all the resources associated with the sockparams entry,
304 * and frees the sockparams entry.
305 *
306 * Arguments:
307 * sp: the sockparams entry to destroy.
308 *
309 * Returns:
310 * Nothing.
311 *
312 * Locking:
313 * The sp_lock of the entry can not be held.
314 */
315void
316sockparams_destroy(struct sockparams *sp)
317{
318 ASSERT(sp->sp_refcnt == 0);
319 ASSERT(!list_link_active(&sp->sp_node));
320
321 sockparams_sdev_fini(sp);
322
323 if (sp->sp_smod_info != NULL)
324 SMOD_DEC_REF(sp, sp->sp_smod_info);
325 kmem_free(sp->sp_smod_name, strlen(sp->sp_smod_name) + 1);
326 sp->sp_smod_name = NULL;
327 sp->sp_smod_info = NULL;
328 mutex_destroy(&sp->sp_lock);
Anders Persson7d64f412009-02-11 15:38:45 -0800329 sockparams_kstat_fini(sp);
Yu Xiangning0f1702c2008-12-11 20:04:13 -0800330
331 kmem_free(sp, sizeof (*sp));
332}
333
334/*
335 * Clean up the STREAMS device part of the sockparams entry.
336 */
337static void
338sockparams_sdev_fini(struct sockparams *sp)
339{
340 sdev_info_t sd;
341
342 /*
343 * if the entry does not have a STREAMS device, then there
344 * is nothing to do.
345 */
346 if (!SOCKPARAMS_HAS_DEVICE(sp))
347 return;
348
349 sd = sp->sp_sdev_info;
350 if (sd.sd_vnode != NULL)
351 VN_RELE(sd.sd_vnode);
352 if (sd.sd_devpathlen != 0)
353 kmem_free(sd.sd_devpath, sd.sd_devpathlen);
354
355 sp->sp_sdev_info.sd_vnode = NULL;
356 sp->sp_sdev_info.sd_devpath = NULL;
357}
358
359/*
360 * Look for a matching sockparams entry on the given list.
Yu Xiangning0f1702c2008-12-11 20:04:13 -0800361 * The caller must hold the associated list lock.
362 */
363static struct sockparams *
364sockparams_find(list_t *list, int family, int type, int protocol,
shenjian22238f72009-01-07 13:45:08 +0800365 boolean_t by_devpath, const char *name)
Yu Xiangning0f1702c2008-12-11 20:04:13 -0800366{
367 struct sockparams *sp;
Yu Xiangning0f1702c2008-12-11 20:04:13 -0800368
369 for (sp = list_head(list); sp != NULL; sp = list_next(list, sp)) {
shenjian22238f72009-01-07 13:45:08 +0800370 if (sp->sp_family == family && sp->sp_type == type) {
Yu Xiangning0f1702c2008-12-11 20:04:13 -0800371 if (sp->sp_protocol == protocol) {
shenjian22238f72009-01-07 13:45:08 +0800372 if (name == NULL)
Yu Xiangning0f1702c2008-12-11 20:04:13 -0800373 break;
shenjian22238f72009-01-07 13:45:08 +0800374 else if (by_devpath &&
Yu Xiangning0f1702c2008-12-11 20:04:13 -0800375 sp->sp_sdev_info.sd_devpath != NULL &&
376 strcmp(sp->sp_sdev_info.sd_devpath,
377 name) == 0)
378 break;
shenjian22238f72009-01-07 13:45:08 +0800379 else if (strcmp(sp->sp_smod_name, name) == 0)
Yu Xiangning0f1702c2008-12-11 20:04:13 -0800380 break;
Yu Xiangning0f1702c2008-12-11 20:04:13 -0800381 }
382 }
383 }
shenjian22238f72009-01-07 13:45:08 +0800384 return (sp);
Yu Xiangning0f1702c2008-12-11 20:04:13 -0800385}
386
387/*
388 * sockparams_hold_ephemeral()
389 *
390 * Returns an ephemeral sockparams entry of the requested family, type and
391 * protocol. The entry is returned held, and the caller is responsible for
392 * dropping the reference using SOCKPARAMS_DEC_REF() once done.
393 *
394 * All ephemeral entries are on list (sp_ephem_list). If there is an
395 * entry on the list that match the search criteria, then a reference is
396 * placed on that entry. Otherwise, a new entry is created and inserted
397 * in the list. The entry is removed from the list when the last reference
398 * is dropped.
399 *
400 * The tpi flag is used to determine whether name refers to a device or
401 * module name.
402 */
403static struct sockparams *
404sockparams_hold_ephemeral(int family, int type, int protocol,
shenjian22238f72009-01-07 13:45:08 +0800405 const char *name, boolean_t by_devpath, int kmflag, int *errorp)
Yu Xiangning0f1702c2008-12-11 20:04:13 -0800406{
407 struct sockparams *sp = NULL;
Yu Xiangning0f1702c2008-12-11 20:04:13 -0800408 *errorp = 0;
409
410 /*
411 * First look for an existing entry
412 */
413 rw_enter(&sp_ephem_lock, RW_READER);
414 sp = sockparams_find(&sp_ephem_list, family, type, protocol,
shenjian22238f72009-01-07 13:45:08 +0800415 by_devpath, name);
Yu Xiangning0f1702c2008-12-11 20:04:13 -0800416 if (sp != NULL) {
417 SOCKPARAMS_INC_REF(sp);
418 rw_exit(&sp_ephem_lock);
Anders Persson7d64f412009-02-11 15:38:45 -0800419 sp_g_stats.spgs_ephem_nreuse.value.ui64++;
Yu Xiangning0f1702c2008-12-11 20:04:13 -0800420
421 return (sp);
422 } else {
423 struct sockparams *newsp = NULL;
424 char *namebuf = NULL;
425 int namelen = 0;
426
427 rw_exit(&sp_ephem_lock);
428
429 namelen = strlen(name) + 1;
430 namebuf = kmem_alloc(namelen, kmflag);
431 if (namebuf == NULL) {
432 *errorp = ENOMEM;
433 return (NULL);
434 }
435
436 (void *)strncpy(namebuf, name, namelen);
shenjian22238f72009-01-07 13:45:08 +0800437 if (by_devpath) {
Yu Xiangning0f1702c2008-12-11 20:04:13 -0800438 newsp = sockparams_create(family, type,
439 protocol, NULL, namebuf, namelen,
440 SOCKPARAMS_EPHEMERAL, kmflag, errorp);
441 } else {
442 newsp = sockparams_create(family, type,
443 protocol, namebuf, NULL, 0,
444 SOCKPARAMS_EPHEMERAL, kmflag, errorp);
445 }
446
447 if (newsp == NULL) {
448 ASSERT(*errorp != 0);
449 return (NULL);
450 }
451
452 /*
453 * Time to load the socket module.
454 */
455 ASSERT(newsp->sp_smod_info == NULL);
456 newsp->sp_smod_info =
457 smod_lookup_byname(newsp->sp_smod_name);
458 if (newsp->sp_smod_info == NULL) {
459 /* Failed to load */
460 sockparams_destroy(newsp);
461 *errorp = ENXIO;
462 return (NULL);
463 }
464
465 /*
466 * The sockparams entry was created, now try to add it
467 * to the list. We need to hold the lock as a WRITER.
468 */
469 rw_enter(&sp_ephem_lock, RW_WRITER);
470 sp = sockparams_find(&sp_ephem_list, family, type, protocol,
shenjian22238f72009-01-07 13:45:08 +0800471 by_devpath, name);
Yu Xiangning0f1702c2008-12-11 20:04:13 -0800472 if (sp != NULL) {
473 /*
474 * Someone has requested a matching entry, so just
475 * place a hold on it and release the entry we alloc'ed.
476 */
477 SOCKPARAMS_INC_REF(sp);
478 rw_exit(&sp_ephem_lock);
479
480 sockparams_destroy(newsp);
481 } else {
482 SOCKPARAMS_INC_REF(newsp);
483 list_insert_tail(&sp_ephem_list, newsp);
484 rw_exit(&sp_ephem_lock);
485
486 sp = newsp;
487 }
488 ASSERT(*errorp == 0);
489
490 return (sp);
491 }
492}
493
494struct sockparams *
495sockparams_hold_ephemeral_bydev(int family, int type, int protocol,
496 const char *dev, int kmflag, int *errorp)
497{
498 return (sockparams_hold_ephemeral(family, type, protocol, dev, B_TRUE,
499 kmflag, errorp));
500}
501
502struct sockparams *
503sockparams_hold_ephemeral_bymod(int family, int type, int protocol,
504 const char *mod, int kmflag, int *errorp)
505{
506 return (sockparams_hold_ephemeral(family, type, protocol, mod, B_FALSE,
507 kmflag, errorp));
508}
509
510/*
511 * Called when the last socket using the ephemeral entry is dropping
512 * its' reference. To maintain lock order we must drop the sockparams
513 * lock before calling this function. As a result, a new reference
514 * might be placed on the entry, in which case there is nothing to
515 * do. However, if ref count goes to zero, we delete the entry.
516 */
517void
518sockparams_ephemeral_drop_last_ref(struct sockparams *sp)
519{
520 ASSERT(sp->sp_flags & SOCKPARAMS_EPHEMERAL);
521 ASSERT(MUTEX_NOT_HELD(&sp->sp_lock));
522
523 rw_enter(&sp_ephem_lock, RW_WRITER);
524 mutex_enter(&sp->sp_lock);
525
526 if (--sp->sp_refcnt == 0) {
527 list_remove(&sp_ephem_list, sp);
528 mutex_exit(&sp->sp_lock);
529 rw_exit(&sp_ephem_lock);
530
531 sockparams_destroy(sp);
532 } else {
533 mutex_exit(&sp->sp_lock);
534 rw_exit(&sp_ephem_lock);
535 }
536}
537
538/*
539 * sockparams_add(struct sockparams *sp)
540 *
541 * Tries to add the given sockparams entry to the global list.
542 *
543 * Arguments:
544 * sp: the sockparms entry to add
545 *
546 * Returns:
547 * On success 0, but if an entry already exists, then EEXIST
548 * is returned.
549 *
550 * Locking:
551 * The caller can not be holding splist_lock.
552 */
553static int
554sockparams_add(struct sockparams *sp)
555{
556 ASSERT(!(sp->sp_flags & SOCKPARAMS_EPHEMERAL));
557
558 rw_enter(&splist_lock, RW_WRITER);
559 if (sockparams_find(&sphead, sp->sp_family, sp->sp_type,
shenjian22238f72009-01-07 13:45:08 +0800560 sp->sp_protocol, B_TRUE, NULL) != 0) {
Yu Xiangning0f1702c2008-12-11 20:04:13 -0800561 rw_exit(&splist_lock);
562 return (EEXIST);
563 } else {
564 list_insert_tail(&sphead, sp);
565 rw_exit(&splist_lock);
566 return (0);
567 }
568}
569
570/*
571 * sockparams_delete(int family, int type, int protocol)
572 *
573 * Marks the sockparams entry for a specific family, type and protocol
574 * for deletion. The entry is removed from the list and destroyed
575 * if no one is holding a reference to it.
576 *
577 * Arguments:
578 * family, type, protocol: the socket type that should be removed.
579 *
580 * Returns:
581 * On success 0, otherwise ENXIO.
582 *
583 * Locking:
584 * Caller can not be holding splist_lock or the sp_lock of
585 * any sockparams entry.
586 */
587static int
588sockparams_delete(int family, int type, int protocol)
589{
590 struct sockparams *sp;
591
592 rw_enter(&splist_lock, RW_WRITER);
shenjian22238f72009-01-07 13:45:08 +0800593 sp = sockparams_find(&sphead, family, type, protocol, B_TRUE, NULL);
Yu Xiangning0f1702c2008-12-11 20:04:13 -0800594
595 if (sp != NULL) {
596 /*
597 * If no one is holding a reference to the entry, then
598 * we go ahead and remove it from the list and then
599 * destroy it.
600 */
601 mutex_enter(&sp->sp_lock);
602 if (sp->sp_refcnt != 0) {
603 mutex_exit(&sp->sp_lock);
604 rw_exit(&splist_lock);
605 return (EBUSY);
606 }
607 mutex_exit(&sp->sp_lock);
608 /* Delete the sockparams entry. */
609 list_remove(&sphead, sp);
610 rw_exit(&splist_lock);
611
612 sockparams_destroy(sp);
613 return (0);
614 } else {
615 rw_exit(&splist_lock);
616 return (ENXIO);
617 }
618}
619
620/*
621 * soconfig(int family, int type, int protocol,
622 * char *devpath, int devpathlen, char *module)
623 *
624 * Add or delete an entry to the sockparams table.
625 * When devpath and module both are NULL, it will delete an entry.
626 *
627 * Arguments:
628 * family, type, protocol: the tuple in question
629 * devpath: STREAMS device path. Can be NULL for module based sockets.
630 * module : Name of the socket module. Can be NULL for STREAMS
631 * based sockets.
632 * devpathlen: length of the devpath string, or 0 if devpath
633 * was statically allocated.
634 *
635 * Note:
636 * This routine assumes that the caller has kmem_alloced
637 * devpath (if devpathlen > 0) and module for this routine to
638 * consume.
639 */
640int
641soconfig(int family, int type, int protocol,
642 char *devpath, int devpathlen, char *module)
643{
644 struct sockparams *sp;
645 int error = 0;
646
647 dprint(0, ("soconfig(%d,%d,%d,%s,%d,%s)\n",
648 family, type, protocol, devpath, devpathlen,
649 module == NULL ? "NULL" : module));
650
651 if (sockfs_defer_nl7c_init) {
652 nl7c_init();
653 sockfs_defer_nl7c_init = 0;
654 }
655
656 if (devpath == NULL && module == NULL) {
657 /*
658 * Delete existing entry,
659 * both socket module and STEAMS device.
660 */
661 ASSERT(module == NULL);
662 error = sockparams_delete(family, type, protocol);
663 } else {
664 /*
665 * Adding an entry
666 * sockparams_create frees mod name and devpath upon failure.
667 */
668 sp = sockparams_create(family, type, protocol, module,
669 devpath, devpathlen, 0, KM_SLEEP, &error);
670
671 if (sp != NULL) {
672 error = sockparams_add(sp);
673 if (error != 0)
674 sockparams_destroy(sp);
675 }
676 }
677
678 return (error);
679}
680
681/*
682 * solookup(int family, int type, int protocol, struct sockparams **spp)
683 *
684 * Lookup an entry in the sockparams list based on the triple. The returned
685 * entry either exactly match the given tuple, or it is the 'default' entry
686 * for the given <family, type>. A default entry is on with a protocol
687 * value of zero.
688 *
689 * Arguments:
690 * family, type, protocol: tuple to search for
691 * spp: Value-return argument
692 *
693 * Returns:
694 * If an entry is found, 0 is returned and *spp is set to point to the
695 * entry. In case an entry is not found, *spp is set to NULL, and an
696 * error code is returned. The errors are (in decreasing precedence):
697 * EAFNOSUPPORT - address family not in list
698 * EPROTONOSUPPORT - address family supported but not protocol.
699 * EPROTOTYPE - address family and protocol supported but not socket type.
700 *
701 * TODO: should use ddi_modopen()/ddi_modclose()
702 */
Yu Xiangning0f1702c2008-12-11 20:04:13 -0800703int
704solookup(int family, int type, int protocol, struct sockparams **spp)
705{
706 struct sockparams *sp = NULL;
707 int error = 0;
708
709 *spp = NULL;
710 rw_enter(&splist_lock, RW_READER);
711
712 /*
713 * Search the sockparams list for an appropiate entry.
714 * Hopefully we find an entry that match the exact family,
715 * type and protocol specified by the user, in which case
716 * we return that entry. However, we also keep track of
717 * the default entry for a specific family and type, the
718 * entry of which would have a protocol value of 0.
719 */
shenjian22238f72009-01-07 13:45:08 +0800720 sp = sockparams_find(&sphead, family, type, protocol, B_TRUE, NULL);
Yu Xiangning0f1702c2008-12-11 20:04:13 -0800721
722 if (sp == NULL) {
723 int found = 0;
724
725 /* Determine correct error code */
726 for (sp = list_head(&sphead); sp != NULL;
727 sp = list_next(&sphead, sp)) {
728 if (sp->sp_family == family && found < 1)
729 found = 1;
730 if (sp->sp_family == family &&
731 sp->sp_protocol == protocol && found < 2)
732 found = 2;
733 }
734 rw_exit(&splist_lock);
Yu Xiangning0f1702c2008-12-11 20:04:13 -0800735 switch (found) {
736 case 0:
737 error = EAFNOSUPPORT;
738 break;
739 case 1:
740 error = EPROTONOSUPPORT;
741 break;
742 case 2:
743 error = EPROTOTYPE;
744 break;
745 }
746 return (error);
747 }
748
749 /*
750 * An entry was found.
751 *
752 * We put a hold on the entry early on, so if the
753 * sockmod is not loaded, and we have to exit
754 * splist_lock to call modload(), we know that the
755 * sockparams entry wont go away. That way we don't
756 * have to look up the entry once we come back from
757 * modload().
758 */
759 SOCKPARAMS_INC_REF(sp);
760 rw_exit(&splist_lock);
761
762 if (sp->sp_smod_info == NULL) {
763 sp->sp_smod_info = smod_lookup_byname(sp->sp_smod_name);
764 if (sp->sp_smod_info == NULL) {
765 /*
766 * We put a hold on the sockparams entry
767 * earlier, hoping everything would work out.
768 * That obviously did not happen, so release
769 * the hold here.
770 */
771 SOCKPARAMS_DEC_REF(sp);
772 /*
773 * We should probably mark the sockparams as
774 * "bad", and redo the lookup skipping the
775 * "bad" entries. I.e., sp->sp_mod_state |= BAD,
776 * return (solookup(...))
777 */
778 return (ENXIO);
779 }
780 }
781
782 /*
783 * Alright, we have a valid sockparams entry.
784 */
785 *spp = sp;
786 return (0);
787}