/*
* 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
* 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 2008 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*
* Copyright 2013 Joyent, Inc. All rights reserved.
*/
#include <sys/hook_impl.h>
/*
* This file provides kernel hook framework.
*/
&mod_miscops, /* drv_modops */
"Hooks Interface v1.0", /* drv_linkinfo */
};
MODREV_1, /* ml_rev */
&modlmisc, /* ml_linkage */
};
/*
* How it works.
* =============
* Use of the hook framework here is tied up with zones - when a new zone
* is created, we create a new hook_stack_t and are open to business for
* allowing new hook families and their events.
*
* A consumer of these hooks is expected to operate in this fashion:
* 1) call hook_family_add() to create a new family of hooks. It is a
* current requirement that this call must be made with the value
* returned from hook_stack_init, by way of infrastructure elsewhere.
* 2) add events to the registered family with calls to hook_event_add.
*
* At this point, the structures in place should be open to others to
* add hooks to the event or add notifiers for when the contents of the
* hook stack changes.
*
* The interesting stuff happens on teardown.
*
* It is a requirement that the provider of hook events work in the reverse
* order to the above, so that the first step is:
* 1) remove events from each hook family created earlier
* 2) remove hook families from the hook stack.
*
* When doing teardown of both events and families, a check is made to see
* if either structure is still "busy". If so then a boolean flag (FWF_DESTROY)
* is set to say that the structure is condemned. The presence of this flag
* being set must be checked for in _add()/_register()/ functions and a
* failure returned if it is set. It is ignored by the _find() functions
* because they're used by _remove()/_unregister().
* While setting the condemned flag when trying to delete a structure would
* normally be keyed from the presence of a reference count being greater
* than 1, in this implementation there are no reference counts required:
* instead the presence of objects on linked lists is taken to mean
* something is still "busy."
*
* ONLY the caller that adds the family and the events ever has a direct
* reference to the internal structures and thus ONLY it should be doing
* the removal of either the event or family. In practise, what this means
* is that in ip_netinfo.c, we have calls to net_protocol_register(), followed
* by net_event_register() (these interface to hook_family_add() and
* hook_event_add(), respectively) that are made when we create an instance
* net_event_unregister() and net_protocol_unregister(), which in turn call
* hook_event_remove() and hook_family_remove() respectively. Nobody else
* is entitled to call the _unregister() functions. It is imperative that
* there be only one _remove() call for every _add() call.
*
* It is possible that code which is interfacing with this hook framework
* won't do all the cleaning up that it needs to at the right time. While
* we can't prevent programmers from creating memory leaks, we can synchronise
* when we clean up data structures to prevent code accessing free'd memory.
*
* A simple diagram showing the ownership is as follows:
*
* Owned +--------------+
* by | hook_stack_t |
* the +--------------+
* Instance |
* - - - - - - - -|- - - - - - - - - - - - - - - - - -
* V
* Owned +-------------------+ +-------------------+
* | hook_family_int_t |---->| hook_family_int_t |
* by +-------------------+ +-------------------+
* | \+---------------+ \+---------------+
* network | | hook_family_t | | hook_family_t |
* V +---------------+ +---------------+
* protocol +------------------+ +------------------+
* | hook_event_int_t |---->| hook_event_int_t |
* (ipv4,ipv6) +------------------+ +------------------+
* | \+--------------+ \+--------------+
* | | hook_event_t | | hook_event_t |
* | +--------------+ +--------------+
* - - - - - - - -|- - - - - - - - - - - - - - - - - -
* V
* Owned +------------+
* | hook_int_t |
* by +------------+
* \+--------+
* the consumer | hook_t |
* +--------+
*
* The consumers, such as IPFilter, do not have any pointers or hold any
* references to hook_int_t, hook_event_t or hook_event_int_t. By placing
* a hook on an event through net_hook_register(), an implicit reference
* to the hook_event_int_t is returned with a successful call. Additionally,
* IPFilter does not see the hook_family_int_t or hook_family_t directly.
* Rather it is returned a net_handle_t (from net_protocol_lookup()) that
* contains a pointer to hook_family_int_t. The structure behind the
* net_handle_t (struct net_data) *is* reference counted and managed
* appropriately.
*
* are linked together can be found in <sys/hook_impl.h>
*
* Notification callbacks.
* =======================
* For each of the hook stack, hook family and hook event, it is possible
* to request notificatin of change to them. Why?
* First, lets equate the hook stack to an IP instance, a hook family to
* a network protocol and a hook event to IP packets on the input path.
* If a kernel module wants to apply security from the very start of
* things, it needs to know as soon as a new instance of networking
* is initiated. Whilst for the global zone, it is taken for granted that
* this instance will always exist before any interaction takes place,
* that is not true for zones running with an exclusive networking instance.
* Thus when a local zone is started and a new instance is created to support
* that, parties that wish to monitor it and apply a security policy from
* the onset need to be informed as early as possible - quite probably
* before any networking is started by the zone's boot scripts.
* Inside each instance, it is possible to have a number of network protocols
* (hook families) in operation. Inside the context of the global zone,
* it is possible to have code run before the kernel module providing the
* IP networking is loaded. From here, to apply the appropriate security,
* it is necessary to become informed of when IP is being configured into
* the zone and this is done by registering a notification callback with
* the hook stack for changes to it. The next step is to know when packets
* can be received through the physical_in, etc, events. This is achieved
* by registering a callback with the appropriate network protocol (or in
* this file, the correct hook family.) Thus when IP finally attaches a
* physical_in event to inet, the module looking to enforce a security
* policy can become aware of it being present. Of course there's no
* requirement for such a module to be present before all of the above
* happens and in such a case, it is reasonable for the same module to
* work after everything has been put in place. For this reason, when
* a notification callback is added, a series of fake callback events
* is generated to simulate the arrival of those entities. There is one
* final series of callbacks that can be registered - those to monitor
* actual hooks that are added or removed from an event. In practice,
* this is useful when there are multiple kernel modules participating
* in the processing of packets and there are behaviour dependencies
* involved, such that one kernel module might only register its hook
* if another is already present and also might want to remove its hook
* when the other disappears.
*
* If you know a kernel module will not be loaded before the infrastructure
* used in this file is present then it is not necessary to use this
* notification callback mechanism.
*/
/*
* Locking
* =======
* The use of CVW_* macros to do locking is driven by the need to allow
* recursive locking with read locks when we're processing packets. This
* is necessary because various netinfo functions need to hold read locks,
* by design, as they can be called in or out of packet context.
*/
/*
* Hook internal functions
*/
hook_stack_t *hks);
static void hook_init(void);
static void hook_fini(void);
hook_int_t *hi);
hook_notify_fn_t callback, void **);
/*
* A list of the hook stacks is kept here because we need to enable
* net_instance_notify_register() to be called during the creation
* of a new instance. Previously hook_stack_get() would just use
* the netstack functions for this work but they will return NULL
* until the zone has been fully initialised.
*/
/*
* Module entry points.
*/
int
_init(void)
{
int error;
hook_init();
if (error != 0)
hook_fini();
return (error);
}
int
_fini(void)
{
int error;
if (error == 0)
hook_fini();
return (error);
}
int
{
}
/*
* Function: hook_init
* Returns: None
* Parameters: None
*
* Initialize hooks
*/
static void
hook_init(void)
{
/*
* We want to be informed each time a stack is created or
* destroyed in the kernel.
*/
}
/*
* Function: hook_fini
* Returns: None
* Parameters: None
*
* Deinitialize hooks
*/
static void
hook_fini(void)
{
}
/*
* Function: hook_wait_setflag
* Returns: -1 = setting flag is disallowed, 0 = flag set and did
* not have to wait (ie no lock droped), 1 = flag set but
* it was necessary to drop locks to set it.
* Parameters: waiter(I) - control data structure
* busyset(I) - set of flags that we don't want set while
* we are active.
* wanted(I) - flag associated with newflag to indicate
* what we want to do.
* newflag(I) - the new ACTIVE flag we want to set that
* indicates what we are doing.
*
* The set of functions hook_wait_* implement an API that builds on top of
* the kcondvar_t to provide controlled execution through a critical region.
* For each flag that indicates work is being done (FWF_*_ACTIVE) there is
* also a flag that we set to indicate that we want to do it (FWF_*_WANTED).
* The combination of flags is required as when this function exits to do
* the task, the structure is then free for another caller to use and
* to indicate that it wants to do work. The flags used when a caller wants
* to destroy an object take precedence over those that are used for making
* ability to run and return with an error.
*
* "wantedset" is used here to determine who has the right to clear the
* wanted but from the fw_flags set: only he that sets the flag has the
* right to clear it at the bottom of the loop, even if someone else
* wants to set it.
*
* wanted - the FWF_*_WANTED flag that describes the action being requested
* busyset- the set of FWF_* flags we don't want set when we run
* newflag- the FWF_*_ACTIVE flag we will set to indicate we are busy
*/
int
{
int waited = 0;
return (-1);
}
if (!wantedset)
/*
* This lock needs to be dropped here to preserve the order
* of acquisition that is fw_owner followed by fw_lock, else
* we can deadlock.
*/
waited = 1;
if (!wantedset)
return (-1);
}
}
return (waited);
}
/*
* Function: hook_wait_unsetflag
* Returns: None
* Parameters: waiter(I) - control data structure
* oldflag(I) - flag to reset
*
* Turn off the bit that we had set to run and let others know that
* they should now check to see if they can run.
*/
void
{
}
/*
* Function: hook_wait_destroy
* Returns: None
* Parameters: waiter(I) - control data structure
*
* Since outer locking (on fw_owner) should ensure that only one function
* at a time gets to call hook_wait_destroy() on a given object, there is
* no need to guard against setting FWF_DESTROY_WANTED already being set.
* It is, however, necessary to wait for all activity on the owning
* structure to cease.
*/
int
{
return (EINPROGRESS);
}
while (!FWF_DESTROY_OK(waiter)) {
}
/*
* There should now be nothing else using "waiter" or its
* owner, so we can safely assign here without risk of wiiping
* out someone's bit.
*/
return (0);
}
/*
* Function: hook_wait_init
* Returns: None
* Parameters: waiter(I) - control data structure
* ownder(I) - pointer to lock that the owner of this
* waiter uses
*
* "owner" gets passed in here so that when we need to call cv_wait,
* for example in hook_wait_setflag(), we can drop the lock for the
* next layer out, which is likely to be held in an exclusive manner.
*/
void
{
}
/*
* Function: hook_stack_init
* Returns: void * - pointer to new hook stack structure
* Parameters: stackid(I) - identifier for the network instance that owns this
* ns(I) - pointer to the network instance data structure
*
* Allocate and initialize the hook stack instance. This function is not
* allowed to fail, so KM_SLEEP is used here when allocating memory. The
* value returned is passed back into the shutdown and destroy hooks.
*/
/*ARGSUSED*/
static void *
{
#ifdef NS_DEBUG
#endif
return (hks);
}
/*
* Function: hook_stack_shutdown
* Returns: void
* Parameters: stackid(I) - identifier for the network instance that owns this
* arg(I) - pointer returned by hook_stack_init
*
* Set the shutdown flag to indicate that we should stop accepting new
* register calls as we're now in the cleanup process. The cleanup is a
* two stage process and we're not required to free any memory here.
*
* The curious would wonder why isn't there any code that walks through
* all of the data structures and sets the flag(s) there? The answer is
* that it is expected that this will happen when the zone shutdown calls
* the shutdown callbacks for other modules that they will initiate the
* free'ing and shutdown of the hooks themselves.
*/
/*ARGSUSED*/
static void
{
/*
* Once this flag gets set to one, no more additions are allowed
* to any of the structures that make up this stack.
*/
}
/*
* Function: hook_stack_destroy
* Returns: void
* Parameters: stackid(I) - identifier for the network instance that owns this
* arg(I) - pointer returned by hook_stack_init
*
* Free the hook stack instance.
*
* The rationale for the shutdown being lazy (see the comment above for
* hook_stack_shutdown) also applies to the destroy being lazy. Only if
* the hook_stack_t data structure is unused will it go away. Else it
* is left up to the last user of a data structure to actually free it.
*/
/*ARGSUSED*/
static void
{
}
/*
* Function: hook_stack_remove
* Returns: void
* Parameters: hks(I) - pointer to an instance of a hook_stack_t
*
* This function assumes that it is called with hook_stack_lock held.
* It functions differently to hook_family/event_remove in that it does
* the checks to see if it can be removed. This difference exists
* because this structure has nothing higher up that depends on it.
*/
static void
{
/*
* Is the structure still in use?
*/
return;
}
/*
* Function: hook_stack_get
* Returns: hook_stack_t * - NULL if not found, else matching instance
* Parameters: stackid(I) - instance id to search for
*
* Search the list of currently active hook_stack_t structures for one that
* has a matching netstackid_t to the value passed in. The linked list can
* only ever have at most one match for this value.
*/
static hook_stack_t *
{
break;
}
return (hks);
}
/*
* Function: hook_stack_notify_register
* Returns: int - 0 = success, else failure
* Parameters: stackid(I) - netstack identifier
* callback(I)- function to be called
* arg(I) - arg to provide callback when it is called
*
* If we're not shutting down this instance, append a new function to the
* list of those to call when a new family of hooks is added to this stack.
* If the function can be successfully added to the list of callbacks
* activated when there is a change to the stack (addition or removal of
* a hook family) then generate a fake HN_REGISTER event by directly
* calling the callback with the relevant information for each hook
* family that currently exists (and isn't being shutdown.)
*/
int
void *arg)
{
int error;
if (hks->hks_shutdown != 0) {
} else {
FWF_ADD_ACTIVE) != -1);
}
} else {
}
/*
* Generate fake register event for callback that
* is being added, letting it know everything that
* already exists.
*/
continue;
}
}
if (canrun)
return (error);
}
/*
* Function: hook_stack_notify_unregister
* Returns: int - 0 = success, else failure
* Parameters: stackid(I) - netstack identifier
* callback(I) - function to be called
*
* Attempt to remove a registered function from a hook stack's list of
* As with hook_stack_notify_register, if all things are going well then
* a fake unregister event is delivered to the callback being removed
* for each hook family that presently exists.
*/
int
{
void *arg;
int error;
} else {
}
if (error == 0) {
if (canrun) {
/*
* Generate fake unregister event for callback that
* is being removed, letting it know everything that
* currently exists is now "disappearing."
*/
}
}
}
return (error);
}
/*
* Function: hook_stack_notify_run
* Returns: None
* Parameters: hks(I) - hook stack pointer to execute callbacks for
* name(I) - name of a hook family
* cmd(I) - either HN_UNREGISTER or HN_REGISTER
*
* Run through the list of callbacks on the hook stack to be called when
* a new hook family is added
*
* As hook_notify_run() expects 3 names, one for the family that is associated
* with the cmd (HN_REGISTER or HN_UNREGISTER), one for the event and one
* for the object being introduced and we really only have one name (that
* of the new hook family), fake the hook stack's name by converting the
* integer to a string and for the event just pass NULL.
*/
static void
{
}
/*
* 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
{
int rval = 0;
hook_data_t, info);
/*
* If we consider that this function is only called from within the
* stack while an instance is currently active,
*/
hook_int_t *, hi);
hook_int_t *, hi,
int, rval);
if (rval != 0)
break;
}
hook_int_t *, hi);
return (rval);
}
/*
* Function: hook_family_add
* Returns: internal family pointer - NULL = Fail
* Parameters: hf(I) - family pointer
* hks(I) - pointer to an instance of a hook_stack_t
* store(O) - where returned pointer will be stored
*
* Add new family to the family list. The requirements for the addition to
* succeed are that the family name must not already be registered and that
* the hook stack is not being shutdown.
* If store is non-NULL, it is expected to be a pointer to the same variable
* that is awaiting to be assigned the return value of this function.
* In its current use, the returned value is assigned to netd_hooks in
* net_family_register. The use of "store" allows the return value to be
* used before this function returns. How can this happen? Through the
* callbacks that can be activated at the bottom of this function, when
* hook_stack_notify_run is called.
*/
{
return (NULL);
if (hks->hks_shutdown != 0) {
return (NULL);
}
/* search family list */
return (NULL);
}
/*
* Try and set the FWF_ADD_ACTIVE flag so that we can drop all the
* lock further down when calling all of the functions registered
* for notification when a new hook family is added.
*/
return (NULL);
}
/* Add to family list head */
return (new);
}
/*
* Function: hook_family_remove
* Returns: int - 0 = success, else = failure
* Parameters: hfi(I) - internal family pointer
*
* Remove family from family list. This function has been designed to be
* called once and once only per hook_family_int_t. Thus when cleaning up
* this structure as an orphan, callers should only call hook_family_free.
*/
int
{
/*
* If we're trying to destroy the hook_stack_t...
*/
return (ENXIO);
}
/*
* Check if the family is in use by the presence of either events
* or notify callbacks on the hook family.
*/
} else {
/*
* Although hfi_condemned = B_FALSE is implied from creation,
* putting a comment here inside the else upsets lint.
*/
}
if (!notifydone)
/*
* If we don't have to wait for anything else to disappear from this
* structure then we can free it up.
*/
if (!hfi->hfi_condemned)
return (0);
}
/*
* Function: hook_family_free
* Returns: None
* Parameters: hfi(I) - internal family pointer
*
* Free alloc memory for family
*/
static void
{
/*
* This lock gives us possession of the hks pointer after the
* SLIST_REMOVE, for which it is not needed, when hks_shutdown
* is checked and hook_stack_remove called.
*/
/* Remove from family list */
}
/* Free name space */
}
/* Free container */
}
/*
* Function: hook_family_shutdown
* Returns: int - 0 = success, else = failure
* Parameters: hfi(I) - internal family pointer
*
* As an alternative to removing a family, we may desire to just generate
* a series of callbacks to indicate that we will be going away in the
* future. The hfi_condemned flag isn't set because we aren't trying to
* remove the structure.
*/
int
{
/*
* If we're trying to destroy the hook_stack_t...
*/
return (ENXIO);
}
if (!notifydone)
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 *
{
/* Copy body */
/* Copy 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 hfi_lock must be held when called.
*/
static hook_family_int_t *
{
break;
}
return (hfi);
}
/*
* Function: hook_family_notify_register
* Returns: int - 0 = success, else failure
* Parameters: hfi(I) - hook family
* callback(I) - function to be called
* arg(I) - arg to provide callback when it is called
*
* So long as this hook stack isn't being shut down, register a new
* callback to be activated each time a new event is added to this
* family.
*
* To call this function we must have an active handle in use on the family,
* so if we take this into account, then neither the hook_family_int_t nor
* the hook_stack_t that owns it can disappear. We have to put some trust
* in the callers to be properly synchronised...
*
* Holding hks_lock is required to provide synchronisation for hks_shutdown.
*/
int
{
int error;
return (ESHUTDOWN);
}
}
}
if (canrun)
return (error);
}
/*
* Function: hook_family_notify_unregister
* Returns: int - 0 = success, else failure
* Parameters: hfi(I) - hook family
* callback(I) - function to be called
*
* Remove a callback from the list of those executed when a new event is
* added to a hook family. If the family is not in the process of being
* destroyed then simulate an unregister callback for each event that is
* on the family. This pairs up with the hook_family_notify_register
* action that simulates register events.
* The order of what happens here is important and goes like this.
* 1) Remove the callback from the list of functions to be called as part
* of the notify operation when an event is added or removed from the
* hook family.
* 2) If the hook_family_int_t structure is on death row (free_family will
* be set to true) then there's nothing else to do than let it be free'd.
* 3) If the structure isn't about to die, mark it up as being busy using
* hook_wait_setflag and then drop the lock so the loop can be run.
* 4) if hook_wait_setflag was successful, tell all of the notify callback
* functions that this family has been unregistered.
* 5) Cleanup
*/
int
{
int error;
void *arg;
/*
* If hook_family_remove has been called but the structure was still
* "busy" ... but we might have just made it "unbusy"...
*/
} else {
}
if (error == 0 && !free_family) {
}
if (canrun) {
}
} else if (free_family) {
}
return (error);
}
/*
* 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.)
*/
{
return (NULL);
if (hks->hks_shutdown != 0) {
return (NULL);
}
/* Check whether this event pointer is already registered */
return (NULL);
}
return (NULL);
}
return (NULL);
}
/* Add to event list head */
return (new);
}
/*
* Function: hook_event_init_kstats
* Returns: None
* Parameters: hfi(I) - pointer to the family that owns this event.
* hei(I) - pointer to the hook event that needs some kstats.
*
* Create a set of kstats that relate to each event registered with
* the hook framework. A counter is kept for each time the event is
* activated and for each time a hook is added or removed. As the
* kstats just count the events as they happen, the total number of
* hooks registered must be obtained by subtractived removed from added.
*/
static void
{
{ "hooksAdded", KSTAT_DATA_UINT64 },
{ "hooksRemoved", KSTAT_DATA_UINT64 },
{ "events", KSTAT_DATA_UINT64 }
};
}
}
/*
* Function: hook_event_remove
* Returns: int - 0 = success, else = failure
* Parameters: hfi(I) - internal family pointer
* he(I) - event pointer
*
* Remove event from event list on specific family
*
* This function assumes that the caller has received a pointer to a the
* hook_family_int_t via a call to net_protocol_lookup or net_protocol_unreg'.
* This the hook_family_int_t is guaranteed to be around for the life of this
* call, unless the caller has decided to call net_protocol_release or
* net_protocol_unregister before calling net_event_unregister - an error.
*/
int
{
/*
* Set the flag so that we can call hook_event_notify_run without
* holding any locks but at the same time prevent other changes to
* the event at the same time.
*/
return (ENXIO);
}
return (ESRCH);
}
/*
* The hei_shutdown flag is used to indicate whether or not we have
* done a shutdown and thus already walked through the notify list.
*/
/*
* If there are any hooks still registered for this event or
* there are any notifiers registered, return an error indicating
* that the event is still busy.
*/
} else {
/* hei_condemned = B_FALSE is implied from creation */
/*
* Even though we know the notify list is empty, we call
* hook_wait_destroy here to synchronise wait removing a
* hook from an event.
*/
}
if (!notifydone)
if (!hei->hei_condemned) {
if (free_family)
}
return (0);
}
/*
* Function: hook_event_shutdown
* Returns: int - 0 = success, else = failure
* Parameters: hfi(I) - internal family pointer
* he(I) - event pointer
*
* As with hook_family_shutdown, we want to generate the notify callbacks
* as if the event was being removed but not actually do the remove.
*/
int
{
/*
* Set the flag so that we can call hook_event_notify_run without
* holding any locks but at the same time prevent other changes to
* the event at the same time.
*/
return (ENXIO);
}
return (ESRCH);
}
if (!notifydone)
return (0);
}
/*
* Function: hook_event_free
* Returns: None
* Parameters: hei(I) - internal event pointer
*
* Free alloc memory for event
*/
static void
{
/*
* Remove the event from the hook family's list.
*/
} else {
}
}
}
/* Free container */
if (free_family)
}
/*
* Function: hook_event_checkdup
* Returns: internal event pointer - NULL = Not match
* Parameters: he(I) - event pointer
*
* Search all of the hook families to see if the event being passed in
* has already been associated with one.
*/
static hook_event_int_t *
{
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 *
{
/* Copy body */
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 hfi->hfi_lock must be held when called.
*/
static hook_event_int_t *
{
break;
}
return (hei);
}
/*
* Function: hook_event_notify_register
* Returns: int - 0 = success, else failure
* Parameters: hfi(I) - hook family
* event(I) - name of the event
* callback(I) - function to be called
* arg(I) - arg to provide callback when it is called
*
* Adds a new callback to the event named by "event" (we must find it)
* that will be executed each time a new hook is added to the event.
* Of course, if the stack is being shut down, this call should fail.
*/
int
{
hook_int_t *h;
int error;
if (hks->hks_shutdown != 0) {
return (ESHUTDOWN);
}
return (ESHUTDOWN);
}
return (ESRCH);
}
return (ESHUTDOWN);
}
}
}
if (canrun)
return (error);
}
/*
* Function: hook_event_notify_unregister
* Returns: int - 0 = success, else failure
* Parameters: hfi(I) - hook family
* event(I) - name of the event
* callback(I) - function to be called
*
* Remove the given callback from the named event's list of functions
* to call when a hook is added or removed.
*/
int
{
hook_int_t *h;
void *arg;
int error;
return (ESRCH);
}
/*
* hei_condemned has been set if someone tried to remove the
* event but couldn't because there were still things attached to
* it. Now that we've done a successful remove, if it is now empty
* then by all rights we should be free'ing it too. Note that the
* expectation is that only the caller of hook_event_add will ever
* call hook_event_remove.
*/
free_event = B_TRUE;
} else {
}
if (error == 0 && !free_event) {
}
if (canrun) {
}
}
if (free_event) {
/*
* It is safe to pass in hfi here, without a lock, because
* our structure (hei) is still on one of its lists and thus
* it won't be able to disappear yet...
*/
}
return (error);
}
/*
* Function: hook_event_notify_run
* Returns: None
* Parameters: nrun(I) - pointer to the list of callbacks to execute
* hfi(I) - hook stack pointer to execute callbacks for
* name(I) - name of a hook family
* cmd(I) - either HN_UNREGISTER or HN_REGISTER
*
* Execute all of the callbacks registered for this event.
*/
static void
{
}
/*
* Function: hook_register
* Returns: int - 0 = success, else = failure
* Parameters: hfi(I) - internal family pointer
* event(I) - event name string
* h(I) - hook pointer
*
* Add new hook to hook list on the specified family and event.
*/
int
{
int error;
return (NULL);
/* Alloc hook_int_t and copy hook */
return (ENOMEM);
/*
* to hold global family write lock. Just get read lock here to
* ensure event will not be removed when doing hooks operation
*/
return (ENXIO);
}
/*
* If we've run either the remove() or shutdown(), do not allow any
* more hooks to be added to this event.
*/
if (hei->hei_shutdown) {
goto bad_add;
}
goto bad_add;
}
return (error);
}
/* Add to hook list head */
if (error == 0) {
}
/*
* Note that the name string passed through to the notify callbacks
* is from the original hook being registered, not the copy being
* inserted.
*/
if (error == 0)
return (error);
}
/*
* Function: hook_insert
* Returns: int - 0 = success, else = failure
* Parameters: head(I) - pointer to hook list to insert hook onto
* new(I) - pointer to hook to be inserted
*
* Try to insert the hook onto the list of hooks according to the hints
* given in the hook to be inserted and those that already exist on the
* list. For now, the implementation permits only a single hook to be
* either first or last and names provided with before or after are only
* loosely coupled with the action.
*/
static int
{
case HH_NONE :
/*
* If there is no hint present (or not one that can be
* satisfied now) then try to at least respect the wishes
* of those that want to be last. If there are none wanting
* to be last then add the new hook to the tail of the
* list - this means we keep any wanting to be first
* happy without having to search for HH_FIRST.
*/
(char *)hih->h_hintvalue) == 0)) {
return (0);
}
(char *)hih->h_hintvalue) == 0)) {
}
}
return (0);
}
break;
case HH_FIRST :
return (EBUSY);
break;
case HH_LAST :
return (EBUSY);
break;
case HH_BEFORE :
return (EBUSY);
break;
case HH_AFTER :
return (EBUSY);
break;
default :
return (EINVAL);
}
return (0);
}
/*
* Function: hook_insert_plain
* Returns: int - 0 = success, else = failure
* Parameters: head(I) - pointer to hook list to insert hook onto
* new(I) - pointer to hook to be inserted
*
* Insert a hook such that it respects the wishes of those that want to
* be last. If there are none wanting to be last then add the new hook
* to the tail of the list - this means we keep any wanting to be first
* happy without having to search for HH_FIRST.
*/
static void
{
} else {
}
} else {
}
}
/*
* Function: hook_insert_afterbefore
* Returns: int - 0 = success, else = failure
* Parameters: head(I) - pointer to hook list to insert hook onto
* new(I) - pointer to hook to be inserted
*
* Simple insertion of a hook specifying a HH_BEFORE or HH_AFTER was not
* possible, so now we need to be more careful. The first pass is to go
* through the list and look for any other hooks that also specify the
* same hint name as the new one. The object of this exercise is to make
* sure that hooks with HH_BEFORE always appear on the list before those
* with HH_AFTER so that when said hook arrives, it can be placed in the
* middle of the BEFOREs and AFTERs. If this condition does not arise,
* just use hook_insert_plain() to try and insert the hook somewhere that
* is innocuous to existing efforts.
*/
static int
{
hook_t *h;
/*
* First, look through the list to see if there are any other
* before's or after's that have a matching hint name.
*/
switch (h->h_hint) {
case HH_FIRST :
case HH_LAST :
case HH_NONE :
break;
case HH_BEFORE :
(strcmp((char *)h->h_hintvalue,
(char *)nh->h_hintvalue) == 0)) {
return (0);
}
(strcmp((char *)h->h_hintvalue,
(char *)nh->h_hintvalue) == 0)) {
return (0);
}
break;
case HH_AFTER :
(strcmp((char *)h->h_hintvalue,
(char *)nh->h_hintvalue) == 0)) {
return (0);
}
(strcmp((char *)h->h_hintvalue,
(char *)nh->h_hintvalue) == 0)) {
return (0);
}
break;
}
}
return (0);
}
/*
* Function: hook_unregister
* Returns: int - 0 = success, else = failure
* 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
{
return (ENXIO);
}
/* Hold write lock for event */
return (ENXIO);
}
return (ENOENT);
}
/* Remove from hook list */
/*
* If the delete pending flag has been set and there are
* no notifiers on the event (and we've removed the last
* hook) then we need to free this event after we're done.
*/
free_event = B_TRUE;
}
/*
* While the FWF_DEL_ACTIVE flag is set, the hook_event_int_t
* will not be free'd and thus the hook_family_int_t wil not
* be free'd either.
*/
if (free_event)
return (0);
}
/*
* Function: hook_find_byname
* Returns: internal hook pointer - NULL = Not match
* Parameters: hei(I) - internal event pointer
* name(I)- hook name
*
* Search an event's list of hooks to see if there is a hook present that
* has a matching name to the one being looked for.
*/
static hook_int_t *
{
return (hi);
}
return (NULL);
}
/*
* Function: hook_find
* Returns: internal hook pointer - NULL = Not match
* Parameters: hei(I) - internal event pointer
* h(I) - hook pointer
*
* Search an event's list of hooks to see if there is already one that
* matches the hook being passed in. Currently the only criteria for a
* successful search here is for the names to be the same.
*/
static hook_int_t *
{
}
/*
* 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.
* Because hook_copy() is responsible for the creation of the internal
* hook structure that is used here, it takes on population the structure
* with the kstat information. Note that while the kstat bits are
* seeded here, their installation of the kstats is handled elsewhere.
*/
static hook_int_t *
{
int len;
/* Copy body */
/* Copy name */
/*
* This is initialised in this manner to make it safer to use the
* same pointer in the kstats field.
*/
if (len > 0) {
KM_SLEEP);
(char *)src->h_hintvalue);
}
}
return (new);
}
/*
* Function: hook_init_kstats
* Returns: None
* Parameters: hfi(I) - pointer to the family that owns the event.
* hei(I) - pointer to the event that owns this hook
* hi(I) - pointer to the hook for which we create kstats for
*
* Each hook that is registered with this framework has its own kstats
* set up so that we can provide an easy way in which to observe the
* look of hooks (using the kstat command.) The position is set to 0
* here but is recalculated after we know the insertion has been a
* success.
*/
static void
{
{ "version", KSTAT_DATA_INT32 },
{ "flags", KSTAT_DATA_UINT32 },
{ "hint", KSTAT_DATA_INT32 },
{ "hint_value", KSTAT_DATA_STRING },
{ "position", KSTAT_DATA_INT32 },
{ "hook_hits", KSTAT_DATA_UINT64 }
};
int position;
hook_int_t *h;
/* Initialise the kstats for the structure */
case HH_BEFORE :
case HH_AFTER :
break;
default :
break;
}
}
position = 1;
}
}
/*
* Function: hook_int_free
* Returns: None
* Parameters: hi(I) - internal hook pointer
*
* Free memory allocated to support a hook.
*/
static void
{
int len;
/* Free name space */
}
}
case HH_BEFORE :
case HH_AFTER :
if (len > 0)
break;
default :
break;
}
/* Free container */
}
/*
* Function: hook_alloc
* Returns: hook_t * - pointer to new hook structure
* Parameters: version(I) - version number of the API when compiled
*
* This function serves as the interface for consumers to obtain a hook_t
* structure. At this point in time, there is only a single "version" of
* it, leading to a straight forward function. In a perfect world the
* h_vesion would be a protected data structure member, but C isn't that
* advanced...
*/
hook_t *
{
hook_t *h;
return (h);
}
/*
* Function: hook_free
* Returns: None
* Parameters: h(I) - external hook pointer
*
* This function only free's memory allocated with hook_alloc(), so that if
* (for example) kernel memory was allocated for h_name, this needs to be
* free'd before calling hook_free().
*/
void
{
kmem_free(h, sizeof (*h));
}
/*
* Function: hook_notify_register
* Returns: int - 0 = success, else failure
* Parameters: head(I) - top of the list of callbacks
* callback(I) - function to be called
* arg(I) - arg to pass back to the function
*
* This function implements the modification of the list of callbacks
* that are registered when someone wants to be advised of a change
* that has happened.
*/
static int
void *arg)
{
return (EEXIST);
}
}
return (0);
}
/*
* Function: hook_notify_unregister
* Returns: int - 0 = success, else failure
* Parameters: stackid(I) - netstack identifier
* callback(I) - function to be called
* parg(O) - pointer to storage for pointer
*
* When calling this function, the provision of a valid pointer in parg
* allows the caller to be made aware of what argument the hook function
* was expecting. This then allows the simulation of HN_UNREGISTER events
* when a notify-unregister is performed.
*/
static int
{
break;
}
return (ESRCH);
return (0);
}
/*
* Function: hook_notify_run
* Returns: None
* Parameters: head(I) - top of the list of callbacks
* family(I) - name of the hook family that owns the event
* event(I) - name of the event being changed
* name(I) - name of the object causing change
* cmd(I) - either HN_UNREGISTER or HN_REGISTER
*
* This function walks through the list of registered callbacks and
* executes each one, passing back the arg supplied when registered
* and the name of the family (that owns the event), event (the thing
* to which we're making a change) and finally a name that describes
* what is being added or removed, as indicated by cmd.
*
* This function does not acquire or release any lock as it is required
* that code calling it do so before hand. The use of hook_notify_head_t
* is protected by the use of flagwait_t in the structures that own this
* list and with the use of the FWF_ADD/DEL_ACTIVE flags.
*/
static void
{
}
}