hhook
, hhook_head_register
,
hhook_head_deregister
,
hhook_head_deregister_lookup
,
hhook_run_hooks
,
HHOOKS_RUN_IF
,
HHOOKS_RUN_LOOKUP_IF
—
Helper Hook Framework
#include <sys/hhook.h>
typedef int
(*hhook_func_t)
(int32_t
hhook_type, int32_t
hhook_id, void
*udata, void
*ctx_data, void
*hdata, struct osd
*hosd);
int
hhook_head_register
(int32_t
hhook_type, int32_t
hhook_id, struct
hhook_head **hhh,
uint32_t flags);
int
hhook_head_deregister
(struct
hhook_head *hhh);
int
hhook_head_deregister_lookup
(int32_t
hhook_type, int32_t
hhook_id);
void
hhook_run_hooks
(struct
hhook_head *hhh, void
*ctx_data, struct osd
*hosd);
HHOOKS_RUN_IF
(hhh,
ctx_data,
hosd);
HHOOKS_RUN_LOOKUP_IF
(hhook_type,
hhook_id,
ctx_data,
hosd);
hhook
provides a framework for managing and running
arbitrary hook functions at defined hook points within the kernel. The KPI was
inspired by
pfil(9),
and in many respects can be thought of as a more generic superset of pfil.
The
khelp(9)
and hhook
frameworks are tightly integrated. Khelp
is responsible for registering and deregistering Khelp module hook functions
with hhook
points. The KPI functions used by
khelp(9)
to do this are not documented here as they are not relevant to consumers
wishing to instantiate hook points.
Khelp modules indirectly interact with hhook
by defining
appropriate hook functions for insertion into hook points. Hook functions must
conform to the hhook_func_t function pointer declaration
outlined in the SYNOPSIS.
The hhook_type and
hhook_id arguments identify the hook point which has
called into the hook function. These are useful when a single hook function
is registered for multiple hook points and wants to know which hook point
has called into it.
<sys/hhook.h>
lists
available hhook_type defines and subsystems which
export hook points are responsible for defining the
hhook_id value in appropriate header files.
The udata argument will be passed to the
hook function if it was specified in the struct
hookinfo at hook registration time.
The ctx_data argument contains context
specific data from the hook point call site. The data type passed is
subsystem dependent.
The hdata argument is a pointer to the
persistent per-object storage allocated for use by the module if required.
The pointer will only ever be NULL if the module did not request per-object
storage.
The hosd argument can be used with the
khelp(9)
framework's khelp_get_osd
() function to access data
belonging to a different Khelp module.
Khelp modules instruct the Khelp framework to register their hook
functions with hhook
points by creating a
struct hookinfo per hook point, which contains the
following members:
struct hookinfo {
hhook_func_t hook_func;
struct helper *hook_helper;
void *hook_udata;
int32_t hook_id;
int32_t hook_type;
};
Khelp modules are responsible for setting all members of the
struct except hook_helper which is handled by the
Khelp framework.
Kernel subsystems that wish to provide hhook
points
typically need to make four and possibly five key changes to their
implementation:
- Define a list of hhook_id mappings in an appropriate
subsystem header.
- Register each hook point with the
hhook_head_register
() function during
initialisation of the subsystem.
- Select or create a standardised data type to pass to hook functions as
contextual data.
- Add a call to
HHOOKS_RUN_IF
() or
HHOOKS_RUN_IF_LOOKUP
() at the point in the
subsystem's code where the hook point should be executed.
- If the subsystem can be dynamically added/removed at runtime, each hook
point registered with the
hhook_head_register
()
function when the subsystem was initialised needs to be deregistered with
the hhook_head_deregister
() or
hhook_head_deregister_lookup
() functions when the
subsystem is being deinitialised prior to removal.
The hhook_head_register
() function
registers a hook point with the hhook
framework. The
hook_type argument defines the high level type for the
hook point. Valid types are defined in
<sys/hhook.h>
and new types
should be added as required. The hook_id argument
specifies a unique, subsystem specific identifier for the hook point. The
hhh argument will, if not NULL, be used to store a
reference to the struct hhook_head created as part of
the registration process. Subsystems will generally want to store a local
copy of the struct hhook_head so that they can use the
HHOOKS_RUN_IF
() macro to instantiate hook points.
The HHOOK_WAITOK flag may be passed in via the flags
argument if
malloc(9)
is allowed to sleep waiting for memory to become available. If the hook
point is within a virtualised subsystem (e.g. the network stack), the
HHOOK_HEADISINVNET flag should be passed in via the
flags argument so that the struct
hhook_head created during the registration process will be added to a
virtualised list.
The hhook_head_deregister
() function
deregisters a previously registered hook point from the
hhook
framework. The hhh
argument is the pointer to the struct hhook_head
returned by hhoook_head_register
() when the hook
point was registered.
The hhook_head_deregister_lookup
()
function can be used instead of
hhook_head_deregister
() in situations where the
caller does not have a cached copy of the struct
hhook_head and wants to deregister a hook point using the appropriate
hook_type and hook_id
identifiers instead.
The hhook_run_hooks
() function should
normally not be called directly and should instead be called indirectly via
the HHOOKS_RUN_IF
() macro. However, there may be
circumstances where it is preferable to call the function directly, and so
it is documented here for completeness. The hhh
argument references the hhook
point to call all
registered hook functions for. The ctx_data argument
specifies a pointer to the contextual hook point data to pass into the hook
functions. The hosd argument should be the pointer to
the appropriate object's struct osd if the subsystem
provides the ability for Khelp modules to associate per-object data.
Subsystems which do not should pass NULL.
The HHOOKS_RUN_IF
() macro is the preferred
way to implement hook points. It only calls the
hhook_run_hooks
() function if at least one hook
function is registered for the hook point. By checking for registered hook
functions, the macro minimises the cost associated with adding hook points
to frequently used code paths by reducing to a simple if test in the common
case where no hook functions are registered. The arguments are as described
for the hhook_run_hooks
() function.
The HHOOKS_RUN_IF_LOOKUP
() macro performs
the same function as the HHOOKS_RUN_IF
() macro, but
performs an additional step to look up the struct
hhook_head for the specified hook_type and
hook_id identifiers. It should not be used except in
code paths which are infrequently executed because of the reference counting
overhead associated with the look up.
Each struct hhook_head protects its internal list of hook
functions with a
rmlock(9).
Therefore, anytime hhook_run_hooks
() is called
directly or indirectly via the HHOOKS_RUN_IF
() or
HHOOKS_RUN_IF_LOOKUP
() macros, a non-sleepable read
lock will be acquired and held across the calls to all registered hook
functions.
hhook_head_register
() returns 0 if no errors occurred.
It returns EEXIST if a hook point with the same
hook_type and hook_id is already
registered. It returns EINVAL if the HHOOK_HEADISINVNET flag is not set in
flags because the implementation does not yet support
hook points in non-virtualised subsystems (see the
BUGS section for details). It returns ENOMEM if
malloc(9)
failed to allocate memory for the new struct hhook_head.
hhook_head_deregister
() and
hhook_head_deregister_lookup
() return 0 if no errors
occurred. They return ENOENT if hhh is NULL. They
return EBUSY if the reference count of hhh is greater
than one.
A well commented example Khelp module can be found at:
/usr/share/examples/kld/khelp/h_example.c
The
tcp(4)
implementation provides two hhook
points which are
called for packets sent/received when a connection is in the established
phase. Search for HHOOK in the following files:
sys/netinet/tcp_var.h,
sys/netinet/tcp_input.c,
sys/netinet/tcp_output.c and
sys/netinet/tcp_subr.c.
Development and testing of this software were made possible in part by grants
from the FreeBSD Foundation and Cisco University Research Program Fund at
Community Foundation Silicon Valley.
The hhook
framework first appeared in
FreeBSD 9.0.
The hhook
framework was first released in
2010 by Lawrence Stewart whilst studying at Swinburne University of
Technology's Centre for Advanced Internet Architectures, Melbourne,
Australia. More details are available at:
http://caia.swin.edu.au/urp/newtcp/