blob: 60a1a1580c96c3a49676a2c6cab28347e2b46dcb [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/*
Anders Persson5f1fdc12010-04-26 14:42:41 -070023 * Copyright (c) 1996, 2010, Oracle and/or its affiliates. All rights reserved.
Yu Xiangning0f1702c2008-12-11 20:04:13 -080024 */
25
26#include <sys/types.h>
27#include <sys/t_lock.h>
28#include <sys/param.h>
29#include <sys/systm.h>
30#include <sys/sysmacros.h>
31#include <sys/cmn_err.h>
32#include <sys/list.h>
33
34#include <sys/stropts.h>
35#include <sys/socket.h>
36#include <sys/socketvar.h>
37
38#include <fs/sockfs/sockcommon.h>
Anders Persson3e95bd42010-06-17 17:22:09 -070039#include <fs/sockfs/sockfilter_impl.h>
Yu Xiangning0f1702c2008-12-11 20:04:13 -080040#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:
Anders Persson3e95bd42010-06-17 17:22:09 -070057 * The lock order is sockconf_lock -> sp_lock.
Yu Xiangning0f1702c2008-12-11 20:04:13 -080058 */
59extern int kobj_path_exists(char *, int);
Yu Xiangning0f1702c2008-12-11 20:04:13 -080060
61static int sockparams_sdev_init(struct sockparams *, char *, int);
62static void sockparams_sdev_fini(struct sockparams *);
63
64/*
65 * Global sockparams list (populated via soconfig(1M)).
66 */
67static list_t sphead;
Yu Xiangning0f1702c2008-12-11 20:04:13 -080068
69/*
70 * List of ephemeral sockparams.
71 */
72static list_t sp_ephem_list;
Yu Xiangning0f1702c2008-12-11 20:04:13 -080073
Anders Persson7d64f412009-02-11 15:38:45 -080074/* Global kstats for sockparams */
75typedef struct sockparams_g_stats {
76 kstat_named_t spgs_ephem_nalloc;
77 kstat_named_t spgs_ephem_nreuse;
78} sockparams_g_stats_t;
79
80static sockparams_g_stats_t sp_g_stats;
81static kstat_t *sp_g_kstat;
82
83
Yu Xiangning0f1702c2008-12-11 20:04:13 -080084void
85sockparams_init(void)
86{
87 list_create(&sphead, sizeof (struct sockparams),
88 offsetof(struct sockparams, sp_node));
89 list_create(&sp_ephem_list, sizeof (struct sockparams),
90 offsetof(struct sockparams, sp_node));
91
Anders Persson7d64f412009-02-11 15:38:45 -080092 kstat_named_init(&sp_g_stats.spgs_ephem_nalloc, "ephemeral_nalloc",
93 KSTAT_DATA_UINT64);
94 kstat_named_init(&sp_g_stats.spgs_ephem_nreuse, "ephemeral_nreuse",
95 KSTAT_DATA_UINT64);
96
97 sp_g_kstat = kstat_create("sockfs", 0, "sockparams", "misc",
98 KSTAT_TYPE_NAMED, sizeof (sp_g_stats) / sizeof (kstat_named_t),
99 KSTAT_FLAG_VIRTUAL);
100 if (sp_g_kstat == NULL)
101 return;
102
103 sp_g_kstat->ks_data = &sp_g_stats;
104
105 kstat_install(sp_g_kstat);
106}
107
108static int
109sockparams_kstat_update(kstat_t *ksp, int rw)
110{
111 struct sockparams *sp = ksp->ks_private;
112 sockparams_stats_t *sps = ksp->ks_data;
113
114 if (rw == KSTAT_WRITE)
115 return (EACCES);
116
117 sps->sps_nactive.value.ui64 = sp->sp_refcnt;
118
119 return (0);
120}
121
122/*
123 * Setup kstats for the given sockparams entry.
124 */
125static void
126sockparams_kstat_init(struct sockparams *sp)
127{
128 char name[KSTAT_STRLEN];
129
Anders Persson7d64f412009-02-11 15:38:45 -0800130 (void) snprintf(name, KSTAT_STRLEN, "socket_%d_%d_%d", sp->sp_family,
131 sp->sp_type, sp->sp_protocol);
132
133 sp->sp_kstat = kstat_create("sockfs", 0, name, "misc", KSTAT_TYPE_NAMED,
134 sizeof (sockparams_stats_t) / sizeof (kstat_named_t),
135 KSTAT_FLAG_VIRTUAL);
136
137 if (sp->sp_kstat == NULL)
138 return;
139
140 sp->sp_kstat->ks_data = &sp->sp_stats;
141 sp->sp_kstat->ks_update = sockparams_kstat_update;
142 sp->sp_kstat->ks_private = sp;
143 kstat_install(sp->sp_kstat);
144}
145
146static void
147sockparams_kstat_fini(struct sockparams *sp)
148{
149 if (sp->sp_kstat != NULL) {
150 kstat_delete(sp->sp_kstat);
151 sp->sp_kstat = NULL;
152 }
Yu Xiangning0f1702c2008-12-11 20:04:13 -0800153}
154
155/*
156 * sockparams_create(int family, int type, int protocol, char *modname,
157 * char *devpath, int devpathlen, int flags, int kmflags, int *errorp)
158 *
159 * Create a new sockparams entry.
160 *
161 * Arguments:
162 * family, type, protocol: specifies the socket type
163 * modname: Name of the module associated with the socket type. The
164 * module can be NULL if a device path is given, in which
165 * case the TPI module is used.
Anders Persson3e95bd42010-06-17 17:22:09 -0700166 * devpath: Path to the STREAMS device. Must be NULL for non-STREAMS
167 * based transports.
Yu Xiangning0f1702c2008-12-11 20:04:13 -0800168 * devpathlen: Length of the devpath string. The argument can be 0,
169 * indicating that devpath was allocated statically, and should
170 * not be freed when the sockparams entry is destroyed.
171 *
172 * flags : SOCKPARAMS_EPHEMERAL is the only flag that is allowed.
173 * kmflags: KM_{NO,}SLEEP
174 * errorp : Value-return argument, set when an error occurs.
175 *
176 * Returns:
177 * On success a new sockparams entry is returned, and *errorp is set
178 * to 0. On failure NULL is returned and *errorp is set to indicate the
179 * type of error that occured.
180 *
181 * Notes:
182 * devpath and modname are freed upon failure.
183 */
184struct sockparams *
185sockparams_create(int family, int type, int protocol, char *modname,
186 char *devpath, int devpathlen, int flags, int kmflags, int *errorp)
187{
188 struct sockparams *sp = NULL;
189 size_t size;
190
191 ASSERT((flags & ~SOCKPARAMS_EPHEMERAL) == 0);
192 if (flags & ~SOCKPARAMS_EPHEMERAL) {
193 *errorp = EINVAL;
194 goto error;
195 }
196
Anders Persson3e95bd42010-06-17 17:22:09 -0700197 /* either a module or device must be given, but not both */
Yu Xiangning0f1702c2008-12-11 20:04:13 -0800198 if (modname == NULL && devpath == NULL) {
199 *errorp = EINVAL;
200 goto error;
201 }
202
203 sp = kmem_zalloc(sizeof (*sp), kmflags);
204 if (sp == NULL) {
205 *errorp = ENOMEM;
206 goto error;
207 }
208 sp->sp_family = family;
209 sp->sp_type = type;
210 sp->sp_protocol = protocol;
211 sp->sp_refcnt = 0;
212 sp->sp_flags = flags;
213
Anders Persson3e95bd42010-06-17 17:22:09 -0700214 list_create(&sp->sp_auto_filters, sizeof (sp_filter_t),
215 offsetof(sp_filter_t, spf_node));
216 list_create(&sp->sp_prog_filters, sizeof (sp_filter_t),
217 offsetof(sp_filter_t, spf_node));
218
Anders Persson51899d82009-03-04 15:03:04 -0800219 kstat_named_init(&sp->sp_stats.sps_nfallback, "nfallback",
220 KSTAT_DATA_UINT64);
221 kstat_named_init(&sp->sp_stats.sps_nactive, "nactive",
222 KSTAT_DATA_UINT64);
223 kstat_named_init(&sp->sp_stats.sps_ncreate, "ncreate",
224 KSTAT_DATA_UINT64);
225
Anders Persson7d64f412009-02-11 15:38:45 -0800226 /*
Anders Persson51899d82009-03-04 15:03:04 -0800227 * Track how many ephemeral entries we have created.
Anders Persson7d64f412009-02-11 15:38:45 -0800228 */
229 if (sp->sp_flags & SOCKPARAMS_EPHEMERAL)
230 sp_g_stats.spgs_ephem_nalloc.value.ui64++;
Anders Persson7d64f412009-02-11 15:38:45 -0800231
Yu Xiangning0f1702c2008-12-11 20:04:13 -0800232 if (modname != NULL) {
233 sp->sp_smod_name = modname;
234 } else {
235 size = strlen(SOTPI_SMOD_NAME) + 1;
236 modname = kmem_zalloc(size, kmflags);
237 if (modname == NULL) {
238 *errorp = ENOMEM;
239 goto error;
240 }
241 sp->sp_smod_name = modname;
242 (void) sprintf(sp->sp_smod_name, "%s", SOTPI_SMOD_NAME);
243 }
244
245 if (devpath != NULL) {
246 /* Set up the device entry. */
247 *errorp = sockparams_sdev_init(sp, devpath, devpathlen);
248 if (*errorp != 0)
249 goto error;
250 }
251
252 mutex_init(&sp->sp_lock, NULL, MUTEX_DEFAULT, NULL);
253 *errorp = 0;
254 return (sp);
255error:
256 ASSERT(*errorp != 0);
257 if (modname != NULL)
258 kmem_free(modname, strlen(modname) + 1);
259 if (devpathlen != 0)
260 kmem_free(devpath, devpathlen);
Anders Persson51899d82009-03-04 15:03:04 -0800261 if (sp != NULL)
Yu Xiangning0f1702c2008-12-11 20:04:13 -0800262 kmem_free(sp, sizeof (*sp));
263 return (NULL);
264}
265
266/*
267 * Initialize the STREAMS device aspect of the sockparams entry.
268 */
269static int
270sockparams_sdev_init(struct sockparams *sp, char *devpath, int devpathlen)
271{
272 vnode_t *vp = NULL;
273 int error;
274
275 ASSERT(devpath != NULL);
276
277 if ((error = sogetvp(devpath, &vp, UIO_SYSSPACE)) != 0) {
278 dprint(0, ("sockparams_sdev_init: vp %s failed with %d\n",
279 devpath, error));
280 return (error);
281 }
282
283 ASSERT(vp != NULL);
284 sp->sp_sdev_info.sd_vnode = vp;
285 sp->sp_sdev_info.sd_devpath = devpath;
286 sp->sp_sdev_info.sd_devpathlen = devpathlen;
287
288 return (0);
289}
290
291/*
292 * sockparams_destroy(struct sockparams *sp)
293 *
294 * Releases all the resources associated with the sockparams entry,
295 * and frees the sockparams entry.
296 *
297 * Arguments:
298 * sp: the sockparams entry to destroy.
299 *
300 * Returns:
301 * Nothing.
302 *
303 * Locking:
304 * The sp_lock of the entry can not be held.
305 */
306void
307sockparams_destroy(struct sockparams *sp)
308{
309 ASSERT(sp->sp_refcnt == 0);
310 ASSERT(!list_link_active(&sp->sp_node));
311
312 sockparams_sdev_fini(sp);
313
314 if (sp->sp_smod_info != NULL)
Anders Persson5f1fdc12010-04-26 14:42:41 -0700315 SMOD_DEC_REF(sp->sp_smod_info, sp->sp_smod_name);
Yu Xiangning0f1702c2008-12-11 20:04:13 -0800316 kmem_free(sp->sp_smod_name, strlen(sp->sp_smod_name) + 1);
317 sp->sp_smod_name = NULL;
318 sp->sp_smod_info = NULL;
319 mutex_destroy(&sp->sp_lock);
Anders Persson7d64f412009-02-11 15:38:45 -0800320 sockparams_kstat_fini(sp);
Yu Xiangning0f1702c2008-12-11 20:04:13 -0800321
Anders Persson3e95bd42010-06-17 17:22:09 -0700322 sof_sockparams_fini(sp);
323 list_destroy(&sp->sp_auto_filters);
324 list_destroy(&sp->sp_prog_filters);
325
Yu Xiangning0f1702c2008-12-11 20:04:13 -0800326 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 */
Anders Persson3e95bd42010-06-17 17:22:09 -0700408 rw_enter(&sockconf_lock, RW_READER);
Yu Xiangning0f1702c2008-12-11 20:04:13 -0800409 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);
Anders Persson3e95bd42010-06-17 17:22:09 -0700413 rw_exit(&sockconf_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
Anders Persson3e95bd42010-06-17 17:22:09 -0700422 rw_exit(&sockconf_lock);
Yu Xiangning0f1702c2008-12-11 20:04:13 -0800423
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 */
Anders Persson3e95bd42010-06-17 17:22:09 -0700464 rw_enter(&sockconf_lock, RW_WRITER);
Yu Xiangning0f1702c2008-12-11 20:04:13 -0800465 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);
Anders Persson3e95bd42010-06-17 17:22:09 -0700473 rw_exit(&sockconf_lock);
Yu Xiangning0f1702c2008-12-11 20:04:13 -0800474
475 sockparams_destroy(newsp);
476 } else {
Anders Persson3e95bd42010-06-17 17:22:09 -0700477 *errorp = sof_sockparams_init(newsp);
478 if (*errorp != 0) {
479 rw_exit(&sockconf_lock);
480 sockparams_destroy(newsp);
481 return (NULL);
482 }
Yu Xiangning0f1702c2008-12-11 20:04:13 -0800483 SOCKPARAMS_INC_REF(newsp);
484 list_insert_tail(&sp_ephem_list, newsp);
Anders Persson3e95bd42010-06-17 17:22:09 -0700485 rw_exit(&sockconf_lock);
Yu Xiangning0f1702c2008-12-11 20:04:13 -0800486
487 sp = newsp;
488 }
489 ASSERT(*errorp == 0);
490
491 return (sp);
492 }
493}
494
495struct sockparams *
496sockparams_hold_ephemeral_bydev(int family, int type, int protocol,
497 const char *dev, int kmflag, int *errorp)
498{
499 return (sockparams_hold_ephemeral(family, type, protocol, dev, B_TRUE,
500 kmflag, errorp));
501}
502
503struct sockparams *
504sockparams_hold_ephemeral_bymod(int family, int type, int protocol,
505 const char *mod, int kmflag, int *errorp)
506{
507 return (sockparams_hold_ephemeral(family, type, protocol, mod, B_FALSE,
508 kmflag, errorp));
509}
510
511/*
512 * Called when the last socket using the ephemeral entry is dropping
513 * its' reference. To maintain lock order we must drop the sockparams
514 * lock before calling this function. As a result, a new reference
515 * might be placed on the entry, in which case there is nothing to
516 * do. However, if ref count goes to zero, we delete the entry.
517 */
518void
519sockparams_ephemeral_drop_last_ref(struct sockparams *sp)
520{
521 ASSERT(sp->sp_flags & SOCKPARAMS_EPHEMERAL);
522 ASSERT(MUTEX_NOT_HELD(&sp->sp_lock));
523
Anders Persson3e95bd42010-06-17 17:22:09 -0700524 rw_enter(&sockconf_lock, RW_WRITER);
Yu Xiangning0f1702c2008-12-11 20:04:13 -0800525 mutex_enter(&sp->sp_lock);
526
527 if (--sp->sp_refcnt == 0) {
528 list_remove(&sp_ephem_list, sp);
529 mutex_exit(&sp->sp_lock);
Anders Persson3e95bd42010-06-17 17:22:09 -0700530 rw_exit(&sockconf_lock);
Yu Xiangning0f1702c2008-12-11 20:04:13 -0800531
532 sockparams_destroy(sp);
533 } else {
534 mutex_exit(&sp->sp_lock);
Anders Persson3e95bd42010-06-17 17:22:09 -0700535 rw_exit(&sockconf_lock);
Yu Xiangning0f1702c2008-12-11 20:04:13 -0800536 }
537}
538
539/*
540 * sockparams_add(struct sockparams *sp)
541 *
542 * Tries to add the given sockparams entry to the global list.
543 *
544 * Arguments:
545 * sp: the sockparms entry to add
546 *
547 * Returns:
548 * On success 0, but if an entry already exists, then EEXIST
549 * is returned.
550 *
551 * Locking:
Anders Persson3e95bd42010-06-17 17:22:09 -0700552 * The caller can not be holding sockconf_lock.
Yu Xiangning0f1702c2008-12-11 20:04:13 -0800553 */
Anders Persson3e95bd42010-06-17 17:22:09 -0700554int
Yu Xiangning0f1702c2008-12-11 20:04:13 -0800555sockparams_add(struct sockparams *sp)
556{
Anders Persson3e95bd42010-06-17 17:22:09 -0700557 int error;
558
Yu Xiangning0f1702c2008-12-11 20:04:13 -0800559 ASSERT(!(sp->sp_flags & SOCKPARAMS_EPHEMERAL));
560
Anders Persson3e95bd42010-06-17 17:22:09 -0700561 rw_enter(&sockconf_lock, RW_WRITER);
Yu Xiangning0f1702c2008-12-11 20:04:13 -0800562 if (sockparams_find(&sphead, sp->sp_family, sp->sp_type,
shenjian22238f72009-01-07 13:45:08 +0800563 sp->sp_protocol, B_TRUE, NULL) != 0) {
Anders Persson3e95bd42010-06-17 17:22:09 -0700564 rw_exit(&sockconf_lock);
Yu Xiangning0f1702c2008-12-11 20:04:13 -0800565 return (EEXIST);
566 } else {
Anders Persson3e95bd42010-06-17 17:22:09 -0700567 /*
568 * Unique sockparams entry, so init the kstats.
569 */
570 sockparams_kstat_init(sp);
571
572 /*
573 * Before making the socket type available we must make
574 * sure that interested socket filters are aware of it.
575 */
576 error = sof_sockparams_init(sp);
577 if (error != 0) {
578 rw_exit(&sockconf_lock);
579 return (error);
580 }
Yu Xiangning0f1702c2008-12-11 20:04:13 -0800581 list_insert_tail(&sphead, sp);
Anders Persson3e95bd42010-06-17 17:22:09 -0700582 rw_exit(&sockconf_lock);
Yu Xiangning0f1702c2008-12-11 20:04:13 -0800583 return (0);
584 }
585}
586
587/*
588 * sockparams_delete(int family, int type, int protocol)
589 *
590 * Marks the sockparams entry for a specific family, type and protocol
591 * for deletion. The entry is removed from the list and destroyed
592 * if no one is holding a reference to it.
593 *
594 * Arguments:
595 * family, type, protocol: the socket type that should be removed.
596 *
597 * Returns:
598 * On success 0, otherwise ENXIO.
599 *
600 * Locking:
Anders Persson3e95bd42010-06-17 17:22:09 -0700601 * Caller can not be holding sockconf_lock or the sp_lock of
Yu Xiangning0f1702c2008-12-11 20:04:13 -0800602 * any sockparams entry.
603 */
Anders Persson3e95bd42010-06-17 17:22:09 -0700604int
Yu Xiangning0f1702c2008-12-11 20:04:13 -0800605sockparams_delete(int family, int type, int protocol)
606{
607 struct sockparams *sp;
608
Anders Persson3e95bd42010-06-17 17:22:09 -0700609 rw_enter(&sockconf_lock, RW_WRITER);
shenjian22238f72009-01-07 13:45:08 +0800610 sp = sockparams_find(&sphead, family, type, protocol, B_TRUE, NULL);
Yu Xiangning0f1702c2008-12-11 20:04:13 -0800611
612 if (sp != NULL) {
613 /*
614 * If no one is holding a reference to the entry, then
615 * we go ahead and remove it from the list and then
616 * destroy it.
617 */
618 mutex_enter(&sp->sp_lock);
619 if (sp->sp_refcnt != 0) {
620 mutex_exit(&sp->sp_lock);
Anders Persson3e95bd42010-06-17 17:22:09 -0700621 rw_exit(&sockconf_lock);
Yu Xiangning0f1702c2008-12-11 20:04:13 -0800622 return (EBUSY);
623 }
624 mutex_exit(&sp->sp_lock);
625 /* Delete the sockparams entry. */
626 list_remove(&sphead, sp);
Anders Persson3e95bd42010-06-17 17:22:09 -0700627 rw_exit(&sockconf_lock);
Yu Xiangning0f1702c2008-12-11 20:04:13 -0800628
629 sockparams_destroy(sp);
630 return (0);
631 } else {
Anders Persson3e95bd42010-06-17 17:22:09 -0700632 rw_exit(&sockconf_lock);
Yu Xiangning0f1702c2008-12-11 20:04:13 -0800633 return (ENXIO);
634 }
635}
636
Yu Xiangning0f1702c2008-12-11 20:04:13 -0800637
638/*
639 * solookup(int family, int type, int protocol, struct sockparams **spp)
640 *
641 * Lookup an entry in the sockparams list based on the triple. The returned
642 * entry either exactly match the given tuple, or it is the 'default' entry
643 * for the given <family, type>. A default entry is on with a protocol
644 * value of zero.
645 *
646 * Arguments:
647 * family, type, protocol: tuple to search for
648 * spp: Value-return argument
649 *
650 * Returns:
651 * If an entry is found, 0 is returned and *spp is set to point to the
652 * entry. In case an entry is not found, *spp is set to NULL, and an
653 * error code is returned. The errors are (in decreasing precedence):
654 * EAFNOSUPPORT - address family not in list
655 * EPROTONOSUPPORT - address family supported but not protocol.
656 * EPROTOTYPE - address family and protocol supported but not socket type.
657 *
658 * TODO: should use ddi_modopen()/ddi_modclose()
659 */
Yu Xiangning0f1702c2008-12-11 20:04:13 -0800660int
661solookup(int family, int type, int protocol, struct sockparams **spp)
662{
663 struct sockparams *sp = NULL;
664 int error = 0;
665
666 *spp = NULL;
Anders Persson3e95bd42010-06-17 17:22:09 -0700667 rw_enter(&sockconf_lock, RW_READER);
Yu Xiangning0f1702c2008-12-11 20:04:13 -0800668
669 /*
670 * Search the sockparams list for an appropiate entry.
671 * Hopefully we find an entry that match the exact family,
672 * type and protocol specified by the user, in which case
673 * we return that entry. However, we also keep track of
674 * the default entry for a specific family and type, the
675 * entry of which would have a protocol value of 0.
676 */
shenjian22238f72009-01-07 13:45:08 +0800677 sp = sockparams_find(&sphead, family, type, protocol, B_TRUE, NULL);
Yu Xiangning0f1702c2008-12-11 20:04:13 -0800678
679 if (sp == NULL) {
680 int found = 0;
681
682 /* Determine correct error code */
683 for (sp = list_head(&sphead); sp != NULL;
684 sp = list_next(&sphead, sp)) {
685 if (sp->sp_family == family && found < 1)
686 found = 1;
687 if (sp->sp_family == family &&
688 sp->sp_protocol == protocol && found < 2)
689 found = 2;
690 }
Anders Persson3e95bd42010-06-17 17:22:09 -0700691 rw_exit(&sockconf_lock);
Yu Xiangning0f1702c2008-12-11 20:04:13 -0800692 switch (found) {
693 case 0:
694 error = EAFNOSUPPORT;
695 break;
696 case 1:
697 error = EPROTONOSUPPORT;
698 break;
699 case 2:
700 error = EPROTOTYPE;
701 break;
702 }
703 return (error);
704 }
705
706 /*
707 * An entry was found.
708 *
709 * We put a hold on the entry early on, so if the
710 * sockmod is not loaded, and we have to exit
Anders Persson3e95bd42010-06-17 17:22:09 -0700711 * sockconf_lock to call modload(), we know that the
Yu Xiangning0f1702c2008-12-11 20:04:13 -0800712 * sockparams entry wont go away. That way we don't
713 * have to look up the entry once we come back from
714 * modload().
715 */
716 SOCKPARAMS_INC_REF(sp);
Anders Persson3e95bd42010-06-17 17:22:09 -0700717 rw_exit(&sockconf_lock);
Yu Xiangning0f1702c2008-12-11 20:04:13 -0800718
719 if (sp->sp_smod_info == NULL) {
Anders Persson5f1fdc12010-04-26 14:42:41 -0700720 smod_info_t *smod = smod_lookup_byname(sp->sp_smod_name);
721
722 if (smod == NULL) {
Yu Xiangning0f1702c2008-12-11 20:04:13 -0800723 /*
724 * We put a hold on the sockparams entry
725 * earlier, hoping everything would work out.
726 * That obviously did not happen, so release
727 * the hold here.
728 */
729 SOCKPARAMS_DEC_REF(sp);
730 /*
731 * We should probably mark the sockparams as
732 * "bad", and redo the lookup skipping the
733 * "bad" entries. I.e., sp->sp_mod_state |= BAD,
734 * return (solookup(...))
735 */
736 return (ENXIO);
737 }
Anders Persson5f1fdc12010-04-26 14:42:41 -0700738 /*
739 * Another thread might have already looked up the socket
740 * module for this entry. In that case we need to drop our
741 * reference to `smod' to ensure that the sockparams entry
742 * only holds one reference.
743 */
744 mutex_enter(&sp->sp_lock);
745 if (sp->sp_smod_info == NULL)
746 sp->sp_smod_info = smod;
747 else
748 SMOD_DEC_REF(smod, sp->sp_smod_name);
749 mutex_exit(&sp->sp_lock);
Yu Xiangning0f1702c2008-12-11 20:04:13 -0800750 }
751
752 /*
753 * Alright, we have a valid sockparams entry.
754 */
755 *spp = sp;
756 return (0);
757}
Anders Persson3e95bd42010-06-17 17:22:09 -0700758
759/*
760 * Called when filter entry `ent' is going away. All sockparams remove
761 * their references to `ent'.
762 */
763static void
764sockparams_filter_cleanup_impl(sof_entry_t *ent, list_t *list)
765{
766 struct sockparams *sp;
767 sp_filter_t *fil;
768 list_t *flist;
769
770 ASSERT(RW_WRITE_HELD(&sockconf_lock));
771
772 for (sp = list_head(list); sp != NULL;
773 sp = list_next(list, sp)) {
774 flist = (ent->sofe_flags & SOFEF_AUTO) ?
775 &sp->sp_auto_filters : &sp->sp_prog_filters;
776 fil = list_head(flist);
777 for (fil = list_head(flist); fil != NULL;
778 fil = list_next(flist, fil)) {
779 if (fil->spf_filter == ent) {
780 list_remove(flist, fil);
781 kmem_free(fil, sizeof (sp_filter_t));
782 break;
783 }
784 }
785 }
786}
787void
788sockparams_filter_cleanup(sof_entry_t *ent)
789{
790 sockparams_filter_cleanup_impl(ent, &sphead);
791 sockparams_filter_cleanup_impl(ent, &sp_ephem_list);
792}
793
794/*
795 * New filter is being added; walk the list of sockparams to see if
796 * the filter is interested in any of the sockparams.
797 */
798static int
799sockparams_new_filter_impl(sof_entry_t *ent, list_t *list)
800{
801 struct sockparams *sp;
802 int err;
803
804 ASSERT(RW_WRITE_HELD(&sockconf_lock));
805
806 for (sp = list_head(list); sp != NULL;
807 sp = list_next(list, sp)) {
808 if ((err = sof_entry_proc_sockparams(ent, sp)) != 0) {
809 sockparams_filter_cleanup(ent);
810 return (err);
811 }
812 }
813 return (0);
814}
815
816int
817sockparams_new_filter(sof_entry_t *ent)
818{
819 int error;
820
821 if ((error = sockparams_new_filter_impl(ent, &sphead)) != 0)
822 return (error);
823
824 if ((error = sockparams_new_filter_impl(ent, &sp_ephem_list)) != 0)
825 sockparams_filter_cleanup_impl(ent, &sphead);
826 return (error);
827}