blob: e20870d3460911d3d1078ce72d66daa80bd7fdbe [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
Anders Persson7d64f412009-02-11 15:38:45 -0800138 (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
154static void
155sockparams_kstat_fini(struct sockparams *sp)
156{
157 if (sp->sp_kstat != NULL) {
158 kstat_delete(sp->sp_kstat);
159 sp->sp_kstat = NULL;
160 }
Yu Xiangning0f1702c2008-12-11 20:04:13 -0800161}
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 */
193struct sockparams *
194sockparams_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
Anders Persson51899d82009-03-04 15:03:04 -0800223 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
Anders Persson7d64f412009-02-11 15:38:45 -0800230 /*
Anders Persson51899d82009-03-04 15:03:04 -0800231 * Track how many ephemeral entries we have created.
Anders Persson7d64f412009-02-11 15:38:45 -0800232 */
233 if (sp->sp_flags & SOCKPARAMS_EPHEMERAL)
234 sp_g_stats.spgs_ephem_nalloc.value.ui64++;
Anders Persson7d64f412009-02-11 15:38:45 -0800235
Yu Xiangning0f1702c2008-12-11 20:04:13 -0800236 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);
259error:
260 ASSERT(*errorp != 0);
261 if (modname != NULL)
262 kmem_free(modname, strlen(modname) + 1);
263 if (devpathlen != 0)
264 kmem_free(devpath, devpathlen);
Anders Persson51899d82009-03-04 15:03:04 -0800265 if (sp != NULL)
Yu Xiangning0f1702c2008-12-11 20:04:13 -0800266 kmem_free(sp, sizeof (*sp));
267 return (NULL);
268}
269
270/*
271 * Initialize the STREAMS device aspect of the sockparams entry.
272 */
273static int
274sockparams_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 */
310void
311sockparams_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);
Anders Persson7d64f412009-02-11 15:38:45 -0800324 sockparams_kstat_fini(sp);
Yu Xiangning0f1702c2008-12-11 20:04:13 -0800325
326 kmem_free(sp, sizeof (*sp));
327}
328
329/*
330 * Clean up the STREAMS device part of the sockparams entry.
331 */
332static void
333sockparams_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.
Yu Xiangning0f1702c2008-12-11 20:04:13 -0800356 * The caller must hold the associated list lock.
357 */
358static struct sockparams *
359sockparams_find(list_t *list, int family, int type, int protocol,
shenjian22238f72009-01-07 13:45:08 +0800360 boolean_t by_devpath, const char *name)
Yu Xiangning0f1702c2008-12-11 20:04:13 -0800361{
362 struct sockparams *sp;
Yu Xiangning0f1702c2008-12-11 20:04:13 -0800363
364 for (sp = list_head(list); sp != NULL; sp = list_next(list, sp)) {
shenjian22238f72009-01-07 13:45:08 +0800365 if (sp->sp_family == family && sp->sp_type == type) {
Yu Xiangning0f1702c2008-12-11 20:04:13 -0800366 if (sp->sp_protocol == protocol) {
shenjian22238f72009-01-07 13:45:08 +0800367 if (name == NULL)
Yu Xiangning0f1702c2008-12-11 20:04:13 -0800368 break;
shenjian22238f72009-01-07 13:45:08 +0800369 else if (by_devpath &&
Yu Xiangning0f1702c2008-12-11 20:04:13 -0800370 sp->sp_sdev_info.sd_devpath != NULL &&
371 strcmp(sp->sp_sdev_info.sd_devpath,
372 name) == 0)
373 break;
shenjian22238f72009-01-07 13:45:08 +0800374 else if (strcmp(sp->sp_smod_name, name) == 0)
Yu Xiangning0f1702c2008-12-11 20:04:13 -0800375 break;
Yu Xiangning0f1702c2008-12-11 20:04:13 -0800376 }
377 }
378 }
shenjian22238f72009-01-07 13:45:08 +0800379 return (sp);
Yu Xiangning0f1702c2008-12-11 20:04:13 -0800380}
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 */
398static struct sockparams *
399sockparams_hold_ephemeral(int family, int type, int protocol,
shenjian22238f72009-01-07 13:45:08 +0800400 const char *name, boolean_t by_devpath, int kmflag, int *errorp)
Yu Xiangning0f1702c2008-12-11 20:04:13 -0800401{
402 struct sockparams *sp = NULL;
Yu Xiangning0f1702c2008-12-11 20:04:13 -0800403 *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,
shenjian22238f72009-01-07 13:45:08 +0800410 by_devpath, name);
Yu Xiangning0f1702c2008-12-11 20:04:13 -0800411 if (sp != NULL) {
412 SOCKPARAMS_INC_REF(sp);
413 rw_exit(&sp_ephem_lock);
Anders Persson7d64f412009-02-11 15:38:45 -0800414 sp_g_stats.spgs_ephem_nreuse.value.ui64++;
Yu Xiangning0f1702c2008-12-11 20:04:13 -0800415
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);
shenjian22238f72009-01-07 13:45:08 +0800432 if (by_devpath) {
Yu Xiangning0f1702c2008-12-11 20:04:13 -0800433 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,
shenjian22238f72009-01-07 13:45:08 +0800466 by_devpath, name);
Yu Xiangning0f1702c2008-12-11 20:04:13 -0800467 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
489struct sockparams *
490sockparams_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
497struct sockparams *
498sockparams_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 */
512void
513sockparams_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 */
548static int
549sockparams_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,
shenjian22238f72009-01-07 13:45:08 +0800555 sp->sp_protocol, B_TRUE, NULL) != 0) {
Yu Xiangning0f1702c2008-12-11 20:04:13 -0800556 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 */
582static int
583sockparams_delete(int family, int type, int protocol)
584{
585 struct sockparams *sp;
586
587 rw_enter(&splist_lock, RW_WRITER);
shenjian22238f72009-01-07 13:45:08 +0800588 sp = sockparams_find(&sphead, family, type, protocol, B_TRUE, NULL);
Yu Xiangning0f1702c2008-12-11 20:04:13 -0800589
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 */
635int
636soconfig(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) {
Anders Persson51899d82009-03-04 15:03:04 -0800667 /*
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);
Yu Xiangning0f1702c2008-12-11 20:04:13 -0800674 error = sockparams_add(sp);
Anders Persson51899d82009-03-04 15:03:04 -0800675 if (error != 0) {
676 SOCKPARAMS_DEC_REF(sp);
Yu Xiangning0f1702c2008-12-11 20:04:13 -0800677 sockparams_destroy(sp);
Anders Persson51899d82009-03-04 15:03:04 -0800678 } else {
679 /*
680 * Unique sockparams entry, so init the kstats.
681 */
682 sockparams_kstat_init(sp);
683 SOCKPARAMS_DEC_REF(sp);
684 }
Yu Xiangning0f1702c2008-12-11 20:04:13 -0800685 }
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 */
Yu Xiangning0f1702c2008-12-11 20:04:13 -0800713int
714solookup(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 */
shenjian22238f72009-01-07 13:45:08 +0800730 sp = sockparams_find(&sphead, family, type, protocol, B_TRUE, NULL);
Yu Xiangning0f1702c2008-12-11 20:04:13 -0800731
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);
Yu Xiangning0f1702c2008-12-11 20:04:13 -0800745 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}