xref: /titanic_41/usr/src/uts/common/fs/sockfs/sockparams.c (revision 8b368c0519e27194cc6a5a02ed74c25055f05dde)
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