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 * Copyright 2007 Sun Microsystems, Inc. All rights reserved. 23 * Use is subject to license terms. 24 */ 25 #pragma ident "%Z%%M% %I% %E% SMI" 26 27 #include <sys/param.h> 28 #include <sys/types.h> 29 #include <sys/systm.h> 30 #include <sys/errno.h> 31 #include <sys/kmem.h> 32 #include <sys/mutex.h> 33 #include <sys/condvar.h> 34 #include <sys/modctl.h> 35 #include <sys/hook_impl.h> 36 #include <sys/sdt.h> 37 38 /* 39 * This file provides kernel hook framework. 40 */ 41 42 static struct modldrv modlmisc = { 43 &mod_miscops, /* drv_modops */ 44 "Hooks Interface v1.0", /* drv_linkinfo */ 45 }; 46 47 static struct modlinkage modlinkage = { 48 MODREV_1, /* ml_rev */ 49 &modlmisc, /* ml_linkage */ 50 NULL 51 }; 52 53 /* 54 * Hook internal functions 55 */ 56 static hook_int_t *hook_copy(hook_t *src); 57 static hook_event_int_t *hook_event_checkdup(hook_event_t *he, 58 hook_stack_t *hks); 59 static hook_event_int_t *hook_event_copy(hook_event_t *src); 60 static hook_event_int_t *hook_event_find(hook_family_int_t *hfi, char *event); 61 static void hook_event_free(hook_event_int_t *hei); 62 static hook_family_int_t *hook_family_copy(hook_family_t *src); 63 static hook_family_int_t *hook_family_find(char *family, hook_stack_t *hks); 64 static void hook_family_free(hook_family_int_t *hfi); 65 static hook_int_t *hook_find(hook_event_int_t *hei, hook_t *h); 66 static void hook_free(hook_int_t *hi); 67 static void hook_init(void); 68 static void hook_fini(void); 69 static void *hook_stack_init(netstackid_t stackid, netstack_t *ns); 70 static void hook_stack_fini(netstackid_t stackid, void *arg); 71 72 /* 73 * Module entry points. 74 */ 75 int 76 _init(void) 77 { 78 int error; 79 80 hook_init(); 81 error = mod_install(&modlinkage); 82 if (error != 0) 83 hook_fini(); 84 85 return (error); 86 } 87 88 89 int 90 _fini(void) 91 { 92 int error; 93 94 error = mod_remove(&modlinkage); 95 if (error == 0) 96 hook_fini(); 97 98 return (error); 99 } 100 101 102 int 103 _info(struct modinfo *modinfop) 104 { 105 return (mod_info(&modlinkage, modinfop)); 106 } 107 108 109 /* 110 * Function: hook_init 111 * Returns: None 112 * Parameters: None 113 * 114 * Initialize hooks 115 */ 116 static void 117 hook_init(void) 118 { 119 /* 120 * We want to be informed each time a stack is created or 121 * destroyed in the kernel. 122 */ 123 netstack_register(NS_HOOK, hook_stack_init, NULL, 124 hook_stack_fini); 125 } 126 127 /* 128 * Function: hook_fini 129 * Returns: None 130 * Parameters: None 131 * 132 * Deinitialize hooks 133 */ 134 static void 135 hook_fini(void) 136 { 137 netstack_unregister(NS_HOOK); 138 } 139 140 /* 141 * Initialize the hook stack instance. 142 */ 143 /*ARGSUSED*/ 144 static void * 145 hook_stack_init(netstackid_t stackid, netstack_t *ns) 146 { 147 hook_stack_t *hks; 148 149 #ifdef NS_DEBUG 150 printf("hook_stack_init(stack %d)\n", stackid); 151 #endif 152 153 hks = (hook_stack_t *)kmem_zalloc(sizeof (*hks), KM_SLEEP); 154 hks->hk_netstack = ns; 155 156 CVW_INIT(&hks->hks_familylock); 157 SLIST_INIT(&hks->hks_familylist); 158 159 return (hks); 160 } 161 162 /* 163 * Free the hook stack instance. 164 */ 165 /*ARGSUSED*/ 166 static void 167 hook_stack_fini(netstackid_t stackid, void *arg) 168 { 169 hook_stack_t *hks = (hook_stack_t *)arg; 170 #ifdef NS_DEBUG 171 printf("hook_stack_fini(%p, stack %d)\n", arg, stackid); 172 #endif 173 CVW_DESTROY(&hks->hks_familylock); 174 kmem_free(hks, sizeof (*hks)); 175 } 176 177 /* 178 * Function: hook_run 179 * Returns: int - return value according to callback func 180 * Parameters: token(I) - event pointer 181 * info(I) - message 182 * 183 * Run hooks for specific provider. The hooks registered are stepped through 184 * until either the end of the list is reached or a hook function returns a 185 * non-zero value. If a non-zero value is returned from a hook function, we 186 * return that value back to our caller. By design, a hook function can be 187 * called more than once, simultaneously. 188 */ 189 int 190 hook_run(hook_event_token_t token, hook_data_t info, netstack_t *ns) 191 { 192 hook_int_t *hi; 193 hook_event_int_t *hei; 194 hook_stack_t *hks = ns->netstack_hook; 195 int rval = 0; 196 197 ASSERT(token != NULL); 198 199 hei = (hook_event_int_t *)token; 200 DTRACE_PROBE2(hook__run__start, 201 hook_event_token_t, token, 202 hook_data_t, info); 203 204 /* Hold global read lock to ensure event will not be deleted */ 205 CVW_ENTER_READ(&hks->hks_familylock); 206 207 /* Hold event read lock to ensure hook will not be changed */ 208 CVW_ENTER_READ(&hei->hei_lock); 209 210 TAILQ_FOREACH(hi, &hei->hei_head, hi_entry) { 211 ASSERT(hi->hi_hook.h_func != NULL); 212 DTRACE_PROBE3(hook__func__start, 213 hook_event_token_t, token, 214 hook_data_t, info, 215 hook_int_t *, hi); 216 rval = (*hi->hi_hook.h_func)(token, info, ns); 217 DTRACE_PROBE4(hook__func__end, 218 hook_event_token_t, token, 219 hook_data_t, info, 220 hook_int_t *, hi, 221 int, rval); 222 if (rval != 0) 223 break; 224 } 225 226 CVW_EXIT_READ(&hei->hei_lock); 227 CVW_EXIT_READ(&hks->hks_familylock); 228 229 DTRACE_PROBE3(hook__run__end, 230 hook_event_token_t, token, 231 hook_data_t, info, 232 hook_int_t *, hi); 233 234 return (rval); 235 } 236 237 238 /* 239 * Function: hook_family_add 240 * Returns: internal family pointer - NULL = Fail 241 * Parameters: hf(I) - family pointer 242 * 243 * Add new family to family list 244 */ 245 hook_family_int_t * 246 hook_family_add(hook_family_t *hf, hook_stack_t *hks) 247 { 248 hook_family_int_t *hfi, *new; 249 250 ASSERT(hf != NULL); 251 ASSERT(hf->hf_name != NULL); 252 253 new = hook_family_copy(hf); 254 if (new == NULL) 255 return (NULL); 256 257 CVW_ENTER_WRITE(&hks->hks_familylock); 258 259 /* search family list */ 260 hfi = hook_family_find(hf->hf_name, hks); 261 if (hfi != NULL) { 262 CVW_EXIT_WRITE(&hks->hks_familylock); 263 hook_family_free(new); 264 return (NULL); 265 } 266 267 new->hfi_ptr = (void *)hks; 268 269 /* Add to family list head */ 270 SLIST_INSERT_HEAD(&hks->hks_familylist, new, hfi_entry); 271 272 CVW_EXIT_WRITE(&hks->hks_familylock); 273 return (new); 274 } 275 276 277 /* 278 * Function: hook_family_remove 279 * Returns: int - 0 = Succ, Else = Fail 280 * Parameters: hfi(I) - internal family pointer 281 * 282 * Remove family from family list 283 */ 284 int 285 hook_family_remove(hook_family_int_t *hfi) 286 { 287 hook_stack_t *hks; 288 289 ASSERT(hfi != NULL); 290 hks = (hook_stack_t *)hfi->hfi_ptr; 291 292 CVW_ENTER_WRITE(&hks->hks_familylock); 293 294 /* Check if there are events */ 295 if (!SLIST_EMPTY(&hfi->hfi_head)) { 296 CVW_EXIT_WRITE(&hks->hks_familylock); 297 return (EBUSY); 298 } 299 300 /* Remove from family list */ 301 SLIST_REMOVE(&hks->hks_familylist, hfi, hook_family_int, hfi_entry); 302 303 CVW_EXIT_WRITE(&hks->hks_familylock); 304 hook_family_free(hfi); 305 306 return (0); 307 } 308 309 310 /* 311 * Function: hook_family_copy 312 * Returns: internal family pointer - NULL = Failed 313 * Parameters: src(I) - family pointer 314 * 315 * Allocate internal family block and duplicate incoming family 316 * No locks should be held across this function as it may sleep. 317 */ 318 static hook_family_int_t * 319 hook_family_copy(hook_family_t *src) 320 { 321 hook_family_int_t *new; 322 hook_family_t *dst; 323 324 ASSERT(src != NULL); 325 ASSERT(src->hf_name != NULL); 326 327 new = (hook_family_int_t *)kmem_zalloc(sizeof (*new), KM_SLEEP); 328 329 /* Copy body */ 330 SLIST_INIT(&new->hfi_head); 331 dst = &new->hfi_family; 332 *dst = *src; 333 334 /* Copy name */ 335 dst->hf_name = (char *)kmem_alloc(strlen(src->hf_name) + 1, KM_SLEEP); 336 (void) strcpy(dst->hf_name, src->hf_name); 337 338 return (new); 339 } 340 341 342 /* 343 * Returns: internal family pointer - NULL = Not match 344 * Parameters: family(I) - family name string 345 * 346 * Search family list with family name 347 * A lock on familylock must be held when called. 348 */ 349 static hook_family_int_t * 350 hook_family_find(char *family, hook_stack_t *hks) 351 { 352 hook_family_int_t *hfi = NULL; 353 354 ASSERT(family != NULL); 355 356 SLIST_FOREACH(hfi, &hks->hks_familylist, hfi_entry) { 357 if (strcmp(hfi->hfi_family.hf_name, family) == 0) 358 break; 359 } 360 return (hfi); 361 } 362 363 364 /* 365 * Function: hook_family_free 366 * Returns: None 367 * Parameters: hfi(I) - internal family pointer 368 * 369 * Free alloc memory for family 370 */ 371 static void 372 hook_family_free(hook_family_int_t *hfi) 373 { 374 ASSERT(hfi != NULL); 375 376 /* Free name space */ 377 if (hfi->hfi_family.hf_name != NULL) { 378 kmem_free(hfi->hfi_family.hf_name, 379 strlen(hfi->hfi_family.hf_name) + 1); 380 } 381 382 /* Free container */ 383 kmem_free(hfi, sizeof (*hfi)); 384 } 385 386 387 /* 388 * Function: hook_event_add 389 * Returns: internal event pointer - NULL = Fail 390 * Parameters: hfi(I) - internal family pointer 391 * he(I) - event pointer 392 * 393 * Add new event to event list on specific family. 394 * This function can fail to return successfully if (1) it cannot allocate 395 * enough memory for its own internal data structures, (2) the event has 396 * already been registered (for any hook family.) 397 */ 398 hook_event_int_t * 399 hook_event_add(hook_family_int_t *hfi, hook_event_t *he) 400 { 401 hook_stack_t *hks; 402 hook_event_int_t *hei, *new; 403 404 ASSERT(hfi != NULL); 405 ASSERT(he != NULL); 406 ASSERT(he->he_name != NULL); 407 hks = (hook_stack_t *)hfi->hfi_ptr; 408 409 new = hook_event_copy(he); 410 if (new == NULL) 411 return (NULL); 412 413 CVW_ENTER_WRITE(&hks->hks_familylock); 414 415 /* Check whether this event pointer is already registered */ 416 hei = hook_event_checkdup(he, hks); 417 if (hei != NULL) { 418 CVW_EXIT_WRITE(&hks->hks_familylock); 419 hook_event_free(new); 420 return (NULL); 421 } 422 423 /* Add to event list head */ 424 SLIST_INSERT_HEAD(&hfi->hfi_head, new, hei_entry); 425 426 CVW_EXIT_WRITE(&hks->hks_familylock); 427 return (new); 428 } 429 430 431 /* 432 * Function: hook_event_remove 433 * Returns: int - 0 = Succ, Else = Fail 434 * Parameters: hfi(I) - internal family pointer 435 * he(I) - event pointer 436 * 437 * Remove event from event list on specific family 438 */ 439 int 440 hook_event_remove(hook_family_int_t *hfi, hook_event_t *he) 441 { 442 hook_stack_t *hks; 443 hook_event_int_t *hei; 444 445 ASSERT(hfi != NULL); 446 ASSERT(he != NULL); 447 hks = (hook_stack_t *)hfi->hfi_ptr; 448 449 CVW_ENTER_WRITE(&hks->hks_familylock); 450 451 hei = hook_event_find(hfi, he->he_name); 452 if (hei == NULL) { 453 CVW_EXIT_WRITE(&hks->hks_familylock); 454 return (ENXIO); 455 } 456 457 /* Check if there are registered hooks for this event */ 458 if (!TAILQ_EMPTY(&hei->hei_head)) { 459 CVW_EXIT_WRITE(&hks->hks_familylock); 460 return (EBUSY); 461 } 462 463 /* Remove from event list */ 464 SLIST_REMOVE(&hfi->hfi_head, hei, hook_event_int, hei_entry); 465 466 CVW_EXIT_WRITE(&hks->hks_familylock); 467 hook_event_free(hei); 468 469 return (0); 470 } 471 472 473 /* 474 * Function: hook_event_checkdup 475 * Returns: internal event pointer - NULL = Not match 476 * Parameters: he(I) - event pointer 477 * 478 * Search whole list with event pointer 479 * A lock on familylock must be held when called. 480 */ 481 static hook_event_int_t * 482 hook_event_checkdup(hook_event_t *he, hook_stack_t *hks) 483 { 484 hook_family_int_t *hfi; 485 hook_event_int_t *hei; 486 487 ASSERT(he != NULL); 488 489 SLIST_FOREACH(hfi, &hks->hks_familylist, hfi_entry) { 490 SLIST_FOREACH(hei, &hfi->hfi_head, hei_entry) { 491 if (hei->hei_event == he) 492 return (hei); 493 } 494 } 495 496 return (NULL); 497 } 498 499 500 /* 501 * Function: hook_event_copy 502 * Returns: internal event pointer - NULL = Failed 503 * Parameters: src(I) - event pointer 504 * 505 * Allocate internal event block and duplicate incoming event 506 * No locks should be held across this function as it may sleep. 507 */ 508 static hook_event_int_t * 509 hook_event_copy(hook_event_t *src) 510 { 511 hook_event_int_t *new; 512 513 ASSERT(src != NULL); 514 ASSERT(src->he_name != NULL); 515 516 new = (hook_event_int_t *)kmem_zalloc(sizeof (*new), KM_SLEEP); 517 518 /* Copy body */ 519 TAILQ_INIT(&new->hei_head); 520 new->hei_event = src; 521 522 return (new); 523 } 524 525 526 /* 527 * Function: hook_event_find 528 * Returns: internal event pointer - NULL = Not match 529 * Parameters: hfi(I) - internal family pointer 530 * event(I) - event name string 531 * 532 * Search event list with event name 533 * A lock on hks->hks_familylock must be held when called. 534 */ 535 static hook_event_int_t * 536 hook_event_find(hook_family_int_t *hfi, char *event) 537 { 538 hook_event_int_t *hei = NULL; 539 540 ASSERT(hfi != NULL); 541 ASSERT(event != NULL); 542 543 SLIST_FOREACH(hei, &hfi->hfi_head, hei_entry) { 544 if (strcmp(hei->hei_event->he_name, event) == 0) 545 break; 546 } 547 return (hei); 548 } 549 550 551 /* 552 * Function: hook_event_free 553 * Returns: None 554 * Parameters: hei(I) - internal event pointer 555 * 556 * Free alloc memory for event 557 */ 558 static void 559 hook_event_free(hook_event_int_t *hei) 560 { 561 ASSERT(hei != NULL); 562 563 /* Free container */ 564 kmem_free(hei, sizeof (*hei)); 565 } 566 567 568 /* 569 * Function: hook_register 570 * Returns: int- 0 = Succ, Else = Fail 571 * Parameters: hfi(I) - internal family pointer 572 * event(I) - event name string 573 * h(I) - hook pointer 574 * 575 * Add new hook to hook list on spefic family, event 576 */ 577 int 578 hook_register(hook_family_int_t *hfi, char *event, hook_t *h) 579 { 580 hook_stack_t *hks; 581 hook_event_int_t *hei; 582 hook_int_t *hi, *new; 583 584 ASSERT(hfi != NULL); 585 ASSERT(event != NULL); 586 ASSERT(h != NULL); 587 hks = (hook_stack_t *)hfi->hfi_ptr; 588 589 /* Alloc hook_int_t and copy hook */ 590 new = hook_copy(h); 591 if (new == NULL) 592 return (ENOMEM); 593 594 /* 595 * Since hook add/remove only impact event, so it is unnecessary 596 * to hold global family write lock. Just get read lock here to 597 * ensure event will not be removed when doing hooks operation 598 */ 599 CVW_ENTER_READ(&hks->hks_familylock); 600 601 hei = hook_event_find(hfi, event); 602 if (hei == NULL) { 603 CVW_EXIT_READ(&hks->hks_familylock); 604 hook_free(new); 605 return (ENXIO); 606 } 607 608 CVW_ENTER_WRITE(&hei->hei_lock); 609 610 /* Multiple hooks are only allowed for read-only events. */ 611 if (((hei->hei_event->he_flags & HOOK_RDONLY) == 0) && 612 (!TAILQ_EMPTY(&hei->hei_head))) { 613 CVW_EXIT_WRITE(&hei->hei_lock); 614 CVW_EXIT_READ(&hks->hks_familylock); 615 hook_free(new); 616 return (EEXIST); 617 } 618 619 hi = hook_find(hei, h); 620 if (hi != NULL) { 621 CVW_EXIT_WRITE(&hei->hei_lock); 622 CVW_EXIT_READ(&hks->hks_familylock); 623 hook_free(new); 624 return (EEXIST); 625 } 626 627 /* Add to hook list head */ 628 TAILQ_INSERT_HEAD(&hei->hei_head, new, hi_entry); 629 hei->hei_event->he_interested = B_TRUE; 630 631 CVW_EXIT_WRITE(&hei->hei_lock); 632 CVW_EXIT_READ(&hks->hks_familylock); 633 return (0); 634 } 635 636 637 /* 638 * Function: hook_unregister 639 * Returns: int - 0 = Succ, Else = Fail 640 * Parameters: hfi(I) - internal family pointer 641 * event(I) - event name string 642 * h(I) - hook pointer 643 * 644 * Remove hook from hook list on specific family, event 645 */ 646 int 647 hook_unregister(hook_family_int_t *hfi, char *event, hook_t *h) 648 { 649 hook_stack_t *hks; 650 hook_event_int_t *hei; 651 hook_int_t *hi; 652 653 ASSERT(hfi != NULL); 654 ASSERT(h != NULL); 655 hks = (hook_stack_t *)hfi->hfi_ptr; 656 657 CVW_ENTER_READ(&hks->hks_familylock); 658 659 hei = hook_event_find(hfi, event); 660 if (hei == NULL) { 661 CVW_EXIT_READ(&hks->hks_familylock); 662 return (ENXIO); 663 } 664 665 /* Hold write lock for event */ 666 CVW_ENTER_WRITE(&hei->hei_lock); 667 668 hi = hook_find(hei, h); 669 if (hi == NULL) { 670 CVW_EXIT_WRITE(&hei->hei_lock); 671 CVW_EXIT_READ(&hks->hks_familylock); 672 return (ENXIO); 673 } 674 675 /* Remove from hook list */ 676 TAILQ_REMOVE(&hei->hei_head, hi, hi_entry); 677 if (TAILQ_EMPTY(&hei->hei_head)) { 678 hei->hei_event->he_interested = B_FALSE; 679 } 680 681 CVW_EXIT_WRITE(&hei->hei_lock); 682 CVW_EXIT_READ(&hks->hks_familylock); 683 684 hook_free(hi); 685 return (0); 686 } 687 688 689 /* 690 * Function: hook_find 691 * Returns: internal hook pointer - NULL = Not match 692 * Parameters: hei(I) - internal event pointer 693 * h(I) - hook pointer 694 * 695 * Search hook list 696 * A lock on familylock must be held when called. 697 */ 698 static hook_int_t * 699 hook_find(hook_event_int_t *hei, hook_t *h) 700 { 701 hook_int_t *hi; 702 703 ASSERT(hei != NULL); 704 ASSERT(h != NULL); 705 706 TAILQ_FOREACH(hi, &hei->hei_head, hi_entry) { 707 if (strcmp(hi->hi_hook.h_name, h->h_name) == 0) 708 break; 709 } 710 return (hi); 711 } 712 713 714 /* 715 * Function: hook_copy 716 * Returns: internal hook pointer - NULL = Failed 717 * Parameters: src(I) - hook pointer 718 * 719 * Allocate internal hook block and duplicate incoming hook. 720 * No locks should be held across this function as it may sleep. 721 */ 722 static hook_int_t * 723 hook_copy(hook_t *src) 724 { 725 hook_int_t *new; 726 hook_t *dst; 727 728 ASSERT(src != NULL); 729 ASSERT(src->h_name != NULL); 730 731 new = (hook_int_t *)kmem_zalloc(sizeof (*new), KM_SLEEP); 732 733 /* Copy body */ 734 dst = &new->hi_hook; 735 *dst = *src; 736 737 /* Copy name */ 738 dst->h_name = (char *)kmem_alloc(strlen(src->h_name) + 1, KM_SLEEP); 739 (void) strcpy(dst->h_name, src->h_name); 740 741 return (new); 742 } 743 744 /* 745 * Function: hook_free 746 * Returns: None 747 * Parameters: hi(I) - internal hook pointer 748 * 749 * Free alloc memory for hook 750 */ 751 static void 752 hook_free(hook_int_t *hi) 753 { 754 ASSERT(hi != NULL); 755 756 /* Free name space */ 757 if (hi->hi_hook.h_name != NULL) { 758 kmem_free(hi->hi_hook.h_name, strlen(hi->hi_hook.h_name) + 1); 759 } 760 761 /* Free container */ 762 kmem_free(hi, sizeof (*hi)); 763 } 764