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