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 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 */ 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 /* 80 * Mearch criteria used by sockparams_find() 81 */ 82 typedef 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 90 void 91 sockparams_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 */ 132 struct sockparams * 133 sockparams_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); 185 error: 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 */ 199 static int 200 sockparams_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 */ 236 void 237 sockparams_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 */ 257 static void 258 sockparams_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 */ 284 static struct sockparams * 285 sockparams_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 */ 334 static struct sockparams * 335 sockparams_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 426 struct sockparams * 427 sockparams_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 434 struct sockparams * 435 sockparams_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 */ 449 void 450 sockparams_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 */ 485 static int 486 sockparams_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 */ 519 static int 520 sockparams_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 */ 573 int 574 soconfig(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 637 int 638 solookup(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 } 724