1 /*
2 * CDDL HEADER START
3 *
4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License (the "License").
6 * You may not use this file except in compliance with the License.
7 *
8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 * or http://www.opensolaris.org/os/licensing.
10 * See the License for the specific language governing permissions
11 * and limitations under the License.
12 *
13 * When distributing Covered Code, include this CDDL HEADER in each
14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15 * If applicable, add the following below this CDDL HEADER, with the
16 * fields enclosed by brackets "[]" replaced with your own identifying
17 * information: Portions Copyright [yyyy] [name of copyright owner]
18 *
19 * CDDL HEADER END
20 */
21
22 /*
23 * Copyright (c) 1996, 2010, Oracle and/or its affiliates. All rights reserved.
24 * Copyright 2015 Nexenta Systems, Inc. All rights reserved.
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 #include <sys/sunddi.h>
35
36 #include <sys/stropts.h>
37 #include <sys/socket.h>
38 #include <sys/socketvar.h>
39
40 #include <fs/sockfs/sockcommon.h>
41 #include <fs/sockfs/sockfilter_impl.h>
42 #include <fs/sockfs/socktpi.h>
43
44 /*
45 * Socket Parameters
46 *
47 * Socket parameter (struct sockparams) entries represent the socket types
48 * available on the system.
49 *
50 * Flags (sp_flags):
51 *
52 * SOCKPARAMS_EPHEMERAL: A temporary sockparams entry that will be deleted
53 * as soon as its' ref count drops to zero. In addition, ephemeral entries will
54 * never be hooked onto the global sockparams list. Ephemeral entries are
55 * created when application requests to create a socket using an application
56 * supplied device path, or when a socket is falling back to TPI.
57 *
58 * Lock order:
59 * The lock order is sockconf_lock -> sp_lock.
60 */
61 extern int kobj_path_exists(char *, int);
62
63 static int sockparams_sdev_init(struct sockparams *, char *, int);
64 static void sockparams_sdev_fini(struct sockparams *);
65
66 /*
67 * Global sockparams list (populated via soconfig(1M)).
68 */
69 static list_t sphead;
70
71 /*
72 * List of ephemeral sockparams.
73 */
74 static list_t sp_ephem_list;
75
76 /* Global kstats for sockparams */
77 typedef struct sockparams_g_stats {
78 kstat_named_t spgs_ephem_nalloc;
79 kstat_named_t spgs_ephem_nreuse;
80 } sockparams_g_stats_t;
81
82 static sockparams_g_stats_t sp_g_stats;
83 static kstat_t *sp_g_kstat;
84
85
86 void
sockparams_init(void)87 sockparams_init(void)
88 {
89 list_create(&sphead, sizeof (struct sockparams),
90 offsetof(struct sockparams, sp_node));
91 list_create(&sp_ephem_list, sizeof (struct sockparams),
92 offsetof(struct sockparams, sp_node));
93
94 kstat_named_init(&sp_g_stats.spgs_ephem_nalloc, "ephemeral_nalloc",
95 KSTAT_DATA_UINT64);
96 kstat_named_init(&sp_g_stats.spgs_ephem_nreuse, "ephemeral_nreuse",
97 KSTAT_DATA_UINT64);
98
99 sp_g_kstat = kstat_create("sockfs", 0, "sockparams", "misc",
100 KSTAT_TYPE_NAMED, sizeof (sp_g_stats) / sizeof (kstat_named_t),
101 KSTAT_FLAG_VIRTUAL);
102 if (sp_g_kstat == NULL)
103 return;
104
105 sp_g_kstat->ks_data = &sp_g_stats;
106
107 kstat_install(sp_g_kstat);
108 }
109
110 static int
sockparams_kstat_update(kstat_t * ksp,int rw)111 sockparams_kstat_update(kstat_t *ksp, int rw)
112 {
113 struct sockparams *sp = ksp->ks_private;
114 sockparams_stats_t *sps = ksp->ks_data;
115
116 if (rw == KSTAT_WRITE)
117 return (EACCES);
118
119 sps->sps_nactive.value.ui64 = sp->sp_refcnt;
120
121 return (0);
122 }
123
124 /*
125 * Setup kstats for the given sockparams entry.
126 */
127 static void
sockparams_kstat_init(struct sockparams * sp)128 sockparams_kstat_init(struct sockparams *sp)
129 {
130 char name[KSTAT_STRLEN];
131
132 (void) snprintf(name, KSTAT_STRLEN, "socket_%d_%d_%d", sp->sp_family,
133 sp->sp_type, sp->sp_protocol);
134
135 sp->sp_kstat = kstat_create("sockfs", 0, name, "misc", KSTAT_TYPE_NAMED,
136 sizeof (sockparams_stats_t) / sizeof (kstat_named_t),
137 KSTAT_FLAG_VIRTUAL);
138
139 if (sp->sp_kstat == NULL)
140 return;
141
142 sp->sp_kstat->ks_data = &sp->sp_stats;
143 sp->sp_kstat->ks_update = sockparams_kstat_update;
144 sp->sp_kstat->ks_private = sp;
145 kstat_install(sp->sp_kstat);
146 }
147
148 static void
sockparams_kstat_fini(struct sockparams * sp)149 sockparams_kstat_fini(struct sockparams *sp)
150 {
151 if (sp->sp_kstat != NULL) {
152 kstat_delete(sp->sp_kstat);
153 sp->sp_kstat = NULL;
154 }
155 }
156
157 /*
158 * sockparams_create(int family, int type, int protocol, char *modname,
159 * char *devpath, int devpathlen, int flags, int kmflags, int *errorp)
160 *
161 * Create a new sockparams entry.
162 *
163 * Arguments:
164 * family, type, protocol: specifies the socket type
165 * modname: Name of the module associated with the socket type. The
166 * module can be NULL if a device path is given, in which
167 * case the TPI module is used.
168 * devpath: Path to the STREAMS device. Must be NULL for non-STREAMS
169 * based transports.
170 * devpathlen: Length of the devpath string. The argument can be 0,
171 * indicating that devpath was allocated statically, and should
172 * not be freed when the sockparams entry is destroyed.
173 *
174 * flags : SOCKPARAMS_EPHEMERAL is the only flag that is allowed.
175 * kmflags: KM_{NO,}SLEEP
176 * errorp : Value-return argument, set when an error occurs.
177 *
178 * Returns:
179 * On success a new sockparams entry is returned, and *errorp is set
180 * to 0. On failure NULL is returned and *errorp is set to indicate the
181 * type of error that occured.
182 *
183 * Notes:
184 * devpath and modname are freed upon failure.
185 */
186 struct sockparams *
sockparams_create(int family,int type,int protocol,char * modname,char * devpath,int devpathlen,int flags,int kmflags,int * errorp)187 sockparams_create(int family, int type, int protocol, char *modname,
188 char *devpath, int devpathlen, int flags, int kmflags, int *errorp)
189 {
190 struct sockparams *sp = NULL;
191 size_t size;
192
193 ASSERT((flags & ~SOCKPARAMS_EPHEMERAL) == 0);
194 if (flags & ~SOCKPARAMS_EPHEMERAL) {
195 *errorp = EINVAL;
196 goto error;
197 }
198
199 /* either a module or device must be given, but not both */
200 if (modname == NULL && devpath == NULL) {
201 *errorp = EINVAL;
202 goto error;
203 }
204
205 sp = kmem_zalloc(sizeof (*sp), kmflags);
206 if (sp == NULL) {
207 *errorp = ENOMEM;
208 goto error;
209 }
210 sp->sp_family = family;
211 sp->sp_type = type;
212 sp->sp_protocol = protocol;
213 sp->sp_refcnt = 0;
214 sp->sp_flags = flags;
215
216 list_create(&sp->sp_auto_filters, sizeof (sp_filter_t),
217 offsetof(sp_filter_t, spf_node));
218 list_create(&sp->sp_prog_filters, sizeof (sp_filter_t),
219 offsetof(sp_filter_t, spf_node));
220
221 kstat_named_init(&sp->sp_stats.sps_nfallback, "nfallback",
222 KSTAT_DATA_UINT64);
223 kstat_named_init(&sp->sp_stats.sps_nactive, "nactive",
224 KSTAT_DATA_UINT64);
225 kstat_named_init(&sp->sp_stats.sps_ncreate, "ncreate",
226 KSTAT_DATA_UINT64);
227
228 /*
229 * Track how many ephemeral entries we have created.
230 */
231 if (sp->sp_flags & SOCKPARAMS_EPHEMERAL)
232 sp_g_stats.spgs_ephem_nalloc.value.ui64++;
233
234 if (modname != NULL) {
235 sp->sp_smod_name = modname;
236 } else {
237 size = strlen(SOTPI_SMOD_NAME) + 1;
238 modname = kmem_zalloc(size, kmflags);
239 if (modname == NULL) {
240 *errorp = ENOMEM;
241 goto error;
242 }
243 sp->sp_smod_name = modname;
244 (void) sprintf(sp->sp_smod_name, "%s", SOTPI_SMOD_NAME);
245 }
246
247 if (devpath != NULL) {
248 /* Set up the device entry. */
249 *errorp = sockparams_sdev_init(sp, devpath, devpathlen);
250 if (*errorp != 0)
251 goto error;
252 }
253
254 mutex_init(&sp->sp_lock, NULL, MUTEX_DEFAULT, NULL);
255 *errorp = 0;
256 return (sp);
257 error:
258 ASSERT(*errorp != 0);
259 if (modname != NULL)
260 kmem_free(modname, strlen(modname) + 1);
261 if (devpathlen != 0)
262 kmem_free(devpath, devpathlen);
263 if (sp != NULL)
264 kmem_free(sp, sizeof (*sp));
265 return (NULL);
266 }
267
268 /*
269 * Initialize the STREAMS device aspect of the sockparams entry.
270 */
271 static int
sockparams_sdev_init(struct sockparams * sp,char * devpath,int devpathlen)272 sockparams_sdev_init(struct sockparams *sp, char *devpath, int devpathlen)
273 {
274 vnode_t *vp = NULL;
275 int error;
276
277 ASSERT(devpath != NULL);
278
279 if ((error = sogetvp(devpath, &vp, UIO_SYSSPACE)) != 0) {
280 dprint(0, ("sockparams_sdev_init: vp %s failed with %d\n",
281 devpath, error));
282 return (error);
283 }
284
285 ASSERT(vp != NULL);
286 sp->sp_sdev_info.sd_vnode = vp;
287 sp->sp_sdev_info.sd_devpath = devpath;
288 sp->sp_sdev_info.sd_devpathlen = devpathlen;
289
290 return (0);
291 }
292
293 /*
294 * sockparams_destroy(struct sockparams *sp)
295 *
296 * Releases all the resources associated with the sockparams entry,
297 * and frees the sockparams entry.
298 *
299 * Arguments:
300 * sp: the sockparams entry to destroy.
301 *
302 * Returns:
303 * Nothing.
304 *
305 * Locking:
306 * The sp_lock of the entry can not be held.
307 */
308 void
sockparams_destroy(struct sockparams * sp)309 sockparams_destroy(struct sockparams *sp)
310 {
311 ASSERT(sp->sp_refcnt == 0);
312 ASSERT(!list_link_active(&sp->sp_node));
313
314 sockparams_sdev_fini(sp);
315
316 if (sp->sp_smod_info != NULL)
317 SMOD_DEC_REF(sp->sp_smod_info, sp->sp_smod_name);
318 kmem_free(sp->sp_smod_name, strlen(sp->sp_smod_name) + 1);
319 sp->sp_smod_name = NULL;
320 sp->sp_smod_info = NULL;
321 mutex_destroy(&sp->sp_lock);
322 sockparams_kstat_fini(sp);
323
324 sof_sockparams_fini(sp);
325 list_destroy(&sp->sp_auto_filters);
326 list_destroy(&sp->sp_prog_filters);
327
328 kmem_free(sp, sizeof (*sp));
329 }
330
331 /*
332 * Clean up the STREAMS device part of the sockparams entry.
333 */
334 static void
sockparams_sdev_fini(struct sockparams * sp)335 sockparams_sdev_fini(struct sockparams *sp)
336 {
337 sdev_info_t sd;
338
339 /*
340 * if the entry does not have a STREAMS device, then there
341 * is nothing to do.
342 */
343 if (!SOCKPARAMS_HAS_DEVICE(sp))
344 return;
345
346 sd = sp->sp_sdev_info;
347 if (sd.sd_vnode != NULL)
348 VN_RELE(sd.sd_vnode);
349 if (sd.sd_devpathlen != 0)
350 kmem_free(sd.sd_devpath, sd.sd_devpathlen);
351
352 sp->sp_sdev_info.sd_vnode = NULL;
353 sp->sp_sdev_info.sd_devpath = NULL;
354 }
355
356 /*
357 * Look for a matching sockparams entry on the given list.
358 * The caller must hold the associated list lock.
359 */
360 static struct sockparams *
sockparams_find(list_t * list,int family,int type,int protocol,boolean_t by_devpath,const char * name)361 sockparams_find(list_t *list, int family, int type, int protocol,
362 boolean_t by_devpath, const char *name)
363 {
364 struct sockparams *sp;
365
366 for (sp = list_head(list); sp != NULL; sp = list_next(list, sp)) {
367 if (sp->sp_family == family && sp->sp_type == type) {
368 if (sp->sp_protocol == protocol) {
369 if (name == NULL)
370 break;
371 else if (by_devpath &&
372 sp->sp_sdev_info.sd_devpath != NULL &&
373 strcmp(sp->sp_sdev_info.sd_devpath,
374 name) == 0)
375 break;
376 else if (strcmp(sp->sp_smod_name, name) == 0)
377 break;
378 }
379 }
380 }
381 return (sp);
382 }
383
384 /*
385 * sockparams_hold_ephemeral()
386 *
387 * Returns an ephemeral sockparams entry of the requested family, type and
388 * protocol. The entry is returned held, and the caller is responsible for
389 * dropping the reference using SOCKPARAMS_DEC_REF() once done.
390 *
391 * All ephemeral entries are on list (sp_ephem_list). If there is an
392 * entry on the list that match the search criteria, then a reference is
393 * placed on that entry. Otherwise, a new entry is created and inserted
394 * in the list. The entry is removed from the list when the last reference
395 * is dropped.
396 *
397 * The tpi flag is used to determine whether name refers to a device or
398 * module name.
399 */
400 static struct sockparams *
sockparams_hold_ephemeral(int family,int type,int protocol,const char * name,boolean_t by_devpath,int kmflag,int * errorp)401 sockparams_hold_ephemeral(int family, int type, int protocol,
402 const char *name, boolean_t by_devpath, int kmflag, int *errorp)
403 {
404 struct sockparams *sp = NULL;
405 *errorp = 0;
406
407 /*
408 * First look for an existing entry
409 */
410 rw_enter(&sockconf_lock, RW_READER);
411 sp = sockparams_find(&sp_ephem_list, family, type, protocol,
412 by_devpath, name);
413 if (sp != NULL) {
414 SOCKPARAMS_INC_REF(sp);
415 rw_exit(&sockconf_lock);
416 sp_g_stats.spgs_ephem_nreuse.value.ui64++;
417
418 return (sp);
419 } else {
420 struct sockparams *newsp = NULL;
421 char *namebuf = NULL;
422 int namelen = 0;
423
424 rw_exit(&sockconf_lock);
425
426 namelen = strlen(name) + 1;
427 namebuf = kmem_alloc(namelen, kmflag);
428 if (namebuf == NULL) {
429 *errorp = ENOMEM;
430 return (NULL);
431 }
432
433 (void *)strncpy(namebuf, name, namelen);
434 if (by_devpath) {
435 newsp = sockparams_create(family, type,
436 protocol, NULL, namebuf, namelen,
437 SOCKPARAMS_EPHEMERAL, kmflag, errorp);
438 } else {
439 newsp = sockparams_create(family, type,
440 protocol, namebuf, NULL, 0,
441 SOCKPARAMS_EPHEMERAL, kmflag, errorp);
442 }
443
444 if (newsp == NULL) {
445 ASSERT(*errorp != 0);
446 return (NULL);
447 }
448
449 /*
450 * Time to load the socket module.
451 */
452 ASSERT(newsp->sp_smod_info == NULL);
453 newsp->sp_smod_info =
454 smod_lookup_byname(newsp->sp_smod_name);
455 if (newsp->sp_smod_info == NULL) {
456 /* Failed to load */
457 sockparams_destroy(newsp);
458 *errorp = ENXIO;
459 return (NULL);
460 }
461
462 /*
463 * The sockparams entry was created, now try to add it
464 * to the list. We need to hold the lock as a WRITER.
465 */
466 rw_enter(&sockconf_lock, RW_WRITER);
467 sp = sockparams_find(&sp_ephem_list, family, type, protocol,
468 by_devpath, name);
469 if (sp != NULL) {
470 /*
471 * Someone has requested a matching entry, so just
472 * place a hold on it and release the entry we alloc'ed.
473 */
474 SOCKPARAMS_INC_REF(sp);
475 rw_exit(&sockconf_lock);
476
477 sockparams_destroy(newsp);
478 } else {
479 *errorp = sof_sockparams_init(newsp);
480 if (*errorp != 0) {
481 rw_exit(&sockconf_lock);
482 sockparams_destroy(newsp);
483 return (NULL);
484 }
485 SOCKPARAMS_INC_REF(newsp);
486 list_insert_tail(&sp_ephem_list, newsp);
487 rw_exit(&sockconf_lock);
488
489 sp = newsp;
490 }
491 ASSERT(*errorp == 0);
492
493 return (sp);
494 }
495 }
496
497 struct sockparams *
sockparams_hold_ephemeral_bydev(int family,int type,int protocol,const char * dev,int kmflag,int * errorp)498 sockparams_hold_ephemeral_bydev(int family, int type, int protocol,
499 const char *dev, int kmflag, int *errorp)
500 {
501 return (sockparams_hold_ephemeral(family, type, protocol, dev, B_TRUE,
502 kmflag, errorp));
503 }
504
505 struct sockparams *
sockparams_hold_ephemeral_bymod(int family,int type,int protocol,const char * mod,int kmflag,int * errorp)506 sockparams_hold_ephemeral_bymod(int family, int type, int protocol,
507 const char *mod, int kmflag, int *errorp)
508 {
509 return (sockparams_hold_ephemeral(family, type, protocol, mod, B_FALSE,
510 kmflag, errorp));
511 }
512
513 /*
514 * Called when the last socket using the ephemeral entry is dropping
515 * its' reference. To maintain lock order we must drop the sockparams
516 * lock before calling this function. As a result, a new reference
517 * might be placed on the entry, in which case there is nothing to
518 * do. However, if ref count goes to zero, we delete the entry.
519 */
520 void
sockparams_ephemeral_drop_last_ref(struct sockparams * sp)521 sockparams_ephemeral_drop_last_ref(struct sockparams *sp)
522 {
523 ASSERT(sp->sp_flags & SOCKPARAMS_EPHEMERAL);
524 ASSERT(MUTEX_NOT_HELD(&sp->sp_lock));
525
526 rw_enter(&sockconf_lock, RW_WRITER);
527 mutex_enter(&sp->sp_lock);
528
529 if (--sp->sp_refcnt == 0) {
530 list_remove(&sp_ephem_list, sp);
531 mutex_exit(&sp->sp_lock);
532 rw_exit(&sockconf_lock);
533
534 sockparams_destroy(sp);
535 } else {
536 mutex_exit(&sp->sp_lock);
537 rw_exit(&sockconf_lock);
538 }
539 }
540
541 /*
542 * sockparams_add(struct sockparams *sp)
543 *
544 * Tries to add the given sockparams entry to the global list.
545 *
546 * Arguments:
547 * sp: the sockparms entry to add
548 *
549 * Returns:
550 * On success 0, but if an entry already exists, then EEXIST
551 * is returned.
552 *
553 * Locking:
554 * The caller can not be holding sockconf_lock.
555 */
556 int
sockparams_add(struct sockparams * sp)557 sockparams_add(struct sockparams *sp)
558 {
559 int error;
560
561 ASSERT(!(sp->sp_flags & SOCKPARAMS_EPHEMERAL));
562
563 rw_enter(&sockconf_lock, RW_WRITER);
564 if (sockparams_find(&sphead, sp->sp_family, sp->sp_type,
565 sp->sp_protocol, B_TRUE, NULL) != 0) {
566 rw_exit(&sockconf_lock);
567 return (EEXIST);
568 } else {
569 /*
570 * Unique sockparams entry, so init the kstats.
571 */
572 sockparams_kstat_init(sp);
573
574 /*
575 * Before making the socket type available we must make
576 * sure that interested socket filters are aware of it.
577 */
578 error = sof_sockparams_init(sp);
579 if (error != 0) {
580 rw_exit(&sockconf_lock);
581 return (error);
582 }
583 list_insert_tail(&sphead, sp);
584 rw_exit(&sockconf_lock);
585 return (0);
586 }
587 }
588
589 /*
590 * sockparams_delete(int family, int type, int protocol)
591 *
592 * Marks the sockparams entry for a specific family, type and protocol
593 * for deletion. The entry is removed from the list and destroyed
594 * if no one is holding a reference to it.
595 *
596 * Arguments:
597 * family, type, protocol: the socket type that should be removed.
598 *
599 * Returns:
600 * On success 0, otherwise ENXIO.
601 *
602 * Locking:
603 * Caller can not be holding sockconf_lock or the sp_lock of
604 * any sockparams entry.
605 */
606 int
sockparams_delete(int family,int type,int protocol)607 sockparams_delete(int family, int type, int protocol)
608 {
609 struct sockparams *sp;
610
611 rw_enter(&sockconf_lock, RW_WRITER);
612 sp = sockparams_find(&sphead, family, type, protocol, B_TRUE, NULL);
613
614 if (sp != NULL) {
615 /*
616 * If no one is holding a reference to the entry, then
617 * we go ahead and remove it from the list and then
618 * destroy it.
619 */
620 mutex_enter(&sp->sp_lock);
621 if (sp->sp_refcnt != 0) {
622 mutex_exit(&sp->sp_lock);
623 rw_exit(&sockconf_lock);
624 return (EBUSY);
625 }
626 mutex_exit(&sp->sp_lock);
627 /* Delete the sockparams entry. */
628 list_remove(&sphead, sp);
629 rw_exit(&sockconf_lock);
630
631 sockparams_destroy(sp);
632 return (0);
633 } else {
634 rw_exit(&sockconf_lock);
635 return (ENXIO);
636 }
637 }
638
639
640 /*
641 * solookup(int family, int type, int protocol, struct sockparams **spp)
642 *
643 * Lookup an entry in the sockparams list based on the triple. The returned
644 * entry either exactly match the given tuple, or it is the 'default' entry
645 * for the given <family, type>. A default entry is on with a protocol
646 * value of zero.
647 *
648 * Arguments:
649 * family, type, protocol: tuple to search for
650 * spp: Value-return argument
651 *
652 * Returns:
653 * If an entry is found, 0 is returned and *spp is set to point to the
654 * entry. In case an entry is not found, *spp is set to NULL, and an
655 * error code is returned. The errors are (in decreasing precedence):
656 * EAFNOSUPPORT - address family not in list
657 * EPROTONOSUPPORT - address family supported but not protocol.
658 * EPROTOTYPE - address family and protocol supported but not socket type.
659 *
660 * TODO: should use ddi_modopen()/ddi_modclose()
661 */
662 int
solookup(int family,int type,int protocol,struct sockparams ** spp)663 solookup(int family, int type, int protocol, struct sockparams **spp)
664 {
665 struct sockparams *sp = NULL;
666 int error = 0;
667
668 *spp = NULL;
669 rw_enter(&sockconf_lock, RW_READER);
670
671 /*
672 * Search the sockparams list for an appropiate entry.
673 * Hopefully we find an entry that match the exact family,
674 * type and protocol specified by the user, in which case
675 * we return that entry. However, we also keep track of
676 * the default entry for a specific family and type, the
677 * entry of which would have a protocol value of 0.
678 */
679 sp = sockparams_find(&sphead, family, type, protocol, B_TRUE, NULL);
680
681 if (sp == NULL) {
682 int found = 0;
683
684 /* Determine correct error code */
685 for (sp = list_head(&sphead); sp != NULL;
686 sp = list_next(&sphead, sp)) {
687 if (sp->sp_family == family && found < 1)
688 found = 1;
689 if (sp->sp_family == family &&
690 sp->sp_protocol == protocol && found < 2)
691 found = 2;
692 }
693 rw_exit(&sockconf_lock);
694 switch (found) {
695 case 0:
696 error = EAFNOSUPPORT;
697 break;
698 case 1:
699 error = EPROTONOSUPPORT;
700 break;
701 case 2:
702 error = EPROTOTYPE;
703 break;
704 }
705 return (error);
706 }
707
708 /*
709 * An entry was found.
710 *
711 * We put a hold on the entry early on, so if the
712 * sockmod is not loaded, and we have to exit
713 * sockconf_lock to call modload(), we know that the
714 * sockparams entry wont go away. That way we don't
715 * have to look up the entry once we come back from
716 * modload().
717 */
718 SOCKPARAMS_INC_REF(sp);
719 rw_exit(&sockconf_lock);
720
721 if (sp->sp_smod_info == NULL) {
722 smod_info_t *smod = smod_lookup_byname(sp->sp_smod_name);
723
724 if (smod == NULL) {
725 /*
726 * We put a hold on the sockparams entry
727 * earlier, hoping everything would work out.
728 * That obviously did not happen, so release
729 * the hold here.
730 */
731 SOCKPARAMS_DEC_REF(sp);
732 /*
733 * We should probably mark the sockparams as
734 * "bad", and redo the lookup skipping the
735 * "bad" entries. I.e., sp->sp_mod_state |= BAD,
736 * return (solookup(...))
737 */
738 return (ENXIO);
739 }
740 /*
741 * Another thread might have already looked up the socket
742 * module for this entry. In that case we need to drop our
743 * reference to `smod' to ensure that the sockparams entry
744 * only holds one reference.
745 */
746 mutex_enter(&sp->sp_lock);
747 if (sp->sp_smod_info == NULL)
748 sp->sp_smod_info = smod;
749 else
750 SMOD_DEC_REF(smod, sp->sp_smod_name);
751 mutex_exit(&sp->sp_lock);
752 }
753
754 /*
755 * Alright, we have a valid sockparams entry.
756 */
757 *spp = sp;
758 return (0);
759 }
760
761 /*
762 * Called when filter entry `ent' is going away. All sockparams remove
763 * their references to `ent'.
764 */
765 static void
sockparams_filter_cleanup_impl(sof_entry_t * ent,list_t * list)766 sockparams_filter_cleanup_impl(sof_entry_t *ent, list_t *list)
767 {
768 struct sockparams *sp;
769 sp_filter_t *fil;
770 list_t *flist;
771
772 ASSERT(RW_WRITE_HELD(&sockconf_lock));
773
774 for (sp = list_head(list); sp != NULL;
775 sp = list_next(list, sp)) {
776 flist = (ent->sofe_flags & SOFEF_AUTO) ?
777 &sp->sp_auto_filters : &sp->sp_prog_filters;
778 for (fil = list_head(flist); fil != NULL;
779 fil = list_next(flist, fil)) {
780 if (fil->spf_filter == ent) {
781 list_remove(flist, fil);
782 kmem_free(fil, sizeof (sp_filter_t));
783 break;
784 }
785 }
786 }
787 }
788 void
sockparams_filter_cleanup(sof_entry_t * ent)789 sockparams_filter_cleanup(sof_entry_t *ent)
790 {
791 sockparams_filter_cleanup_impl(ent, &sphead);
792 sockparams_filter_cleanup_impl(ent, &sp_ephem_list);
793 }
794
795 /*
796 * New filter is being added; walk the list of sockparams to see if
797 * the filter is interested in any of the sockparams.
798 */
799 static int
sockparams_new_filter_impl(sof_entry_t * ent,list_t * list)800 sockparams_new_filter_impl(sof_entry_t *ent, list_t *list)
801 {
802 struct sockparams *sp;
803 int err;
804
805 ASSERT(RW_WRITE_HELD(&sockconf_lock));
806
807 for (sp = list_head(list); sp != NULL;
808 sp = list_next(list, sp)) {
809 if ((err = sof_entry_proc_sockparams(ent, sp)) != 0) {
810 sockparams_filter_cleanup(ent);
811 return (err);
812 }
813 }
814 return (0);
815 }
816
817 int
sockparams_new_filter(sof_entry_t * ent)818 sockparams_new_filter(sof_entry_t *ent)
819 {
820 int error;
821
822 if ((error = sockparams_new_filter_impl(ent, &sphead)) != 0)
823 return (error);
824
825 if ((error = sockparams_new_filter_impl(ent, &sp_ephem_list)) != 0)
826 sockparams_filter_cleanup_impl(ent, &sphead);
827 return (error);
828 }
829
830 /*
831 * Setup and return socket configuration table.
832 */
833 int
sockparams_copyout_socktable(uintptr_t socktable)834 sockparams_copyout_socktable(uintptr_t socktable)
835 {
836 STRUCT_DECL(sockconfig_socktable, st);
837 struct sockparams *sp;
838 uint_t count;
839 uint_t i = 0;
840 int ret = 0;
841 sockconfig_socktable_entry_t *se;
842
843 STRUCT_INIT(st, get_udatamodel());
844 if (ddi_copyin((void *)socktable, STRUCT_BUF(st),
845 STRUCT_SIZE(st), 0) != 0)
846 return (EFAULT);
847
848 rw_enter(&sockconf_lock, RW_READER);
849
850 count = STRUCT_FGET(st, num_of_entries);
851 /*
852 * If the output buffer is size zero, just copy out the count.
853 */
854 if (count == 0) {
855 for (sp = list_head(&sphead); sp != NULL;
856 sp = list_next(&sphead, sp)) {
857 count++;
858 }
859 STRUCT_FSET(st, num_of_entries, count);
860
861 rw_exit(&sockconf_lock);
862 if (ddi_copyout(STRUCT_BUF(st), (void *)socktable,
863 STRUCT_SIZE(st), 0) != 0)
864 return (EFAULT);
865
866 return (0);
867 }
868
869 se = kmem_alloc(count * sizeof (sockconfig_socktable_entry_t),
870 KM_SLEEP);
871 for (sp = list_head(&sphead); sp != NULL;
872 sp = list_next(&sphead, sp)) {
873 if (i >= count) {
874 /*
875 * Return if the number of entries has changed.
876 */
877 rw_exit(&sockconf_lock);
878 kmem_free(se,
879 count * sizeof (sockconfig_socktable_entry_t));
880 return (EAGAIN);
881 }
882 se[i].se_family = sp->sp_family;
883 se[i].se_type = sp->sp_type;
884 se[i].se_protocol = sp->sp_protocol;
885 (void) strncpy(se[i].se_modname, sp->sp_smod_name,
886 MODMAXNAMELEN);
887 if (sp->sp_sdev_info.sd_devpath != NULL)
888 (void) strncpy(se[i].se_strdev,
889 sp->sp_sdev_info.sd_devpath, MAXPATHLEN);
890 se[i].se_refcnt = sp->sp_refcnt;
891 se[i].se_flags = sp->sp_flags;
892 i++;
893 }
894 rw_exit(&sockconf_lock);
895 if (ddi_copyout(se, STRUCT_FGETP(st, st_entries),
896 i * sizeof (sockconfig_socktable_entry_t), 0) != 0)
897 ret = EFAULT;
898
899 STRUCT_FSET(st, num_of_entries, i);
900 kmem_free(se, count * sizeof (sockconfig_socktable_entry_t));
901
902 if (ddi_copyout(STRUCT_BUF(st), (void *)socktable,
903 STRUCT_SIZE(st), 0) != 0)
904 ret = EFAULT;
905
906 return (ret);
907 }
908