summaryrefslogtreecommitdiff
path: root/usr/src/uts/common/io/hook.c
diff options
context:
space:
mode:
authordr146992 <none@none>2006-10-20 16:37:58 -0700
committerdr146992 <none@none>2006-10-20 16:37:58 -0700
commit381a2a9a387f449fab7d0c7e97c4184c26963abf (patch)
tree9beb8c59e549aba6f888d2adc733dae7e248d9e2 /usr/src/uts/common/io/hook.c
parentf273041ff6419d6156c10c02bb1a527bfcfdc457 (diff)
downloadillumos-joyent-381a2a9a387f449fab7d0c7e97c4184c26963abf.tar.gz
PSARC/2005/334 Packet Filtering Hooks
PSARC/2006/321 ARP packet filtering Hooks 6401219 use of pullupmsg() considered destructive - clears h/w checksum flags 6418698 PSARC/2005/334 - Packet Filtering Hooks API 6449290 package prototype files in usr/src/pkgdefs/SUNWipfr missing CDDL 6449292 package prototype files in usr/src/pkgdefs/SUNWipfu missing CDDL 6449296 Makefiles for ipf kernel module building missing CDDL 6473996 "fastroute" + "nat" packets cause memory leaks in ipfilter --HG-- rename : usr/src/cmd/ipf/etc/pfil.ap.sh => deleted_files/usr/src/cmd/ipf/etc/pfil.ap.sh rename : usr/src/cmd/ipf/pfild/Makefile => deleted_files/usr/src/cmd/ipf/pfild/Makefile rename : usr/src/cmd/ipf/pfild/pfild.c => deleted_files/usr/src/cmd/ipf/pfild/pfild.c rename : usr/src/cmd/ipf/pfild/vas.c => deleted_files/usr/src/cmd/ipf/pfild/vas.c rename : usr/src/cmd/ipf/svc/pfil => deleted_files/usr/src/cmd/ipf/svc/pfil rename : usr/src/cmd/ipf/svc/pfil.xml => deleted_files/usr/src/cmd/ipf/svc/pfil.xml rename : usr/src/uts/common/inet/pfil/compat.h => deleted_files/usr/src/uts/common/inet/pfil/compat.h rename : usr/src/uts/common/inet/pfil/ndd.c => deleted_files/usr/src/uts/common/inet/pfil/ndd.c rename : usr/src/uts/common/inet/pfil/os.h => deleted_files/usr/src/uts/common/inet/pfil/os.h rename : usr/src/uts/common/inet/pfil/pfil.c => deleted_files/usr/src/uts/common/inet/pfil/pfil.c rename : usr/src/uts/common/inet/pfil/pfil.conf => deleted_files/usr/src/uts/common/inet/pfil/pfil.conf rename : usr/src/uts/common/inet/pfil/pfil.h => deleted_files/usr/src/uts/common/inet/pfil/pfil.h rename : usr/src/uts/common/inet/pfil/pfild.h => deleted_files/usr/src/uts/common/inet/pfil/pfild.h rename : usr/src/uts/common/inet/pfil/pfildrv.c => deleted_files/usr/src/uts/common/inet/pfil/pfildrv.c rename : usr/src/uts/common/inet/pfil/pfilstream.c => deleted_files/usr/src/uts/common/inet/pfil/pfilstream.c rename : usr/src/uts/common/inet/pfil/pkt.c => deleted_files/usr/src/uts/common/inet/pfil/pkt.c rename : usr/src/uts/common/inet/pfil/qif.c => deleted_files/usr/src/uts/common/inet/pfil/qif.c rename : usr/src/uts/common/inet/pfil/qif.h => deleted_files/usr/src/uts/common/inet/pfil/qif.h rename : usr/src/uts/intel/pfil/Makefile => deleted_files/usr/src/uts/intel/pfil/Makefile rename : usr/src/uts/sparc/pfil/Makefile => deleted_files/usr/src/uts/sparc/pfil/Makefile rename : usr/src/uts/common/inet/pfil/misc.c => usr/src/uts/common/inet/ipf/misc.c
Diffstat (limited to 'usr/src/uts/common/io/hook.c')
-rw-r--r--usr/src/uts/common/io/hook.c685
1 files changed, 685 insertions, 0 deletions
diff --git a/usr/src/uts/common/io/hook.c b/usr/src/uts/common/io/hook.c
new file mode 100644
index 0000000000..7e791647e4
--- /dev/null
+++ b/usr/src/uts/common/io/hook.c
@@ -0,0 +1,685 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright 2006 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#include <sys/param.h>
+#include <sys/types.h>
+#include <sys/systm.h>
+#include <sys/errno.h>
+#include <sys/kmem.h>
+#include <sys/mutex.h>
+#include <sys/condvar.h>
+#include <sys/modctl.h>
+#include <sys/hook_impl.h>
+#include <sys/sdt.h>
+
+/*
+ * This file provides kernel hook framework.
+ */
+
+static struct modldrv modlmisc = {
+ &mod_miscops, /* drv_modops */
+ "Hooks Interface v1.0", /* drv_linkinfo */
+};
+
+static struct modlinkage modlinkage = {
+ MODREV_1, /* ml_rev */
+ &modlmisc, /* ml_linkage */
+ NULL
+};
+
+/*
+ * Hook internal functions
+ */
+static hook_int_t *hook_copy(hook_t *src);
+static hook_event_int_t *hook_event_checkdup(hook_event_t *he);
+static hook_event_int_t *hook_event_copy(hook_event_t *src);
+static hook_event_int_t *hook_event_find(hook_family_int_t *hfi, char *event);
+static void hook_event_free(hook_event_int_t *hei);
+static hook_family_int_t *hook_family_copy(hook_family_t *src);
+static hook_family_int_t *hook_family_find(char *family);
+static void hook_family_free(hook_family_int_t *hfi);
+static hook_int_t *hook_find(hook_event_int_t *hei, hook_t *h);
+static void hook_free(hook_int_t *hi);
+static void hook_init(void);
+
+static cvwaitlock_t familylock; /* global lock */
+static hook_family_int_head_t familylist; /* family list head */
+
+/*
+ * Module entry points.
+ */
+int
+_init(void)
+{
+ hook_init();
+ return (mod_install(&modlinkage));
+}
+
+
+int
+_fini(void)
+{
+ return (mod_remove(&modlinkage));
+}
+
+
+int
+_info(struct modinfo *modinfop)
+{
+ return (mod_info(&modlinkage, modinfop));
+}
+
+
+/*
+ * Function: hook_init
+ * Returns: None
+ * Parameters: None
+ *
+ * Initialize hooks
+ */
+static void
+hook_init(void)
+{
+ CVW_INIT(&familylock);
+ SLIST_INIT(&familylist);
+}
+
+
+/*
+ * Function: hook_run
+ * Returns: int - return value according to callback func
+ * Parameters: token(I) - event pointer
+ * info(I) - message
+ *
+ * Run hooks for specific provider. The hooks registered are stepped through
+ * until either the end of the list is reached or a hook function returns a
+ * non-zero value. If a non-zero value is returned from a hook function, we
+ * return that value back to our caller. By design, a hook function can be
+ * called more than once, simultaneously.
+ */
+int
+hook_run(hook_event_token_t token, hook_data_t info)
+{
+ hook_int_t *hi;
+ hook_event_int_t *hei;
+ int rval = 0;
+
+ ASSERT(token != NULL);
+
+ hei = (hook_event_int_t *)token;
+ DTRACE_PROBE2(hook__run__start,
+ hook_event_token_t, token,
+ hook_data_t, info);
+
+ /* Hold global read lock to ensure event will not be deleted */
+ CVW_ENTER_READ(&familylock);
+
+ /* Hold event read lock to ensure hook will not be changed */
+ CVW_ENTER_READ(&hei->hei_lock);
+
+ TAILQ_FOREACH(hi, &hei->hei_head, hi_entry) {
+ ASSERT(hi->hi_hook.h_func != NULL);
+ DTRACE_PROBE3(hook__func__start,
+ hook_event_token_t, token,
+ hook_data_t, info,
+ hook_int_t *, hi);
+ rval = (*hi->hi_hook.h_func)(token, info);
+ DTRACE_PROBE4(hook__func__end,
+ hook_event_token_t, token,
+ hook_data_t, info,
+ hook_int_t *, hi,
+ int, rval);
+ if (rval != 0)
+ break;
+ }
+
+ CVW_EXIT_READ(&hei->hei_lock);
+ CVW_EXIT_READ(&familylock);
+
+ DTRACE_PROBE3(hook__run__end,
+ hook_event_token_t, token,
+ hook_data_t, info,
+ hook_int_t *, hi);
+
+ return (rval);
+}
+
+
+/*
+ * Function: hook_family_add
+ * Returns: internal family pointer - NULL = Fail
+ * Parameters: hf(I) - family pointer
+ *
+ * Add new family to family list
+ */
+hook_family_int_t *
+hook_family_add(hook_family_t *hf)
+{
+ hook_family_int_t *hfi, *new;
+
+ ASSERT(hf != NULL);
+ ASSERT(hf->hf_name != NULL);
+
+ new = hook_family_copy(hf);
+ if (new == NULL)
+ return (NULL);
+
+ CVW_ENTER_WRITE(&familylock);
+
+ /* search family list */
+ hfi = hook_family_find(hf->hf_name);
+ if (hfi != NULL) {
+ CVW_EXIT_WRITE(&familylock);
+ hook_family_free(new);
+ return (NULL);
+ }
+
+ /* Add to family list head */
+ SLIST_INSERT_HEAD(&familylist, new, hfi_entry);
+
+ CVW_EXIT_WRITE(&familylock);
+ return (new);
+}
+
+
+/*
+ * Function: hook_family_remove
+ * Returns: int - 0 = Succ, Else = Fail
+ * Parameters: hfi(I) - internal family pointer
+ *
+ * Remove family from family list
+ */
+int
+hook_family_remove(hook_family_int_t *hfi)
+{
+
+ ASSERT(hfi != NULL);
+
+ CVW_ENTER_WRITE(&familylock);
+
+ /* Check if there are events */
+ if (!SLIST_EMPTY(&hfi->hfi_head)) {
+ CVW_EXIT_WRITE(&familylock);
+ return (EBUSY);
+ }
+
+ /* Remove from family list */
+ SLIST_REMOVE(&familylist, hfi, hook_family_int, hfi_entry);
+
+ CVW_EXIT_WRITE(&familylock);
+ hook_family_free(hfi);
+
+ return (0);
+}
+
+
+/*
+ * Function: hook_family_copy
+ * Returns: internal family pointer - NULL = Failed
+ * Parameters: src(I) - family pointer
+ *
+ * Allocate internal family block and duplicate incoming family
+ * No locks should be held across this function as it may sleep.
+ */
+static hook_family_int_t *
+hook_family_copy(hook_family_t *src)
+{
+ hook_family_int_t *new;
+ hook_family_t *dst;
+
+ ASSERT(src != NULL);
+ ASSERT(src->hf_name != NULL);
+
+ new = (hook_family_int_t *)kmem_zalloc(sizeof (*new), KM_SLEEP);
+
+ /* Copy body */
+ SLIST_INIT(&new->hfi_head);
+ dst = &new->hfi_family;
+ *dst = *src;
+
+ /* Copy name */
+ dst->hf_name = (char *)kmem_alloc(strlen(src->hf_name) + 1, KM_SLEEP);
+ (void) strcpy(dst->hf_name, src->hf_name);
+
+ return (new);
+}
+
+
+/*
+ * Function: hook_family_find
+ * Returns: internal family pointer - NULL = Not match
+ * Parameters: family(I) - family name string
+ *
+ * Search family list with family name
+ * A lock on familylock must be held when called.
+ */
+static hook_family_int_t *
+hook_family_find(char *family)
+{
+ hook_family_int_t *hfi = NULL;
+
+ ASSERT(family != NULL);
+
+ SLIST_FOREACH(hfi, &familylist, hfi_entry) {
+ if (strcmp(hfi->hfi_family.hf_name, family) == 0)
+ break;
+ }
+ return (hfi);
+}
+
+
+/*
+ * Function: hook_family_free
+ * Returns: None
+ * Parameters: hfi(I) - internal family pointer
+ *
+ * Free alloc memory for family
+ */
+static void
+hook_family_free(hook_family_int_t *hfi)
+{
+ ASSERT(hfi != NULL);
+
+ /* Free name space */
+ if (hfi->hfi_family.hf_name != NULL) {
+ kmem_free(hfi->hfi_family.hf_name,
+ strlen(hfi->hfi_family.hf_name) + 1);
+ }
+
+ /* Free container */
+ kmem_free(hfi, sizeof (*hfi));
+}
+
+
+/*
+ * Function: hook_event_add
+ * Returns: internal event pointer - NULL = Fail
+ * Parameters: hfi(I) - internal family pointer
+ * he(I) - event pointer
+ *
+ * Add new event to event list on specific family.
+ * This function can fail to return successfully if (1) it cannot allocate
+ * enough memory for its own internal data structures, (2) the event has
+ * already been registered (for any hook family.)
+ */
+hook_event_int_t *
+hook_event_add(hook_family_int_t *hfi, hook_event_t *he)
+{
+ hook_event_int_t *hei, *new;
+
+ ASSERT(hfi != NULL);
+ ASSERT(he != NULL);
+ ASSERT(he->he_name != NULL);
+
+ new = hook_event_copy(he);
+ if (new == NULL)
+ return (NULL);
+
+ CVW_ENTER_WRITE(&familylock);
+
+ /* Check whether this event pointer is already registered */
+ hei = hook_event_checkdup(he);
+ if (hei != NULL) {
+ CVW_EXIT_WRITE(&familylock);
+ hook_event_free(new);
+ return (NULL);
+ }
+
+ /* Add to event list head */
+ SLIST_INSERT_HEAD(&hfi->hfi_head, new, hei_entry);
+
+ CVW_EXIT_WRITE(&familylock);
+ return (new);
+}
+
+
+/*
+ * Function: hook_event_remove
+ * Returns: int - 0 = Succ, Else = Fail
+ * Parameters: hfi(I) - internal family pointer
+ * he(I) - event pointer
+ *
+ * Remove event from event list on specific family
+ */
+int
+hook_event_remove(hook_family_int_t *hfi, hook_event_t *he)
+{
+ hook_event_int_t *hei;
+
+ ASSERT(hfi != NULL);
+ ASSERT(he != NULL);
+
+ CVW_ENTER_WRITE(&familylock);
+
+ hei = hook_event_find(hfi, he->he_name);
+ if (hei == NULL) {
+ CVW_EXIT_WRITE(&familylock);
+ return (ENXIO);
+ }
+
+ /* Check if there are registered hooks for this event */
+ if (!TAILQ_EMPTY(&hei->hei_head)) {
+ CVW_EXIT_WRITE(&familylock);
+ return (EBUSY);
+ }
+
+ /* Remove from event list */
+ SLIST_REMOVE(&hfi->hfi_head, hei, hook_event_int, hei_entry);
+
+ CVW_EXIT_WRITE(&familylock);
+ hook_event_free(hei);
+
+ return (0);
+}
+
+
+/*
+ * Function: hook_event_checkdup
+ * Returns: internal event pointer - NULL = Not match
+ * Parameters: he(I) - event pointer
+ *
+ * Search whole list with event pointer
+ * A lock on familylock must be held when called.
+ */
+static hook_event_int_t *
+hook_event_checkdup(hook_event_t *he)
+{
+ hook_family_int_t *hfi;
+ hook_event_int_t *hei;
+
+ ASSERT(he != NULL);
+
+ SLIST_FOREACH(hfi, &familylist, hfi_entry) {
+ SLIST_FOREACH(hei, &hfi->hfi_head, hei_entry) {
+ if (hei->hei_event == he)
+ return (hei);
+ }
+ }
+
+ return (NULL);
+}
+
+
+/*
+ * Function: hook_event_copy
+ * Returns: internal event pointer - NULL = Failed
+ * Parameters: src(I) - event pointer
+ *
+ * Allocate internal event block and duplicate incoming event
+ * No locks should be held across this function as it may sleep.
+ */
+static hook_event_int_t *
+hook_event_copy(hook_event_t *src)
+{
+ hook_event_int_t *new;
+
+ ASSERT(src != NULL);
+ ASSERT(src->he_name != NULL);
+
+ new = (hook_event_int_t *)kmem_zalloc(sizeof (*new), KM_SLEEP);
+
+ /* Copy body */
+ TAILQ_INIT(&new->hei_head);
+ new->hei_event = src;
+
+ return (new);
+}
+
+
+/*
+ * Function: hook_event_find
+ * Returns: internal event pointer - NULL = Not match
+ * Parameters: hfi(I) - internal family pointer
+ * event(I) - event name string
+ *
+ * Search event list with event name
+ * A lock on familylock must be held when called.
+ */
+static hook_event_int_t *
+hook_event_find(hook_family_int_t *hfi, char *event)
+{
+ hook_event_int_t *hei = NULL;
+
+ ASSERT(hfi != NULL);
+ ASSERT(event != NULL);
+
+ SLIST_FOREACH(hei, &hfi->hfi_head, hei_entry) {
+ if (strcmp(hei->hei_event->he_name, event) == 0)
+ break;
+ }
+ return (hei);
+}
+
+
+/*
+ * Function: hook_event_free
+ * Returns: None
+ * Parameters: hei(I) - internal event pointer
+ *
+ * Free alloc memory for event
+ */
+static void
+hook_event_free(hook_event_int_t *hei)
+{
+ ASSERT(hei != NULL);
+
+ /* Free container */
+ kmem_free(hei, sizeof (*hei));
+}
+
+
+/*
+ * Function: hook_register
+ * Returns: int- 0 = Succ, Else = Fail
+ * Parameters: hfi(I) - internal family pointer
+ * event(I) - event name string
+ * h(I) - hook pointer
+ *
+ * Add new hook to hook list on spefic family, event
+ */
+int
+hook_register(hook_family_int_t *hfi, char *event, hook_t *h)
+{
+ hook_event_int_t *hei;
+ hook_int_t *hi, *new;
+
+ ASSERT(hfi != NULL);
+ ASSERT(event != NULL);
+ ASSERT(h != NULL);
+
+ /* Alloc hook_int_t and copy hook */
+ new = hook_copy(h);
+ if (new == NULL)
+ return (ENOMEM);
+
+ /*
+ * Since hook add/remove only impact event, so it is unnecessary
+ * to hold global family write lock. Just get read lock here to
+ * ensure event will not be removed when doing hooks operation
+ */
+ CVW_ENTER_READ(&familylock);
+
+ hei = hook_event_find(hfi, event);
+ if (hei == NULL) {
+ CVW_EXIT_READ(&familylock);
+ hook_free(new);
+ return (ENXIO);
+ }
+
+ CVW_ENTER_WRITE(&hei->hei_lock);
+
+ /* Multiple hooks are only allowed for read-only events. */
+ if (((hei->hei_event->he_flags & HOOK_RDONLY) == 0) &&
+ (!TAILQ_EMPTY(&hei->hei_head))) {
+ CVW_EXIT_WRITE(&hei->hei_lock);
+ CVW_EXIT_READ(&familylock);
+ hook_free(new);
+ return (EEXIST);
+ }
+
+ hi = hook_find(hei, h);
+ if (hi != NULL) {
+ CVW_EXIT_WRITE(&hei->hei_lock);
+ CVW_EXIT_READ(&familylock);
+ hook_free(new);
+ return (EEXIST);
+ }
+
+ /* Add to hook list head */
+ TAILQ_INSERT_HEAD(&hei->hei_head, new, hi_entry);
+ hei->hei_event->he_interested = B_TRUE;
+
+ CVW_EXIT_WRITE(&hei->hei_lock);
+ CVW_EXIT_READ(&familylock);
+ return (0);
+}
+
+
+/*
+ * Function: hook_unregister
+ * Returns: int - 0 = Succ, Else = Fail
+ * Parameters: hfi(I) - internal family pointer
+ * event(I) - event name string
+ * h(I) - hook pointer
+ *
+ * Remove hook from hook list on specific family, event
+ */
+int
+hook_unregister(hook_family_int_t *hfi, char *event, hook_t *h)
+{
+ hook_event_int_t *hei;
+ hook_int_t *hi;
+
+ ASSERT(hfi != NULL);
+ ASSERT(h != NULL);
+
+ CVW_ENTER_READ(&familylock);
+
+ hei = hook_event_find(hfi, event);
+ if (hei == NULL) {
+ CVW_EXIT_READ(&familylock);
+ return (ENXIO);
+ }
+
+ /* Hold write lock for event */
+ CVW_ENTER_WRITE(&hei->hei_lock);
+
+ hi = hook_find(hei, h);
+ if (hi == NULL) {
+ CVW_EXIT_WRITE(&hei->hei_lock);
+ CVW_EXIT_READ(&familylock);
+ return (ENXIO);
+ }
+
+ /* Remove from hook list */
+ TAILQ_REMOVE(&hei->hei_head, hi, hi_entry);
+ if (TAILQ_EMPTY(&hei->hei_head)) {
+ hei->hei_event->he_interested = B_FALSE;
+ }
+
+ CVW_EXIT_WRITE(&hei->hei_lock);
+ CVW_EXIT_READ(&familylock);
+
+ hook_free(hi);
+ return (0);
+}
+
+
+/*
+ * Function: hook_find
+ * Returns: internal hook pointer - NULL = Not match
+ * Parameters: hei(I) - internal event pointer
+ * h(I) - hook pointer
+ *
+ * Search hook list
+ * A lock on familylock must be held when called.
+ */
+static hook_int_t *
+hook_find(hook_event_int_t *hei, hook_t *h)
+{
+ hook_int_t *hi;
+
+ ASSERT(hei != NULL);
+ ASSERT(h != NULL);
+
+ TAILQ_FOREACH(hi, &hei->hei_head, hi_entry) {
+ if (strcmp(hi->hi_hook.h_name, h->h_name) == 0)
+ break;
+ }
+ return (hi);
+}
+
+
+/*
+ * Function: hook_copy
+ * Returns: internal hook pointer - NULL = Failed
+ * Parameters: src(I) - hook pointer
+ *
+ * Allocate internal hook block and duplicate incoming hook.
+ * No locks should be held across this function as it may sleep.
+ */
+static hook_int_t *
+hook_copy(hook_t *src)
+{
+ hook_int_t *new;
+ hook_t *dst;
+
+ ASSERT(src != NULL);
+ ASSERT(src->h_name != NULL);
+
+ new = (hook_int_t *)kmem_zalloc(sizeof (*new), KM_SLEEP);
+
+ /* Copy body */
+ dst = &new->hi_hook;
+ *dst = *src;
+
+ /* Copy name */
+ dst->h_name = (char *)kmem_alloc(strlen(src->h_name) + 1, KM_SLEEP);
+ (void) strcpy(dst->h_name, src->h_name);
+
+ return (new);
+}
+
+/*
+ * Function: hook_free
+ * Returns: None
+ * Parameters: hi(I) - internal hook pointer
+ *
+ * Free alloc memory for hook
+ */
+static void
+hook_free(hook_int_t *hi)
+{
+ ASSERT(hi != NULL);
+
+ /* Free name space */
+ if (hi->hi_hook.h_name != NULL) {
+ kmem_free(hi->hi_hook.h_name, strlen(hi->hi_hook.h_name) + 1);
+ }
+
+ /* Free container */
+ kmem_free(hi, sizeof (*hi));
+}