Browse Source

[2982] First draft of Hooks user guide

Stephen Morris 12 years ago
parent
commit
77c1644d0d

+ 19 - 18
doc/Doxyfile

@@ -661,35 +661,36 @@ WARN_LOGFILE           =
 # directories like "/usr/src/myproject". Separate the files or directories
 # with spaces.
 
-INPUT                  = ../src/lib/exceptions \
+INPUT                  = ../src/bin/auth \
+                         ../src/bin/d2 \
+                         ../src/bin/dhcp4 \
+                         ../src/bin/dhcp6 \
+                         ../src/bin/resolver \
+                         ../src/bin/sockcreator/ \
+                         ../src/lib/acl \
+                         ../src/lib/asiolink/ \
+                         ../src/lib/bench \
+                         ../src/lib/cache \
                          ../src/lib/cc \
                          ../src/lib/config \
                          ../src/lib/cryptolink \
-                         ../src/lib/dns \
                          ../src/lib/datasrc \
                          ../src/lib/datasrc/memory \
-                         ../src/bin/auth \
-                         ../src/bin/resolver \
-                         ../src/lib/bench \
+                         ../src/lib/dhcp \
+                         ../src/lib/dhcpsrv \
+                         ../src/lib/dns \
+                         ../src/lib/exceptions \
+                         ../src/lib/hooks \
                          ../src/lib/log \
                          ../src/lib/log/compiler \
-                         ../src/lib/asiolink/ \
                          ../src/lib/nsas \
-                         ../src/lib/testutils \
-                         ../src/lib/cache \
+                         ../src/lib/resolve \
                          ../src/lib/server_common/ \
-                         ../src/bin/sockcreator/ \
+                         ../src/lib/statistics \
+                         ../src/lib/testutils \
                          ../src/lib/util/ \
                          ../src/lib/util/io/ \
                          ../src/lib/util/threads/ \
-                         ../src/lib/resolve \
-                         ../src/lib/acl \
-                         ../src/lib/statistics \
-                         ../src/bin/dhcp6 \
-                         ../src/lib/dhcp \
-                         ../src/lib/dhcpsrv \
-                         ../src/bin/dhcp4 \
-                         ../src/bin/d2 \
                          ../tests/tools/perfdhcp \
                          devel
 
@@ -776,7 +777,7 @@ EXAMPLE_RECURSIVE      = NO
 # directories that contain image that are included in the documentation (see
 # the \image command).
 
-IMAGE_PATH             = ../doc/images
+IMAGE_PATH             = ../doc/images ../src/lib/hooks/images
 
 # The INPUT_FILTER tag can be used to specify a program that doxygen should
 # invoke to filter for each input file. Doxygen will invoke the filter program

+ 51 - 9
doc/devel/mainpage.dox

@@ -1,11 +1,33 @@
+// Copyright (C) 2012-2013  Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
 /**
- *
  * @mainpage BIND10 Developer's Guide
  *
  * Welcome to BIND10 Developer's Guide. This documentation is addressed
- * at existing and prospecting developers and programmers, who would like
- * to gain insight into internal workings of BIND 10. It could also be useful
- * for existing and prospective contributors.
+ * at existing and prospecting developers and programmers and provides
+ * information needed to both extend and maintain BIND 10.
+ *
+ * If you wish to write "hook" code - code that is loaded by BIND 10 at
+ * run-time and modifies its behavior you should read the section
+ * @ref hookDevelopersGuide.
+ *
+ * BIND 10 maintanenace information is divided into a number of sections
+ * depending on focus.  DNS-specific issues are covered in the
+ * @ref dnsMaintenanceGuide while information on DHCP-specific topics can
+ * be found in the @ref dhcpMaintenanceGuide.  General BIND 10 topics, not
+ * specific to any protocol, are discussed in @ref miscellaneousTopics.
  *
  * If you are a user or system administrator, rather than software engineer,
  * you should read <a href="http://bind10.isc.org/docs/bind10-guide.html">BIND10
@@ -14,12 +36,29 @@
  * Regardless of your field of expertise, you are encouraged to visit
  * <a href="http://bind10.isc.org/">BIND10 webpage (http://bind10.isc.org)</a>
  *
- * @section DNS
+ * @section hookDevelopersGuide
+ * - @subpage hookIntroduction
+ *   - @subpage hookLanguages
+ *   - @subpage hookTerminology
+ * - @subpage hookTutorial
+ *   - @subpage hookFrameworkFunctions
+ *   - @subpage hookCallouts
+ *   - @subpage hookExampleCallouts
+ *   - @subpage hookBuild
+ *   - @subpage hookConfiguration
+ * - @subpage hookAdvancedTopics
+ *   - @subpage hookContextCreateDestroy
+ *   - @subpage hookCalloutRegistration
+ *   - @subpage hookMultipleLibraries
+ *   - @subpage hookInterLibraryData
+ *   - @subpage hookRegisterMultipleLibraries
+ *
+ * @section dnsMaintenanceGuide DNS Maintenance Guide
  * - Authoritative DNS (todo)
  * - Recursive resolver (todo)
  * - @subpage DataScrubbing
  *
- * @section DHCP
+ * @section dhcpMaintenanceGuide DHCP Maintenance Guide
  * - @subpage dhcp4
  *   - @subpage dhcpv4Session
  *   - @subpage dhcpv4ConfigParser
@@ -39,7 +78,7 @@
  * - @subpage dhcpDatabaseBackends
  * - @subpage perfdhcpInternals
  *
- * @section misc Miscellaneous topics
+ * @section miscellaneousTopics Miscellaneous topics
  * - @subpage LoggingApi
  *   - @subpage LoggingApiOverview
  *   - @subpage LoggingApiLoggerNames
@@ -47,7 +86,10 @@
  * - @subpage SocketSessionUtility
  * - <a href="./doxygen-error.log">Documentation warnings and errors</a>
  *
- * @todo: Move this logo to the right (and possibly up). Not sure what
- * is the best way to do it in Doxygen, without using CSS hacks.
  * @image html isc-logo.png
  */
