blob: 2e1d11c64e93c1a4e292b70f0d4476c1eb65dfa6 [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/*
23 * Copyright 2008 Sun Microsystems, Inc. All rights reserved.
24 * 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
79/*
80 * Mearch criteria used by sockparams_find()
81 */
82typedef enum sp_match_criteria {
83 SP_MATCH_EXACT, /* family, type & proto must match */
84 SP_MATCH_WILDCARD, /* family & type must match, proto can be 0 */
85 SP_MATCH_INC_DEV, /* same as exact, but dev must also match */
86 SP_MATCH_INC_MOD /* same as exact, but mod must also match */
87} sp_match_criteria_t;
88
89
90void
91sockparams_init(void)
92{
93 list_create(&sphead, sizeof (struct sockparams),
94 offsetof(struct sockparams, sp_node));
95 list_create(&sp_ephem_list, sizeof (struct sockparams),
96 offsetof(struct sockparams, sp_node));
97
98 rw_init(&splist_lock, NULL, RW_DEFAULT, NULL);
99 rw_init(&sp_ephem_lock, NULL, RW_DEFAULT, NULL);
100}
101
102/*
103 * sockparams_create(int family, int type, int protocol, char *modname,
104 * char *devpath, int devpathlen, int flags, int kmflags, int *errorp)
105 *
106 * Create a new sockparams entry.
107 *
108 * Arguments:
109 * family, type, protocol: specifies the socket type
110 * modname: Name of the module associated with the socket type. The
111 * module can be NULL if a device path is given, in which
112 * case the TPI module is used.
113 * devpath: Path to the STREAMS device. May be NULL for non-STREAMS
114 * based transports, or those transports that do not provide
115 * the capability to fallback to STREAMS.
116 * devpathlen: Length of the devpath string. The argument can be 0,
117 * indicating that devpath was allocated statically, and should
118 * not be freed when the sockparams entry is destroyed.
119 *
120 * flags : SOCKPARAMS_EPHEMERAL is the only flag that is allowed.
121 * kmflags: KM_{NO,}SLEEP
122 * errorp : Value-return argument, set when an error occurs.
123 *
124 * Returns:
125 * On success a new sockparams entry is returned, and *errorp is set
126 * to 0. On failure NULL is returned and *errorp is set to indicate the
127 * type of error that occured.
128 *
129 * Notes:
130 * devpath and modname are freed upon failure.
131 */
132struct sockparams *
133sockparams_create(int family, int type, int protocol, char *modname,
134 char *devpath, int devpathlen, int flags, int kmflags, int *errorp)
135{
136 struct sockparams *sp = NULL;
137 size_t size;
138
139 ASSERT((flags & ~SOCKPARAMS_EPHEMERAL) == 0);
140 if (flags & ~SOCKPARAMS_EPHEMERAL) {
141 *errorp = EINVAL;
142 goto error;
143 }
144
145 /* either a module or device must be given */
146 if (modname == NULL && devpath == NULL) {
147 *errorp = EINVAL;
148 goto error;
149 }
150
151 sp = kmem_zalloc(sizeof (*sp), kmflags);
152 if (sp == NULL) {
153 *errorp = ENOMEM;
154 goto error;
155 }
156 sp->sp_family = family;
157 sp->sp_type = type;
158 sp->sp_protocol = protocol;
159 sp->sp_refcnt = 0;
160 sp->sp_flags = flags;
161
162 if (modname != NULL) {
163 sp->sp_smod_name = modname;
164 } else {
165 size = strlen(SOTPI_SMOD_NAME) + 1;
166 modname = kmem_zalloc(size, kmflags);
167 if (modname == NULL) {
168 *errorp = ENOMEM;
169 goto error;
170 }
171 sp->sp_smod_name = modname;
172 (void) sprintf(sp->sp_smod_name, "%s", SOTPI_SMOD_NAME);
173 }
174
175 if (devpath != NULL) {
176 /* Set up the device entry. */
177 *errorp = sockparams_sdev_init(sp, devpath, devpathlen);
178 if (*errorp != 0)
179 goto error;
180 }
181
182 mutex_init(&sp->sp_lock, NULL, MUTEX_DEFAULT, NULL);
183 *errorp = 0;
184 return (sp);
185error:
186 ASSERT(*errorp != 0);
187 if (modname != NULL)
188 kmem_free(modname, strlen(modname) + 1);
189 if (devpathlen != 0)
190 kmem_free(devpath, devpathlen);
191 if (sp != NULL)
192 kmem_free(sp, sizeof (*sp));
193 return (NULL);
194}
195
196/*
197 * Initialize the STREAMS device aspect of the sockparams entry.
198 */
199static int
200sockparams_sdev_init(struct sockparams *sp, char *devpath, int devpathlen)
201{
202 vnode_t *vp = NULL;
203 int error;
204
205 ASSERT(devpath != NULL);
206
207 if ((error = sogetvp(devpath, &vp, UIO_SYSSPACE)) != 0) {
208 dprint(0, ("sockparams_sdev_init: vp %s failed with %d\n",
209 devpath, error));
210 return (error);
211 }
212
213 ASSERT(vp != NULL);
214 sp->sp_sdev_info.sd_vnode = vp;
215 sp->sp_sdev_info.sd_devpath = devpath;
216 sp->sp_sdev_info.sd_devpathlen = devpathlen;
217
218 return (0);
219}
220
221/*
222 * sockparams_destroy(struct sockparams *sp)
223 *
224 * Releases all the resources associated with the sockparams entry,
225 * and frees the sockparams entry.
226 *
227 * Arguments:
228 * sp: the sockparams entry to destroy.
229 *
230 * Returns:
231 * Nothing.
232 *
233 * Locking:
234 * The sp_lock of the entry can not be held.
235 */
236void
237sockparams_destroy(struct sockparams *sp)
238{
239 ASSERT(sp->sp_refcnt == 0);
240 ASSERT(!list_link_active(&sp->sp_node));
241
242 sockparams_sdev_fini(sp);
243
244 if (sp->sp_smod_info != NULL)
245 SMOD_DEC_REF(sp, sp->sp_smod_info);
246 kmem_free(sp->sp_smod_name, strlen(sp->sp_smod_name) + 1);
247 sp->sp_smod_name = NULL;
248 sp->sp_smod_info = NULL;
249 mutex_destroy(&sp->sp_lock);
250
251 kmem_free(sp, sizeof (*sp));
252}
253
254/*
255 * Clean up the STREAMS device part of the sockparams entry.
256 */
257static void
258sockparams_sdev_fini(struct sockparams *sp)
259{
260 sdev_info_t sd;
261
262 /*
263 * if the entry does not have a STREAMS device, then there
264 * is nothing to do.
265 */
266 if (!SOCKPARAMS_HAS_DEVICE(sp))
267 return;
268
269 sd = sp->sp_sdev_info;
270 if (sd.sd_vnode != NULL)
271 VN_RELE(sd.sd_vnode);
272 if (sd.sd_devpathlen != 0)
273 kmem_free(sd.sd_devpath, sd.sd_devpathlen);
274
275 sp->sp_sdev_info.sd_vnode = NULL;
276 sp->sp_sdev_info.sd_devpath = NULL;
277}
278
279/*
280 * Look for a matching sockparams entry on the given list.
281 *
282 * The caller must hold the associated list lock.
283 */
284static struct sockparams *
285sockparams_find(list_t *list, int family, int type, int protocol,
286 enum sp_match_criteria crit, const char *name)
287{
288 struct sockparams *sp;
289 struct sockparams *wild = NULL;
290
291 for (sp = list_head(list); sp != NULL; sp = list_next(list, sp)) {
292 if (sp->sp_family == family &&
293 sp->sp_type == type) {
294
295 if (sp->sp_protocol == protocol) {
296 if (crit == SP_MATCH_EXACT ||
297 crit == SP_MATCH_WILDCARD)
298 break;
299 else if (crit == SP_MATCH_INC_DEV &&
300 sp->sp_sdev_info.sd_devpath != NULL &&
301 strcmp(sp->sp_sdev_info.sd_devpath,
302 name) == 0)
303 break;
304 else if (crit == SP_MATCH_INC_MOD &&
305 strcmp(sp->sp_smod_name, name) == 0)
306 break;
307 } else if (crit == SP_MATCH_WILDCARD &&
308 sp->sp_protocol == 0) {
309 /* best match so far */
310 wild = sp;
311 }
312 }
313 }
314
315 return ((sp == NULL) ? wild : sp);
316}
317
318/*
319 * sockparams_hold_ephemeral()
320 *
321 * Returns an ephemeral sockparams entry of the requested family, type and
322 * protocol. The entry is returned held, and the caller is responsible for
323 * dropping the reference using SOCKPARAMS_DEC_REF() once done.
324 *
325 * All ephemeral entries are on list (sp_ephem_list). If there is an
326 * entry on the list that match the search criteria, then a reference is
327 * placed on that entry. Otherwise, a new entry is created and inserted
328 * in the list. The entry is removed from the list when the last reference
329 * is dropped.
330 *
331 * The tpi flag is used to determine whether name refers to a device or
332 * module name.
333 */
334static struct sockparams *
335sockparams_hold_ephemeral(int family, int type, int protocol,
336 const char *name, boolean_t tpi, int kmflag, int *errorp)
337{
338 struct sockparams *sp = NULL;
339 sp_match_criteria_t crit = (tpi) ? SP_MATCH_INC_DEV : SP_MATCH_INC_MOD;
340
341 *errorp = 0;
342
343 /*
344 * First look for an existing entry
345 */
346 rw_enter(&sp_ephem_lock, RW_READER);
347 sp = sockparams_find(&sp_ephem_list, family, type, protocol,
348 crit, name);
349 if (sp != NULL) {
350 SOCKPARAMS_INC_REF(sp);
351 rw_exit(&sp_ephem_lock);
352
353 return (sp);
354 } else {
355 struct sockparams *newsp = NULL;
356 char *namebuf = NULL;
357 int namelen = 0;
358
359 rw_exit(&sp_ephem_lock);
360
361 namelen = strlen(name) + 1;
362 namebuf = kmem_alloc(namelen, kmflag);
363 if (namebuf == NULL) {
364 *errorp = ENOMEM;
365 return (NULL);
366 }
367
368 (void *)strncpy(namebuf, name, namelen);
369 if (tpi) {
370 newsp = sockparams_create(family, type,
371 protocol, NULL, namebuf, namelen,
372 SOCKPARAMS_EPHEMERAL, kmflag, errorp);
373 } else {
374 newsp = sockparams_create(family, type,
375 protocol, namebuf, NULL, 0,
376 SOCKPARAMS_EPHEMERAL, kmflag, errorp);
377 }
378
379 if (newsp == NULL) {
380 ASSERT(*errorp != 0);
381 return (NULL);
382 }
383
384 /*
385 * Time to load the socket module.
386 */
387 ASSERT(newsp->sp_smod_info == NULL);
388 newsp->sp_smod_info =
389 smod_lookup_byname(newsp->sp_smod_name);
390 if (newsp->sp_smod_info == NULL) {
391 /* Failed to load */
392 sockparams_destroy(newsp);
393 *errorp = ENXIO;
394 return (NULL);
395 }
396
397 /*
398 * The sockparams entry was created, now try to add it
399 * to the list. We need to hold the lock as a WRITER.
400 */
401 rw_enter(&sp_ephem_lock, RW_WRITER);
402 sp = sockparams_find(&sp_ephem_list, family, type, protocol,
403 crit, name);
404 if (sp != NULL) {
405 /*
406 * Someone has requested a matching entry, so just
407 * place a hold on it and release the entry we alloc'ed.
408 */
409 SOCKPARAMS_INC_REF(sp);
410 rw_exit(&sp_ephem_lock);
411
412 sockparams_destroy(newsp);
413 } else {
414 SOCKPARAMS_INC_REF(newsp);
415 list_insert_tail(&sp_ephem_list, newsp);
416 rw_exit(&sp_ephem_lock);
417
418 sp = newsp;
419 }
420 ASSERT(*errorp == 0);
421
422 return (sp);
423 }
424}
425
426struct sockparams *
427sockparams_hold_ephemeral_bydev(int family, int type, int protocol,
428 const char *dev, int kmflag, int *errorp)
429{
430 return (sockparams_hold_ephemeral(family, type, protocol, dev, B_TRUE,
431 kmflag, errorp));
432}
433
434struct sockparams *
435sockparams_hold_ephemeral_bymod(int family, int type, int protocol,
436 const char *mod, int kmflag, int *errorp)
437{
438 return (sockparams_hold_ephemeral(family, type, protocol, mod, B_FALSE,
439 kmflag, errorp));
440}
441
442/*
443 * Called when the last socket using the ephemeral entry is dropping
444 * its' reference. To maintain lock order we must drop the sockparams
445 * lock before calling this function. As a result, a new reference
446 * might be placed on the entry, in which case there is nothing to
447 * do. However, if ref count goes to zero, we delete the entry.
448 */
449void
450sockparams_ephemeral_drop_last_ref(struct sockparams *sp)
451{
452 ASSERT(sp->sp_flags & SOCKPARAMS_EPHEMERAL);
453 ASSERT(MUTEX_NOT_HELD(&sp->sp_lock));
454
455 rw_enter(&sp_ephem_lock, RW_WRITER);
456 mutex_enter(&sp->sp_lock);
457
458 if (--sp->sp_refcnt == 0) {
459 list_remove(&sp_ephem_list, sp);
460 mutex_exit(&sp->sp_lock);
461 rw_exit(&sp_ephem_lock);
462
463 sockparams_destroy(sp);
464 } else {
465 mutex_exit(&sp->sp_lock);
466 rw_exit(&sp_ephem_lock);
467 }
468}
469
470/*
471 * sockparams_add(struct sockparams *sp)
472 *
473 * Tries to add the given sockparams entry to the global list.
474 *
475 * Arguments:
476 * sp: the sockparms entry to add
477 *
478 * Returns:
479 * On success 0, but if an entry already exists, then EEXIST
480 * is returned.
481 *
482 * Locking:
483 * The caller can not be holding splist_lock.
484 */
485static int
486sockparams_add(struct sockparams *sp)
487{
488 ASSERT(!(sp->sp_flags & SOCKPARAMS_EPHEMERAL));
489
490 rw_enter(&splist_lock, RW_WRITER);
491 if (sockparams_find(&sphead, sp->sp_family, sp->sp_type,
492 sp->sp_protocol, SP_MATCH_EXACT, NULL) != 0) {
493 rw_exit(&splist_lock);
494 return (EEXIST);
495 } else {
496 list_insert_tail(&sphead, sp);
497 rw_exit(&splist_lock);
498 return (0);
499 }
500}
501
502/*
503 * sockparams_delete(int family, int type, int protocol)
504 *
505 * Marks the sockparams entry for a specific family, type and protocol
506 * for deletion. The entry is removed from the list and destroyed
507 * if no one is holding a reference to it.
508 *
509 * Arguments:
510 * family, type, protocol: the socket type that should be removed.
511 *
512 * Returns:
513 * On success 0, otherwise ENXIO.
514 *
515 * Locking:
516 * Caller can not be holding splist_lock or the sp_lock of
517 * any sockparams entry.
518 */
519static int
520sockparams_delete(int family, int type, int protocol)
521{
522 struct sockparams *sp;
523
524 rw_enter(&splist_lock, RW_WRITER);
525 sp = sockparams_find(&sphead, family, type, protocol, SP_MATCH_EXACT,
526 NULL);
527
528 if (sp != NULL) {
529 /*
530 * If no one is holding a reference to the entry, then
531 * we go ahead and remove it from the list and then
532 * destroy it.
533 */
534 mutex_enter(&sp->sp_lock);
535 if (sp->sp_refcnt != 0) {
536 mutex_exit(&sp->sp_lock);
537 rw_exit(&splist_lock);
538 return (EBUSY);
539 }
540 mutex_exit(&sp->sp_lock);
541 /* Delete the sockparams entry. */
542 list_remove(&sphead, sp);
543 rw_exit(&splist_lock);
544
545 sockparams_destroy(sp);
546 return (0);
547 } else {
548 rw_exit(&splist_lock);
549 return (ENXIO);
550 }
551}
552
553/*
554 * soconfig(int family, int type, int protocol,
555 * char *devpath, int devpathlen, char *module)
556 *
557 * Add or delete an entry to the sockparams table.
558 * When devpath and module both are NULL, it will delete an entry.
559 *
560 * Arguments:
561 * family, type, protocol: the tuple in question
562 * devpath: STREAMS device path. Can be NULL for module based sockets.
563 * module : Name of the socket module. Can be NULL for STREAMS
564 * based sockets.
565 * devpathlen: length of the devpath string, or 0 if devpath
566 * was statically allocated.
567 *
568 * Note:
569 * This routine assumes that the caller has kmem_alloced
570 * devpath (if devpathlen > 0) and module for this routine to
571 * consume.
572 */
573int
574soconfig(int family, int type, int protocol,
575 char *devpath, int devpathlen, char *module)
576{
577 struct sockparams *sp;
578 int error = 0;
579
580 dprint(0, ("soconfig(%d,%d,%d,%s,%d,%s)\n",
581 family, type, protocol, devpath, devpathlen,
582 module == NULL ? "NULL" : module));
583
584 if (sockfs_defer_nl7c_init) {
585 nl7c_init();
586 sockfs_defer_nl7c_init = 0;
587 }
588
589 if (devpath == NULL && module == NULL) {
590 /*
591 * Delete existing entry,
592 * both socket module and STEAMS device.
593 */
594 ASSERT(module == NULL);
595 error = sockparams_delete(family, type, protocol);
596 } else {
597 /*
598 * Adding an entry
599 * sockparams_create frees mod name and devpath upon failure.
600 */
601 sp = sockparams_create(family, type, protocol, module,
602 devpath, devpathlen, 0, KM_SLEEP, &error);
603
604 if (sp != NULL) {
605 error = sockparams_add(sp);
606 if (error != 0)
607 sockparams_destroy(sp);
608 }
609 }
610
611 return (error);
612}
613
614/*
615 * solookup(int family, int type, int protocol, struct sockparams **spp)
616 *
617 * Lookup an entry in the sockparams list based on the triple. The returned
618 * entry either exactly match the given tuple, or it is the 'default' entry
619 * for the given <family, type>. A default entry is on with a protocol
620 * value of zero.
621 *
622 * Arguments:
623 * family, type, protocol: tuple to search for
624 * spp: Value-return argument
625 *
626 * Returns:
627 * If an entry is found, 0 is returned and *spp is set to point to the
628 * entry. In case an entry is not found, *spp is set to NULL, and an
629 * error code is returned. The errors are (in decreasing precedence):
630 * EAFNOSUPPORT - address family not in list
631 * EPROTONOSUPPORT - address family supported but not protocol.
632 * EPROTOTYPE - address family and protocol supported but not socket type.
633 *
634 * TODO: should use ddi_modopen()/ddi_modclose()
635 */
636
637int
638solookup(int family, int type, int protocol, struct sockparams **spp)
639{
640 struct sockparams *sp = NULL;
641 int error = 0;
642
643 *spp = NULL;
644 rw_enter(&splist_lock, RW_READER);
645
646 /*
647 * Search the sockparams list for an appropiate entry.
648 * Hopefully we find an entry that match the exact family,
649 * type and protocol specified by the user, in which case
650 * we return that entry. However, we also keep track of
651 * the default entry for a specific family and type, the
652 * entry of which would have a protocol value of 0.
653 */
654 sp = sockparams_find(&sphead, family, type, protocol, SP_MATCH_WILDCARD,
655 NULL);
656
657 if (sp == NULL) {
658 int found = 0;
659
660 /* Determine correct error code */
661 for (sp = list_head(&sphead); sp != NULL;
662 sp = list_next(&sphead, sp)) {
663 if (sp->sp_family == family && found < 1)
664 found = 1;
665 if (sp->sp_family == family &&
666 sp->sp_protocol == protocol && found < 2)
667 found = 2;
668 }
669 rw_exit(&splist_lock);
670
671 switch (found) {
672 case 0:
673 error = EAFNOSUPPORT;
674 break;
675 case 1:
676 error = EPROTONOSUPPORT;
677 break;
678 case 2:
679 error = EPROTOTYPE;
680 break;
681 }
682 return (error);
683 }
684
685 /*
686 * An entry was found.
687 *
688 * We put a hold on the entry early on, so if the
689 * sockmod is not loaded, and we have to exit
690 * splist_lock to call modload(), we know that the
691 * sockparams entry wont go away. That way we don't
692 * have to look up the entry once we come back from
693 * modload().
694 */
695 SOCKPARAMS_INC_REF(sp);
696 rw_exit(&splist_lock);
697
698 if (sp->sp_smod_info == NULL) {
699 sp->sp_smod_info = smod_lookup_byname(sp->sp_smod_name);
700 if (sp->sp_smod_info == NULL) {
701 /*
702 * We put a hold on the sockparams entry
703 * earlier, hoping everything would work out.
704 * That obviously did not happen, so release
705 * the hold here.
706 */
707 SOCKPARAMS_DEC_REF(sp);
708 /*
709 * We should probably mark the sockparams as
710 * "bad", and redo the lookup skipping the
711 * "bad" entries. I.e., sp->sp_mod_state |= BAD,
712 * return (solookup(...))
713 */
714 return (ENXIO);
715 }
716 }
717
718 /*
719 * Alright, we have a valid sockparams entry.
720 */
721 *spp = sp;
722 return (0);
723}