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