+/*
+ * @todo: Move the logo to the right (and possibly up). Not sure what
+ * is the best way to do it in Doxygen, without using CSS hacks.
+ */
+

+ 869 - 0
src/lib/hooks/hook_user.dox

@@ -0,0 +1,869 @@
+// Copyright (C) 2013  Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+/**
+ @page hookDevelopersGuide Hook Developer's Guide
+
+ @section hookIntroduction Introduction
+
+Although the BIND 10 framework and its associated DNS and DHCP programs
+provide comprehensive functionality, there will be times when it does
+not quite do what you require: the processing has to be extended in some
+way to solve your problem.
+
+Since the BIND 10 source code is freely available (BIND 10 being an
+open-source project), one option is to modify it to do what
+you want.  Whilst perfectly feasible, there are drawbacks:
+
+- Although well-documented, BIND 10 is a large program.  Just
+understanding how it works will take a significant amount of time. In
+addition, despite the fact that its object-oriented design keeps the
+coupling between modules to a minimum, an inappropriate change to one
+part of the program during the extension could cause another to
+behave oddly or to stop working altogether, adding to development time.
+
+- The change may need to be re-applied or re-written with every new
+version of BIND 10.  As new functionality is added or bugs are fixed,
+the code or algorithms in the core software may change - and may change
+significantly.
+
+To overcome these problems, BIND 10 provides the "Hooks" interface -
+a defined interface for third-party or user-written code (for ease of
+reference in the rest of this document, such code will be referred to
+as "user-written code").  At specific points in its processing ("hook
+points") BIND 10 will make a call to this code.  The call passes data
+that the user can examine and, if required, modify.  BIND 10 used the
+modified code in the remainder of its processing.
+
+In order to minimise the interaction between BIND 10 and the 
+code, the latter is built independently of BIND 10 in the form of
+a shared library (or libraries).  These are made known to BIND 10
+through its configuration mechanism, and BIND 10 loads the library at
+run time. Libraries can be unloaded and reloaded as needed while BIND
+10 is running.
+
+Use of a defined API and BIND 10 configuration mechanism means that as
+new versions of BIND 10 are released, there is no need to modify the
+user-written code.  Unless there is a major change in an interface
+(which will be clearly documented) all that will be required is a
+rebuild of the libraries.
+
+@note Although the defined interface should not change, the internals
+of some of the classes and structures referenced by the user-written
+code may alter.  These changes will need to be reflected in the compiled
+version of the software, hence the need for a rebuild.
+
+@subsection hookLanguages Languages
+
+The core of BIND 10 is written in C++.  While it is the intention to
+provide interfaces into user code written into other languages, the
+initial versions of the Hooks system requires that user code be written
+in C++.  All examples in this guide are in that language.
+
+@subsection hookTerminology Terminology
+
+In the remainder of this guide, the following terminology is used:
+
+- Hook/Hook Point - used interchageably, this is a point in the code at
+which a call to user-written functions is made. Each hook has a name and
+each hook can have any number (including 0) of user-written functions
+attached to it.
+
+- Callout - a user-written function called by the server at a hook
+point. This is so-named because the server "calls out" to the library
+to execute a user-written function.
+
+- Framework function - the functions that a user-written library needs to
+supply in order for the hooks framework for load and unload the library.
+
+- User code/user library - non-BIND 10 code that is compiled into a
+shared library and loaded by BIND 10 into its address space.
+
+
+@section hookTutorial Tutorial
+
+To illustrate how to write code that integrates with BIND 10, we will
+use the following (rather contrived) example:
+
+The BIND 10 DHCPv4 server is used to allocate IPv4 addresses to clients
+(as well as to pass them other information such as the address of DNS
+servers).  We will suppose that we need to classify clients requesting
+IPv4 addresses according to their hardware address, and want to log both
+the hardware address and allocated IP address for the clients of interest.
+
+The following sections describe how to implement these requirements.
+The code presented here is not efficient and there are better ways
+of doing the task.  The aim is to illustrate the main features of
+user-written hook code rather than provide an optimal solution.
+
+
+@subsection hookFrameworkFunctions Framework Functions
+
+Loading an initializing a library holding user-written code makes use
+of three (user-supplied) functions:
+
+- version - defines the version of BIND 10 code with which the user-library
+is built
+- load - called when the library is loaded by the server.
+- unload - called when the library is unloaded by the server.
+
+Of these, only "version" is mandatory, although our in out example, all three
+are used.
+
+@subsubsection hookVersionFunction The "version" Function
+
+"version" is used by the hooks framework to check that the libraries
+it is loading are compatible with the version of BIND 10 being run.
+Although the hooks system allows BIND 10 and user code to interface
+through a defined API, the relationship is somewhat tight in that the
+user code will depend on the internal structures of BIND 10.  If these
+change - as they can between BIND 10 releases - and BIND 10 is run with
+a version of user-written code built against an earlier version of BIND
+10, a program crash could result.
+
+To guard against this, the "version" function must be provided in
+every library.  It returns a constant defined in the version of header
+files against which it was built.  The hooks framework checks this for
+compatibility with the running version of BIND 10 before proceeding with
+the library load.
+
+In this tutorial, we'll put "version" in its own file, version.cc.  The
+contents are:
+
+@code
+// version.cc
+
+#include <hooks/hooks.h>
+
+extern "C" {
+
+int version() {
+    return (BIND10_HOOKS_VERSION);
+}
+
+};
+@endcode
+
+The file "hooks/hooks.h" is specified relative to the BIND 10 libraries source
+directory - this is covered later in the section @ref hookBuild.  It defines the
+symbol BIND10_HOOKS_VERSION, which has a value that changes on every release
+of BIND 10: this is the value that needs to be returned to the hooks framework.
+
+A final point to note is that the definition of "version" is enclosed
+within 'extern "C"' braces.  All functions accessed by the hooks
+framework use C linkage, mainly to avoid the name mangling that
+accompanies use of the C++ compiler, but also to avoid issues related
+to namespaces.
+
+@subsubsection hookLoadUnloadFunctions The "load" and "unload" Functions
+
+As the names suggest, "load" is called when a library is loaded and
+"unload" called when it is unloaded.  (It is always guaranteed that "load"
+is called: "unload" may not be called in some circumstances, e.g. if the
+system shuts down abnormally.)  These functions are the places where any
+library-wide resources are allocated and deallocated.  "load" is also
+the place where any callouts with non-standard names can be registered:
+this is covered further in the section @ref hookCalloutRegistration.
+
+The example does not make any use callouts with non-standard names.  However,
+as our design requires that the log file be open while BIND 10 is active
+and the library loaded, we'll open the file in the "load" function and close
+it in "unload".  We create two files, one for the file handle declaration:
+
+@code
+// library_common.h
+
+#ifndef LIBRARY_COMMON_H
+#define LIBRARY_COMMON_H
+#include <fstream>
+
+// "Interesting clients" log file handle declaration.
+extern std::fstream interesting;
+#endif
+@endcode
+
+... and one to hold the "load" and "unload" functions:
+
+@code
+// load_unload.cc
+
+#include <hooks/hooks.h>
+#include "library_common.h"
+
+// "Interesting clients" log file handle definition.
+std::fstream interesting;
+
+extern "C" {
+
+int load(LibraryHandle&) {
+    interesting.open("/data/clients/interesting.log",
+                     std::fstream::out | std::fstream::app);
+    return (interesting ? 0 : 1);
+}
+
+int unload() {
+    if (interesting) {
+        interesting.close();
+    }
+    return (0);
+}
+
+};
+@endcode
+
+Notes:
+- The file handle ("interesting") is declared in a header file and defined
+outside of any function.  This means it can be accessed by any function
+within the user library.  For convenience, the definition is in the
+load_unload.cc file.
+- "load" is called with a LibraryHandle argument, used in the registration
+of functions.  As no functions are being called in this example,
+the argument specification omits the variable name (whilst retaining the type)
+to avoid an "unused variable" compiler warning. (The LibraryHandle is
+discussed in the section @ref hookLibraryHandle.)
+- In the current version of the hooks framework, it is not possible to pass
+any configuration information to the "load" function.  The name of the log
+file must therefore be hard-coded as an absolute path name, or communicated
+to the user-written code by some other means.
+- "load" returns 0 on success and non-zero on error.  The hooks framework
+will abandon the loading of the library if "load" returns an error status.
+(In this example, "interesting" can be tested as a boolean value,
+returning "true" if the file opened successfully.)
+- "unload" closes the log file if it is open and is a no-op otherwise. As
+with "load", a zero value is returned on success and a non-zero value
+on an error.  The hooks framework will record a non-zero status return
+as an error in the current BIND 10 log but otherwise ignore it.
+- As before, the function definitions are enclosed in 'extern "C"' braces.
+
+@subsection hookCallouts Callouts
+
+Having sorted out the framework, we now come to the functions that
+actually do something.  These functions are known as "callouts" because
+the BIND 10 code "calls out" to them.  Each BIND 10 server has a number
+of hooks to which callouts can be attached: the purpose of the hooks
+and the data passed to callouts is documented elsewhere.
+
+Before we continue with the example, we'll discuss how arguments are
+passed to callouts and how information can be moved between them.
+
+@subsubsection hookCalloutSignature The Callout Signature
+
+All callouts are declared with the signature:
+@code
+extern "C" {
+int callout(CalloutHandle& handle);
+};
+@endcode
+
+(As before, the callout is declared with "C" linkage.)  Information is passed
+between BIND 10 and the callout through name/value pairs in the CalloutHandle
+object. The object is also used to pass information between callouts on a
+per-request basis. (Both of these concepts are explained below.)
+
+A callout returns an "int" as a status return.  A value of 0 indicates
+success, anything else signifies an error.  The status return has no
+effect on server processing; the only difference between a success
+and error code is that if the latter is returned, the server will
+log an error, specifying both the library and hook that generated it.
+Effectively the return status provides a quick way for a callout to log
+error information to the BIND 10 logging system.
+
+@subsubsection hookArguments Callout Arguments
+
+The CalloutHandle object provides two methods to get and set the
+arguments passed to the callout.  These methods are called (naturally
+enough) getArgument and SetArgument.  Their usage is illustrated by the
+following code snippets.
+
+@code
+    // Server-side code snippet to show the setting of arguments
+
+    int count = 10;
+    boost::shared_ptr<Pkt4> pktptr = ... // Set to appropriate value
+
+    // Assume that "handle" has been created
+    handle.setArgument("data_count", count);
+    handle.setArgument("inpacket", pktptr);
+
+    // Call the hook code...
+    ...
+
+    // Retrieve the modified values
+    handle.getArgument("data_count", count);
+    handle.getArgument("inpacket", pktptr);
+@endcode
+
+In the callout
+
+@code
+    int number;
+    boost::shared_ptr<Pkt4> packet;
+
+    // Retrieve data set by the server.
+    handle.getArgument("data_count", number);
+    handle.getArgument("inpacket", packet);
+
+    // Modify "number"
+    number = ...;
+
+    // Update the arguments to send the value back to the server.
+    handle.setArgument("data_count", number);
+@endcode
+
+As can be seen "getArgument" is used to retrieve data from the
+CalloutHandle, and setArgument used to put data into it.  If a callout
+wishes to alter data and pass it back to the server, it should retrieve
+the data with getArgument, modify it, and call setArgument to send
+it back.
+
+There are several points to be aware of:
+
+- the data type of the variable in the call to getArgument must match
+the data type of the variable passed to the corresponding setArgument
+<B>exactly</B>: using what would normally be considered to be a
+"compatible" type is not enough.  For example, if the server passed
+an argument as an "int" and the callout attempted to retrieve it as a
+"long", an exception would be thrown even though any value that can
+be stored in an "int" will fit into a "long".  This restriction also
+applies the "const" attribute but only as applied to data pointed to by
+pointers, e.g. if an argument is defined as a "char*", an exception will
+be thrown if an attempt is made to retrieve it into a variable of type
+"const char*".  (However, if an argument is set as a "const int", it can
+be retrieved into an "int".)  The documentation of each hook point will
+detail the data type of each argument.
+
+- Although all arguments can be modified, some altered values may not
+be read by the server. (These would be ones that the server considers
+"read-only".) Consult the documentation of each hook to see whether an
+argument can be used to transfer data back to the server.
+
+- If a pointer to an object is passed to a callout (either a "raw"
+pointer, or a boost smart pointer (as in the example above), and the
+underlying object altered through that pointer, the change will be
+reflected in the server even if no call is made to setArgument.
+
+In all cases, consult the documentation for the particular hook to see whether
+parameters can be modified.  As a general rule:
+
+- Do not alter arguments unless you mean the change to be reflected in
+the server.
+- If you alter an argument, call CalloutHandle::setArgument to update the
+value in the CalloutHandle object.
+
+@subsubsection hookCalloutContext Per-Request Context
+
+Although many of the BIND 10 modules can be characterised as handling
+a single packet - e.g. the DHCPv4 server receives a DISCOVER packet,
+processes it and responds with an OFFER, this is not true in all cases.
+The principal exception is the recursive DNS resolver: this receives a
+packet from a client but that packet may itself generate multiple packets
+being sent to upstream servers. To avoid possible confusion the rest of
+this section uses the term "request" to indicate a request by a client
+for some information or action.
+
+As well as argument information, the CalloutHandle object can be used by
+callouts to attach information to a request being handled by the server.
+This information (known as "context") is not used by the server: its purpose
+is to allow callouts to pass information between one another on a
+per-request basis.
+
+Context only exists only for the duration of the request: when a request
+is completed, the context is destroyed.  A new request starts with no
+context information.  Context is particularly useful in servers that may
+be processing multiple requests simultaneously: callouts are effectively
+attaching data to a request and that data follows the request around the
+system.
+
+Context information is held as name/value pairs in the same way
+as arguments, being accessed by the pair of methods setContext and
+getContext.  They have the same restrictions as the setArgument and
+getArgument methods - the type of data retrieved from context must
+<B>exactly</B> match the type of the data set.
+
+As the example in the tutorial uses per-request context, no separate
+example is given here.
+
+
+@subsection hookExampleCallouts Example Callouts
+
+Continuing with the tutorial, the requirements need us to retrieve the
+hardware address of the incoming packet, classify it, and write it,
+together with the assigned IP address, to a log file.  Although we could
+do this in one callout, for this example we'll use two:
+
+- pkt_rcvd - a callout on this hook is invoked when a packet has been
+received and has been parsed.  It is passed a single argument, "query"
+which is an isc::dhcp::Pkt4 object (representing a DHCP v4 packet).
+We will do the classification here.
+
+- v4_lease_write_post - called when the lease (an assignment of an IPv4
+address to a client for a fixed period of time) has been written to the
+database. It is passed two (constant) arguments, the query ("query")
+and the response (called "reply").  This is the point at which the
+example code will write the hardware and IP addresses to the log file.
+
+The standard for naming callouts is to give them the same name as
+the hook.  If this is done, the callouts will be automatically found
+by the Hooks system (this is discussed further in section @ref
+hookCalloutRegistration).  For our example, we will assume this is the
+case, so the code for the first callout (used to classify the client's
+hardware address) is:
+
+@code
+// pkt_rcvd.cc
+
+#include <hooks/hooks.h>
+#include <dhcp/pkt4.h>
+#include "library_common.h"
+
+#include <string>
+
+using namespace isc::dhcp;
+using namespace std;
+
+extern "C" {
+
+// This callout is called at the "pkt_rcvd" hook.
+
+int pkt_rcvd(CalloutHandle& handle) {
+
+    // A pointer to the packet is passed to the callout via a "boost" smart
+    // pointer. The include file "pkt4.h" typedefs a pointer to the Pkt4
+    // object as Pkt4Ptr.  Retrieve a pointer to the object.
+    Pkt4Ptr query_ptr;
+    handle.getArgument("query", query_ptr);
+
+    // Point to the hardware address.
+    HwAddrPtr hwaddr_ptr = query_ptr->getHWAddr();
+
+    // The hardware address is held in a public member variable. We'll classify
+    // it as interesting if the sum of all the bytes in it is divisible by 4.
+    //  (This is a contrived example after all!)
+    long sum = 0;
+    for (int i = 0; i < hwaddr_ptr->hwaddr_.size(); ++i) {
+        sum += hwaddr_ptr->hwadr_[i];
+    }
+
+    // Classify it.
+    if (sum % 4 == 0) {
+        // Store the text form of the hardware address in the context to pass
+        // to the next callout.
+        handle.setContext("hwaddr", hwaddr_ptr->hwaddr_.toText());
+    }
+
+    return (0);
+};
+@endcode
+
+The pct_rcvd callout placed the hardware address of an interesting client in
+the "hwaddr" context for the packet.  Turning now to the callout that will
+write this information to the log file:
+
+@code
+// v4_lease_write.cc
+
+#include <hooks/hooks.h>
+#include <dhcp/pkt4.h>
+#include "library_common.h"
+
+#include <string>
+
+using namespace isc::dhcp;
+using namespace std;
+
+extern "C" {
+
+// This callout is called at the "v4_lease_write_post" hook.
+
+int v4_lease_write_post(CalloutHandle& handle) {
+
+    // Obtain the hardware address of the "interesting" client.  We have to
+    // use a try...catch block here because if the client was not interesting,
+    // no information would be set and getArgument would thrown an exception.
+    string hwaddr;
+    try (handle.getArgument("hwaddr", hwaddr) {
+
+        // getArgument didn't throw so the client is interesting.  Get a pointer
+        // to the reply.  This is read-only so is passed through a smart pointer
+        // const Pkt4 object.  Note that the argument list also contains a
+        // pointer to the query: we don't need to access that in this example.
+        ConstPkt4Ptr reply;
+        handle.getArgument("reply", reply);
+
+        // Get the string form of the IP address.
+        string ipaddr = reply->getYiaddr().toText();
+
+        // Write the information to the log file.
+        interesting << hwaddr << " " << ipaddr << "\n";
+
+        // ... and to guard against a crash, we'll flush the output stream.
+        flush(interesting);
+
+    } catch (const NoSuchCalloutContext&) {
+
+        // No such element in the per-request context with the name
+        // "hwaddr".  We will do nothing, so just dismiss the exception.
+
+    }
+
+    return (0);
+}
+
+};
+@endcode
+
+@subsection hookBuild Building the Library
+
+Building the code requires building a shareable library.  This requires
+the the code be compiled as positition-independent code (using the
+compiler's -fpic switch) and linked as a shared library (with the linker's
+-shared switch).  The build command also needs to point to the BIND 10 include
+directory and link in the appropriate libraries.
+
+Assuming that BIND 10 has been installed in the default location, the
+command line needed to create the library using the Gnu C++ compiler on a
+Linux system is:
+
+@code
+g++ -I /usr/include/bind10 -L /usr/lib/bind10 -fpic -shared -o example.so \
+    load_unload.cc pkt_rcvd.cc v4_lease_write.cc version.cc \
+    -lb10-dhcp++ -lb10-util -lb10-exceptions
+@endcode
+
+Notes:
+- the compilation command and switches required may vary depending on
+your operating system and compiler - consult the relevant documentation
+for details.
+- the values for the -I and -L switches depend on where you have installed
+BIND 10.
+- the list of libraries that need to be included in the command line
+depends on the functionality used by the hook code and the module to
+which they are attached (e.g. hook code for DNS will need to link against
+the libb10-dns++ library).  Depending on operating system, you may also need
+to explicitly list libraries on which the BIND 10 libraries depend, e.g.
+in the command line above, libb10-exceptions depends on log4cplus, so it
+is possible that "-llog4cplus" may need to be appended to the command line.
+
+@subsection hookConfiguration Configuring the Hook Library
+
+The final step is to make the library known to BIND 10.  All BIND 10 modules to
+which hooks can be added contain the "hook_library" element, and user
+libraries are added to this. (The BIND 10 hooks system can handle multiple libraries - this is discussed below.).
+
+To add the example library (assumed to be in /usr/local/lib) to the DHCPv4
+module, the following bindctl commands must be executed:
+
+@code
+> config add Dhcp4/hook_library
+> config set Dhcp4/hook_library[0]/name "/usr/local/lib/example.so"
+> config commit
+@endcode
+
+The DHCPv4 server will load the library and execute the callouts each time a
+request is received.
+
+@section hookAdvancedTopics Advanced Topics
+
+@subsection hookContextCreateDestroy Context Creation and Destruction
+
+As well as the hooks defined by the server, the hooks framework defines
+two hooks of its own, "context_create" and "context_destroy".  The first
+is called when a request is created in the server, before any of the
+server-specific hooks gets called.  It's purpose it to allow a library
+to initialize per-request context. The second is called after all
+server-defined hooks have been processed, and is to allow a library to
+tidy up.
+
+As an example, the v4_lease_write
+example above required that the code check for an exception being
+thrown when accessing the "hwaddr" context item in case it was not set.
+An alternative strategy would have been to provide a callout for the
+"context_create" hook and set the context item "hwaddr" to an empty
+string. Instead of needing to handle an exception, v4_lease_write would
+be guaranteed to get something when looking for the hwaddr item and so
+could write or not write the output depending on the value.
+
+In most cases, "context_destroy" is not needed as the Hooks system
+automatically deletes context. An example where it could be required
+is where memory has been allocated by a callout during the processing
+of a request and a raw pointer to it stored in the context object. On
+destruction of the context, that memory will not be automatically
+released. Freeing in the memory in the "context_destroy callout will solve
+that problem. (Actually, when the context is destroyed, the destructor
+associated with any objects stored in it are run. Rather than point to
+allocated memory with a raw pointer, a better idea would be to point to
+it with a boost "smart" pointer and store that pointer in the context.
+When the context is destroyed, the smart pointer's destructor is run,
+which will automatically delete the pointed-to object.)
+
+
+@subsection hookCalloutRegistration Registering Callouts
+
+As briefly mentioned in @ref hookExampleCallouts, the standard is for 
+callouts in the user library to have the same name as the name of the hook
+to which they are being attached.  This was followed in the tutorial, e.g.
+the callout that needed to be attached to the "pkt_rcvd" hook was named
+pkt_rcvd.
+
+The reason for this standard is that when the library is loaded, the
+hook framework automatically searches the library for functions with the
+same names as the server hooks.  When it finds one, it attaches it to
+that hook point.  This simplifies the loading process and bookkeeping
+required to create a library of callouts.
+
+However, the hooks system is flexible in this area: callouts can have
+non-standard names, and multiple callouts can be registered on a hook.
+
+@subsubsection hookLibraryHandle The LibraryHandle Object
+
+The way into the part of the hooks framework that allows callout
+registration is through the LibraryHandle object.  This was briefly
+introduced in the discussion of the framework functions, in that
+an object of this type is pass to the "load" function.  A LibraryHandle
+can also be obtained from within a callout by calling the CalloutHandle's
+getLibraryHandle() method.
+
+The LibraryHandle provides three methods to manipulate callouts:
+
+- registerCallout - register a callout on a hook.
+- deregisterCallout - deregister a callout from a hook.
+- deregisterAllCallouts - deregister all callouts on a hook.
+
+The following sections cover some of the ways in which these can be used.
+
+@subsubsection hookNonstandardCalloutNames Non-Standard Callout Names
+
+The example in the tutorial used standard names for the callouts.  As noted
+above, it is possible to use non-standard names.  Suppose, instead of the
+callout names "pkt_rcvd" and "v4_lease_write", we had named out callouts
+"classify" and "write_data".  The hooks framework would not have registered
+these callouts, so we would have needed to do it ourself.  The place to
+do this is the "load" framework function, and its code would have had to
+been modified to:
+
+@code
+int load(LibraryHandle& libhandle) {
+    // Register the callouts on the hooks. We assume that a header file
+    // declares the "classify" and "write_data" functions.
+    libhandle.registerCallout("pkt_rcvd", classify);
+    libhandle.registerCallout("v4_lease_write", write_data);
+
+    // Open the log file
+    interesting.open("/data/clients/interesting.log",
+                     std::fstream::out | std::fstream::app);
+    return (interesting ? 0 : 1);
+}
+@endcode
+
+It is possible for a library to contain callouts with both standard and
+non-standard names: ones with standard names will be registered automatically,
+ones with non-standard names need to be registered manually.
+
+@subsubsection hookMultipleCallouts Multiple Callouts on a Hook
+
+The BIND 10 hooks framework allows multiple callouts to be attached to 
+a hook point.  Although it is likely to be rare for user code to need to
+do this, there may be instances where it make sense.
+
+To register multiple callouts on a hook, just call
+LibraryHandle::registerCallout multiple times on the same hook, e.g.
+
+@code
+    libhandle.registerCallout("pkt_rcvd", classify);
+    libhandle.registerCallout("pkt_rcvd", write_data);
+@endcode
+
+The hooks framework will call the callouts in the order they are registered.
+The same CalloutHandle is passed between them, so any change made to the
+CalloutHandle's arguments or per-request context by the first is visible
+to the second.
+
+@subsubsection hookDynamicRegistration Dynamic Registration and Reregistration of Callouts
+
+The previous sections have dealt with callouts being registered during
+the call to "load".  The hooks framework is more flexible than that
+in that callouts and be registered and deregistered within a callout.
+In fact, a callout is able to register or deregister itself, and a callout
+is able to be registered on a hook multiple times.
+
+Using our contrived example again, the DHCPv4 server processes one request
+to completion before it starts processing the next.  With this knowledge,
+we could alter the logic of the code so that the callout attached to the
+"pkt_rcvd" hook registers the callout doing the logging when it detects
+an interesting packet, and the callout doing the logging deregisters
+itself in its execution.  The relevant modifications to the code in
+the tutorial are shown below:
+
+@code
+// pkt_rcvd.cc
+//      :
+
+int pkt_rcvd(CalloutHandle& handle) {
+
+            :
+            :
+
+    // Classify it.
+    if (sum % 4 == 0) {
+        // Interesting, register the callback to log the data.
+        handle.getLibraryHandle().registerCallout("v4_lease_write", write_data);
+    }
+
+    return (0);
+};
+@endcode
+
+@code
+// v4_lease_write.cc
+        :
+
+int write_data(CalloutHandle& handle) {
+
+    // Obtain the hardware address of the "interesting" client. As the
+    // callback is only registered when interesting data is present, we
+    // know that the context contains the hardware address so an exception
+    // will not be thrown when we call getArgument().
+    string hwaddr;
+    handle.getArgument("hwaddr", hwaddr);
+
+    // The pointer to the reply.
+    ConstPkt4Ptr reply;
+    handle.getArgument("reply", reply);
+
+    // Get the string form of the IP address.
+    string ipaddr = reply->getYiaddr().toText():
+
+    // Write the information to the log file and flush.
+    interesting << hwaddr << " " << ipaddr << "\n";
+    flush(interesting);
+
+    // We've logged the data, so deregister ourself.  This callout will not
+    // be called again until it is registered by pkt_rcvd.
+
+    handle.getLibraryHandle().deregisterCallout("v4_lease_write", write_data);
+
+    return (0);
+}
+@endcode
+
+Note that the above example used a non-standard name for the callout
+that wronte the data.  Had the name been a standard one, it would have been
+registered when the library was loaded and called for the first request,
+regardless of whether that was defined as "interesting".  (Although as
+callouts with standard names are always registered before "load" gets called,
+we could have got round that problem by deregistering that particular
+callout in the "load" function.)
+
+
+@note Deregistration of a callout on the hook that is currently
+being called only takes effect when the server next calls the hook.
+To illustrate this, suppose the callouts attached to a hook are A,
+B and C (in that order), and during execution, A deregisters B and C
+and adds D.  When callout A returns, B and C will still run.  The next
+time the server calls the callouts attached to the hook, callouts
+A and D will run (in that order).
+
+@subsection hookMultipleLibraries Multiple User Libraries
+
+As alluded to in the section @ref hookConfiguration, BIND 10 can load
+multiple libraries.  The libraries are loaded in the order specified in
+the configuration, and the callouts attached to the hooks in the order
+presented by the libraries.
+
+The following picture illustrates this, and also illustrates the scope of
+data passed around the system.
+
+@image html DataScopeArgument.png "Scope of Arguments"
+
+In this illustration, a server has three hook points, alpha, beta
+and gamma.  Two libraries are configured, library 1 and library 2.
+Library 1 registers the callout "authorize" for hook alpha, "check" for
+hook beta and "add_option" for hook gamma.  Library 2 registers "logpkt",
+"validate" and "putopt"
+
+The horizontal red lines represent arguments to callouts.  When the server
+calls hook alpha, it creates an argument list and calls the
+first callout for the hook, "authorize".  When that callout returns, the
+same (but possibly modified) argument list is passed to the next callout
+in the chain, "logpkt".  Another, separate argument list is created for
+hook beta and passed to the callouts "check" and "validate" in
+that order.  A similar sequence occurs for hook gamma.
+
+The next picture shows the scope of the context associated with a
+request.
+
+@image html DataScopeContext.png "Illustration of per-library context"
+
+The vertical blue lines represent callout context. Context is
+per-packet but also per-library.  When the server calls "authorize",
+the CalloutHandle's getContext and setContext methods access a context
+created purely for library 1. The next callout on the hook will access
+context created for library 2. These contexts are passed to the callouts
+associated with the next hook.  So when "check" is called, it gets the
+context data that was set by "authorize", when "validate" is called,
+it gets the context data set by "logpkt".
+
+It is stressed that the context for callouts associated with different
+libraries is entirely separate.  For example, suppose "authorize" sets
+the CalloutHandle's context item "foo" to 2 and "logpkt" sets an item of
+the same name to the string "bar".  When "check" accesses the context
+item "foo", it gets a value of 2: when "validate" accesses an item of
+the same name, it gets the value "bar".
+
+It is also stressed that all this context exists only for the life of the
+request being processed.  When that request is complete, all the
+context associated with that request - for all libraries - is destroyed,
+and new context created for the next request.
+
+This structure means that library authors can use per-request context
+without worrying about the presence of other libraries.  Other libraries
+may be present, but will not affect the context values set by a library's
+callouts.
+
+@subsection hookInterLibraryData Passing Data Between Libraries
+
+In rare cases, it is possible that one library may want to pass
+data to another.  This can be done in a limited way by means of the
+CalloutHandle's setArgument and getArgument calls.  For example, in the
+above diagram, the callout "add_option" can pass a value to "putopt"
+by setting a name.value pair in the hook's argument list.  "putopt"
+would be able to read this, but would not be able to return information
+back to "add_option".
+
+All argument names used by BIND 10 will be a combination of letters
+(both upper- and lower-case), digits, hyphens and underscores: no
+other characters will be used.  As argument names are simple strings,
+it is suggested that if such a mechanism be used, the names of the data
+values passed between the libraries include a special character such as
+the dollar symbol or percent sign.  In this way there is no danger that
+a name will conflict with any existing or future BIND 10 argument names.
+
+
+@subsection hookRegisterMultipleLibraries Dynamic Callout Registration and Multiple Libraries
+
+On a particular hook, callouts are called in the order the libraries appear
+in the configuration and, within a library, in the order the callouts
+are registered.
+
+This order applies to dynamically-registered callouts as well.  As an
+example, consider the diagram above where for hook "beta", callout "check"
+is followed by callout "validate".  Suppose that when "authorize" is run,
+it registers a new callout ("double_check") on hook "beta".  That
+callout will be inserted at the end of the callouts registered by
+library 1 and before any registered by library 2.  It would therefore
+appear between "check" and "validate".  On the other hand, if it were
+"logpkt" that registered the new callout, "double_check" would appear
+after "validate".
+
+*/

BIN
src/lib/hooks/images/DataScopeArgument.dia


BIN
src/lib/hooks/images/DataScopeArgument.png


BIN
src/lib/hooks/images/DataScopeContext.dia


BIN
src/lib/hooks/images/DataScopeContext.png