hhook, hhook_head_register, hhook_head_deregister, hhook_head_deregister_lookup, hhook_run_hooks,
Contents
Acknowledgements
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.
Description
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.
InformationforKhelpModuleImplementors
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 structhookinfo 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 structhookinfo 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.
CreatingandManagingHookPoints
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
structhhook_head created as part of the registration process. Subsystems will generally want to store a
local copy of the structhhook_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 structhhook_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 structhhook_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 structhhook_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 structosd 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 structhhook_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.
Examples
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.
History
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/Implementation Notes
Each structhhook_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.
Name
hhook, hhook_head_register, hhook_head_deregister, hhook_head_deregister_lookup, hhook_run_hooks,
HHOOKS_RUN_IF, HHOOKS_RUN_LOOKUP_IF — Helper Hook Framework
Return Values
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
structhhook_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.
See Also
khelp(9)
Synopsis
#include<sys/hhook.h>typedefint
(*hhook_func_t)(int32_thhook_type, int32_thhook_id, void*udata, void*ctx_data, void*hdata,
structosd*hosd);
inthhook_head_register(int32_thhook_type, int32_thhook_id, structhhook_head**hhh, uint32_tflags);
inthhook_head_deregister(structhhook_head*hhh);
inthhook_head_deregister_lookup(int32_thhook_type, int32_thhook_id);
voidhhook_run_hooks(structhhook_head*hhh, void*ctx_data, structosd*hosd);
HHOOKS_RUN_IF(hhh, ctx_data, hosd);
HHOOKS_RUN_LOOKUP_IF(hhook_type, hhook_id, ctx_data, hosd);
