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
  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
 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
 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
 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 *
 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
 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
 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
 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 *
 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 *
 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 *
 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 *
 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
 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
 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
 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
 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
 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
 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
 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
 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
 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 }