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 2009 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 */ 60 extern int kobj_path_exists(char *, int); 61 extern void nl7c_init(void); 62 extern int sockfs_defer_nl7c_init; 63 64 static int sockparams_sdev_init(struct sockparams *, char *, int); 65 static void sockparams_sdev_fini(struct sockparams *); 66 67 /* 68 * Global sockparams list (populated via soconfig(1M)). 69 */ 70 static list_t sphead; 71 static krwlock_t splist_lock; 72 73 /* 74 * List of ephemeral sockparams. 75 */ 76 static list_t sp_ephem_list; 77 static krwlock_t sp_ephem_lock; 78 79 /* Global kstats for sockparams */ 80 typedef struct sockparams_g_stats { 81 kstat_named_t spgs_ephem_nalloc; 82 kstat_named_t spgs_ephem_nreuse; 83 } sockparams_g_stats_t; 84 85 static sockparams_g_stats_t sp_g_stats; 86 static kstat_t *sp_g_kstat; 87 88 89 void 90 sockparams_init(void) 91 { 92 list_create(&sphead, sizeof (struct sockparams), 93 offsetof(struct sockparams, sp_node)); 94 list_create(&sp_ephem_list, sizeof (struct sockparams), 95 offsetof(struct sockparams, sp_node)); 96 97 rw_init(&splist_lock, NULL, RW_DEFAULT, NULL); 98 rw_init(&sp_ephem_lock, NULL, RW_DEFAULT, NULL); 99 100 kstat_named_init(&sp_g_stats.spgs_ephem_nalloc, "ephemeral_nalloc", 101 KSTAT_DATA_UINT64); 102 kstat_named_init(&sp_g_stats.spgs_ephem_nreuse, "ephemeral_nreuse", 103 KSTAT_DATA_UINT64); 104 105 sp_g_kstat = kstat_create("sockfs", 0, "sockparams", "misc", 106 KSTAT_TYPE_NAMED, sizeof (sp_g_stats) / sizeof (kstat_named_t), 107 KSTAT_FLAG_VIRTUAL); 108 if (sp_g_kstat == NULL) 109 return; 110 111 sp_g_kstat->ks_data = &sp_g_stats; 112 113 kstat_install(sp_g_kstat); 114 } 115 116 static int 117 sockparams_kstat_update(kstat_t *ksp, int rw) 118 { 119 struct sockparams *sp = ksp->ks_private; 120 sockparams_stats_t *sps = ksp->ks_data; 121 122 if (rw == KSTAT_WRITE) 123 return (EACCES); 124 125 sps->sps_nactive.value.ui64 = sp->sp_refcnt; 126 127 return (0); 128 } 129 130 /* 131 * Setup kstats for the given sockparams entry. 132 */ 133 static void 134 sockparams_kstat_init(struct sockparams *sp) 135 { 136 char name[KSTAT_STRLEN]; 137 138 (void) snprintf(name, KSTAT_STRLEN, "socket_%d_%d_%d", sp->sp_family, 139 sp->sp_type, sp->sp_protocol); 140 141 sp->sp_kstat = kstat_create("sockfs", 0, name, "misc", KSTAT_TYPE_NAMED, 142 sizeof (sockparams_stats_t) / sizeof (kstat_named_t), 143 KSTAT_FLAG_VIRTUAL); 144 145 if (sp->sp_kstat == NULL) 146 return; 147 148 sp->sp_kstat->ks_data = &sp->sp_stats; 149 sp->sp_kstat->ks_update = sockparams_kstat_update; 150 sp->sp_kstat->ks_private = sp; 151 kstat_install(sp->sp_kstat); 152 } 153 154 static void 155 sockparams_kstat_fini(struct sockparams *sp) 156 { 157 if (sp->sp_kstat != NULL) { 158 kstat_delete(sp->sp_kstat); 159 sp->sp_kstat = NULL; 160 } 161 } 162 163 /* 164 * sockparams_create(int family, int type, int protocol, char *modname, 165 * char *devpath, int devpathlen, int flags, int kmflags, int *errorp) 166 * 167 * Create a new sockparams entry. 168 * 169 * Arguments: 170 * family, type, protocol: specifies the socket type 171 * modname: Name of the module associated with the socket type. The 172 * module can be NULL if a device path is given, in which 173 * case the TPI module is used. 174 * devpath: Path to the STREAMS device. May be NULL for non-STREAMS 175 * based transports, or those transports that do not provide 176 * the capability to fallback to STREAMS. 177 * devpathlen: Length of the devpath string. The argument can be 0, 178 * indicating that devpath was allocated statically, and should 179 * not be freed when the sockparams entry is destroyed. 180 * 181 * flags : SOCKPARAMS_EPHEMERAL is the only flag that is allowed. 182 * kmflags: KM_{NO,}SLEEP 183 * errorp : Value-return argument, set when an error occurs. 184 * 185 * Returns: 186 * On success a new sockparams entry is returned, and *errorp is set 187 * to 0. On failure NULL is returned and *errorp is set to indicate the 188 * type of error that occured. 189 * 190 * Notes: 191 * devpath and modname are freed upon failure. 192 */ 193 struct sockparams * 194 sockparams_create(int family, int type, int protocol, char *modname, 195 char *devpath, int devpathlen, int flags, int kmflags, int *errorp) 196 { 197 struct sockparams *sp = NULL; 198 size_t size; 199 200 ASSERT((flags & ~SOCKPARAMS_EPHEMERAL) == 0); 201 if (flags & ~SOCKPARAMS_EPHEMERAL) { 202 *errorp = EINVAL; 203 goto error; 204 } 205 206 /* either a module or device must be given */ 207 if (modname == NULL && devpath == NULL) { 208 *errorp = EINVAL; 209 goto error; 210 } 211 212 sp = kmem_zalloc(sizeof (*sp), kmflags); 213 if (sp == NULL) { 214 *errorp = ENOMEM; 215 goto error; 216 } 217 sp->sp_family = family; 218 sp->sp_type = type; 219 sp->sp_protocol = protocol; 220 sp->sp_refcnt = 0; 221 sp->sp_flags = flags; 222 223 kstat_named_init(&sp->sp_stats.sps_nfallback, "nfallback", 224 KSTAT_DATA_UINT64); 225 kstat_named_init(&sp->sp_stats.sps_nactive, "nactive", 226 KSTAT_DATA_UINT64); 227 kstat_named_init(&sp->sp_stats.sps_ncreate, "ncreate", 228 KSTAT_DATA_UINT64); 229 230 /* 231 * Track how many ephemeral entries we have created. 232 */ 233 if (sp->sp_flags & SOCKPARAMS_EPHEMERAL) 234 sp_g_stats.spgs_ephem_nalloc.value.ui64++; 235 236 if (modname != NULL) { 237 sp->sp_smod_name = modname; 238 } else { 239 size = strlen(SOTPI_SMOD_NAME) + 1; 240 modname = kmem_zalloc(size, kmflags); 241 if (modname == NULL) { 242 *errorp = ENOMEM; 243 goto error; 244 } 245 sp->sp_smod_name = modname; 246 (void) sprintf(sp->sp_smod_name, "%s", SOTPI_SMOD_NAME); 247 } 248 249 if (devpath != NULL) { 250 /* Set up the device entry. */ 251 *errorp = sockparams_sdev_init(sp, devpath, devpathlen); 252 if (*errorp != 0) 253 goto error; 254 } 255 256 mutex_init(&sp->sp_lock, NULL, MUTEX_DEFAULT, NULL); 257 *errorp = 0; 258 return (sp); 259 error: 260 ASSERT(*errorp != 0); 261 if (modname != NULL) 262 kmem_free(modname, strlen(modname) + 1); 263 if (devpathlen != 0) 264 kmem_free(devpath, devpathlen); 265 if (sp != NULL) 266 kmem_free(sp, sizeof (*sp)); 267 return (NULL); 268 } 269 270 /* 271 * Initialize the STREAMS device aspect of the sockparams entry. 272 */ 273 static int 274 sockparams_sdev_init(struct sockparams *sp, char *devpath, int devpathlen) 275 { 276 vnode_t *vp = NULL; 277 int error; 278 279 ASSERT(devpath != NULL); 280 281 if ((error = sogetvp(devpath, &vp, UIO_SYSSPACE)) != 0) { 282 dprint(0, ("sockparams_sdev_init: vp %s failed with %d\n", 283 devpath, error)); 284 return (error); 285 } 286 287 ASSERT(vp != NULL); 288 sp->sp_sdev_info.sd_vnode = vp; 289 sp->sp_sdev_info.sd_devpath = devpath; 290 sp->sp_sdev_info.sd_devpathlen = devpathlen; 291 292 return (0); 293 } 294 295 /* 296 * sockparams_destroy(struct sockparams *sp) 297 * 298 * Releases all the resources associated with the sockparams entry, 299 * and frees the sockparams entry. 300 * 301 * Arguments: 302 * sp: the sockparams entry to destroy. 303 * 304 * Returns: 305 * Nothing. 306 * 307 * Locking: 308 * The sp_lock of the entry can not be held. 309 */ 310 void 311 sockparams_destroy(struct sockparams *sp) 312 { 313 ASSERT(sp->sp_refcnt == 0); 314 ASSERT(!list_link_active(&sp->sp_node)); 315 316 sockparams_sdev_fini(sp); 317 318 if (sp->sp_smod_info != NULL) 319 SMOD_DEC_REF(sp, sp->sp_smod_info); 320 kmem_free(sp->sp_smod_name, strlen(sp->sp_smod_name) + 1); 321 sp->sp_smod_name = NULL; 322 sp->sp_smod_info = NULL; 323 mutex_destroy(&sp->sp_lock); 324 sockparams_kstat_fini(sp); 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(&sp_ephem_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(&sp_ephem_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(&sp_ephem_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(&sp_ephem_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(&sp_ephem_lock); 474 475 sockparams_destroy(newsp); 476 } else { 477 SOCKPARAMS_INC_REF(newsp); 478 list_insert_tail(&sp_ephem_list, newsp); 479 rw_exit(&sp_ephem_lock); 480 481 sp = newsp; 482 } 483 ASSERT(*errorp == 0); 484 485 return (sp); 486 } 487 } 488 489 struct sockparams * 490 sockparams_hold_ephemeral_bydev(int family, int type, int protocol, 491 const char *dev, int kmflag, int *errorp) 492 { 493 return (sockparams_hold_ephemeral(family, type, protocol, dev, B_TRUE, 494 kmflag, errorp)); 495 } 496 497 struct sockparams * 498 sockparams_hold_ephemeral_bymod(int family, int type, int protocol, 499 const char *mod, int kmflag, int *errorp) 500 { 501 return (sockparams_hold_ephemeral(family, type, protocol, mod, B_FALSE, 502 kmflag, errorp)); 503 } 504 505 /* 506 * Called when the last socket using the ephemeral entry is dropping 507 * its' reference. To maintain lock order we must drop the sockparams 508 * lock before calling this function. As a result, a new reference 509 * might be placed on the entry, in which case there is nothing to 510 * do. However, if ref count goes to zero, we delete the entry. 511 */ 512 void 513 sockparams_ephemeral_drop_last_ref(struct sockparams *sp) 514 { 515 ASSERT(sp->sp_flags & SOCKPARAMS_EPHEMERAL); 516 ASSERT(MUTEX_NOT_HELD(&sp->sp_lock)); 517 518 rw_enter(&sp_ephem_lock, RW_WRITER); 519 mutex_enter(&sp->sp_lock); 520 521 if (--sp->sp_refcnt == 0) { 522 list_remove(&sp_ephem_list, sp); 523 mutex_exit(&sp->sp_lock); 524 rw_exit(&sp_ephem_lock); 525 526 sockparams_destroy(sp); 527 } else { 528 mutex_exit(&sp->sp_lock); 529 rw_exit(&sp_ephem_lock); 530 } 531 } 532 533 /* 534 * sockparams_add(struct sockparams *sp) 535 * 536 * Tries to add the given sockparams entry to the global list. 537 * 538 * Arguments: 539 * sp: the sockparms entry to add 540 * 541 * Returns: 542 * On success 0, but if an entry already exists, then EEXIST 543 * is returned. 544 * 545 * Locking: 546 * The caller can not be holding splist_lock. 547 */ 548 static int 549 sockparams_add(struct sockparams *sp) 550 { 551 ASSERT(!(sp->sp_flags & SOCKPARAMS_EPHEMERAL)); 552 553 rw_enter(&splist_lock, RW_WRITER); 554 if (sockparams_find(&sphead, sp->sp_family, sp->sp_type, 555 sp->sp_protocol, B_TRUE, NULL) != 0) { 556 rw_exit(&splist_lock); 557 return (EEXIST); 558 } else { 559 list_insert_tail(&sphead, sp); 560 rw_exit(&splist_lock); 561 return (0); 562 } 563 } 564 565 /* 566 * sockparams_delete(int family, int type, int protocol) 567 * 568 * Marks the sockparams entry for a specific family, type and protocol 569 * for deletion. The entry is removed from the list and destroyed 570 * if no one is holding a reference to it. 571 * 572 * Arguments: 573 * family, type, protocol: the socket type that should be removed. 574 * 575 * Returns: 576 * On success 0, otherwise ENXIO. 577 * 578 * Locking: 579 * Caller can not be holding splist_lock or the sp_lock of 580 * any sockparams entry. 581 */ 582 static int 583 sockparams_delete(int family, int type, int protocol) 584 { 585 struct sockparams *sp; 586 587 rw_enter(&splist_lock, RW_WRITER); 588 sp = sockparams_find(&sphead, family, type, protocol, B_TRUE, NULL); 589 590 if (sp != NULL) { 591 /* 592 * If no one is holding a reference to the entry, then 593 * we go ahead and remove it from the list and then 594 * destroy it. 595 */ 596 mutex_enter(&sp->sp_lock); 597 if (sp->sp_refcnt != 0) { 598 mutex_exit(&sp->sp_lock); 599 rw_exit(&splist_lock); 600 return (EBUSY); 601 } 602 mutex_exit(&sp->sp_lock); 603 /* Delete the sockparams entry. */ 604 list_remove(&sphead, sp); 605 rw_exit(&splist_lock); 606 607 sockparams_destroy(sp); 608 return (0); 609 } else { 610 rw_exit(&splist_lock); 611 return (ENXIO); 612 } 613 } 614 615 /* 616 * soconfig(int family, int type, int protocol, 617 * char *devpath, int devpathlen, char *module) 618 * 619 * Add or delete an entry to the sockparams table. 620 * When devpath and module both are NULL, it will delete an entry. 621 * 622 * Arguments: 623 * family, type, protocol: the tuple in question 624 * devpath: STREAMS device path. Can be NULL for module based sockets. 625 * module : Name of the socket module. Can be NULL for STREAMS 626 * based sockets. 627 * devpathlen: length of the devpath string, or 0 if devpath 628 * was statically allocated. 629 * 630 * Note: 631 * This routine assumes that the caller has kmem_alloced 632 * devpath (if devpathlen > 0) and module for this routine to 633 * consume. 634 */ 635 int 636 soconfig(int family, int type, int protocol, 637 char *devpath, int devpathlen, char *module) 638 { 639 struct sockparams *sp; 640 int error = 0; 641 642 dprint(0, ("soconfig(%d,%d,%d,%s,%d,%s)\n", 643 family, type, protocol, devpath, devpathlen, 644 module == NULL ? "NULL" : module)); 645 646 if (sockfs_defer_nl7c_init) { 647 nl7c_init(); 648 sockfs_defer_nl7c_init = 0; 649 } 650 651 if (devpath == NULL && module == NULL) { 652 /* 653 * Delete existing entry, 654 * both socket module and STEAMS device. 655 */ 656 ASSERT(module == NULL); 657 error = sockparams_delete(family, type, protocol); 658 } else { 659 /* 660 * Adding an entry 661 * sockparams_create frees mod name and devpath upon failure. 662 */ 663 sp = sockparams_create(family, type, protocol, module, 664 devpath, devpathlen, 0, KM_SLEEP, &error); 665 666 if (sp != NULL) { 667 /* 668 * The sockparams entry becomes globally visible once 669 * we call sockparams_add(). So we add a reference so 670 * we do not have to worry about the entry being 671 * immediately deleted. 672 */ 673 SOCKPARAMS_INC_REF(sp); 674 error = sockparams_add(sp); 675 if (error != 0) { 676 SOCKPARAMS_DEC_REF(sp); 677 sockparams_destroy(sp); 678 } else { 679 /* 680 * Unique sockparams entry, so init the kstats. 681 */ 682 sockparams_kstat_init(sp); 683 SOCKPARAMS_DEC_REF(sp); 684 } 685 } 686 } 687 688 return (error); 689 } 690 691 /* 692 * solookup(int family, int type, int protocol, struct sockparams **spp) 693 * 694 * Lookup an entry in the sockparams list based on the triple. The returned 695 * entry either exactly match the given tuple, or it is the 'default' entry 696 * for the given <family, type>. A default entry is on with a protocol 697 * value of zero. 698 * 699 * Arguments: 700 * family, type, protocol: tuple to search for 701 * spp: Value-return argument 702 * 703 * Returns: 704 * If an entry is found, 0 is returned and *spp is set to point to the 705 * entry. In case an entry is not found, *spp is set to NULL, and an 706 * error code is returned. The errors are (in decreasing precedence): 707 * EAFNOSUPPORT - address family not in list 708 * EPROTONOSUPPORT - address family supported but not protocol. 709 * EPROTOTYPE - address family and protocol supported but not socket type. 710 * 711 * TODO: should use ddi_modopen()/ddi_modclose() 712 */ 713 int 714 solookup(int family, int type, int protocol, struct sockparams **spp) 715 { 716 struct sockparams *sp = NULL; 717 int error = 0; 718 719 *spp = NULL; 720 rw_enter(&splist_lock, RW_READER); 721 722 /* 723 * Search the sockparams list for an appropiate entry. 724 * Hopefully we find an entry that match the exact family, 725 * type and protocol specified by the user, in which case 726 * we return that entry. However, we also keep track of 727 * the default entry for a specific family and type, the 728 * entry of which would have a protocol value of 0. 729 */ 730 sp = sockparams_find(&sphead, family, type, protocol, B_TRUE, NULL); 731 732 if (sp == NULL) { 733 int found = 0; 734 735 /* Determine correct error code */ 736 for (sp = list_head(&sphead); sp != NULL; 737 sp = list_next(&sphead, sp)) { 738 if (sp->sp_family == family && found < 1) 739 found = 1; 740 if (sp->sp_family == family && 741 sp->sp_protocol == protocol && found < 2) 742 found = 2; 743 } 744 rw_exit(&splist_lock); 745 switch (found) { 746 case 0: 747 error = EAFNOSUPPORT; 748 break; 749 case 1: 750 error = EPROTONOSUPPORT; 751 break; 752 case 2: 753 error = EPROTOTYPE; 754 break; 755 } 756 return (error); 757 } 758 759 /* 760 * An entry was found. 761 * 762 * We put a hold on the entry early on, so if the 763 * sockmod is not loaded, and we have to exit 764 * splist_lock to call modload(), we know that the 765 * sockparams entry wont go away. That way we don't 766 * have to look up the entry once we come back from 767 * modload(). 768 */ 769 SOCKPARAMS_INC_REF(sp); 770 rw_exit(&splist_lock); 771 772 if (sp->sp_smod_info == NULL) { 773 sp->sp_smod_info = smod_lookup_byname(sp->sp_smod_name); 774 if (sp->sp_smod_info == NULL) { 775 /* 776 * We put a hold on the sockparams entry 777 * earlier, hoping everything would work out. 778 * That obviously did not happen, so release 779 * the hold here. 780 */ 781 SOCKPARAMS_DEC_REF(sp); 782 /* 783 * We should probably mark the sockparams as 784 * "bad", and redo the lookup skipping the 785 * "bad" entries. I.e., sp->sp_mod_state |= BAD, 786 * return (solookup(...)) 787 */ 788 return (ENXIO); 789 } 790 } 791 792 /* 793 * Alright, we have a valid sockparams entry. 794 */ 795 *spp = sp; 796 return (0); 797 } 798