BIND 10 trac3083, updated. 8c203f0a835b8b5a4f6e40603ed2c7ff6ed86706 [3083] Fixed typos in allocation engine, as a result of the review.

BIND 10 source code commits bind10-changes at lists.isc.org
Tue Aug 27 06:22:35 UTC 2013


The branch, trac3083 has been updated
       via  8c203f0a835b8b5a4f6e40603ed2c7ff6ed86706 (commit)
       via  21b9e729efc4ba70e36c69cb223f61d0fb0f81a9 (commit)
       via  343252ae26f8e68890143fda004008e304dee087 (commit)
       via  4e92be8690b5b439d1d022083d6677a5c1a313d7 (commit)
       via  8ab644fc53d264555f4896cf05fdf28ea8753d80 (commit)
       via  fd47f18f898695b98623a63a0a1c68d2e4b37568 (commit)
       via  e40e2e96bee958e49a7bb389435add5d980419f5 (commit)
       via  0d22246092ad4822d48f5a52af5f644f5ae2f5e2 (commit)
       via  803bd5a3f260b5412a58ee61bf450211d4b4e061 (commit)
       via  f17c3559e3353f42150d8fa742e1658285811d47 (commit)
       via  fe33a56cfbcd0f846e09a6151313bbb510f76352 (commit)
       via  55235c63e0aaa87d8a6ef8e42ee98f7f99c532bd (commit)
       via  5d1ee7b7470fc644b798ac47db1811c829f5ac24 (commit)
       via  7fef2d7ffdb3b89a18d6a56799564514bffbd0cb (commit)
       via  8abbdb589f6205c51f461541778f40dd355db60b (commit)
       via  9a1036278055840ef554c42d398478430c8ba188 (commit)
       via  28684bcfe5e54ad0421d75d4445a04b75358ce77 (commit)
       via  3b8241154843db71a970ebc30b04fdc3a5e73d33 (commit)
       via  078965388c8160137cfb8ddbe05104d90f3bcf31 (commit)
       via  a8bb19e3163331d8b1137869d4ddd96353d654c1 (commit)
       via  8dcab488bb0d93ec9b97075abecf679db04907ae (commit)
       via  1f4b736004977ec0e089424298d9b5dbf49a3f92 (commit)
       via  f164402c6ddfc870959bf99ea9e5bd4422b7108e (commit)
       via  e3d5529c2df4ae726877c1ede5f4d5133e6ef2a4 (commit)
       via  37d09d3125a73a52a30a418d6f532bfeb8c46b34 (commit)
       via  5b38a1e7569c88f5bafc91504c42e16404356f9a (commit)
       via  a2f074a799b7bdf781b6562f57ecbf9dee991872 (commit)
       via  9ddb7eded5402f31b606b7f22f275b890c0904a7 (commit)
       via  949fc23ebbadfc8a9beaee6fdefb1dfc661d819c (commit)
       via  3bcbbb7f26e1a36ad152912d8bea1a1013c622c9 (commit)
       via  0ece621e566a76a6129425c43792f136f9d857f8 (commit)
       via  4cd6015489d77e842735a1ecd431ea68af1820bd (commit)
       via  507da494700d05fe55a928ad57757a4af3d5f2cb (commit)
       via  e15d409632ae5408531051b3164c2e2b851b214e (commit)
       via  1463b5017b2c3a0d512ac54f8e644156864452c6 (commit)
       via  c183ac1349ddc9f4d76d1426cbdcd80028cb43ed (commit)
      from  9fab3d3a0124810b18c32b181b90da2737cae477 (commit)

Those revisions listed above that are new to this repository have
not appeared on any other notification email; so we list those
revisions in full, below.

- Log -----------------------------------------------------------------
commit 8c203f0a835b8b5a4f6e40603ed2c7ff6ed86706
Author: Marcin Siodelski <marcin at isc.org>
Date:   Tue Aug 27 07:50:54 2013 +0200

    [3083] Fixed typos in allocation engine, as a result of the review.

commit 21b9e729efc4ba70e36c69cb223f61d0fb0f81a9
Merge: 9fab3d3 343252a
Author: Marcin Siodelski <marcin at isc.org>
Date:   Tue Aug 27 07:21:29 2013 +0200

    [3083] Merge branch 'master' into trac3083
    
    Conflicts:
    	src/lib/dhcpsrv/alloc_engine.cc
    	src/lib/dhcpsrv/alloc_engine.h
    	src/lib/dhcpsrv/tests/alloc_engine_unittest.cc

-----------------------------------------------------------------------

Summary of changes:
 ChangeLog                                          |   26 +-
 doc/devel/mainpage.dox                             |   10 +-
 src/bin/cmdctl/b10-cmdctl.xml                      |    2 -
 src/bin/cmdctl/cmdctl.py.in                        |    2 -
 src/bin/cmdctl/cmdctl.spec.pre.in                  |    5 -
 src/bin/cmdctl/tests/cmdctl_test.py                |   20 +-
 src/bin/dbutil/b10-dbutil.xml                      |    2 +-
 src/bin/dhcp4/dhcp4_hooks.dox                      |  130 +++-
 src/bin/dhcp4/dhcp4_messages.mes                   |   22 +
 src/bin/dhcp4/dhcp4_srv.cc                         |  468 ++++++++-----
 src/bin/dhcp4/dhcp4_srv.h                          |    7 -
 src/bin/dhcp4/tests/dhcp4_srv_unittest.cc          |  703 +++++++++++++++++++-
 src/bin/dhcp6/dhcp6_messages.mes                   |    2 +-
 src/bin/dhcp6/dhcp6_srv.cc                         |   34 +-
 src/bin/dhcp6/dhcp6_srv.h                          |   10 +-
 src/bin/dhcp6/tests/hooks_unittest.cc              |    8 +-
 src/lib/dhcp/pkt4.cc                               |   54 +-
 src/lib/dhcp/pkt4.h                                |   66 +-
 src/lib/dhcpsrv/Makefile.am                        |    1 +
 src/lib/dhcpsrv/alloc_engine.cc                    |   62 +-
 src/lib/dhcpsrv/alloc_engine.h                     |   43 +-
 src/lib/dhcpsrv/callout_handle_store.h             |   91 +++
 src/lib/dhcpsrv/dhcp_parsers.cc                    |   51 +-
 src/lib/dhcpsrv/dhcp_parsers.h                     |   19 +-
 src/lib/dhcpsrv/dhcpsrv_messages.mes               |    6 +
 src/lib/dhcpsrv/tests/Makefile.am                  |    5 +
 src/lib/dhcpsrv/tests/alloc_engine_unittest.cc     |    9 +-
 .../dhcpsrv/tests/callout_handle_store_unittest.cc |  122 ++++
 src/lib/dhcpsrv/tests/dhcp_parsers_unittest.cc     |  308 ++++++++-
 .../tests/test_get_callout_handle.cc}              |   18 +-
 src/lib/dhcpsrv/tests/test_get_callout_handle.h    |   46 ++
 src/lib/hooks/callout_manager.cc                   |    5 +-
 src/lib/hooks/hooks_component_developer.dox        |    8 +-
 src/lib/hooks/hooks_maintenance.dox                |  274 ++++++++
 src/lib/hooks/hooks_messages.mes                   |    2 +-
 src/lib/hooks/{hook_user.dox => hooks_user.dox}    |    2 +-
 src/lib/hooks/images/HooksUml.dia                  |  Bin 0 -> 2354 bytes
 src/lib/hooks/images/HooksUml.png                  |  Bin 0 -> 13841 bytes
 tests/tools/perfdhcp/perf_pkt4.cc                  |    2 +-
 39 files changed, 2237 insertions(+), 408 deletions(-)
 create mode 100644 src/lib/dhcpsrv/callout_handle_store.h
 create mode 100644 src/lib/dhcpsrv/tests/callout_handle_store_unittest.cc
 copy src/lib/{dhcp_ddns/dhcp_ddns_log.cc => dhcpsrv/tests/test_get_callout_handle.cc} (70%)
 create mode 100644 src/lib/dhcpsrv/tests/test_get_callout_handle.h
 create mode 100644 src/lib/hooks/hooks_maintenance.dox
 rename src/lib/hooks/{hook_user.dox => hooks_user.dox} (99%)
 create mode 100644 src/lib/hooks/images/HooksUml.dia
 create mode 100644 src/lib/hooks/images/HooksUml.png

-----------------------------------------------------------------------
diff --git a/ChangeLog b/ChangeLog
index 139d0ae..0dab489 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,12 +1,34 @@
+667.	[func]		tomek
+	Additional hooks (buffer4_receive, lease4_renew,
+	lease4_release, buffer4_send) added to the DHCPv4 server.
+	(Trac #2983, git fd47f18f898695b98623a63a0a1c68d2e4b37568)
+
+666.	[func]		vorner
+	The CmdCtl's command "print_settings" was removed. It served no real
+	purpose and was just experimental leftover from early development.
+	(Trac #3028, git 0d22246092ad4822d48f5a52af5f644f5ae2f5e2)
+
+665.	[doc]		stephen
+	Added the "Hook's Maintenance Guide" to the BIND 10 developer
+	documentation.
+	(Trac #3063, git 5d1ee7b7470fc644b798ac47db1811c829f5ac24)
+
+664.	[bug]		tmark
+	Corrects a bug in Hooks processing that was improperly
+	creating a new callout handle on every call, rather
+	than maintaining it throughout the context of the
+	packet being processed.
+	(Trac #3062, git 28684bcfe5e54ad0421d75d4445a04b75358ce77)
+
 663.	[func]		marcin
-	bind10-dhcp6: Server processes the DHCPv6 Client FQDN Option
+	b10-dhcp6: Server processes the DHCPv6 Client FQDN Option
 	sent by a client and generates the response. The DHCPv6 Client
 	FQDN Option is represented by the new class in the libdhcp++.
 	As a result of FQDN Option processing, the server generates
 	NameChangeRequests which represent changes to DNS mappings for
 	a particular lease (addition or removal of DNS mappings).
 	Currently all generated NameChangeRequests are dropped. Sending
-	them to bind10-d2 will be implemented with the future tickets.
+	them to b10-dhcp-ddns will be implemented with the future tickets.
 	(Trac #3036, git 209f3964b9f12afbf36f3fa6b62964e03049ec6e)
 
 662.	[func]		marcin
diff --git a/doc/devel/mainpage.dox b/doc/devel/mainpage.dox
index c670eaf..2b1ea8d 100644
--- a/doc/devel/mainpage.dox
+++ b/doc/devel/mainpage.dox
@@ -21,7 +21,7 @@
  *
  * 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.
+ * @ref hooksdgDevelopersGuide.
  *
  * BIND 10 maintanenace information is divided into a number of sections
  * depending on focus.  DNS-specific issues are covered in the
@@ -30,16 +30,18 @@
  * 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
+ * you should read the
+ * <a href="http://bind10.isc.org/docs/bind10-guide.html">BIND10
  * Guide (Administrator Reference for BIND10)</a> instead.
  *
- * Regardless of your field of expertise, you are encouraged to visit
+ * Regardless of your field of expertise, you are encouraged to visit the
  * <a href="http://bind10.isc.org/">BIND10 webpage (http://bind10.isc.org)</a>
  * @section hooksFramework Hooks Framework
  * - @subpage hooksdgDevelopersGuide
  * - @subpage dhcpv4Hooks
  * - @subpage dhcpv6Hooks
  * - @subpage hooksComponentDeveloperGuide
+ * - @subpage hooksmgMaintenanceGuide
  *
  * @section dnsMaintenanceGuide DNS Maintenance Guide
  * - Authoritative DNS (todo)
@@ -70,7 +72,7 @@
  * - @subpage perfdhcpInternals
  * - @subpage libdhcp_ddns
  *
- * @section miscellaneousTopics Miscellaneous topics
+ * @section miscellaneousTopics Miscellaneous Topics
  * - @subpage LoggingApi
  *   - @subpage LoggingApiOverview
  *   - @subpage LoggingApiLoggerNames
diff --git a/src/bin/cmdctl/b10-cmdctl.xml b/src/bin/cmdctl/b10-cmdctl.xml
index 871265c..c66837c 100644
--- a/src/bin/cmdctl/b10-cmdctl.xml
+++ b/src/bin/cmdctl/b10-cmdctl.xml
@@ -169,8 +169,6 @@
       The configuration command is:
     </para>
 
-<!-- NOTE: print_settings is not documented since I think will be removed -->
-
     <para>
       <command>shutdown</command> exits <command>b10-cmdctl</command>.
       This has an optional <varname>pid</varname> argument to
diff --git a/src/bin/cmdctl/cmdctl.py.in b/src/bin/cmdctl/cmdctl.py.in
index 0b402fe..ea56da9 100755
--- a/src/bin/cmdctl/cmdctl.py.in
+++ b/src/bin/cmdctl/cmdctl.py.in
@@ -370,8 +370,6 @@ class CommandControl():
             self._httpserver.shutdown()
             self._serving = False
 
-        elif command == 'print_settings':
-            answer = ccsession.create_answer(0, self._cmdctl_config_data)
         else:
             answer = ccsession.create_answer(1, 'unknown command: ' + command)
 
diff --git a/src/bin/cmdctl/cmdctl.spec.pre.in b/src/bin/cmdctl/cmdctl.spec.pre.in
index d04e2e3..87aeb11 100644
--- a/src/bin/cmdctl/cmdctl.spec.pre.in
+++ b/src/bin/cmdctl/cmdctl.spec.pre.in
@@ -24,11 +24,6 @@
     ],
     "commands": [
       {
-        "command_name": "print_settings",
-        "command_description": "Print some_string and some_int to stdout",
-        "command_args": []
-      },
-      {
         "command_name": "shutdown",
         "command_description": "shutdown cmdctl",
         "command_args": [
diff --git a/src/bin/cmdctl/tests/cmdctl_test.py b/src/bin/cmdctl/tests/cmdctl_test.py
index dfd66ad..7af8b0d 100644
--- a/src/bin/cmdctl/tests/cmdctl_test.py
+++ b/src/bin/cmdctl/tests/cmdctl_test.py
@@ -462,10 +462,22 @@ class TestCommandControl(unittest.TestCase):
         answer = self.cmdctl.command_handler('unknown-command', None)
         self._check_answer(answer, 1, 'unknown command: unknown-command')
 
-        answer = self.cmdctl.command_handler('print_settings', None)
+        # Send a real command. Mock stuff so the shutdown command doesn't
+        # cause an exception.
+        class ModuleCC:
+            def send_stopping():
+                pass
+        self.cmdctl._module_cc = ModuleCC
+        called = []
+        class Server:
+            def shutdown():
+                called.append('shutdown')
+        self.cmdctl._httpserver = Server
+        answer = self.cmdctl.command_handler('shutdown', None)
         rcode, msg = ccsession.parse_answer(answer)
         self.assertEqual(rcode, 0)
-        self.assertTrue(msg != None)
+        self.assertIsNone(msg)
+        self.assertEqual(['shutdown'], called)
 
     def test_command_handler_spec_update(self):
         # Should not be present
@@ -543,10 +555,10 @@ class TestCommandControl(unittest.TestCase):
         self.assertEqual(1, rcode)
 
         # Send a command to cmdctl itself.  Should be the same effect.
-        rcode, value = self.cmdctl.send_command('Cmdctl', 'print_settings',
+        rcode, value = self.cmdctl.send_command('Cmdctl', 'shutdown',
                                                 None)
         self.assertEqual(2, len(self.cmdctl.sent_messages))
-        self.assertEqual(({'command': ['print_settings']}, 'Cmdctl'),
+        self.assertEqual(({'command': ['shutdown']}, 'Cmdctl'),
                          self.cmdctl.sent_messages[-1])
         self.assertEqual(1, rcode)
 
diff --git a/src/bin/dbutil/b10-dbutil.xml b/src/bin/dbutil/b10-dbutil.xml
index c93d060..f32301f 100644
--- a/src/bin/dbutil/b10-dbutil.xml
+++ b/src/bin/dbutil/b10-dbutil.xml
@@ -68,7 +68,7 @@
     </para>
 
     <para>
-      <command>b10-dbutil</command> operates in one of two modesr: check mode
+      <command>b10-dbutil</command> operates in one of two modes: check mode
       or upgrade mode.
     </para>
 
diff --git a/src/bin/dhcp4/dhcp4_hooks.dox b/src/bin/dhcp4/dhcp4_hooks.dox
index 85fcfc4..ebe2d89 100644
--- a/src/bin/dhcp4/dhcp4_hooks.dox
+++ b/src/bin/dhcp4/dhcp4_hooks.dox
@@ -49,22 +49,45 @@ The following list is ordered by appearance of specific hook points during
 packet processing. Hook points that are not specific to packet processing
 (e.g. lease expiration) will be added to the end of this list.
 
+ @subsection dhcpv4HooksBuffer4Receive buffer4_receive
+
+ - @b Arguments:
+   - name: @b query4, type: isc::dhcp::Pkt4Ptr, direction: <b>in/out</b>
+
+ - @b Description: this callout is executed when an incoming DHCPv4
+   buffer is received, before its content is parsed. The sole argument
+   - query4 - contains a pointer to an isc::dhcp::Pkt4 object that
+   contains raw information regarding incoming packet, including its
+   source and destination addresses, interface over which it was
+   received, and a raw buffer stored in data_ field. None of the
+   packet fields (op_, hlen_, chaddr_, etc.) are set yet. Callouts
+   installed on this hook point can modify the incoming buffer. The
+   server will parse the buffer afterwards.
+
+ - <b>Skip flag action</b>: If any callout sets the skip flag, the server will
+   skip the buffer parsing. In such case there is an expectation that
+   the callout will parse the buffer and create option objects (see
+   isc::dhcp::Pkt4::addOption()). Otherwise the server will find out
+   that some mandatory options are missing (e.g. DHCP Message Type) and
+   will drop the packet. If you want to have the capability to drop
+   a message, it is better to use skip flag in pkt4_receive callout.
+
  @subsection dhcpv4HooksPkt4Receive pkt4_receive
 
  - @b Arguments:
    - name: @b query4, type: isc::dhcp::Pkt4Ptr, direction: <b>in/out</b>
 
  - @b Description: this callout is executed when an incoming DHCPv4
-   packet is received and its content is parsed. The sole argument -
-   query4 - contains a pointer to an isc::dhcp::Pkt4 object that contains
-   all information regarding incoming packet, including its source and
-   destination addresses, interface over which it was received, a list
-   of all options present within and relay information.  All fields of
-   the Pkt4 object can be modified at this time, except data_. (data_
-   contains the incoming packet as raw buffer. By the time this hook is
-   reached, that information has already parsed and is available though
-   other fields in the Pkt4 object.  For this reason, it doesn't make
-   sense to modify it.)
+   packet is received and its content has been parsed. The sole
+   argument - query4 - contains a pointer to an isc::dhcp::Pkt4 object
+   that contains all information regarding incoming packet, including
+   its source and destination addresses, interface over which it was
+   received, a list of all options present within and relay
+   information.  All fields of the Pkt4 object can be modified at this
+   time, except data_. (By the time this hook is reached, the contents
+   of the data_ field has been already parsed and stored in other
+   fields. Therefore, the modification in the data_ field has no
+   effect.)
 
  - <b>Skip flag action</b>: If any callout sets the skip flag, the server will
    drop the packet and start processing the next one.  The reason for the drop
@@ -97,42 +120,97 @@ packet processing. Hook points that are not specific to packet processing
    - name: @b lease4, type: isc::dhcp::Lease4Ptr, direction: <b>in/out</b>
 
  - @b Description: this callout is executed after the server engine
-   has selected a lease for client's request but before the lease
-   has been inserted into the database. Any modifications made to the
-   isc::dhcp::Lease4 object will be stored in the lease's record in the
-   database. The callout should make sure that any modifications are
-   sanity checked as the server will use that data as is with no further
-   checking.\n\n The server processes lease requests for DISCOVER and
-   REQUEST in a very similar way. The only major difference is that
-   for DISCOVER the lease is just selected, but not inserted into
-   the database.  It is possible to distinguish between DISCOVER and
-   REQUEST by checking value of the fake_allocation flag: a value of true
-   means that the lease won't be inserted into the database (DISCOVER),
-   a value of false means that it will (REQUEST).
+   has selected a lease for client's request but before the lease has
+   been inserted into the database. Any modifications made to the
+   isc::dhcp::Lease4 object will be stored in the lease's record in
+   the database. The callout should sanity check all modifications as
+   the server will use that data as is with no further checking.\n\n
+   The server processes lease requests for DISCOVER and REQUEST in a
+   very similar way. The only major difference is that for DISCOVER
+   the lease is just selected, but not inserted into the database.  It
+   is possible to distinguish between DISCOVER and REQUEST by checking
+   value of the fake_allocation flag: a value of true indicates that the
+   lease won't be inserted into the database (DISCOVER), a value of
+   false indicates that it will (REQUEST).
 
  - <b>Skip flag action</b>: If any callout installed on 'lease4_select'
    sets the skip flag, the server will not assign any lease. Packet
    processing will continue, but client will not get an address.
 
+ at subsection dhcpv4HooksLeaseRenew lease4_renew
+
+ - @b Arguments:
+   - name: @b subnet4, type: isc::dhcp::Subnet4Ptr, direction: <b>in</b>
+   - name: @b clientid, type: isc::dhcp::ClientId, direction: <b>in</b>
+   - name: @b hwaddr, type: isc::dhcp::HWAddr, direction: <b>in</b>
+   - name: @b lease4, type: isc::dhcp::Lease4Ptr, direction: <b>in/out</b>
+
+ - @b Description: this callout is executed when the server engine
+   is about to renew a lease, as a result of receiving REQUEST/Renewing
+   packet. The lease4 argument points to Lease4 object that contains
+   the updated values. Callout can modify those values. Care should be taken
+   as the server will attempt to update the lease in the database without
+   any additional checks.
+
+ - <b>Skip flag action</b>: If any callout installed on 'lease4_renew'
+   sets the skip flag, the server will not update the lease and will
+   use old values instead.
+
+ at subsection dhcpv4HooksLeaseRelease lease4_release
+
+ - @b Arguments:
+   - name: @b query4, type: isc::dhcp::Pkt4Ptr, direction: <b>in</b>
+   - name: @b lease4, type: isc::dhcp::Lease4Ptr, direction: <b>in</b>
+
+ - @b Description: this callout is executed when the server engine
+   is about to release a lease, as a result of receiving RELEASE packet.
+   The lease4 argument points to Lease4 object that contains the lease to
+   be released. It doesn't make sense to modify it at this time.
+
+ - <b>Skip flag action</b>: If any callout installed on 'lease4_release'
+   sets the skip flag, the server will not delete the lease. It will be
+   kept in the database and will go through the regular expiration/reuse
+   process.
+
 @subsection dhcpv4HooksPkt4Send pkt4_send
 
  - @b Arguments:
    - name: @b response4, type: isc::dhcp::Pkt4Ptr, direction: <b>in/out</b>
 
  - @b Description: this callout is executed when server's response
-   is about to be send back to the client. The sole argument - response4 -
+   is about to be sent back to the client. The sole argument - response4 -
    contains a pointer to an isc::dhcp::Pkt4 object that contains the
-   packet, with set source and destination addresses, interface over which
-   it will be send, list of all options and relay information.  All fields
-   of the Pkt4 object can be modified at this time, except bufferOut_.
+   packet, with source and destination addresses set, interface over which
+   it will be sent, and a list of all options and relay information.  All fields
+   of the Pkt4 object can be modified at this time, except buffer_out_.
    (This is scratch space used for constructing the packet after all
    pkt4_send callouts are complete, so any changes to that field will
    be overwritten.)
 
  - <b>Skip flag action</b>: if any callout sets the skip flag, the server
+   will not construct raw buffer. The expectation is that if the callout
+   set skip flag, it is responsible for constructing raw form on its own.
+   Otherwise the output packet will be sent with zero length.
+
+ at subsection dhcpv4HooksBuffer4Send buffer4_send
+
+ - @b Arguments:
+   - name: @b response4, type: isc::dhcp::Pkt4Ptr, direction: <b>in/out</b>
+
+ - @b Description: this callout is executed when server's response
+   is about to be sent back to the client. The sole argument - response4 -
+   contains a pointer to an isc::dhcp::Pkt4 object that contains the
+   packet, with source and destination addresses set, interface over which
+   it will be sent, and a list of all options and relay information. The raw
+   on-wire form is already prepared in buffer_out_ (see isc::dhcp::Pkt4::getBuffer())
+   It doesn't make any sense to modify packet fields or options content
+   at this time, because they were already used to construct on-wire buffer.
+
+ - <b>Skip flag action</b>: if any callout sets the skip flag, the server
    will drop this response packet. However, the original request packet
    from a client was processed, so server's state was most likely changed
    (e.g. lease was allocated). Setting this flag merely stops the change
    being communicated to the client.
 
+
 */
diff --git a/src/bin/dhcp4/dhcp4_messages.mes b/src/bin/dhcp4/dhcp4_messages.mes
index 8d3252f..fca75bf 100644
--- a/src/bin/dhcp4/dhcp4_messages.mes
+++ b/src/bin/dhcp4/dhcp4_messages.mes
@@ -70,6 +70,24 @@ This message is printed when DHCPv4 server disables an interface from being
 used to receive DHCPv4 traffic. Sockets on this interface will not be opened
 by the Interface Manager until interface is enabled.
 
+% DHCP4_HOOK_BUFFER_RCVD_SKIP received DHCPv4 buffer was dropped because a callout set the skip flag.
+This debug message is printed when a callout installed on buffer4_receive
+hook point set the skip flag. For this particular hook point, the
+setting of the flag by a callout instructs the server to drop the packet.
+
+% DHCP4_HOOK_BUFFER_SEND_SKIP prepared DHCPv4 response was dropped because a callout set the skip flag.
+This debug message is printed when a callout installed on buffer4_send
+hook point set the skip flag. For this particular hook point, the
+setting of the flag by a callout instructs the server to drop the packet.
+Server completed all the processing (e.g. may have assigned, updated
+or released leases), but the response will not be send to the client.
+
+% DHCP4_HOOK_LEASE4_RELEASE_SKIP DHCPv4 lease was not released because a callout set the skip flag.
+This debug message is printed when a callout installed on lease4_release
+hook point set the skip flag. For this particular hook point, the
+setting of the flag by a callout instructs the server to not release
+a lease.
+
 % DHCP4_HOOK_PACKET_RCVD_SKIP received DHCPv4 packet was dropped, because a callout set the skip flag.
 This debug message is printed when a callout installed on the pkt4_receive
 hook point sets the skip flag. For this particular hook point, the
@@ -138,6 +156,10 @@ This is a general catch-all message indicating that the processing of a
 received packet failed.  The reason is given in the message.  The server
 will not send a response but will instead ignore the packet.
 
+% DHCP4_PACKET_DROP_NO_TYPE packet received on interface %1 dropped, because of missing msg-type option
+This is a debug message informing that incoming DHCPv4 packet did not
+have mandatory DHCP message type option and thus was dropped.
+
 % DHCP4_PACKET_RECEIVED %1 (type %2) packet received on interface %3
 A debug message noting that the server has received the specified type of
 packet on the specified interface.  Note that a packet marked as UNKNOWN
diff --git a/src/bin/dhcp4/dhcp4_srv.cc b/src/bin/dhcp4/dhcp4_srv.cc
index f0463f2..bf4f76b 100644
--- a/src/bin/dhcp4/dhcp4_srv.cc
+++ b/src/bin/dhcp4/dhcp4_srv.cc
@@ -14,24 +14,25 @@
 
 #include <asiolink/io_address.h>
 #include <dhcp/dhcp4.h>
+#include <dhcp/duid.h>
+#include <dhcp/hwaddr.h>
 #include <dhcp/iface_mgr.h>
 #include <dhcp/option4_addrlst.h>
 #include <dhcp/option_int.h>
 #include <dhcp/option_int_array.h>
 #include <dhcp/pkt4.h>
-#include <dhcp/duid.h>
-#include <dhcp/hwaddr.h>
 #include <dhcp4/dhcp4_log.h>
 #include <dhcp4/dhcp4_srv.h>
-#include <dhcpsrv/utils.h>
+#include <dhcpsrv/addr_utilities.h>
+#include <dhcpsrv/callout_handle_store.h>
 #include <dhcpsrv/cfgmgr.h>
 #include <dhcpsrv/lease_mgr.h>
 #include <dhcpsrv/lease_mgr_factory.h>
 #include <dhcpsrv/subnet.h>
 #include <dhcpsrv/utils.h>
-#include <dhcpsrv/addr_utilities.h>
-#include <hooks/hooks_manager.h>
+#include <dhcpsrv/utils.h>
 #include <hooks/callout_handle.h>
+#include <hooks/hooks_manager.h>
 
 #include <boost/algorithm/string/erase.hpp>
 
@@ -46,16 +47,22 @@ using namespace isc::log;
 using namespace std;
 
 /// Structure that holds registered hook indexes
-struct Dhcp6Hooks {
+struct Dhcp4Hooks {
+    int hook_index_buffer4_receive_;///< index for "buffer4_receive" hook point
     int hook_index_pkt4_receive_;   ///< index for "pkt4_receive" hook point
     int hook_index_subnet4_select_; ///< index for "subnet4_select" hook point
+    int hook_index_lease4_release_; ///< index for "lease4_release" hook point
     int hook_index_pkt4_send_;      ///< index for "pkt4_send" hook point
+    int hook_index_buffer4_send_;   ///< index for "buffer4_send" hook point
 
-    /// Constructor that registers hook points for DHCPv6 engine
-    Dhcp6Hooks() {
+    /// Constructor that registers hook points for DHCPv4 engine
+    Dhcp4Hooks() {
+        hook_index_buffer4_receive_= HooksManager::registerHook("buffer4_receive");
         hook_index_pkt4_receive_   = HooksManager::registerHook("pkt4_receive");
         hook_index_subnet4_select_ = HooksManager::registerHook("subnet4_select");
         hook_index_pkt4_send_      = HooksManager::registerHook("pkt4_send");
+        hook_index_lease4_release_ = HooksManager::registerHook("lease4_release");
+        hook_index_buffer4_send_   = HooksManager::registerHook("buffer4_send");
     }
 };
 
@@ -63,7 +70,7 @@ struct Dhcp6Hooks {
 // will be instantiated (and the constructor run) when the module is loaded.
 // As a result, the hook indexes will be defined before any method in this
 // module is called.
-Dhcp6Hooks Hooks;
+Dhcp4Hooks Hooks;
 
 namespace isc {
 namespace dhcp {
@@ -82,8 +89,8 @@ static const char* SERVER_ID_FILE = "b10-dhcp4-serverid";
 
 Dhcpv4Srv::Dhcpv4Srv(uint16_t port, const char* dbconfig, const bool use_bcast,
                      const bool direct_response_desired)
-: serverid_(), shutdown_(true), alloc_engine_(), port_(port), 
-    use_bcast_(use_bcast), hook_index_pkt4_receive_(-1), 
+: serverid_(), shutdown_(true), alloc_engine_(), port_(port),
+    use_bcast_(use_bcast), hook_index_pkt4_receive_(-1),
     hook_index_subnet4_select_(-1), hook_index_pkt4_send_(-1) {
 
     LOG_DEBUG(dhcp4_logger, DBG_DHCP4_START, DHCP4_OPEN_SOCKET).arg(port);
@@ -183,151 +190,245 @@ Dhcpv4Srv::run() {
             LOG_ERROR(dhcp4_logger, DHCP4_PACKET_RECEIVE_FAIL).arg(e.what());
         }
 
-        if (query) {
+        // Timeout may be reached or signal received, which breaks select()
+        // with no reception ocurred
+        if (!query) {
+            continue;
+        }
+
+        bool skip_unpack = false;
+
+        // The packet has just been received so contains the uninterpreted wire
+        // data; execute callouts registered for buffer4_receive.
+        if (HooksManager::getHooksManager()
+            .calloutsPresent(Hooks.hook_index_buffer4_receive_)) {
+            CalloutHandlePtr callout_handle = getCalloutHandle(query);
+
+            // Delete previously set arguments
+            callout_handle->deleteAllArguments();
+
+            // Pass incoming packet as argument
+            callout_handle->setArgument("query4", query);
+
+            // Call callouts
+            HooksManager::callCallouts(Hooks.hook_index_buffer4_receive_,
+                                       *callout_handle);
+
+            // Callouts decided to skip the next processing step. The next
+            // processing step would to parse the packet, so skip at this
+            // stage means that callouts did the parsing already, so server
+            // should skip parsing.
+            if (callout_handle->getSkip()) {
+                LOG_DEBUG(dhcp4_logger, DBG_DHCP4_HOOKS, DHCP4_HOOK_BUFFER_RCVD_SKIP);
+                skip_unpack = true;
+            }
+
+            callout_handle->getArgument("query4", query);
+        }
+
+        // Unpack the packet information unless the buffer4_receive callouts
+        // indicated they did it
+        if (!skip_unpack) {
             try {
                 query->unpack();
-
             } catch (const std::exception& e) {
                 // Failed to parse the packet.
                 LOG_DEBUG(dhcp4_logger, DBG_DHCP4_DETAIL,
                           DHCP4_PACKET_PARSE_FAIL).arg(e.what());
                 continue;
             }
-            LOG_DEBUG(dhcp4_logger, DBG_DHCP4_DETAIL, DHCP4_PACKET_RECEIVED)
-                      .arg(serverReceivedPacketName(query->getType()))
-                      .arg(query->getType())
-                      .arg(query->getIface());
-            LOG_DEBUG(dhcp4_logger, DBG_DHCP4_DETAIL_DATA, DHCP4_QUERY_DATA)
-                      .arg(static_cast<int>(query->getType()))
-                      .arg(query->toText());
-
-            // Let's execute all callouts registered for packet_received
-            if (HooksManager::calloutsPresent(hook_index_pkt4_receive_)) {
-                CalloutHandlePtr callout_handle = getCalloutHandle(query);
-
-                // Delete previously set arguments
-                callout_handle->deleteAllArguments();
+        }
 
-                // Pass incoming packet as argument
-                callout_handle->setArgument("query4", query);
+        // When receiving a packet without message type option, getType() will
+        // throw. Let's set type to -1 as default error indicator.
+        int type = -1;
+        try {
+            type = query->getType();
+        } catch (...) {
+            LOG_DEBUG(dhcp4_logger, DBG_DHCP4_DETAIL, DHCP4_PACKET_DROP_NO_TYPE)
+                .arg(query->getIface());
+            continue;
+        }
 
-                // Call callouts
-                HooksManager::callCallouts(hook_index_pkt4_receive_,
-                                           *callout_handle);
+        LOG_DEBUG(dhcp4_logger, DBG_DHCP4_DETAIL, DHCP4_PACKET_RECEIVED)
+            .arg(serverReceivedPacketName(type))
+            .arg(type)
+            .arg(query->getIface());
+        LOG_DEBUG(dhcp4_logger, DBG_DHCP4_DETAIL_DATA, DHCP4_QUERY_DATA)
+            .arg(type)
+            .arg(query->toText());
+
+        // Let's execute all callouts registered for pkt4_receive
+        if (HooksManager::calloutsPresent(hook_index_pkt4_receive_)) {
+            CalloutHandlePtr callout_handle = getCalloutHandle(query);
+
+            // Delete previously set arguments
+            callout_handle->deleteAllArguments();
+
+            // Pass incoming packet as argument
+            callout_handle->setArgument("query4", query);
+
+            // Call callouts
+            HooksManager::callCallouts(hook_index_pkt4_receive_,
+                                       *callout_handle);
+
+            // Callouts decided to skip the next processing step. The next
+            // processing step would to process the packet, so skip at this
+            // stage means drop.
+            if (callout_handle->getSkip()) {
+                LOG_DEBUG(dhcp4_logger, DBG_DHCP4_HOOKS, DHCP4_HOOK_PACKET_RCVD_SKIP);
+                continue;
+            }
 
-                // Callouts decided to skip the next processing step. The next
-                // processing step would to process the packet, so skip at this
-                // stage means drop.
-                if (callout_handle->getSkip()) {
-                    LOG_DEBUG(dhcp4_logger, DBG_DHCP4_HOOKS, DHCP4_HOOK_PACKET_RCVD_SKIP);
-                    continue;
-                }
+            callout_handle->getArgument("query4", query);
+        }
 
-                callout_handle->getArgument("query4", query);
+        try {
+            switch (query->getType()) {
+            case DHCPDISCOVER:
+                rsp = processDiscover(query);
+                break;
+
+            case DHCPREQUEST:
+                // Note that REQUEST is used for many things in DHCPv4: for
+                // requesting new leases, renewing existing ones and even
+                // for rebinding.
+                rsp = processRequest(query);
+                break;
+
+            case DHCPRELEASE:
+                processRelease(query);
+                break;
+
+            case DHCPDECLINE:
+                processDecline(query);
+                break;
+
+            case DHCPINFORM:
+                processInform(query);
+                break;
+
+            default:
+                // Only action is to output a message if debug is enabled,
+                // and that is covered by the debug statement before the
+                // "switch" statement.
+                ;
             }
-
-            try {
-                switch (query->getType()) {
-                case DHCPDISCOVER:
-                    rsp = processDiscover(query);
-                    break;
-
-                case DHCPREQUEST:
-                    rsp = processRequest(query);
-                    break;
-
-                case DHCPRELEASE:
-                    processRelease(query);
-                    break;
-
-                case DHCPDECLINE:
-                    processDecline(query);
-                    break;
-
-                case DHCPINFORM:
-                    processInform(query);
-                    break;
-
-                default:
-                    // Only action is to output a message if debug is enabled,
-                    // and that is covered by the debug statement before the
-                    // "switch" statement.
-                    ;
-                }
-            } catch (const isc::Exception& e) {
-
-                // Catch-all exception (at least for ones based on the isc
-                // Exception class, which covers more or less all that
-                // are explicitly raised in the BIND 10 code).  Just log
-                // the problem and ignore the packet. (The problem is logged
-                // as a debug message because debug is disabled by default -
-                // it prevents a DDOS attack based on the sending of problem
-                // packets.)
-                if (dhcp4_logger.isDebugEnabled(DBG_DHCP4_BASIC)) {
-                    std::string source = "unknown";
-                    HWAddrPtr hwptr = query->getHWAddr();
-                    if (hwptr) {
-                        source = hwptr->toText();
-                    }
-                    LOG_DEBUG(dhcp4_logger, DBG_DHCP4_BASIC,
-                              DHCP4_PACKET_PROCESS_FAIL)
-                              .arg(source).arg(e.what());
+        } catch (const isc::Exception& e) {
+
+            // Catch-all exception (at least for ones based on the isc
+            // Exception class, which covers more or less all that
+            // are explicitly raised in the BIND 10 code).  Just log
+            // the problem and ignore the packet. (The problem is logged
+            // as a debug message because debug is disabled by default -
+            // it prevents a DDOS attack based on the sending of problem
+            // packets.)
+            if (dhcp4_logger.isDebugEnabled(DBG_DHCP4_BASIC)) {
+                std::string source = "unknown";
+                HWAddrPtr hwptr = query->getHWAddr();
+                if (hwptr) {
+                    source = hwptr->toText();
                 }
+                LOG_DEBUG(dhcp4_logger, DBG_DHCP4_BASIC,
+                          DHCP4_PACKET_PROCESS_FAIL)
+                    .arg(source).arg(e.what());
             }
+        }
 
-            if (rsp) {
+        if (!rsp) {
+            continue;
+        }
 
-                adjustRemoteAddr(query, rsp);
+        adjustRemoteAddr(query, rsp);
 
-                if (!rsp->getHops()) {
-                    rsp->setRemotePort(DHCP4_CLIENT_PORT);
-                } else {
-                    rsp->setRemotePort(DHCP4_SERVER_PORT);
-                }
+        if (!rsp->getHops()) {
+            rsp->setRemotePort(DHCP4_CLIENT_PORT);
+        } else {
+            rsp->setRemotePort(DHCP4_SERVER_PORT);
+        }
 
-                rsp->setLocalAddr(query->getLocalAddr());
-                rsp->setLocalPort(DHCP4_SERVER_PORT);
-                rsp->setIface(query->getIface());
-                rsp->setIndex(query->getIndex());
+        rsp->setLocalAddr(query->getLocalAddr());
+        rsp->setLocalPort(DHCP4_SERVER_PORT);
+        rsp->setIface(query->getIface());
+        rsp->setIndex(query->getIndex());
 
-                // Execute all callouts registered for packet6_send
-                if (HooksManager::calloutsPresent(hook_index_pkt4_send_)) {
-                    CalloutHandlePtr callout_handle = getCalloutHandle(query);
+        // Specifies if server should do the packing
+        bool skip_pack = false;
 
-                    // Delete all previous arguments
-                    callout_handle->deleteAllArguments();
+        // Execute all callouts registered for pkt4_send
+        if (HooksManager::calloutsPresent(hook_index_pkt4_send_)) {
+            CalloutHandlePtr callout_handle = getCalloutHandle(query);
 
-                    // Clear skip flag if it was set in previous callouts
-                    callout_handle->setSkip(false);
+            // Delete all previous arguments
+            callout_handle->deleteAllArguments();
 
-                    // Set our response
-                    callout_handle->setArgument("response4", rsp);
+            // Clear skip flag if it was set in previous callouts
+            callout_handle->setSkip(false);
 
-                    // Call all installed callouts
-                    HooksManager::callCallouts(hook_index_pkt4_send_,
-                                               *callout_handle);
+            // Set our response
+            callout_handle->setArgument("response4", rsp);
 
-                    // Callouts decided to skip the next processing step. The next
-                    // processing step would to send the packet, so skip at this
-                    // stage means "drop response".
-                    if (callout_handle->getSkip()) {
-                        LOG_DEBUG(dhcp4_logger, DBG_DHCP4_HOOKS, DHCP4_HOOK_PACKET_SEND_SKIP);
-                        continue;
-                    }
-                }
+            // Call all installed callouts
+            HooksManager::callCallouts(hook_index_pkt4_send_,
+                                       *callout_handle);
 
-                LOG_DEBUG(dhcp4_logger, DBG_DHCP4_DETAIL_DATA,
-                          DHCP4_RESPONSE_DATA)
-                          .arg(rsp->getType()).arg(rsp->toText());
+            // Callouts decided to skip the next processing step. The next
+            // processing step would to send the packet, so skip at this
+            // stage means "drop response".
+            if (callout_handle->getSkip()) {
+                LOG_DEBUG(dhcp4_logger, DBG_DHCP4_HOOKS, DHCP4_HOOK_PACKET_SEND_SKIP);
+                skip_pack = true;
+            }
+        }
+
+        if (!skip_pack) {
+            try {
+                rsp->pack();
+            } catch (const std::exception& e) {
+                LOG_ERROR(dhcp4_logger, DHCP4_PACKET_SEND_FAIL)
+                    .arg(e.what());
+            }
+        }
+
+        try {
+            // Now all fields and options are constructed into output wire buffer.
+            // Option objects modification does not make sense anymore. Hooks
+            // can only manipulate wire buffer at this stage.
+            // Let's execute all callouts registered for buffer4_send
+            if (HooksManager::getHooksManager()
+                .calloutsPresent(Hooks.hook_index_buffer4_send_)) {
+                CalloutHandlePtr callout_handle = getCalloutHandle(query);
+
+                // Delete previously set arguments
+                callout_handle->deleteAllArguments();
 
-                try {
-                    rsp->pack();
-                    sendPacket(rsp);
-                } catch (const std::exception& e) {
-                    LOG_ERROR(dhcp4_logger, DHCP4_PACKET_SEND_FAIL)
-                              .arg(e.what());
+                // Pass incoming packet as argument
+                callout_handle->setArgument("response4", rsp);
+
+                // Call callouts
+                HooksManager::callCallouts(Hooks.hook_index_buffer4_send_,
+                                           *callout_handle);
+
+                // Callouts decided to skip the next processing step. The next
+                // processing step would to parse the packet, so skip at this
+                // stage means drop.
+                if (callout_handle->getSkip()) {
+                    LOG_DEBUG(dhcp4_logger, DBG_DHCP4_HOOKS,
+                              DHCP4_HOOK_BUFFER_SEND_SKIP);
+                    continue;
                 }
+
+                callout_handle->getArgument("response4", rsp);
             }
+
+            LOG_DEBUG(dhcp4_logger, DBG_DHCP4_DETAIL_DATA,
+                      DHCP4_RESPONSE_DATA)
+                .arg(static_cast<int>(rsp->getType())).arg(rsp->toText());
+
+            sendPacket(rsp);
+        } catch (const std::exception& e) {
+            LOG_ERROR(dhcp4_logger, DHCP4_PACKET_SEND_FAIL)
+                .arg(e.what());
         }
     }
 
@@ -755,6 +856,10 @@ Dhcpv4Srv::processDiscover(Pkt4Ptr& discover) {
 
 Pkt4Ptr
 Dhcpv4Srv::processRequest(Pkt4Ptr& request) {
+
+    /// @todo Uncomment this (see ticket #3116)
+    // sanityCheck(request, MANDATORY);
+
     Pkt4Ptr ack = Pkt4Ptr
         (new Pkt4(DHCPACK, request->getTransid()));
 
@@ -762,6 +867,9 @@ Dhcpv4Srv::processRequest(Pkt4Ptr& request) {
     appendDefaultOptions(ack, DHCPACK);
     appendRequestedOptions(request, ack);
 
+    // Note that we treat REQUEST message uniformly, regardless if this is a
+    // first request (requesting for new address), renewing existing address
+    // or even rebinding.
     assignLease(request, ack);
 
     // There are a few basic options that we always want to
@@ -775,6 +883,9 @@ Dhcpv4Srv::processRequest(Pkt4Ptr& request) {
 void
 Dhcpv4Srv::processRelease(Pkt4Ptr& release) {
 
+    /// @todo Uncomment this (see ticket #3116)
+    // sanityCheck(release, MANDATORY);
+
     // Try to find client-id
     ClientIdPtr client_id;
     OptionPtr opt = release->getOption(DHO_DHCP_CLIENT_IDENTIFIER);
@@ -816,21 +927,53 @@ Dhcpv4Srv::processRelease(Pkt4Ptr& release) {
             return;
         }
 
-        // Ok, hw and client-id match - let's release the lease.
-        if (LeaseMgrFactory::instance().deleteLease(lease->addr_)) {
+        bool skip = false;
 
-            // Release successful - we're done here
-            LOG_DEBUG(dhcp4_logger, DBG_DHCP4_DETAIL, DHCP4_RELEASE)
-                .arg(lease->addr_.toText())
-                .arg(client_id ? client_id->toText() : "(no client-id)")
-                .arg(release->getHWAddr()->toText());
-        } else {
+        // Execute all callouts registered for lease4_release
+        if (HooksManager::getHooksManager()
+            .calloutsPresent(Hooks.hook_index_lease4_release_)) {
+            CalloutHandlePtr callout_handle = getCalloutHandle(release);
+
+            // Delete all previous arguments
+            callout_handle->deleteAllArguments();
+
+            // Pass the original packet
+            callout_handle->setArgument("query4", release);
+
+            // Pass the lease to be updated
+            callout_handle->setArgument("lease4", lease);
+
+            // Call all installed callouts
+            HooksManager::callCallouts(Hooks.hook_index_lease4_release_,
+                                       *callout_handle);
+
+            // Callouts decided to skip the next processing step. The next
+            // processing step would to send the packet, so skip at this
+            // stage means "drop response".
+            if (callout_handle->getSkip()) {
+                skip = true;
+                LOG_DEBUG(dhcp4_logger, DBG_DHCP4_HOOKS,
+                          DHCP4_HOOK_LEASE4_RELEASE_SKIP);
+            }
+        }
 
-            // Release failed -
-            LOG_ERROR(dhcp4_logger, DHCP4_RELEASE_FAIL)
-                .arg(lease->addr_.toText())
+        // Ok, hw and client-id match - let's release the lease.
+        if (!skip) {
+            bool success = LeaseMgrFactory::instance().deleteLease(lease->addr_);
+
+            if (success) {
+                // Release successful - we're done here
+                LOG_DEBUG(dhcp4_logger, DBG_DHCP4_DETAIL, DHCP4_RELEASE)
+                    .arg(lease->addr_.toText())
+                    .arg(client_id ? client_id->toText() : "(no client-id)")
+                    .arg(release->getHWAddr()->toText());
+            } else {
+                // Release failed -
+                LOG_ERROR(dhcp4_logger, DHCP4_RELEASE_FAIL)
+                    .arg(lease->addr_.toText())
                 .arg(client_id ? client_id->toText() : "(no client-id)")
-                .arg(release->getHWAddr()->toText());
+                    .arg(release->getHWAddr()->toText());
+            }
         }
     } catch (const isc::Exception& ex) {
         // Rethrow the exception with a bit more data.
@@ -843,12 +986,13 @@ Dhcpv4Srv::processRelease(Pkt4Ptr& release) {
 
 void
 Dhcpv4Srv::processDecline(Pkt4Ptr& /* decline */) {
-    /// TODO: Implement this.
+    /// @todo Implement this (also see ticket #3116)
 }
 
 Pkt4Ptr
 Dhcpv4Srv::processInform(Pkt4Ptr& inform) {
-    /// TODO: Currently implemented echo mode. Implement this for real
+
+    /// @todo Implement this for real. (also see ticket #3116)
     return (inform);
 }
 
@@ -902,7 +1046,7 @@ Dhcpv4Srv::selectSubnet(const Pkt4Ptr& question) {
 
     /// @todo Implement getSubnet4(interface-name)
 
-    // Let's execute all callouts registered for packet_received
+    // Let's execute all callouts registered for subnet4_select
     if (HooksManager::calloutsPresent(hook_index_subnet4_select_)) {
         CalloutHandlePtr callout_handle = getCalloutHandle(question);
 
@@ -912,16 +1056,19 @@ Dhcpv4Srv::selectSubnet(const Pkt4Ptr& question) {
         // Set new arguments
         callout_handle->setArgument("query4", question);
         callout_handle->setArgument("subnet4", subnet);
-        callout_handle->setArgument("subnet4collection", CfgMgr::instance().getSubnets4());
+        callout_handle->setArgument("subnet4collection",
+                                    CfgMgr::instance().getSubnets4());
 
         // Call user (and server-side) callouts
-        HooksManager::callCallouts(hook_index_subnet4_select_, *callout_handle);
+        HooksManager::callCallouts(hook_index_subnet4_select_,
+                                   *callout_handle);
 
         // Callouts decided to skip this step. This means that no subnet will be
-        // selected. Packet processing will continue, but it will be severly limited
-        // (i.e. only global options will be assigned)
+        // selected. Packet processing will continue, but it will be severly
+        // limited (i.e. only global options will be assigned)
         if (callout_handle->getSkip()) {
-            LOG_DEBUG(dhcp4_logger, DBG_DHCP4_HOOKS, DHCP4_HOOK_SUBNET4_SELECT_SKIP);
+            LOG_DEBUG(dhcp4_logger, DBG_DHCP4_HOOKS,
+                      DHCP4_HOOK_SUBNET4_SELECT_SKIP);
             return (Subnet4Ptr());
         }
 
@@ -973,33 +1120,6 @@ Dhcpv4Srv::sanityCheck(const Pkt4Ptr& pkt, RequirementLevel serverid) {
     }
 }
 
-isc::hooks::CalloutHandlePtr Dhcpv4Srv::getCalloutHandle(const Pkt4Ptr& pkt) {
-    // This method returns a CalloutHandle for a given packet. It is guaranteed
-    // to return the same callout_handle (so user library contexts are
-    // preserved). This method works well if the server processes one packet
-    // at a time. Once the server architecture is extended to cover parallel
-    // packets processing (e.g. delayed-ack, some form of buffering etc.), this
-    // method has to be extended (e.g. store callouts in a map and use pkt as
-    // a key). Additional code would be required to release the callout handle
-    // once the server finished processing.
-
-    CalloutHandlePtr callout_handle;
-    static Pkt4Ptr old_pointer;
-
-    if (!callout_handle ||
-        old_pointer != pkt) {
-        // This is the first packet or a different packet than previously
-        // passed to getCalloutHandle()
-
-        // Remember the pointer to this packet
-        old_pointer = pkt;
-
-        callout_handle = HooksManager::createCalloutHandle();
-    }
-
-    return (callout_handle);
-}
-
 void
 Dhcpv4Srv::openActiveSockets(const uint16_t port,
                              const bool use_bcast) {
diff --git a/src/bin/dhcp4/dhcp4_srv.h b/src/bin/dhcp4/dhcp4_srv.h
index 432327c..f53f2a7 100644
--- a/src/bin/dhcp4/dhcp4_srv.h
+++ b/src/bin/dhcp4/dhcp4_srv.h
@@ -366,13 +366,6 @@ private:
     uint16_t port_;  ///< UDP port number on which server listens.
     bool use_bcast_; ///< Should broadcast be enabled on sockets (if true).
 
-    /// @brief returns callout handle for specified packet
-    ///
-    /// @param pkt packet for which the handle should be returned
-    ///
-    /// @return a callout handle to be used in hooks related to said packet
-    isc::hooks::CalloutHandlePtr getCalloutHandle(const Pkt4Ptr& pkt);
-
     /// Indexes for registered hook points
     int hook_index_pkt4_receive_;
     int hook_index_subnet4_select_;
diff --git a/src/bin/dhcp4/tests/dhcp4_srv_unittest.cc b/src/bin/dhcp4/tests/dhcp4_srv_unittest.cc
index 0251991..1b68747 100644
--- a/src/bin/dhcp4/tests/dhcp4_srv_unittest.cc
+++ b/src/bin/dhcp4/tests/dhcp4_srv_unittest.cc
@@ -1631,21 +1631,33 @@ TEST_F(Dhcpv4SrvTest, Hooks) {
     NakedDhcpv4Srv srv(0);
 
     // check if appropriate hooks are registered
-    int hook_index_pkt4_received = -1;
-    int hook_index_select_subnet = -1;
-    int hook_index_pkt4_send     = -1;
+    int hook_index_buffer4_receive = -1;
+    int hook_index_pkt4_receive    = -1;
+    int hook_index_select_subnet   = -1;
+    int hook_index_lease4_release  = -1;
+    int hook_index_pkt4_send       = -1;
+    int hook_index_buffer4_send    = -1;
 
     // check if appropriate indexes are set
-    EXPECT_NO_THROW(hook_index_pkt4_received = ServerHooks::getServerHooks()
+    EXPECT_NO_THROW(hook_index_buffer4_receive = ServerHooks::getServerHooks()
+                    .getIndex("buffer4_receive"));
+    EXPECT_NO_THROW(hook_index_pkt4_receive = ServerHooks::getServerHooks()
                     .getIndex("pkt4_receive"));
     EXPECT_NO_THROW(hook_index_select_subnet = ServerHooks::getServerHooks()
                     .getIndex("subnet4_select"));
-    EXPECT_NO_THROW(hook_index_pkt4_send     = ServerHooks::getServerHooks()
+    EXPECT_NO_THROW(hook_index_lease4_release = ServerHooks::getServerHooks()
+                    .getIndex("lease4_release"));
+    EXPECT_NO_THROW(hook_index_pkt4_send = ServerHooks::getServerHooks()
                     .getIndex("pkt4_send"));
+    EXPECT_NO_THROW(hook_index_buffer4_send = ServerHooks::getServerHooks()
+                    .getIndex("buffer4_send"));
 
-    EXPECT_TRUE(hook_index_pkt4_received > 0);
+    EXPECT_TRUE(hook_index_buffer4_receive > 0);
+    EXPECT_TRUE(hook_index_pkt4_receive > 0);
     EXPECT_TRUE(hook_index_select_subnet > 0);
+    EXPECT_TRUE(hook_index_lease4_release > 0);
     EXPECT_TRUE(hook_index_pkt4_send > 0);
+    EXPECT_TRUE(hook_index_buffer4_send > 0);
 }
 
     // a dummy MAC address
@@ -1689,9 +1701,13 @@ public:
     /// @brief destructor (deletes Dhcpv4Srv)
     virtual ~HooksDhcpv4SrvTest() {
 
+        HooksManager::preCalloutsLibraryHandle().deregisterAllCallouts("buffer4_receive");
+        HooksManager::preCalloutsLibraryHandle().deregisterAllCallouts("buffer4_send");
         HooksManager::preCalloutsLibraryHandle().deregisterAllCallouts("pkt4_receive");
         HooksManager::preCalloutsLibraryHandle().deregisterAllCallouts("pkt4_send");
         HooksManager::preCalloutsLibraryHandle().deregisterAllCallouts("subnet4_select");
+        HooksManager::preCalloutsLibraryHandle().deregisterAllCallouts("lease4_renew");
+        HooksManager::preCalloutsLibraryHandle().deregisterAllCallouts("lease4_release");
 
         delete srv_;
     }
@@ -1766,6 +1782,67 @@ public:
         return (Pkt4Ptr(new Pkt4(&buf[0], buf.size())));
     }
 
+    /// Test callback that stores received callout name and pkt4 value
+    /// @param callout_handle handle passed by the hooks framework
+    /// @return always 0
+    static int
+    buffer4_receive_callout(CalloutHandle& callout_handle) {
+        callback_name_ = string("buffer4_receive");
+
+        callout_handle.getArgument("query4", callback_pkt4_);
+
+        callback_argument_names_ = callout_handle.getArgumentNames();
+        return (0);
+    }
+
+    /// Test callback that changes hwaddr value
+    /// @param callout_handle handle passed by the hooks framework
+    /// @return always 0
+    static int
+    buffer4_receive_change_hwaddr(CalloutHandle& callout_handle) {
+
+        Pkt4Ptr pkt;
+        callout_handle.getArgument("query4", pkt);
+
+        // If there is at least one option with data
+        if (pkt->data_.size() >= Pkt4::DHCPV4_PKT_HDR_LEN) {
+            // Offset of the first byte of the CHWADDR field. Let's the first
+            // byte to some new value that we could later check
+            pkt->data_[28] = 0xff;
+        }
+
+        // Carry on as usual
+        return buffer4_receive_callout(callout_handle);
+    }
+
+    /// Test callback that deletes MAC address
+    /// @param callout_handle handle passed by the hooks framework
+    /// @return always 0
+    static int
+    buffer4_receive_delete_hwaddr(CalloutHandle& callout_handle) {
+
+        Pkt4Ptr pkt;
+        callout_handle.getArgument("query4", pkt);
+
+        pkt->data_[2] = 0; // offset 2 is hlen, let's set it to zero
+        memset(&pkt->data_[28], 0, Pkt4::MAX_CHADDR_LEN); // Clear CHADDR content
+
+        // carry on as usual
+        return buffer4_receive_callout(callout_handle);
+    }
+
+    /// Test callback that sets skip flag
+    /// @param callout_handle handle passed by the hooks framework
+    /// @return always 0
+    static int
+    buffer4_receive_skip(CalloutHandle& callout_handle) {
+
+        callout_handle.setSkip(true);
+
+        // Carry on as usual
+        return buffer4_receive_callout(callout_handle);
+    }
+
     /// test callback that stores received callout name and pkt4 value
     /// @param callout_handle handle passed by the hooks framework
     /// @return always 0
@@ -1894,6 +1971,46 @@ public:
         return pkt4_send_callout(callout_handle);
     }
 
+    /// Test callback that stores received callout name and pkt4 value
+    /// @param callout_handle handle passed by the hooks framework
+    /// @return always 0
+    static int
+    buffer4_send_callout(CalloutHandle& callout_handle) {
+        callback_name_ = string("buffer4_send");
+
+        callout_handle.getArgument("response4", callback_pkt4_);
+
+        callback_argument_names_ = callout_handle.getArgumentNames();
+        return (0);
+    }
+
+    /// Test callback changes the output buffer to a hardcoded value
+    /// @param callout_handle handle passed by the hooks framework
+    /// @return always 0
+    static int
+    buffer4_send_change_callout(CalloutHandle& callout_handle) {
+
+        Pkt4Ptr pkt;
+        callout_handle.getArgument("response4", pkt);
+
+        // modify buffer to set a diffferent payload
+        pkt->getBuffer().clear();
+        pkt->getBuffer().writeData(dummyFile, sizeof(dummyFile));
+
+        return (0);
+    }
+
+    /// Test callback that stores received callout name and pkt4 value
+    /// @param callout_handle handle passed by the hooks framework
+    /// @return always 0
+    static int
+    skip_callout(CalloutHandle& callout_handle) {
+
+        callout_handle.setSkip(true);
+
+        return (0);
+    }
+
     /// Test callback that stores received callout name and subnet4 values
     /// @param callout_handle handle passed by the hooks framework
     /// @return always 0
@@ -1932,10 +2049,44 @@ public:
         return (0);
     }
 
+    /// Test callback that stores received callout name passed parameters
+    /// @param callout_handle handle passed by the hooks framework
+    /// @return always 0
+    static int
+    lease4_release_callout(CalloutHandle& callout_handle) {
+        callback_name_ = string("lease4_release");
+
+        callout_handle.getArgument("query4", callback_pkt4_);
+        callout_handle.getArgument("lease4", callback_lease4_);
+
+        callback_argument_names_ = callout_handle.getArgumentNames();
+        return (0);
+    }
+
+    /// Test callback that stores received callout name and subnet4 values
+    /// @param callout_handle handle passed by the hooks framework
+    /// @return always 0
+    static int
+    lease4_renew_callout(CalloutHandle& callout_handle) {
+        callback_name_ = string("lease4_renew");
+
+        callout_handle.getArgument("subnet4", callback_subnet4_);
+        callout_handle.getArgument("lease4", callback_lease4_);
+        callout_handle.getArgument("hwaddr", callback_hwaddr_);
+        callout_handle.getArgument("clientid", callback_clientid_);
+
+        callback_argument_names_ = callout_handle.getArgumentNames();
+        return (0);
+    }
+
+
     /// resets buffers used to store data received by callouts
     void resetCalloutBuffers() {
         callback_name_ = string("");
         callback_pkt4_.reset();
+        callback_lease4_.reset();
+        callback_hwaddr_.reset();
+        callback_clientid_.reset();
         callback_subnet4_.reset();
         callback_subnet4collection_ = NULL;
         callback_argument_names_.clear();
@@ -1952,6 +2103,15 @@ public:
     /// Pkt4 structure returned in the callout
     static Pkt4Ptr callback_pkt4_;
 
+    /// Lease4 structure returned in the callout
+    static Lease4Ptr callback_lease4_;
+
+    /// Hardware address returned in the callout
+    static HWAddrPtr callback_hwaddr_;
+
+    /// Client-id returned in the callout
+    static ClientIdPtr callback_clientid_;
+
     /// Pointer to a subnet received by callout
     static Subnet4Ptr callback_subnet4_;
 
@@ -1967,23 +2127,124 @@ public:
 string HooksDhcpv4SrvTest::callback_name_;
 Pkt4Ptr HooksDhcpv4SrvTest::callback_pkt4_;
 Subnet4Ptr HooksDhcpv4SrvTest::callback_subnet4_;
+HWAddrPtr HooksDhcpv4SrvTest::callback_hwaddr_;
+ClientIdPtr HooksDhcpv4SrvTest::callback_clientid_;
+Lease4Ptr HooksDhcpv4SrvTest::callback_lease4_;
 const Subnet4Collection* HooksDhcpv4SrvTest::callback_subnet4collection_;
 vector<string> HooksDhcpv4SrvTest::callback_argument_names_;
 
+// Checks if callouts installed on pkt4_receive are indeed called and the
+// all necessary parameters are passed.
+//
+// Note that the test name does not follow test naming convention,
+// but the proper hook name is "buffer4_receive".
+TEST_F(HooksDhcpv4SrvTest, Buffer4ReceiveSimple) {
+
+    // Install pkt6_receive_callout
+    EXPECT_NO_THROW(HooksManager::preCalloutsLibraryHandle().registerCallout(
+                        "buffer4_receive", buffer4_receive_callout));
+
+    // Let's create a simple DISCOVER
+    Pkt4Ptr dis = generateSimpleDiscover();
+
+    // Simulate that we have received that traffic
+    srv_->fakeReceive(dis);
+
+    // Server will now process to run its normal loop, but instead of calling
+    // IfaceMgr::receive4(), it will read all packets from the list set by
+    // fakeReceive()
+    // In particular, it should call registered buffer4_receive callback.
+    srv_->run();
+
+    // Check that the callback called is indeed the one we installed
+    EXPECT_EQ("buffer4_receive", callback_name_);
+
+    // Check that pkt4 argument passing was successful and returned proper value
+    EXPECT_TRUE(callback_pkt4_.get() == dis.get());
 
-// Checks if callouts installed on pkt4_received are indeed called and the
+    // Check that all expected parameters are there
+    vector<string> expected_argument_names;
+    expected_argument_names.push_back(string("query4"));
+
+    EXPECT_TRUE(expected_argument_names == callback_argument_names_);
+}
+
+// Checks if callouts installed on buffer4_receive is able to change
+// the values and the parameters are indeed used by the server.
+TEST_F(HooksDhcpv4SrvTest, buffer4RreceiveValueChange) {
+
+    // Install callback that modifies MAC addr of incoming packet
+    EXPECT_NO_THROW(HooksManager::preCalloutsLibraryHandle().registerCallout(
+                        "buffer4_receive", buffer4_receive_change_hwaddr));
+
+    // Let's create a simple DISCOVER
+    Pkt4Ptr discover = generateSimpleDiscover();
+
+    // Simulate that we have received that traffic
+    srv_->fakeReceive(discover);
+
+    // Server will now process to run its normal loop, but instead of calling
+    // IfaceMgr::receive6(), it will read all packets from the list set by
+    // fakeReceive()
+    // In particular, it should call registered buffer4_receive callback.
+    srv_->run();
+
+    // Check that the server did send a reposonce
+    ASSERT_EQ(1, srv_->fake_sent_.size());
+
+    // Make sure that we received a response
+    Pkt4Ptr offer = srv_->fake_sent_.front();
+    ASSERT_TRUE(offer);
+
+    // Get client-id...
+    HWAddrPtr hwaddr = offer->getHWAddr();
+
+    ASSERT_TRUE(hwaddr); // basic sanity check. HWAddr is always present
+
+    // ... and check if it is the modified value
+    ASSERT_FALSE(hwaddr->hwaddr_.empty()); // there must be a MAC address
+    EXPECT_EQ(0xff, hwaddr->hwaddr_[0]); // check that its first byte was modified
+}
+
+// Checks if callouts installed on buffer4_receive is able to set skip flag that
+// will cause the server to not parse the packet. Even though the packet is valid,
+// the server should eventually drop it, because there won't be mandatory options
+// (or rather option objects) in it.
+TEST_F(HooksDhcpv4SrvTest, buffer4ReceiveSkip) {
+
+    // Install pkt6_receive_callout
+    EXPECT_NO_THROW(HooksManager::preCalloutsLibraryHandle().registerCallout(
+                        "buffer4_receive", buffer4_receive_skip));
+
+    // Let's create a simple DISCOVER
+    Pkt4Ptr discover = generateSimpleDiscover();
+
+    // Simulate that we have received that traffic
+    srv_->fakeReceive(discover);
+
+    // Server will now process to run its normal loop, but instead of calling
+    // IfaceMgr::receive6(), it will read all packets from the list set by
+    // fakeReceive()
+    // In particular, it should call registered pkt6_receive callback.
+    srv_->run();
+
+    // Check that the server dropped the packet and did not produce any response
+    ASSERT_EQ(0, srv_->fake_sent_.size());
+}
+
+// Checks if callouts installed on pkt4_receive are indeed called and the
 // all necessary parameters are passed.
 //
 // Note that the test name does not follow test naming convention,
 // but the proper hook name is "pkt4_receive".
-TEST_F(HooksDhcpv4SrvTest, simple_pkt4_receive) {
+TEST_F(HooksDhcpv4SrvTest, pkt4ReceiveSimple) {
 
     // Install pkt4_receive_callout
     EXPECT_NO_THROW(HooksManager::preCalloutsLibraryHandle().registerCallout(
                         "pkt4_receive", pkt4_receive_callout));
 
     // Let's create a simple DISCOVER
-    Pkt4Ptr sol = Pkt4Ptr(generateSimpleDiscover());
+    Pkt4Ptr sol = generateSimpleDiscover();
 
     // Simulate that we have received that traffic
     srv_->fakeReceive(sol);
@@ -2016,7 +2277,7 @@ TEST_F(HooksDhcpv4SrvTest, valueChange_pkt4_receive) {
                         "pkt4_receive", pkt4_receive_change_clientid));
 
     // Let's create a simple DISCOVER
-    Pkt4Ptr sol = Pkt4Ptr(generateSimpleDiscover());
+    Pkt4Ptr sol = generateSimpleDiscover();
 
     // Simulate that we have received that traffic
     srv_->fakeReceive(sol);
@@ -2045,14 +2306,14 @@ TEST_F(HooksDhcpv4SrvTest, valueChange_pkt4_receive) {
 // Checks if callouts installed on pkt4_received is able to delete
 // existing options and that change impacts server processing (mandatory
 // client-id option is deleted, so the packet is expected to be dropped)
-TEST_F(HooksDhcpv4SrvTest, deleteClientId_pkt4_receive) {
+TEST_F(HooksDhcpv4SrvTest, pkt4ReceiveDeleteClientId) {
 
     // Install pkt4_receive_callout
     EXPECT_NO_THROW(HooksManager::preCalloutsLibraryHandle().registerCallout(
                         "pkt4_receive", pkt4_receive_delete_clientid));
 
     // Let's create a simple DISCOVER
-    Pkt4Ptr sol = Pkt4Ptr(generateSimpleDiscover());
+    Pkt4Ptr sol = generateSimpleDiscover();
 
     // Simulate that we have received that traffic
     srv_->fakeReceive(sol);
@@ -2069,14 +2330,14 @@ TEST_F(HooksDhcpv4SrvTest, deleteClientId_pkt4_receive) {
 
 // Checks if callouts installed on pkt4_received is able to set skip flag that
 // will cause the server to not process the packet (drop), even though it is valid.
-TEST_F(HooksDhcpv4SrvTest, skip_pkt4_receive) {
+TEST_F(HooksDhcpv4SrvTest, pkt4ReceiveSkip) {
 
     // Install pkt4_receive_callout
     EXPECT_NO_THROW(HooksManager::preCalloutsLibraryHandle().registerCallout(
                         "pkt4_receive", pkt4_receive_skip));
 
     // Let's create a simple DISCOVER
-    Pkt4Ptr sol = Pkt4Ptr(generateSimpleDiscover());
+    Pkt4Ptr sol = generateSimpleDiscover();
 
     // Simulate that we have received that traffic
     srv_->fakeReceive(sol);
@@ -2094,14 +2355,14 @@ TEST_F(HooksDhcpv4SrvTest, skip_pkt4_receive) {
 
 // Checks if callouts installed on pkt4_send are indeed called and the
 // all necessary parameters are passed.
-TEST_F(HooksDhcpv4SrvTest, simple_pkt4_send) {
+TEST_F(HooksDhcpv4SrvTest, pkt4SendSimple) {
 
     // Install pkt4_receive_callout
     EXPECT_NO_THROW(HooksManager::preCalloutsLibraryHandle().registerCallout(
                         "pkt4_send", pkt4_send_callout));
 
     // Let's create a simple DISCOVER
-    Pkt4Ptr sol = Pkt4Ptr(generateSimpleDiscover());
+    Pkt4Ptr sol = generateSimpleDiscover();
 
     // Simulate that we have received that traffic
     srv_->fakeReceive(sol);
@@ -2130,14 +2391,14 @@ TEST_F(HooksDhcpv4SrvTest, simple_pkt4_send) {
 
 // Checks if callouts installed on pkt4_send is able to change
 // the values and the packet sent contains those changes
-TEST_F(HooksDhcpv4SrvTest, valueChange_pkt4_send) {
+TEST_F(HooksDhcpv4SrvTest, pkt4SendValueChange) {
 
     // Install pkt4_receive_callout
     EXPECT_NO_THROW(HooksManager::preCalloutsLibraryHandle().registerCallout(
                         "pkt4_send", pkt4_send_change_serverid));
 
     // Let's create a simple DISCOVER
-    Pkt4Ptr sol = Pkt4Ptr(generateSimpleDiscover());
+    Pkt4Ptr sol = generateSimpleDiscover();
 
     // Simulate that we have received that traffic
     srv_->fakeReceive(sol);
@@ -2167,14 +2428,14 @@ TEST_F(HooksDhcpv4SrvTest, valueChange_pkt4_send) {
 // existing options and that server applies those changes. In particular,
 // we are trying to send a packet without server-id. The packet should
 // be sent
-TEST_F(HooksDhcpv4SrvTest, deleteServerId_pkt4_send) {
+TEST_F(HooksDhcpv4SrvTest, pkt4SendDeleteServerId) {
 
     // Install pkt4_receive_callout
     EXPECT_NO_THROW(HooksManager::preCalloutsLibraryHandle().registerCallout(
                         "pkt4_send", pkt4_send_delete_serverid));
 
     // Let's create a simple DISCOVER
-    Pkt4Ptr sol = Pkt4Ptr(generateSimpleDiscover());
+    Pkt4Ptr sol = generateSimpleDiscover();
 
     // Simulate that we have received that traffic
     srv_->fakeReceive(sol);
@@ -2205,7 +2466,7 @@ TEST_F(HooksDhcpv4SrvTest, skip_pkt4_send) {
                         "pkt4_send", pkt4_send_skip));
 
     // Let's create a simple REQUEST
-    Pkt4Ptr sol = Pkt4Ptr(generateSimpleDiscover());
+    Pkt4Ptr sol = generateSimpleDiscover();
 
     // Simulate that we have received that traffic
     srv_->fakeReceive(sol);
@@ -2213,16 +2474,111 @@ TEST_F(HooksDhcpv4SrvTest, skip_pkt4_send) {
     // Server will now process to run its normal loop, but instead of calling
     // IfaceMgr::receive4(), it will read all packets from the list set by
     // fakeReceive()
+    // In particular, it should call registered pkt4_send callback.
+    srv_->run();
+
+    // Check that the server sent the message
+    ASSERT_EQ(1, srv_->fake_sent_.size());
+
+    // Get the first packet and check that it has zero length (i.e. the server
+    // did not do packing on its own)
+    Pkt4Ptr sent = srv_->fake_sent_.front();
+    EXPECT_EQ(0, sent->getBuffer().getLength());
+}
+
+// Checks if callouts installed on buffer4_send are indeed called and the
+// all necessary parameters are passed.
+TEST_F(HooksDhcpv4SrvTest, buffer4SendSimple) {
+
+    // Install pkt4_receive_callout
+    EXPECT_NO_THROW(HooksManager::preCalloutsLibraryHandle().registerCallout(
+                        "buffer4_send", buffer4_send_callout));
+
+    // Let's create a simple DISCOVER
+    Pkt4Ptr discover = generateSimpleDiscover();
+
+    // Simulate that we have received that traffic
+    srv_->fakeReceive(discover);
+
+    // Server will now process to run its normal loop, but instead of calling
+    // IfaceMgr::receive4(), it will read all packets from the list set by
+    // fakeReceive()
     // In particular, it should call registered pkt4_receive callback.
     srv_->run();
 
-    // check that the server dropped the packet and did not produce any response
+    // Check that the callback called is indeed the one we installed
+    EXPECT_EQ("buffer4_send", callback_name_);
+
+    // Check that there is one packet sent
+    ASSERT_EQ(1, srv_->fake_sent_.size());
+    Pkt4Ptr adv = srv_->fake_sent_.front();
+
+    // Check that pkt4 argument passing was successful and returned proper value
+    EXPECT_TRUE(callback_pkt4_.get() == adv.get());
+
+    // Check that all expected parameters are there
+    vector<string> expected_argument_names;
+    expected_argument_names.push_back(string("response4"));
+    EXPECT_TRUE(expected_argument_names == callback_argument_names_);
+}
+
+// Checks if callouts installed on buffer4_send are indeed called and that
+// the output buffer can be changed.
+TEST_F(HooksDhcpv4SrvTest, buffer4Send) {
+
+    // Install pkt4_receive_callout
+    EXPECT_NO_THROW(HooksManager::preCalloutsLibraryHandle().registerCallout(
+                        "buffer4_send", buffer4_send_change_callout));
+
+    // Let's create a simple DISCOVER
+    Pkt4Ptr discover = generateSimpleDiscover();
+
+    // Simulate that we have received that traffic
+    srv_->fakeReceive(discover);
+
+    // Server will now process to run its normal loop, but instead of calling
+    // IfaceMgr::receive4(), it will read all packets from the list set by
+    // fakeReceive()
+    // In particular, it should call registered pkt4_receive callback.
+    srv_->run();
+
+    // Check that there is one packet sent
+    ASSERT_EQ(1, srv_->fake_sent_.size());
+    Pkt4Ptr adv = srv_->fake_sent_.front();
+
+    // The callout is supposed to fill the output buffer with dummyFile content
+    ASSERT_EQ(sizeof(dummyFile), adv->getBuffer().getLength());
+    EXPECT_EQ(0, memcmp(adv->getBuffer().getData(), dummyFile, sizeof(dummyFile)));
+}
+
+// Checks if callouts installed on buffer4_send can set skip flag and that flag
+// causes the packet to not be sent
+TEST_F(HooksDhcpv4SrvTest, buffer4SendSkip) {
+
+    // Install pkt4_receive_callout
+    EXPECT_NO_THROW(HooksManager::preCalloutsLibraryHandle().registerCallout(
+                        "buffer4_send", skip_callout));
+
+    // Let's create a simple DISCOVER
+    Pkt4Ptr discover = generateSimpleDiscover();
+
+    // Simulate that we have received that traffic
+    srv_->fakeReceive(discover);
+
+    // Server will now process to run its normal loop, but instead of calling
+    // IfaceMgr::receive4(), it will read all packets from the list set by
+    // fakeReceive()
+    // In particular, it should call registered pkt4_receive callback.
+    srv_->run();
+
+    // Check that there is no packet sent.
     ASSERT_EQ(0, srv_->fake_sent_.size());
 }
 
+
 // This test checks if subnet4_select callout is triggered and reports
 // valid parameters
-TEST_F(HooksDhcpv4SrvTest, subnet4_select) {
+TEST_F(HooksDhcpv4SrvTest, subnet4SelectSimple) {
 
     // Install pkt4_receive_callout
     EXPECT_NO_THROW(HooksManager::preCalloutsLibraryHandle().registerCallout(
@@ -2288,9 +2644,9 @@ TEST_F(HooksDhcpv4SrvTest, subnet4_select) {
 
 // This test checks if callout installed on subnet4_select hook point can pick
 // a different subnet.
-TEST_F(HooksDhcpv4SrvTest, subnet_select_change) {
+TEST_F(HooksDhcpv4SrvTest, subnet4SelectChange) {
 
-    // Install pkt4_receive_callout
+    // Install a callout
     EXPECT_NO_THROW(HooksManager::preCalloutsLibraryHandle().registerCallout(
                         "subnet4_select", subnet4_select_different_subnet_callout));
 
@@ -2345,6 +2701,303 @@ TEST_F(HooksDhcpv4SrvTest, subnet_select_change) {
     EXPECT_TRUE((*subnets)[1]->inPool(addr));
 }
 
+// This test verifies that incoming (positive) REQUEST/Renewing can be handled
+// properly and that callout installed on lease4_renew is triggered with
+// expected parameters.
+TEST_F(HooksDhcpv4SrvTest, lease4RenewSimple) {
 
+    const IOAddress addr("192.0.2.106");
+    const uint32_t temp_t1 = 50;
+    const uint32_t temp_t2 = 75;
+    const uint32_t temp_valid = 100;
+    const time_t temp_timestamp = time(NULL) - 10;
+
+    // Install a callout
+    EXPECT_NO_THROW(HooksManager::preCalloutsLibraryHandle().registerCallout(
+                        "lease4_renew", lease4_renew_callout));
+
+    // Generate client-id also sets client_id_ member
+    OptionPtr clientid = generateClientId();
+
+    // Check that the address we are about to use is indeed in pool
+    ASSERT_TRUE(subnet_->inPool(addr));
+
+    // let's create a lease and put it in the LeaseMgr
+    uint8_t hwaddr2[] = { 0, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe};
+    Lease4Ptr used(new Lease4(IOAddress("192.0.2.106"), hwaddr2, sizeof(hwaddr2),
+                              &client_id_->getDuid()[0], client_id_->getDuid().size(),
+                              temp_valid, temp_t1, temp_t2, temp_timestamp,
+                              subnet_->getID()));
+    ASSERT_TRUE(LeaseMgrFactory::instance().addLease(used));
+
+    // Check that the lease is really in the database
+    Lease4Ptr l = LeaseMgrFactory::instance().getLease4(addr);
+    ASSERT_TRUE(l);
+
+    // Let's create a RENEW
+    Pkt4Ptr req = Pkt4Ptr(new Pkt4(DHCPREQUEST, 1234));
+    req->setRemoteAddr(IOAddress(addr));
+    req->setYiaddr(addr);
+    req->setCiaddr(addr); // client's address
+
+    req->addOption(clientid);
+    req->addOption(srv_->getServerID());
+
+    // Pass it to the server and hope for a REPLY
+    Pkt4Ptr ack = srv_->processRequest(req);
+
+    // Check if we get response at all
+    checkResponse(ack, DHCPACK, 1234);
+
+    // Check that the lease is really in the database
+    l = checkLease(ack, clientid, req->getHWAddr(), addr);
+    ASSERT_TRUE(l);
+
+    // Check that T1, T2, preferred, valid and cltt were really updated
+    EXPECT_EQ(l->t1_, subnet_->getT1());
+    EXPECT_EQ(l->t2_, subnet_->getT2());
+    EXPECT_EQ(l->valid_lft_, subnet_->getValid());
+
+    // Check that the callback called is indeed the one we installed
+    EXPECT_EQ("lease4_renew", callback_name_);
+
+    // Check that hwaddr parameter is passed properly
+    ASSERT_TRUE(callback_hwaddr_);
+    EXPECT_TRUE(*callback_hwaddr_ == *req->getHWAddr());
+
+    // Check that the subnet is passed properly
+    ASSERT_TRUE(callback_subnet4_);
+    EXPECT_EQ(callback_subnet4_->toText(), subnet_->toText());
+
+    ASSERT_TRUE(callback_clientid_);
+    ASSERT_TRUE(client_id_);
+    EXPECT_TRUE(*client_id_ == *callback_clientid_);
+
+    // Check if all expected parameters were really received
+    vector<string> expected_argument_names;
+    expected_argument_names.push_back("subnet4");
+    expected_argument_names.push_back("clientid");
+    expected_argument_names.push_back("hwaddr");
+    expected_argument_names.push_back("lease4");
+    sort(callback_argument_names_.begin(), callback_argument_names_.end());
+    sort(expected_argument_names.begin(), expected_argument_names.end());
+    EXPECT_TRUE(callback_argument_names_ == expected_argument_names);
+
+    EXPECT_TRUE(LeaseMgrFactory::instance().deleteLease(addr));
+}
+
+// This test verifies that a callout installed on lease4_renew can trigger
+// the server to not renew a lease.
+TEST_F(HooksDhcpv4SrvTest, lease4RenewSkip) {
+
+    const IOAddress addr("192.0.2.106");
+    const uint32_t temp_t1 = 50;
+    const uint32_t temp_t2 = 75;
+    const uint32_t temp_valid = 100;
+    const time_t temp_timestamp = time(NULL) - 10;
+
+    // Install a callout
+    EXPECT_NO_THROW(HooksManager::preCalloutsLibraryHandle().registerCallout(
+                        "lease4_renew", skip_callout));
+
+    // Generate client-id also sets client_id_ member
+    OptionPtr clientid = generateClientId();
+
+    // Check that the address we are about to use is indeed in pool
+    ASSERT_TRUE(subnet_->inPool(addr));
+
+    // let's create a lease and put it in the LeaseMgr
+    uint8_t hwaddr2[] = { 0, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe};
+    Lease4Ptr used(new Lease4(IOAddress("192.0.2.106"), hwaddr2, sizeof(hwaddr2),
+                              &client_id_->getDuid()[0], client_id_->getDuid().size(),
+                              temp_valid, temp_t1, temp_t2, temp_timestamp,
+                              subnet_->getID()));
+    ASSERT_TRUE(LeaseMgrFactory::instance().addLease(used));
+
+    // Check that the lease is really in the database
+    Lease4Ptr l = LeaseMgrFactory::instance().getLease4(addr);
+    ASSERT_TRUE(l);
+
+    // Check that T1, T2, preferred, valid and cltt really set.
+    // Constructed lease looks as if it was assigned 10 seconds ago
+    // EXPECT_EQ(l->t1_, temp_t1);
+    // EXPECT_EQ(l->t2_, temp_t2);
+    EXPECT_EQ(l->valid_lft_, temp_valid);
+    EXPECT_EQ(l->cltt_, temp_timestamp);
+
+    // Let's create a RENEW
+    Pkt4Ptr req = Pkt4Ptr(new Pkt4(DHCPREQUEST, 1234));
+    req->setRemoteAddr(IOAddress(addr));
+    req->setYiaddr(addr);
+    req->setCiaddr(addr); // client's address
+
+    req->addOption(clientid);
+    req->addOption(srv_->getServerID());
+
+    // Pass it to the server and hope for a REPLY
+    Pkt4Ptr ack = srv_->processRequest(req);
+    ASSERT_TRUE(ack);
+
+    // Check that the lease is really in the database
+    l = checkLease(ack, clientid, req->getHWAddr(), addr);
+    ASSERT_TRUE(l);
+
+    // Check that T1, T2, valid and cltt were NOT updated
+    EXPECT_EQ(temp_t1, l->t1_);
+    EXPECT_EQ(temp_t2, l->t2_);
+    EXPECT_EQ(temp_valid, l->valid_lft_);
+    EXPECT_EQ(temp_timestamp, l->cltt_);
+
+    EXPECT_TRUE(LeaseMgrFactory::instance().deleteLease(addr));
+}
+
+// This test verifies that valid RELEASE triggers lease4_release callouts
+TEST_F(HooksDhcpv4SrvTest, lease4ReleaseSimple) {
+
+    const IOAddress addr("192.0.2.106");
+    const uint32_t temp_t1 = 50;
+    const uint32_t temp_t2 = 75;
+    const uint32_t temp_valid = 100;
+    const time_t temp_timestamp = time(NULL) - 10;
+
+    // Install a callout
+    EXPECT_NO_THROW(HooksManager::preCalloutsLibraryHandle().registerCallout(
+                        "lease4_release", lease4_release_callout));
+
+    // Generate client-id also duid_
+    OptionPtr clientid = generateClientId();
+
+    // Check that the address we are about to use is indeed in pool
+    ASSERT_TRUE(subnet_->inPool(addr));
+
+    // Let's create a lease and put it in the LeaseMgr
+    uint8_t mac_addr[] = { 0, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe};
+    HWAddrPtr hw(new HWAddr(mac_addr, sizeof(mac_addr), HTYPE_ETHER));
+    Lease4Ptr used(new Lease4(addr, mac_addr, sizeof(mac_addr),
+                              &client_id_->getDuid()[0], client_id_->getDuid().size(),
+                              temp_valid, temp_t1, temp_t2, temp_timestamp,
+                              subnet_->getID()));
+    ASSERT_TRUE(LeaseMgrFactory::instance().addLease(used));
+
+    // Check that the lease is really in the database
+    Lease4Ptr l = LeaseMgrFactory::instance().getLease4(addr);
+    ASSERT_TRUE(l);
+
+    // Let's create a RELEASE
+    // Generate client-id also duid_
+    Pkt4Ptr rel = Pkt4Ptr(new Pkt4(DHCPRELEASE, 1234));
+    rel->setRemoteAddr(addr);
+    rel->setYiaddr(addr);
+    rel->addOption(clientid);
+    rel->addOption(srv_->getServerID());
+    rel->setHWAddr(hw);
+
+    // Pass it to the server and hope for a REPLY
+    // Note: this is no response to RELEASE in DHCPv4
+    EXPECT_NO_THROW(srv_->processRelease(rel));
+
+    // The lease should be gone from LeaseMgr
+    l = LeaseMgrFactory::instance().getLease4(addr);
+    EXPECT_FALSE(l);
+
+    // Try to get the lease by hardware address
+    // @todo: Uncomment this once trac2592 is implemented
+    // Lease4Collection leases = LeaseMgrFactory::instance().getLease4(hw->hwaddr_);
+    // EXPECT_EQ(leases.size(), 0);
+
+    // Try to get it by hw/subnet_id combination
+    l = LeaseMgrFactory::instance().getLease4(hw->hwaddr_, subnet_->getID());
+    EXPECT_FALSE(l);
+
+    // Try by client-id
+    // @todo: Uncomment this once trac2592 is implemented
+    //Lease4Collection leases = LeaseMgrFactory::instance().getLease4(*client_id_);
+    //EXPECT_EQ(leases.size(), 0);
+
+    // Try by client-id/subnet-id
+    l = LeaseMgrFactory::instance().getLease4(*client_id_, subnet_->getID());
+    EXPECT_FALSE(l);
+
+    // Ok, the lease is *really* not there.
+
+    // Check that the callback called is indeed the one we installed
+    EXPECT_EQ("lease4_release", callback_name_);
+
+    // Check that pkt4 argument passing was successful and returned proper value
+    EXPECT_TRUE(callback_pkt4_.get() == rel.get());
+
+    // Check if all expected parameters were really received
+    vector<string> expected_argument_names;
+    expected_argument_names.push_back("query4");
+    expected_argument_names.push_back("lease4");
+    sort(callback_argument_names_.begin(), callback_argument_names_.end());
+    sort(expected_argument_names.begin(), expected_argument_names.end());
+    EXPECT_TRUE(callback_argument_names_ == expected_argument_names);
+}
+
+// This test verifies that skip flag returned by a callout installed on the
+// lease4_release hook point will keep the lease
+TEST_F(HooksDhcpv4SrvTest, lease4ReleaseSkip) {
+
+    const IOAddress addr("192.0.2.106");
+    const uint32_t temp_t1 = 50;
+    const uint32_t temp_t2 = 75;
+    const uint32_t temp_valid = 100;
+    const time_t temp_timestamp = time(NULL) - 10;
+
+    // Install a callout
+    EXPECT_NO_THROW(HooksManager::preCalloutsLibraryHandle().registerCallout(
+                        "lease4_release", skip_callout));
+
+    // Generate client-id also duid_
+    OptionPtr clientid = generateClientId();
+
+    // Check that the address we are about to use is indeed in pool
+    ASSERT_TRUE(subnet_->inPool(addr));
+
+    // Let's create a lease and put it in the LeaseMgr
+    uint8_t mac_addr[] = { 0, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe};
+    HWAddrPtr hw(new HWAddr(mac_addr, sizeof(mac_addr), HTYPE_ETHER));
+    Lease4Ptr used(new Lease4(addr, mac_addr, sizeof(mac_addr),
+                              &client_id_->getDuid()[0], client_id_->getDuid().size(),
+                              temp_valid, temp_t1, temp_t2, temp_timestamp,
+                              subnet_->getID()));
+    ASSERT_TRUE(LeaseMgrFactory::instance().addLease(used));
+
+    // Check that the lease is really in the database
+    Lease4Ptr l = LeaseMgrFactory::instance().getLease4(addr);
+    ASSERT_TRUE(l);
+
+    // Let's create a RELEASE
+    // Generate client-id also duid_
+    Pkt4Ptr rel = Pkt4Ptr(new Pkt4(DHCPRELEASE, 1234));
+    rel->setRemoteAddr(addr);
+    rel->setYiaddr(addr);
+    rel->addOption(clientid);
+    rel->addOption(srv_->getServerID());
+    rel->setHWAddr(hw);
+
+    // Pass it to the server and hope for a REPLY
+    // Note: this is no response to RELEASE in DHCPv4
+    EXPECT_NO_THROW(srv_->processRelease(rel));
+
+    // The lease should be still there
+    l = LeaseMgrFactory::instance().getLease4(addr);
+    EXPECT_TRUE(l);
+
+    // Try by client-id/subnet-id
+    l = LeaseMgrFactory::instance().getLease4(*client_id_, subnet_->getID());
+    EXPECT_TRUE(l);
+
+    // Try to get the lease by hardware address
+    // @todo: Uncomment this once trac2592 is implemented
+    // Lease4Collection leases = LeaseMgrFactory::instance().getLease4(hw->hwaddr_);
+    // EXPECT_EQ(leases.size(), 1);
+
+    // Try by client-id
+    // @todo: Uncomment this once trac2592 is implemented
+    //Lease4Collection leases = LeaseMgrFactory::instance().getLease4(*client_id_);
+    //EXPECT_EQ(leases.size(), 1);
+}
 
 } // end of anonymous namespace
diff --git a/src/bin/dhcp6/dhcp6_messages.mes b/src/bin/dhcp6/dhcp6_messages.mes
index b168694..f851a7b 100644
--- a/src/bin/dhcp6/dhcp6_messages.mes
+++ b/src/bin/dhcp6/dhcp6_messages.mes
@@ -118,7 +118,7 @@ hook point set the skip flag. For this particular hook point, the
 setting of the flag by a callout instructs the server to drop the packet.
 
 % DHCP6_HOOK_BUFFER_SEND_SKIP prepared DHCPv6 response was dropped because a callout set the skip flag.
-This debug message is printed when a callout installed on buffer6_receive
+This debug message is printed when a callout installed on buffer6_send
 hook point set the skip flag. For this particular hook point, the
 setting of the flag by a callout instructs the server to drop the packet.
 Server completed all the processing (e.g. may have assigned, updated
diff --git a/src/bin/dhcp6/dhcp6_srv.cc b/src/bin/dhcp6/dhcp6_srv.cc
index 5941a6c..41e2cee 100644
--- a/src/bin/dhcp6/dhcp6_srv.cc
+++ b/src/bin/dhcp6/dhcp6_srv.cc
@@ -30,17 +30,18 @@
 #include <dhcp/pkt6.h>
 #include <dhcp6/dhcp6_log.h>
 #include <dhcp6/dhcp6_srv.h>
+#include <dhcpsrv/callout_handle_store.h>
 #include <dhcpsrv/cfgmgr.h>
 #include <dhcpsrv/lease_mgr.h>
 #include <dhcpsrv/lease_mgr_factory.h>
 #include <dhcpsrv/subnet.h>
 #include <dhcpsrv/utils.h>
 #include <exceptions/exceptions.h>
+#include <hooks/callout_handle.h>
+#include <hooks/hooks_manager.h>
+#include <util/encode/hex.h>
 #include <util/io_utilities.h>
 #include <util/range_utilities.h>
-#include <util/encode/hex.h>
-#include <hooks/hooks_manager.h>
-#include <hooks/callout_handle.h>
 
 #include <boost/foreach.hpp>
 #include <boost/tokenizer.hpp>
@@ -1810,33 +1811,6 @@ Dhcpv6Srv::processInfRequest(const Pkt6Ptr& infRequest) {
     return reply;
 }
 
-isc::hooks::CalloutHandlePtr Dhcpv6Srv::getCalloutHandle(const Pkt6Ptr& pkt) {
-    // This method returns a CalloutHandle for a given packet. It is guaranteed
-    // to return the same callout_handle (so user library contexts are
-    // preserved). This method works well if the server processes one packet
-    // at a time. Once the server architecture is extended to cover parallel
-    // packets processing (e.g. delayed-ack, some form of buffering etc.), this
-    // method has to be extended (e.g. store callouts in a map and use pkt as
-    // a key). Additional code would be required to release the callout handle
-    // once the server finished processing.
-
-    CalloutHandlePtr callout_handle;
-    static Pkt6Ptr old_pointer;
-
-    if (!callout_handle ||
-        old_pointer != pkt) {
-        // This is the first packet or a different packet than previously
-        // passed to getCalloutHandle()
-
-        // Remember the pointer to this packet
-        old_pointer = pkt;
-
-        callout_handle = HooksManager::getHooksManager().createCalloutHandle();
-    }
-
-    return (callout_handle);
-}
-
 void
 Dhcpv6Srv::openActiveSockets(const uint16_t port) {
     IfaceMgr::instance().closeSockets();
diff --git a/src/bin/dhcp6/dhcp6_srv.h b/src/bin/dhcp6/dhcp6_srv.h
index dc6e240..f9e5dc5 100644
--- a/src/bin/dhcp6/dhcp6_srv.h
+++ b/src/bin/dhcp6/dhcp6_srv.h
@@ -473,12 +473,10 @@ private:
     /// initiate server shutdown procedure.
     volatile bool shutdown_;
 
-    /// @brief returns callout handle for specified packet
-    ///
-    /// @param pkt packet for which the handle should be returned
-    ///
-    /// @return a callout handle to be used in hooks related to said packet
-    isc::hooks::CalloutHandlePtr getCalloutHandle(const Pkt6Ptr& pkt);
+    /// Indexes for registered hook points
+    int hook_index_pkt6_receive_;
+    int hook_index_subnet6_select_;
+    int hook_index_pkt6_send_;
 
     /// UDP port number on which server listens.
     uint16_t port_;
diff --git a/src/bin/dhcp6/tests/hooks_unittest.cc b/src/bin/dhcp6/tests/hooks_unittest.cc
index 717de08..b12374c 100644
--- a/src/bin/dhcp6/tests/hooks_unittest.cc
+++ b/src/bin/dhcp6/tests/hooks_unittest.cc
@@ -285,10 +285,6 @@ public:
     /// @return always 0
     static int
     buffer6_receive_skip(CalloutHandle& callout_handle) {
-
-        Pkt6Ptr pkt;
-        callout_handle.getArgument("query6", pkt);
-
         callout_handle.setSkip(true);
 
         // Carry on as usual
@@ -561,7 +557,7 @@ TEST_F(HooksDhcpv6SrvTest, simple_buffer6_receive) {
     // Server will now process to run its normal loop, but instead of calling
     // IfaceMgr::receive6(), it will read all packets from the list set by
     // fakeReceive()
-    // In particular, it should call registered pkt6_receive callback.
+    // In particular, it should call registered buffer6_receive callback.
     srv_->run();
 
     // Check that the callback called is indeed the one we installed
@@ -577,7 +573,7 @@ TEST_F(HooksDhcpv6SrvTest, simple_buffer6_receive) {
     EXPECT_TRUE(expected_argument_names == callback_argument_names_);
 }
 
-// Checks if callouts installed on pkt6_received is able to change
+// Checks if callouts installed on buffer6_receive is able to change
 // the values and the parameters are indeed used by the server.
 TEST_F(HooksDhcpv6SrvTest, valueChange_buffer6_receive) {
 
diff --git a/src/lib/dhcp/pkt4.cc b/src/lib/dhcp/pkt4.cc
index 07ff7c7..67c0ae5 100644
--- a/src/lib/dhcp/pkt4.cc
+++ b/src/lib/dhcp/pkt4.cc
@@ -33,7 +33,8 @@ namespace dhcp {
 const IOAddress DEFAULT_ADDRESS("0.0.0.0");
 
 Pkt4::Pkt4(uint8_t msg_type, uint32_t transid)
-     :local_addr_(DEFAULT_ADDRESS),
+     :buffer_out_(DHCPV4_PKT_HDR_LEN),
+      local_addr_(DEFAULT_ADDRESS),
       remote_addr_(DEFAULT_ADDRESS),
       iface_(""),
       ifindex_(0),
@@ -48,8 +49,7 @@ Pkt4::Pkt4(uint8_t msg_type, uint32_t transid)
       ciaddr_(DEFAULT_ADDRESS),
       yiaddr_(DEFAULT_ADDRESS),
       siaddr_(DEFAULT_ADDRESS),
-      giaddr_(DEFAULT_ADDRESS),
-      bufferOut_(DHCPV4_PKT_HDR_LEN)
+      giaddr_(DEFAULT_ADDRESS)
 {
     memset(sname_, 0, MAX_SNAME_LEN);
     memset(file_, 0, MAX_FILE_LEN);
@@ -58,7 +58,8 @@ Pkt4::Pkt4(uint8_t msg_type, uint32_t transid)
 }
 
 Pkt4::Pkt4(const uint8_t* data, size_t len)
-     :local_addr_(DEFAULT_ADDRESS),
+     :buffer_out_(0), // not used, this is RX packet
+      local_addr_(DEFAULT_ADDRESS),
       remote_addr_(DEFAULT_ADDRESS),
       iface_(""),
       ifindex_(0),
@@ -72,8 +73,7 @@ Pkt4::Pkt4(const uint8_t* data, size_t len)
       ciaddr_(DEFAULT_ADDRESS),
       yiaddr_(DEFAULT_ADDRESS),
       siaddr_(DEFAULT_ADDRESS),
-      giaddr_(DEFAULT_ADDRESS),
-      bufferOut_(0) // not used, this is RX packet
+      giaddr_(DEFAULT_ADDRESS)
 {
     if (len < DHCPV4_PKT_HDR_LEN) {
         isc_throw(OutOfRange, "Truncated DHCPv4 packet (len=" << len
@@ -111,25 +111,25 @@ Pkt4::pack() {
     try {
         size_t hw_len = hwaddr_->hwaddr_.size();
 
-        bufferOut_.writeUint8(op_);
-        bufferOut_.writeUint8(hwaddr_->htype_);
-        bufferOut_.writeUint8(hw_len < MAX_CHADDR_LEN ? 
+        buffer_out_.writeUint8(op_);
+        buffer_out_.writeUint8(hwaddr_->htype_);
+        buffer_out_.writeUint8(hw_len < MAX_CHADDR_LEN ?
                               hw_len : MAX_CHADDR_LEN);
-        bufferOut_.writeUint8(hops_);
-        bufferOut_.writeUint32(transid_);
-        bufferOut_.writeUint16(secs_);
-        bufferOut_.writeUint16(flags_);
-        bufferOut_.writeUint32(ciaddr_);
-        bufferOut_.writeUint32(yiaddr_);
-        bufferOut_.writeUint32(siaddr_);
-        bufferOut_.writeUint32(giaddr_);
+        buffer_out_.writeUint8(hops_);
+        buffer_out_.writeUint32(transid_);
+        buffer_out_.writeUint16(secs_);
+        buffer_out_.writeUint16(flags_);
+        buffer_out_.writeUint32(ciaddr_);
+        buffer_out_.writeUint32(yiaddr_);
+        buffer_out_.writeUint32(siaddr_);
+        buffer_out_.writeUint32(giaddr_);
 
 
         if (hw_len <= MAX_CHADDR_LEN) {
             // write up to 16 bytes of the hardware address (CHADDR field is 16
             // bytes long in DHCPv4 message).
-            bufferOut_.writeData(&hwaddr_->hwaddr_[0], 
-                                 (hw_len < MAX_CHADDR_LEN ? 
+            buffer_out_.writeData(&hwaddr_->hwaddr_[0],
+                                 (hw_len < MAX_CHADDR_LEN ?
                                   hw_len : MAX_CHADDR_LEN) );
             hw_len = MAX_CHADDR_LEN - hw_len;
         } else {
@@ -138,20 +138,20 @@ Pkt4::pack() {
 
         // write (len) bytes of padding
         vector<uint8_t> zeros(hw_len, 0);
-        bufferOut_.writeData(&zeros[0], hw_len);
-        // bufferOut_.writeData(chaddr_, MAX_CHADDR_LEN);
+        buffer_out_.writeData(&zeros[0], hw_len);
+        // buffer_out_.writeData(chaddr_, MAX_CHADDR_LEN);
 
-        bufferOut_.writeData(sname_, MAX_SNAME_LEN);
-        bufferOut_.writeData(file_, MAX_FILE_LEN);
+        buffer_out_.writeData(sname_, MAX_SNAME_LEN);
+        buffer_out_.writeData(file_, MAX_FILE_LEN);
 
         // write DHCP magic cookie
-        bufferOut_.writeUint32(DHCP_OPTIONS_COOKIE);
+        buffer_out_.writeUint32(DHCP_OPTIONS_COOKIE);
 
-        LibDHCP::packOptions(bufferOut_, options_);
+        LibDHCP::packOptions(buffer_out_, options_);
 
         // add END option that indicates end of options
         // (End option is very simple, just a 255 octet)
-         bufferOut_.writeUint8(DHO_END);
+         buffer_out_.writeUint8(DHO_END);
      } catch(const Exception& e) {
         // An exception is thrown and message will be written to Logger
          isc_throw(InvalidOperation, e.what());
@@ -259,7 +259,7 @@ void Pkt4::setType(uint8_t dhcp_type) {
 }
 
 void Pkt4::repack() {
-    bufferOut_.writeData(&data_[0], data_.size());
+    buffer_out_.writeData(&data_[0], data_.size());
 }
 
 std::string
diff --git a/src/lib/dhcp/pkt4.h b/src/lib/dhcp/pkt4.h
index 3b945f1..a64734b 100644
--- a/src/lib/dhcp/pkt4.h
+++ b/src/lib/dhcp/pkt4.h
@@ -70,7 +70,7 @@ public:
     ///
     /// Prepares on-wire format of message and all its options.
     /// Options must be stored in options_ field.
-    /// Output buffer will be stored in bufferOut_.
+    /// Output buffer will be stored in buffer_out_.
     ///
     /// @throw InvalidOperation if packing fails
     void
@@ -103,7 +103,7 @@ public:
     ///
     /// This is mostly a diagnostic function. It is being used for sending
     /// received packet. Received packet is stored in bufferIn_, but
-    /// transmitted data is stored in bufferOut_. If we want to send packet
+    /// transmitted data is stored in buffer_out_. If we want to send packet
     /// that we just received, a copy between those two buffers is necessary.
     void repack();
 
@@ -307,11 +307,12 @@ public:
     /// is only valid till Pkt4 object is valid.
     ///
     /// RX packet or TX packet before pack() will return buffer with
-    /// zero length
+    /// zero length. This buffer is returned as non-const, so hooks
+    /// framework (and user's callouts) can modify them if needed
     ///
     /// @return reference to output buffer
-    const isc::util::OutputBuffer&
-    getBuffer() const { return (bufferOut_); };
+    isc::util::OutputBuffer&
+    getBuffer() { return (buffer_out_); };
 
     /// @brief Add an option.
     ///
@@ -489,6 +490,38 @@ public:
     /// @throw isc::Unexpected if timestamp update failed
     void updateTimestamp();
 
+    /// Output buffer (used during message transmission)
+    ///
+    /// @warning This public member is accessed by derived
+    /// classes directly. One of such derived classes is
+    /// @ref perfdhcp::PerfPkt4. The impact on derived clasess'
+    /// behavior must be taken into consideration before making
+    /// changes to this member such as access scope restriction or
+    /// data format change etc. This field is also public, because
+    /// it may be modified by callouts (which are written in C++ now,
+    /// but we expect to also have them in Python, so any accesibility
+    /// methods would overly complicate things here and degrade
+    /// performance).
+    isc::util::OutputBuffer buffer_out_;
+
+    /// @brief That's the data of input buffer used in RX packet.
+    ///
+    /// @note Note that InputBuffer does not store the data itself, but just
+    /// expects that data will be valid for the whole life of InputBuffer.
+    /// Therefore we need to keep the data around.
+    ///
+    /// @warning This public member is accessed by derived
+    /// classes directly. One of such derived classes is
+    /// @ref perfdhcp::PerfPkt4. The impact on derived clasess'
+    /// behavior must be taken into consideration before making
+    /// changes to this member such as access scope restriction or
+    /// data format change etc. This field is also public, because
+    /// it may be modified by callouts (which are written in C++ now,
+    /// but we expect to also have them in Python, so any accesibility
+    /// methods would overly complicate things here and degrade
+    /// performance).
+    std::vector<uint8_t> data_;
+
 private:
 
     /// @brief Generic method that validates and sets HW address.
@@ -591,29 +624,6 @@ protected:
 
     // end of real DHCPv4 fields
 
-    /// output buffer (used during message transmission)
-    ///
-    /// @warning This protected member is accessed by derived
-    /// classes directly. One of such derived classes is
-    /// @ref perfdhcp::PerfPkt4. The impact on derived clasess'
-    /// behavior must be taken into consideration before making
-    /// changes to this member such as access scope restriction or
-    /// data format change etc.
-    isc::util::OutputBuffer bufferOut_;
-
-    /// that's the data of input buffer used in RX packet. Note that
-    /// InputBuffer does not store the data itself, but just expects that
-    /// data will be valid for the whole life of InputBuffer. Therefore we
-    /// need to keep the data around.
-    ///
-    /// @warning This protected member is accessed by derived
-    /// classes directly. One of such derived classes is
-    /// @ref perfdhcp::PerfPkt4. The impact on derived clasess'
-    /// behavior must be taken into consideration before making
-    /// changes to this member such as access scope restriction or
-    /// data format change etc.
-    std::vector<uint8_t> data_;
-
     /// collection of options present in this message
     ///
     /// @warning This protected member is accessed by derived
diff --git a/src/lib/dhcpsrv/Makefile.am b/src/lib/dhcpsrv/Makefile.am
index 8641f4f..5390a01 100644
--- a/src/lib/dhcpsrv/Makefile.am
+++ b/src/lib/dhcpsrv/Makefile.am
@@ -35,6 +35,7 @@ lib_LTLIBRARIES = libb10-dhcpsrv.la
 libb10_dhcpsrv_la_SOURCES  =
 libb10_dhcpsrv_la_SOURCES += addr_utilities.cc addr_utilities.h
 libb10_dhcpsrv_la_SOURCES += alloc_engine.cc alloc_engine.h
+libb10_dhcpsrv_la_SOURCES += callout_handle_store.h
 libb10_dhcpsrv_la_SOURCES += dbaccess_parser.cc dbaccess_parser.h
 libb10_dhcpsrv_la_SOURCES += dhcpsrv_log.cc dhcpsrv_log.h
 libb10_dhcpsrv_la_SOURCES += cfgmgr.cc cfgmgr.h
diff --git a/src/lib/dhcpsrv/alloc_engine.cc b/src/lib/dhcpsrv/alloc_engine.cc
index d840e95..2bef8f8 100644
--- a/src/lib/dhcpsrv/alloc_engine.cc
+++ b/src/lib/dhcpsrv/alloc_engine.cc
@@ -31,11 +31,13 @@ namespace {
 /// Structure that holds registered hook indexes
 struct AllocEngineHooks {
     int hook_index_lease4_select_; ///< index for "lease4_receive" hook point
+    int hook_index_lease4_renew_;  ///< index for "lease4_renew" hook point
     int hook_index_lease6_select_; ///< index for "lease6_receive" hook point
 
     /// Constructor that registers hook points for AllocationEngine
     AllocEngineHooks() {
         hook_index_lease4_select_ = HooksManager::registerHook("lease4_select");
+        hook_index_lease4_renew_  = HooksManager::registerHook("lease4_renew");
         hook_index_lease6_select_ = HooksManager::registerHook("lease6_select");
     }
 };
@@ -91,7 +93,7 @@ AllocEngine::IterativeAllocator::pickAddress(const SubnetPtr& subnet,
 
     // Let's get the last allocated address. It is usually set correctly,
     // but there are times when it won't be (like after removing a pool or
-    // perhaps restaring the server).
+    // perhaps restarting the server).
     IOAddress last = subnet->getLastAllocated();
 
     const PoolCollection& pools = subnet->getPools();
@@ -138,7 +140,7 @@ AllocEngine::IterativeAllocator::pickAddress(const SubnetPtr& subnet,
         return (next);
     }
 
-    // there is a next pool, let's try first adddress from it
+    // there is a next pool, let's try first address from it
     next = (*it)->getFirstAddress();
     subnet->setLastAllocated(next);
     return (next);
@@ -364,7 +366,7 @@ AllocEngine::allocateAddress4(const SubnetPtr& subnet,
             // its reboot.
             existing = renewLease4(subnet, clientid, hwaddr,
                                    fwd_dns_update, rev_dns_update, hostname,
-                                   existing, fake_allocation);
+                                   existing, callout_handle, fake_allocation);
             if (existing) {
                 return (existing);
             }
@@ -382,7 +384,8 @@ AllocEngine::allocateAddress4(const SubnetPtr& subnet,
                 // its reboot.
                 existing = renewLease4(subnet, clientid, hwaddr,
                                        fwd_dns_update, rev_dns_update,
-                                       hostname, existing, fake_allocation);
+                                       hostname, existing, callout_handle,
+                                       fake_allocation);
                 // @todo: produce a warning. We haven't found him using MAC address, but
                 // we found him using client-id
                 if (existing) {
@@ -495,8 +498,19 @@ Lease4Ptr AllocEngine::renewLease4(const SubnetPtr& subnet,
                                    const bool rev_dns_update,
                                    const std::string& hostname,
                                    const Lease4Ptr& lease,
+                                   const isc::hooks::CalloutHandlePtr& callout_handle,
                                    bool fake_allocation /* = false */) {
 
+    if (!lease) {
+        isc_throw(InvalidOperation, "Lease4 must be specified");
+    }
+
+    // Let's keep the old data. This is essential if we are using memfile
+    // (the lease returned points directly to the lease4 object in the database)
+    // We'll need it if we want to skip update (i.e. roll back renewal)
+    /// @todo: remove this once #3083 is implemented
+    Lease4 old_values = *lease;
+
     lease->subnet_id_ = subnet->getID();
     lease->hwaddr_ = hwaddr->hwaddr_;
     lease->client_id_ = clientid;
@@ -508,10 +522,48 @@ Lease4Ptr AllocEngine::renewLease4(const SubnetPtr& subnet,
     lease->fqdn_rev_ = rev_dns_update;
     lease->hostname_ = hostname;
 
-    if (!fake_allocation) {
+    bool skip = false;
+    // Execute all callouts registered for packet6_send
+    if (HooksManager::getHooksManager().calloutsPresent(Hooks.hook_index_lease4_renew_)) {
+
+        // Delete all previous arguments
+        callout_handle->deleteAllArguments();
+
+        // Subnet from which we do the allocation. Convert the general subnet
+        // pointer to a pointer to a Subnet4.  Note that because we are using
+        // boost smart pointers here, we need to do the cast using the boost
+        // version of dynamic_pointer_cast.
+        Subnet4Ptr subnet4 = boost::dynamic_pointer_cast<Subnet4>(subnet);
+
+        // Pass the parameters
+        callout_handle->setArgument("subnet4", subnet4);
+        callout_handle->setArgument("clientid", clientid);
+        callout_handle->setArgument("hwaddr", hwaddr);
+
+        // Pass the lease to be updated
+        callout_handle->setArgument("lease4", lease);
+
+        // Call all installed callouts
+        HooksManager::callCallouts(Hooks.hook_index_lease4_renew_, *callout_handle);
+
+        // Callouts decided to skip the next processing step. The next
+        // processing step would to actually renew the lease, so skip at this
+        // stage means "keep the old lease as it is".
+        if (callout_handle->getSkip()) {
+            skip = true;
+            LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_HOOKS, DHCPSRV_HOOK_LEASE4_RENEW_SKIP);
+        }
+    }
+
+    if (!fake_allocation && !skip) {
         // for REQUEST we do update the lease
         LeaseMgrFactory::instance().updateLease4(lease);
     }
+    if (skip) {
+        // Rollback changes (really useful only for memfile)
+        /// @todo: remove this once #3083 is implemented
+        *lease = old_values;
+    }
 
     return (lease);
 }
diff --git a/src/lib/dhcpsrv/alloc_engine.h b/src/lib/dhcpsrv/alloc_engine.h
index 7cddadf..ad18b83 100644
--- a/src/lib/dhcpsrv/alloc_engine.h
+++ b/src/lib/dhcpsrv/alloc_engine.h
@@ -215,17 +215,17 @@ protected:
     /// @param hwaddr Client's hardware address info
     /// @param hint A hint that the client provided
     /// @param fwd_dns_update Indicates whether forward DNS update will be
-    /// performed for the client (true) or not (false).
+    ///        performed for the client (true) or not (false).
     /// @param rev_dns_update Indicates whether reverse DNS update will be
-    /// performed for the client (true) or not (false).
+    ///        performed for the client (true) or not (false).
     /// @param hostname A string carrying hostname to be used for DNS updates.
     /// @param fake_allocation Is this real i.e. REQUEST (false) or just picking
     ///        an address for DISCOVER that is not really allocated (true)
     /// @param callout_handle A callout handle (used in hooks). A lease callouts
     ///        will be executed if this parameter is passed.
     /// @param [out] old_lease Holds the pointer to a previous instance of a
-    /// lease. The NULL pointer indicates that lease didn't exist prior
-    /// to calling this function (e.g. new lease has been allocated).
+    ///        lease. The NULL pointer indicates that lease didn't exist prior
+    ///        to calling this function (e.g. new lease has been allocated).
     ///
     /// @return Allocated IPv4 lease (or NULL if allocation failed)
     Lease4Ptr
@@ -253,11 +253,13 @@ protected:
     /// @param clientid Client identifier
     /// @param hwaddr Client's hardware address
     /// @param fwd_dns_update Indicates whether forward DNS update will be
-    /// performed for the client (true) or not (false).
+    ///        performed for the client (true) or not (false).
     /// @param rev_dns_update Indicates whether reverse DNS update will be
-    /// performed for the client (true) or not (false).
+    ///        performed for the client (true) or not (false).
     /// @param hostname A string carrying hostname to be used for DNS updates.
     /// @param lease A lease to be renewed
+    /// @param callout_handle a callout handle (used in hooks). A lease callouts
+    ///        will be executed if this parameter is passed.
     /// @param fake_allocation Is this real i.e. REQUEST (false) or just picking
     ///        an address for DISCOVER that is not really allocated (true)
     Lease4Ptr
@@ -268,6 +270,7 @@ protected:
                 const bool rev_dns_update,
                 const std::string& hostname,
                 const Lease4Ptr& lease,
+                const isc::hooks::CalloutHandlePtr& callout_handle,
                 bool fake_allocation /* = false */);
 
     /// @brief Allocates an IPv6 lease
@@ -281,9 +284,11 @@ protected:
     /// @param iaid iaid field from the IA_NA container that client sent
     /// @param hint a hint that the client provided
     /// @param fwd_dns_update A boolean value which indicates that server takes
-    /// responisibility for the forward DNS Update for this lease (if true).
+    ///        responsibility for the forward DNS Update for this lease
+    ///        (if true).
     /// @param rev_dns_update A boolean value which indicates that server takes
-    /// responibility for the reverse DNS Update for this lease (if true).
+    ///        responsibility for the reverse DNS Update for this lease
+    ///        (if true).
     /// @param hostname A fully qualified domain-name of the client.
     /// @param fake_allocation is this real i.e. REQUEST (false) or just picking
     ///        an address for SOLICIT that is not really allocated (true)
@@ -317,9 +322,9 @@ private:
     /// @param hwaddr Client's hardware address
     /// @param addr An address that was selected and is confirmed to be available
     /// @param fwd_dns_update Indicates whether forward DNS update will be
-    /// performed for the client (true) or not (false).
+    ///        performed for the client (true) or not (false).
     /// @param rev_dns_update Indicates whether reverse DNS update will be
-    /// performed for the client (true) or not (false).
+    ///        performed for the client (true) or not (false).
     /// @param hostname A string carrying hostname to be used for DNS updates.
     /// @param callout_handle a callout handle (used in hooks). A lease callouts
     ///        will be executed if this parameter is passed (and there are callouts
@@ -349,9 +354,11 @@ private:
     /// @param addr an address that was selected and is confirmed to be
     /// available
     /// @param fwd_dns_update A boolean value which indicates that server takes
-    /// responisibility for the forward DNS Update for this lease (if true).
+    ///        responsibility for the forward DNS Update for this lease
+    ///        (if true).
     /// @param rev_dns_update A boolean value which indicates that server takes
-    /// responibility for the reverse DNS Update for this lease (if true).
+    ///        responsibility for the reverse DNS Update for this lease
+    ///        (if true).
     /// @param hostname A fully qualified domain-name of the client.
     /// @param callout_handle a callout handle (used in hooks). A lease callouts
     ///        will be executed if this parameter is passed (and there are callouts
@@ -359,7 +366,7 @@ private:
     /// @param fake_allocation is this real i.e. REQUEST (false) or just picking
     ///        an address for SOLICIT that is not really allocated (true)
     /// @return allocated lease (or NULL in the unlikely case of the lease just
-    ///        becomed unavailable)
+    ///         became unavailable)
     Lease6Ptr createLease6(const Subnet6Ptr& subnet, const DuidPtr& duid,
                            uint32_t iaid, const isc::asiolink::IOAddress& addr,
                            const bool fwd_dns_update, const bool rev_dns_update,
@@ -378,9 +385,9 @@ private:
     /// @param clientid Client identifier
     /// @param hwaddr Client's hardware address
     /// @param fwd_dns_update Indicates whether forward DNS update will be
-    /// performed for the client (true) or not (false).
+    ///        performed for the client (true) or not (false).
     /// @param rev_dns_update Indicates whether reverse DNS update will be
-    /// performed for the client (true) or not (false).
+    ///        performed for the client (true) or not (false).
     /// @param hostname A string carrying hostname to be used for DNS updates.
     /// @param callout_handle A callout handle (used in hooks). A lease callouts
     ///        will be executed if this parameter is passed.
@@ -409,9 +416,11 @@ private:
     /// @param duid client's DUID
     /// @param iaid IAID from the IA_NA container the client sent to us
     /// @param fwd_dns_update A boolean value which indicates that server takes
-    /// responisibility for the forward DNS Update for this lease (if true).
+    ///        responsibility for the forward DNS Update for this lease
+    ///        (if true).
     /// @param rev_dns_update A boolean value which indicates that server takes
-    /// responibility for the reverse DNS Update for this lease (if true).
+    ///        responsibility for the reverse DNS Update for this lease
+    ///        (if true).
     /// @param hostname A fully qualified domain-name of the client.
     /// @param callout_handle a callout handle (used in hooks). A lease callouts
     ///        will be executed if this parameter is passed.
diff --git a/src/lib/dhcpsrv/callout_handle_store.h b/src/lib/dhcpsrv/callout_handle_store.h
new file mode 100644
index 0000000..697ba3a
--- /dev/null
+++ b/src/lib/dhcpsrv/callout_handle_store.h
@@ -0,0 +1,91 @@
+// 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.
+
+#ifndef CALLOUT_HANDLE_STORE_H
+#define CALLOUT_HANDLE_STORE_H
+
+#include <hooks/hooks_manager.h>
+#include <hooks/callout_handle.h>
+
+namespace isc {
+namespace dhcp {
+
+/// @brief CalloutHandle Store
+///
+/// When using the Hooks Framework, there is a need to associate an
+/// isc::hooks::CalloutHandle object with each request passing through the
+/// server.  For the DHCP servers, the association is provided by this function.
+///
+/// The DHCP servers process a single request at a time. At points where the
+/// CalloutHandle is required, the pointer to the current request (packet) is
+/// passed to this function.  If the request is a new one, a pointer to
+/// the request is stored, a new CalloutHandle is allocated (and stored) and
+/// a pointer to the latter object returned to the caller.  If the request
+/// matches the one stored, the pointer to the stored CalloutHandle is
+/// returned.
+///
+/// A special case is a null pointer being passed.  This has the effect of
+/// clearing the stored pointers to the packet being processed and
+/// CalloutHandle.  As the stored pointers are shared pointers, clearing them
+/// removes one reference that keeps the pointed-to objects in existence.
+///
+/// @note If the behaviour of the server changes so that multiple packets can
+///       be active at the same time, this simplistic approach will no longer
+///       be adequate and a more complicated structure (such as a map) will
+///       be needed.
+///
+/// @param pktptr Pointer to the packet being processed.  This is typically a
+///        Pkt4Ptr or Pkt6Ptr object.  An empty pointer is passed to clear
+///        the stored pointers.
+///
+/// @return Shared pointer to a CalloutHandle.  This is the previously-stored
+///         CalloutHandle if pktptr points to a packet that has been seen
+///         before or a new CalloutHandle if it points to a new one.  An empty
+///         pointer is returned if pktptr is itself an empty pointer.
+
+template <typename T>
+isc::hooks::CalloutHandlePtr getCalloutHandle(const T& pktptr) {
+
+    // Stored data is declared static, so is initialized when first accessed
+    static T stored_pointer;                // Pointer to last packet seen
+    static isc::hooks::CalloutHandlePtr stored_handle;
+                                            // Pointer to stored handle
+
+    if (pktptr) {
+
+        // Pointer given, have we seen it before? (If we have, we don't need to
+        // do anything as we will automatically return the stored handle.)
+        if (pktptr != stored_pointer) {
+
+            // Not seen before, so store the pointer passed to us and get a new
+            // CalloutHandle.  (The latter operation frees and probably deletes
+            // (depending on other pointers) the stored one.)
+            stored_pointer = pktptr;
+            stored_handle = isc::hooks::HooksManager::createCalloutHandle();
+        }
+        
+    } else {
+
+        // Empty pointer passed, clear stored data
+        stored_pointer.reset();
+        stored_handle.reset();
+    }
+
+    return (stored_handle);
+}
+
+} // namespace shcp
+} // namespace isc
+
+#endif // CALLOUT_HANDLE_STORE_H
diff --git a/src/lib/dhcpsrv/dhcp_parsers.cc b/src/lib/dhcpsrv/dhcp_parsers.cc
index c5ff41d..e03d13f 100644
--- a/src/lib/dhcpsrv/dhcp_parsers.cc
+++ b/src/lib/dhcpsrv/dhcp_parsers.cc
@@ -47,37 +47,58 @@ ParserContext::ParserContext(Option::Universe universe):
     string_values_(new StringStorage()),
     options_(new OptionStorage()),
     option_defs_(new OptionDefStorage()),
+    hooks_libraries_(),
     universe_(universe)
 {
 }
 
 ParserContext::ParserContext(const ParserContext& rhs):
-    boolean_values_(new BooleanStorage(*(rhs.boolean_values_))),
-    uint32_values_(new Uint32Storage(*(rhs.uint32_values_))),
-    string_values_(new StringStorage(*(rhs.string_values_))),
-    options_(new OptionStorage(*(rhs.options_))),
-    option_defs_(new OptionDefStorage(*(rhs.option_defs_))),
+    boolean_values_(),
+    uint32_values_(),
+    string_values_(),
+    options_(),
+    option_defs_(),
+    hooks_libraries_(),
     universe_(rhs.universe_)
 {
+    copyContext(rhs);
 }
 
 ParserContext&
+// The cppcheck version 1.56 doesn't recognize that copyContext
+// copies all context fields.
+// cppcheck-suppress operatorEqVarError
 ParserContext::operator=(const ParserContext& rhs) {
     if (this != &rhs) {
-        boolean_values_ =
-            BooleanStoragePtr(new BooleanStorage(*(rhs.boolean_values_)));
-        uint32_values_ =
-            Uint32StoragePtr(new Uint32Storage(*(rhs.uint32_values_)));
-        string_values_ =
-            StringStoragePtr(new StringStorage(*(rhs.string_values_)));
-        options_ = OptionStoragePtr(new OptionStorage(*(rhs.options_)));
-        option_defs_ =
-            OptionDefStoragePtr(new OptionDefStorage(*(rhs.option_defs_)));
-        universe_ = rhs.universe_;
+        copyContext(rhs);
     }
+
     return (*this);
 }
 
+void
+ParserContext::copyContext(const ParserContext& ctx) {
+    copyContextPointer(ctx.boolean_values_, boolean_values_);
+    copyContextPointer(ctx.uint32_values_, uint32_values_);
+    copyContextPointer(ctx.string_values_, string_values_);
+    copyContextPointer(ctx.options_, options_);
+    copyContextPointer(ctx.option_defs_, option_defs_);
+    copyContextPointer(ctx.hooks_libraries_, hooks_libraries_);
+    // Copy universe.
+    universe_ = ctx.universe_;
+}
+
+template<typename T>
+void
+ParserContext::copyContextPointer(const boost::shared_ptr<T>& source_ptr,
+                                  boost::shared_ptr<T>& dest_ptr) {
+    if (source_ptr) {
+        dest_ptr.reset(new T(*source_ptr));
+    } else {
+        dest_ptr.reset();
+    }
+}
+
 
 // **************************** DebugParser *************************
 
diff --git a/src/lib/dhcpsrv/dhcp_parsers.h b/src/lib/dhcpsrv/dhcp_parsers.h
index 950deb2..0829b21 100644
--- a/src/lib/dhcpsrv/dhcp_parsers.h
+++ b/src/lib/dhcpsrv/dhcp_parsers.h
@@ -45,7 +45,8 @@ typedef OptionSpaceContainer<Subnet::OptionContainer,
 /// @brief Shared pointer to option storage.
 typedef boost::shared_ptr<OptionStorage> OptionStoragePtr;
 
-
+/// @brief Shared pointer to collection of hooks libraries.
+typedef boost::shared_ptr<std::vector<std::string> > HooksLibsStoragePtr;
 
 /// @brief A template class that stores named elements of a given data type.
 ///
@@ -104,7 +105,6 @@ class ValueStorage {
             values_.clear();
         }
 
-
     private:
         /// @brief An std::map of the data values, keyed by parameter names.
         std::map<std::string, ValueType> values_;
@@ -166,13 +166,26 @@ public:
     /// the list of current names can be obtained from the HooksManager) or it
     /// is non-null (this is the new list of names, reload the libraries when
     /// possible).
-    boost::shared_ptr<std::vector<std::string> > hooks_libraries_;
+    HooksLibsStoragePtr hooks_libraries_;
 
     /// @brief The parsing universe of this context.
     Option::Universe universe_;
 
     /// @brief Assignment operator
     ParserContext& operator=(const ParserContext& rhs);
+
+    /// @brief Copy the context fields.
+    ///
+    /// This class method initializes the context data by copying the data
+    /// stored in the context instance provided as an argument. Note that
+    /// this function will also handle copying the NULL pointers.
+    ///
+    /// @param ctx context to be copied.
+    void copyContext(const ParserContext& ctx);
+
+    template<typename T>
+    void copyContextPointer(const boost::shared_ptr<T>& source_ptr,
+                            boost::shared_ptr<T>& dest_ptr);
 };
 
 /// @brief Pointer to various parser context.
diff --git a/src/lib/dhcpsrv/dhcpsrv_messages.mes b/src/lib/dhcpsrv/dhcpsrv_messages.mes
index 3e47a35..a915b02 100644
--- a/src/lib/dhcpsrv/dhcpsrv_messages.mes
+++ b/src/lib/dhcpsrv/dhcpsrv_messages.mes
@@ -141,6 +141,12 @@ hook point sets the skip flag. It means that the server was told that
 no lease4 should be assigned. The server will not put that lease in its
 database and the client will get a NAK packet.
 
+% DHCPSRV_HOOK_LEASE4_RENEW_SKIP DHCPv4 lease was not renewed because a callout set the skip flag.
+This debug message is printed when a callout installed on lease4_renew
+hook point set the skip flag. For this particular hook point, the setting
+of the flag by a callout instructs the server to not renew a lease. The
+server will use existing lease as it is, without extending its lifetime.
+
 % DHCPSRV_HOOK_LEASE6_SELECT_SKIP Lease6 (non-temporary) creation was skipped, because of callout skip flag.
 This debug message is printed when a callout installed on lease6_select
 hook point sets the skip flag. It means that the server was told that
diff --git a/src/lib/dhcpsrv/tests/Makefile.am b/src/lib/dhcpsrv/tests/Makefile.am
index 0ea5591..087b28d 100644
--- a/src/lib/dhcpsrv/tests/Makefile.am
+++ b/src/lib/dhcpsrv/tests/Makefile.am
@@ -15,6 +15,7 @@ AM_CXXFLAGS += $(WARNING_NO_MISSING_FIELD_INITIALIZERS_CFLAG)
 
 if USE_STATIC_LINK
 AM_LDFLAGS = -static
+TEST_LIBS_LDFLAGS = -Bshareable
 endif
 
 CLEANFILES = *.gcno *.gcda
@@ -30,10 +31,12 @@ lib_LTLIBRARIES = libco1.la libco2.la
 libco1_la_SOURCES  = callout_library.cc
 libco1_la_CXXFLAGS = $(AM_CXXFLAGS)
 libco1_la_CPPFLAGS = $(AM_CPPFLAGS) $(LOG4CPLUS_INCLUDES)
+libco1_la_LDFLAGS = $(TEST_LIBS_LDFLAGS)
 
 libco2_la_SOURCES  = callout_library.cc
 libco2_la_CXXFLAGS = $(AM_CXXFLAGS)
 libco2_la_CPPFLAGS = $(AM_CPPFLAGS) $(LOG4CPLUS_INCLUDES)
+libco2_la_LDFLAGS = $(TEST_LIBS_LDFLAGS)
 
 
 TESTS += libdhcpsrv_unittests
@@ -41,6 +44,7 @@ TESTS += libdhcpsrv_unittests
 libdhcpsrv_unittests_SOURCES  = run_unittests.cc
 libdhcpsrv_unittests_SOURCES += addr_utilities_unittest.cc
 libdhcpsrv_unittests_SOURCES += alloc_engine_unittest.cc
+libdhcpsrv_unittests_SOURCES += callout_handle_store_unittest.cc
 libdhcpsrv_unittests_SOURCES += cfgmgr_unittest.cc
 libdhcpsrv_unittests_SOURCES += dbaccess_parser_unittest.cc
 libdhcpsrv_unittests_SOURCES += lease_mgr_factory_unittest.cc
@@ -53,6 +57,7 @@ endif
 libdhcpsrv_unittests_SOURCES += pool_unittest.cc
 libdhcpsrv_unittests_SOURCES += schema_copy.h
 libdhcpsrv_unittests_SOURCES += subnet_unittest.cc
+libdhcpsrv_unittests_SOURCES += test_get_callout_handle.cc test_get_callout_handle.h
 libdhcpsrv_unittests_SOURCES += triplet_unittest.cc
 libdhcpsrv_unittests_SOURCES += test_utils.cc test_utils.h
 
diff --git a/src/lib/dhcpsrv/tests/alloc_engine_unittest.cc b/src/lib/dhcpsrv/tests/alloc_engine_unittest.cc
index 7ea1ccb..083c20f 100644
--- a/src/lib/dhcpsrv/tests/alloc_engine_unittest.cc
+++ b/src/lib/dhcpsrv/tests/alloc_engine_unittest.cc
@@ -1096,6 +1096,8 @@ TEST_F(AllocEngine4Test, requestReuseExpiredLease4) {
 // called
 TEST_F(AllocEngine4Test, renewLease4) {
     boost::scoped_ptr<AllocEngine> engine;
+    CalloutHandlePtr callout_handle = HooksManager::createCalloutHandle();
+
     ASSERT_NO_THROW(engine.reset(new AllocEngine(AllocEngine::ALLOC_ITERATIVE, 100)));
     ASSERT_TRUE(engine);
 
@@ -1117,7 +1119,8 @@ TEST_F(AllocEngine4Test, renewLease4) {
     // renew it.
     ASSERT_FALSE(lease->expired());
     lease = engine->renewLease4(subnet_, clientid_, hwaddr_, true,
-                                true, "host.example.com.", lease, false);
+                                true, "host.example.com.", lease, 
+                                callout_handle, false);
     // Check that he got that single lease
     ASSERT_TRUE(lease);
     EXPECT_EQ(addr.toText(), lease->addr_.toText());
@@ -1358,6 +1361,10 @@ TEST_F(HookAllocEngine6Test, change_lease6_select) {
 ///
 /// It features a couple of callout functions and buffers to store
 /// the data that is accessible via callouts.
+///
+/// Note: lease4_renew callout is tested from DHCPv4 server.
+/// See HooksDhcpv4SrvTest.basic_lease4_renew in
+/// src/bin/dhcp4/tests/dhcp4_srv_unittest.cc
 class HookAllocEngine4Test : public AllocEngine4Test {
 public:
     HookAllocEngine4Test() {
diff --git a/src/lib/dhcpsrv/tests/callout_handle_store_unittest.cc b/src/lib/dhcpsrv/tests/callout_handle_store_unittest.cc
new file mode 100644
index 0000000..71157c3
--- /dev/null
+++ b/src/lib/dhcpsrv/tests/callout_handle_store_unittest.cc
@@ -0,0 +1,122 @@
+// 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.
+
+#include <config.h>
+
+#include <dhcp/dhcp4.h>
+#include <dhcp/dhcp6.h>
+#include <dhcp/pkt4.h>
+#include <dhcpsrv/callout_handle_store.h>
+#include "test_get_callout_handle.h"
+
+#include <gtest/gtest.h>
+
+using namespace isc;
+using namespace isc::dhcp;
+using namespace isc::hooks;
+
+namespace {
+
+TEST(CalloutHandleStoreTest, StoreRetrieve) {
+
+    // Create two DHCP4 packets during tests.  The constructor arguments are
+    // arbitrary.
+    Pkt4Ptr pktptr_1(new Pkt4(DHCPDISCOVER, 1234));
+    Pkt4Ptr pktptr_2(new Pkt4(DHCPDISCOVER, 5678));
+
+    // Check that the pointers point to objects that are different, and that
+    // the pointers are the only pointers pointing to the packets.
+    ASSERT_TRUE(pktptr_1);
+    ASSERT_TRUE(pktptr_2);
+
+    ASSERT_TRUE(pktptr_1 != pktptr_2);
+    EXPECT_EQ(1, pktptr_1.use_count());
+    EXPECT_EQ(1, pktptr_2.use_count());
+
+    // Get the CalloutHandle for the first packet.
+    CalloutHandlePtr chptr_1 = getCalloutHandle(pktptr_1);
+    ASSERT_TRUE(chptr_1);
+
+    // Reference counts on both the callout handle and the packet should have
+    // been incremented because of the stored data.  The reference count on the
+    // other Pkt4 object should not have changed.
+    EXPECT_EQ(2, chptr_1.use_count());
+    EXPECT_EQ(2, pktptr_1.use_count());
+    EXPECT_EQ(1, pktptr_2.use_count());
+
+    // Try getting another pointer for the same packet.  Use a different
+    // pointer object to check that the function returns a handle based on the
+    // pointed-to data and not the pointer.  (Clear the temporary pointer after
+    // use to avoid complicating reference counts.)
+    Pkt4Ptr pktptr_temp = pktptr_1;
+    CalloutHandlePtr chptr_2 = getCalloutHandle(pktptr_temp);
+    pktptr_temp.reset();
+
+    ASSERT_TRUE(chptr_2);
+    EXPECT_TRUE(chptr_1 == chptr_2);
+
+    // Reference count is now 3 on the callout handle - two for pointers here,
+    // one for the static pointer in the function.  The count is 2 for the]
+    // object pointed to by pktptr_1 - one for that pointer and one for the
+    // pointer in the function.
+    EXPECT_EQ(3, chptr_1.use_count());
+    EXPECT_EQ(3, chptr_2.use_count());
+    EXPECT_EQ(2, pktptr_1.use_count());
+    EXPECT_EQ(1, pktptr_2.use_count());
+
+    // Now ask for a CalloutHandle for a different object.  This should return
+    // a different CalloutHandle.
+    chptr_2 = getCalloutHandle(pktptr_2);
+    EXPECT_FALSE(chptr_1 == chptr_2);
+
+    // Check reference counts.  The getCalloutHandle function should be storing
+    // pointers to the objects poiunted to by chptr_2 and pktptr_2.
+    EXPECT_EQ(1, chptr_1.use_count());
+    EXPECT_EQ(1, pktptr_1.use_count());
+    EXPECT_EQ(2, chptr_2.use_count());
+    EXPECT_EQ(2, pktptr_2.use_count());
+
+    // Now try clearing the stored pointers.
+    Pkt4Ptr pktptr_empty;
+    ASSERT_FALSE(pktptr_empty);
+
+    CalloutHandlePtr chptr_empty = getCalloutHandle(pktptr_empty);
+    EXPECT_FALSE(chptr_empty);
+
+    // Reference counts should be back to 1 for the CalloutHandles and the
+    // Packet pointers.
+    EXPECT_EQ(1, chptr_1.use_count());
+    EXPECT_EQ(1, pktptr_1.use_count());
+    EXPECT_EQ(1, chptr_2.use_count());
+    EXPECT_EQ(1, pktptr_2.use_count());
+}
+
+// The followings is a trival test to check that if the template function
+// is referred to in a separate compilation unit, only one copy of the static
+// objects stored in it are returned.  (For a change, we'll use a Pkt6 as the
+// packet object.)
+
+TEST(CalloutHandleStoreTest, SeparateCompilationUnit) {
+
+    // Access the template function here.
+    Pkt6Ptr pktptr_1(new Pkt6(DHCPV6_ADVERTISE, 4321));
+    CalloutHandlePtr chptr_1 = getCalloutHandle(pktptr_1);
+    ASSERT_TRUE(chptr_1);
+
+    // Access it from within another compilation unit.
+    CalloutHandlePtr chptr_2 = isc::dhcp::test::testGetCalloutHandle(pktptr_1);
+    EXPECT_TRUE(chptr_1 == chptr_2);
+}
+
+} // Anonymous namespace
diff --git a/src/lib/dhcpsrv/tests/dhcp_parsers_unittest.cc b/src/lib/dhcpsrv/tests/dhcp_parsers_unittest.cc
index 3da0584..62f901c 100644
--- a/src/lib/dhcpsrv/tests/dhcp_parsers_unittest.cc
+++ b/src/lib/dhcpsrv/tests/dhcp_parsers_unittest.cc
@@ -397,7 +397,7 @@ public:
     ///
     /// @return retuns 0 if the configuration parsed successfully,
     /// non-zero otherwise failure.
-    int parseConfiguration(const std::string& config) {    
+    int parseConfiguration(const std::string& config) {
         int rcode_ = 1;
         // Turn config into elements.
         // Test json just to make sure its valid.
@@ -692,3 +692,309 @@ TEST_F(ParseConfigTest, invalidHooksLibrariesTest) {
     EXPECT_FALSE(error_text_.find(NOT_PRESENT_LIBRARY) == string::npos) <<
         "Error text returned from parse failure is " << error_text_;
 }
+
+/// @brief DHCP Configuration Parser Context test fixture.
+class ParserContextTest : public ::testing::Test {
+public:
+    /// @brief Constructor
+    ParserContextTest() { }
+
+    /// @brief Check that the storages of the specific type hold the
+    /// same value.
+    ///
+    /// This function assumes that the ref_values storage holds exactly
+    /// one parameter called 'foo'.
+    ///
+    /// @param ref_values A storage holding reference value. In the typical
+    /// case it is a storage held in the original context, which is assigned
+    /// to another context.
+    /// @param values A storage holding value to be checked.
+    /// @tparam ContainerType A type of the storage.
+    /// @tparam ValueType A type of the value in the container.
+    template<typename ContainerType, typename ValueType>
+    void checkValueEq(const boost::shared_ptr<ContainerType>& ref_values,
+                      const boost::shared_ptr<ContainerType>& values) {
+        ValueType param;
+        ASSERT_NO_THROW(param = values->getParam("foo"));
+        EXPECT_EQ(ref_values->getParam("foo"), param);
+    }
+
+    /// @brief Check that the storages of the specific type hold different
+    /// value.
+    ///
+    /// This function assumes that the ref_values storage holds exactly
+    /// one parameter called 'foo'.
+    ///
+    /// @param ref_values A storage holding reference value. In the typical
+    /// case it is a storage held in the original context, which is assigned
+    /// to another context.
+    /// @param values A storage holding value to be checked.
+    /// @tparam ContainerType A type of the storage.
+    /// @tparam ValueType A type of the value in the container.
+    template<typename ContainerType, typename ValueType>
+    void checkValueNeq(const boost::shared_ptr<ContainerType>& ref_values,
+                       const boost::shared_ptr<ContainerType>& values) {
+        ValueType param;
+        ASSERT_NO_THROW(param = values->getParam("foo"));
+        EXPECT_NE(ref_values->getParam("foo"), param);
+    }
+
+    /// @brief Check that option definition storage in the context holds
+    /// one option definition of the specified type.
+    ///
+    /// @param ctx A pointer to a context.
+    /// @param opt_type Expected option type.
+    void checkOptionDefinitionType(const ParserContext& ctx,
+                                   const uint16_t opt_type) {
+        OptionDefContainerPtr opt_defs =
+            ctx.option_defs_->getItems("option-space");
+        ASSERT_TRUE(opt_defs);
+        OptionDefContainerTypeIndex& idx = opt_defs->get<1>();
+        OptionDefContainerTypeRange range = idx.equal_range(opt_type);
+        EXPECT_EQ(1, std::distance(range.first, range.second));
+    }
+
+    /// @brief Check that option storage in the context holds one option
+    /// of the specified type.
+    ///
+    /// @param ctx A pointer to a context.
+    /// @param opt_type Expected option type.
+    void checkOptionType(const ParserContext& ctx, const uint16_t opt_type) {
+        Subnet::OptionContainerPtr options =
+            ctx.options_->getItems("option-space");
+        ASSERT_TRUE(options);
+        Subnet::OptionContainerTypeIndex& idx = options->get<1>();
+        Subnet::OptionContainerTypeRange range = idx.equal_range(opt_type);
+        ASSERT_EQ(1, std::distance(range.first, range.second));
+    }
+
+    /// @brief Test copy constructor or assignment operator when values
+    /// being copied are NULL.
+    ///
+    /// @param copy Indicates that copy constructor should be tested
+    /// (if true), or assignment operator (if false).
+    void testCopyAssignmentNull(const bool copy) {
+        ParserContext ctx(Option::V6);
+        // Release all pointers in the context.
+        ctx.boolean_values_.reset();
+        ctx.uint32_values_.reset();
+        ctx.string_values_.reset();
+        ctx.options_.reset();
+        ctx.option_defs_.reset();
+        ctx.hooks_libraries_.reset();
+
+        // Even if the fields of the context are NULL, it should get
+        // copied.
+        ParserContextPtr ctx_new(new ParserContext(Option::V6));
+        if (copy) {
+            ASSERT_NO_THROW(ctx_new.reset(new ParserContext(ctx)));
+        } else {
+            *ctx_new = ctx;
+        }
+
+        // The resulting context has its fields equal to NULL.
+        EXPECT_FALSE(ctx_new->boolean_values_);
+        EXPECT_FALSE(ctx_new->uint32_values_);
+        EXPECT_FALSE(ctx_new->string_values_);
+        EXPECT_FALSE(ctx_new->options_);
+        EXPECT_FALSE(ctx_new->option_defs_);
+        EXPECT_FALSE(ctx_new->hooks_libraries_);
+
+    }
+
+    /// @brief Test copy constructor or assignment operator.
+    ///
+    /// @param copy Indicates that copy constructor should be tested (if true),
+    /// or assignment operator (if false).
+    void testCopyAssignment(const bool copy) {
+        // Create new context. It will be later copied/assigned to another
+        // context.
+        ParserContext ctx(Option::V6);
+
+        // Set boolean parameter 'foo'.
+        ASSERT_TRUE(ctx.boolean_values_);
+        ctx.boolean_values_->setParam("foo", true);
+
+        // Set uint32 parameter 'foo'.
+        ASSERT_TRUE(ctx.uint32_values_);
+        ctx.uint32_values_->setParam("foo", 123);
+
+        // Ser string parameter 'foo'.
+        ASSERT_TRUE(ctx.string_values_);
+        ctx.string_values_->setParam("foo", "some string");
+
+        // Add new option, with option code 10, to the context.
+        ASSERT_TRUE(ctx.options_);
+        OptionPtr opt1(new Option(Option::V6, 10));
+        Subnet::OptionDescriptor desc1(opt1, false);
+        std::string option_space = "option-space";
+        ASSERT_TRUE(desc1.option);
+        ctx.options_->addItem(desc1, option_space);
+
+        // Add new option definition, with option code 123.
+        OptionDefinitionPtr def1(new OptionDefinition("option-foo", 123,
+                                                      "string"));
+        ctx.option_defs_->addItem(def1, option_space);
+
+        // Allocate container for hooks libraries and add one library name.
+        ctx.hooks_libraries_.reset(new std::vector<std::string>());
+        ctx.hooks_libraries_->push_back("library1");
+
+        // We will use ctx_new to assign another context to it or copy
+        // construct.
+        ParserContextPtr ctx_new(new ParserContext(Option::V4));;
+        if (copy) {
+            ctx_new.reset(new ParserContext(ctx));
+        } else {
+            *ctx_new = ctx;
+        }
+
+        // New context has the same boolean value.
+        ASSERT_TRUE(ctx_new->boolean_values_);
+        {
+            SCOPED_TRACE("Check that boolean values are equal in both"
+                         " contexts");
+            checkValueEq<BooleanStorage, bool>(ctx.boolean_values_,
+                                               ctx_new->boolean_values_);
+        }
+
+        // New context has the same uint32 value.
+        ASSERT_TRUE(ctx_new->uint32_values_);
+        {
+            SCOPED_TRACE("Check that uint32_t values are equal in both"
+                         " contexts");
+            checkValueEq<Uint32Storage, uint32_t>(ctx.uint32_values_,
+                                                  ctx_new->uint32_values_);
+        }
+
+        // New context has the same string value.
+        ASSERT_TRUE(ctx_new->string_values_);
+        {
+            SCOPED_TRACE("Check that string values are equal in both contexts");
+            checkValueEq<StringStorage, std::string>(ctx.string_values_,
+                                                     ctx_new->string_values_);
+        }
+
+        // New context has the same option.
+        ASSERT_TRUE(ctx_new->options_);
+        {
+            SCOPED_TRACE("Check that the option values are equal in both"
+                         " contexts");
+            checkOptionType(*ctx_new, 10);
+        }
+
+        // New context has the same option definition.
+        ASSERT_TRUE(ctx_new->option_defs_);
+        {
+            SCOPED_TRACE("check that the option definition is equal in both"
+                         " contexts");
+            checkOptionDefinitionType(*ctx_new, 123);
+        }
+
+        // New context has the same hooks library.
+        ASSERT_TRUE(ctx_new->hooks_libraries_);
+        {
+            ASSERT_EQ(1, ctx_new->hooks_libraries_->size());
+            EXPECT_EQ("library1", (*ctx_new->hooks_libraries_)[0]);
+        }
+
+        // New context has the same universe.
+        EXPECT_EQ(ctx.universe_, ctx_new->universe_);
+
+        // Change the value of the boolean parameter. This should not affect the
+        // corresponding value in the new context.
+        {
+            SCOPED_TRACE("Check that boolean value isn't changed when original"
+                         " value is changed");
+            ctx.boolean_values_->setParam("foo", false);
+            checkValueNeq<BooleanStorage, bool>(ctx.boolean_values_,
+                                                ctx_new->boolean_values_);
+        }
+
+        // Change the value of the uint32_t parameter. This should not affect
+        // the corresponding value in the new context.
+        {
+            SCOPED_TRACE("Check that uint32_t value isn't changed when original"
+                         " value is changed");
+            ctx.uint32_values_->setParam("foo", 987);
+            checkValueNeq<Uint32Storage, uint32_t>(ctx.uint32_values_,
+                                                   ctx_new->uint32_values_);
+        }
+
+        // Change the value of the string parameter. This should not affect the
+        // corresponding value in the new context.
+        {
+            SCOPED_TRACE("Check that string value isn't changed when original"
+                         " value is changed");
+            ctx.string_values_->setParam("foo", "different string");
+            checkValueNeq<StringStorage, std::string>(ctx.string_values_,
+                                                      ctx_new->string_values_);
+        }
+
+        // Change the option. This should not affect the option instance in the
+        // new context.
+        {
+            SCOPED_TRACE("Check that option remains the same in the new context"
+                         " when the option in the original context is changed");
+            ctx.options_->clearItems();
+            Subnet::OptionDescriptor desc(OptionPtr(new Option(Option::V6,
+                                                               100)),
+                                          false);
+
+            ASSERT_TRUE(desc.option);
+            ctx.options_->addItem(desc, "option-space");
+            checkOptionType(*ctx_new, 10);
+        }
+
+        // Change the option definition. This should not affect the option
+        // definition in the new context.
+        {
+            SCOPED_TRACE("Check that option definition remains the same in"
+                         " the new context when the option definition in the"
+                         " original context is changed");
+            ctx.option_defs_->clearItems();
+            OptionDefinitionPtr def(new OptionDefinition("option-foo", 234,
+                                                         "string"));
+
+            ctx.option_defs_->addItem(def, option_space);
+            checkOptionDefinitionType(*ctx_new, 123);
+        }
+
+        // Change the list of libraries. this should not affect the list in the
+        // new context.
+        ctx.hooks_libraries_->clear();
+        ctx.hooks_libraries_->push_back("library2");
+        ASSERT_EQ(1, ctx_new->hooks_libraries_->size());
+        EXPECT_EQ("library1", (*ctx_new->hooks_libraries_)[0]);
+
+        // Change the universe. This should not affect the universe value in the
+        // new context.
+        ctx.universe_ = Option::V4;
+        EXPECT_EQ(Option::V6, ctx_new->universe_);
+
+    }
+
+};
+
+// Check that the assignment operator of the ParserContext class copies all
+// fields correctly.
+TEST_F(ParserContextTest, assignment) {
+    testCopyAssignment(false);
+}
+
+// Check that the assignment operator of the ParserContext class copies all
+// fields correctly when these fields are NULL.
+TEST_F(ParserContextTest, assignmentNull) {
+    testCopyAssignmentNull(false);
+}
+
+// Check that the context is copy constructed correctly.
+TEST_F(ParserContextTest, copyConstruct) {
+    testCopyAssignment(true);
+}
+
+// Check that the context is copy constructed correctly, when context fields
+// are NULL.
+TEST_F(ParserContextTest, copyConstructNull) {
+    testCopyAssignmentNull(true);
+}
diff --git a/src/lib/dhcpsrv/tests/test_get_callout_handle.cc b/src/lib/dhcpsrv/tests/test_get_callout_handle.cc
new file mode 100644
index 0000000..5d7cd9d
--- /dev/null
+++ b/src/lib/dhcpsrv/tests/test_get_callout_handle.cc
@@ -0,0 +1,31 @@
+// 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.
+
+#include <dhcpsrv/callout_handle_store.h>
+#include "test_get_callout_handle.h"
+
+// Just instantiate the getCalloutHandle function and call it.
+
+namespace isc {
+namespace dhcp {
+namespace test {
+
+isc::hooks::CalloutHandlePtr
+testGetCalloutHandle(const Pkt6Ptr& pktptr) {
+    return (isc::dhcp::getCalloutHandle(pktptr));
+}
+
+} // namespace test
+} // namespace dhcp
+} // namespace isc
diff --git a/src/lib/dhcpsrv/tests/test_get_callout_handle.h b/src/lib/dhcpsrv/tests/test_get_callout_handle.h
new file mode 100644
index 0000000..f29ab42
--- /dev/null
+++ b/src/lib/dhcpsrv/tests/test_get_callout_handle.h
@@ -0,0 +1,46 @@
+// 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.
+
+#ifndef TEST_GET_CALLOUT_HANDLE_H
+#define TEST_GET_CALLOUT_HANDLE_H
+
+#include <dhcp/pkt6.h>
+#include <hooks/callout_handle.h>
+
+namespace isc {
+namespace dhcp {
+namespace test {
+
+/// @file
+/// @brief Get Callout Handle
+///
+/// This function is a shall around getCalloutHandle.  It's purpose is to
+/// ensure that the getCalloutHandle() template function is referred to by
+/// two separate compilation units, and so test that data stored in one unit
+/// can be accessed by another. (This should be the case, but some compilers
+/// mabe be odd when it comes to template instantiation.)
+///
+/// @param pktptr Pointer to a Pkt6 object.
+///
+/// @return CalloutHandlePtr pointing to CalloutHandle associated with the
+///         Pkt6 object.
+isc::hooks::CalloutHandlePtr
+testGetCalloutHandle(const Pkt6Ptr& pktptr);
+
+} // namespace test
+} // namespace dhcp
+} // namespace isc
+
+
+#endif // TEST_GET_CALLOUT_HANDLE_H
diff --git a/src/lib/hooks/callout_manager.cc b/src/lib/hooks/callout_manager.cc
index df27f45..166fda1 100644
--- a/src/lib/hooks/callout_manager.cc
+++ b/src/lib/hooks/callout_manager.cc
@@ -157,12 +157,13 @@ CalloutManager::callCallouts(int hook_index, CalloutHandle& callout_handle) {
                             .getName(current_hook_))
                         .arg(PointerConverter(i->second).dlsymPtr());
                 }
-            } catch (...) {
+            } catch (const std::exception& e) {
                 // Any exception, not just ones based on isc::Exception
                 LOG_ERROR(hooks_logger, HOOKS_CALLOUT_EXCEPTION)
                     .arg(current_library_)
                     .arg(ServerHooks::getServerHooks().getName(current_hook_))
-                    .arg(PointerConverter(i->second).dlsymPtr());
+                    .arg(PointerConverter(i->second).dlsymPtr())
+                    .arg(e.what());
             }
 
         }
diff --git a/src/lib/hooks/hook_user.dox b/src/lib/hooks/hook_user.dox
deleted file mode 100644
index a804720..0000000
--- a/src/lib/hooks/hook_user.dox
+++ /dev/null
@@ -1,1031 +0,0 @@
-// 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.
-
-// Note: the prefix "hooksdg" to all labels is an abbreviation for "Hooks
-// Developer's Guide" and is used to prevent a clash with symbols in any
-// other Doxygen file.
-
-/**
- @page hooksdgDevelopersGuide Hook Developer's Guide
-
- @section hooksdgIntroduction 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.
-
-- 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, all such code will be referred
-to as "user 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 code can examine and, if required, modify.
-BIND 10 uses the modified data in the remainder of its processing.
-
-In order to minimise the interaction between BIND 10 and the user
-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 the BIND 10 configuration mechanism means that
-as new versions of BIND 10 are released, there is no need to modify
-the user 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.
-
- at note Although the defined interface should not change, the internals
-of some of the classes and structures referenced by the user code may
-change between versions of BIND 10.  These changes have to be reflected
-in the compiled version of the software, hence the need for a rebuild.
-
- at subsection hooksdgLanguages Languages
-
-The core of BIND 10 is written in C++.  While it is the intention to
-provide interfaces into user code written in 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.
-
- at subsection hooksdgTerminology 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 functions is made. Each hook has a name and
-each hook can have any number (including 0) of user functions
-attached to it.
-
-- Callout - a user 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 function.
-
-- Framework function - the functions that a user library needs to
-supply in order for the hooks framework to 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.
-
-
- at section hooksdgTutorial 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 however, is to illustrate the main features of
-user hook code not to provide an optimal solution.
-
-
- at subsection hooksdgFrameworkFunctions Framework Functions
-
-Loading and initializing a library holding user 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 in our example, all three
-are used.
-
- at subsubsection hooksdgVersionFunction 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 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 header files of the version
-of BIND 10 against which it was built.  The hooks framework checks this
-for compatibility with the running version of BIND 10 before loading
-the library.
-
-In this tutorial, we'll put "version" in its own file, version.cc.  The
-contents are:
-
- at code
-// version.cc
-
-#include <hooks/hooks.h>
-
-extern "C" {
-
-int version() {
-    return (BIND10_HOOKS_VERSION);
-}
-
-};
- at endcode
-
-The file "hooks/hooks.h" is specified relative to the BIND 10 libraries
-source directory - this is covered later in the section @ref hooksdgBuild.
-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.
-
- at subsubsection hooksdgLoadUnloadFunctions 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
-(names that are not hook point names) can be registered:
-this is covered further in the section @ref hooksdgCalloutRegistration.
-
-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:
-
- at 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 // LIBRARY_COMMON_H
- at endcode
-
-... and one to hold the "load" and "unload" functions:
-
- at 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);
-}
-
-};
- at 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, this being used in
-the registration of functions.  As no functions are being registered
-in this example, the argument specification omits the variable name
-(whilst retaining the type) to avoid an "unused variable" compiler
-warning. (The LibraryHandle and its use is discussed in the section
- at ref hooksdgLibraryHandle.)
-- 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 code by some other means.
-- "load" must 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 must be 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.
-
- at subsection hooksdgCallouts 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: server-specific documentation
-describes in detail the points in the server at which the hooks are
-present together with the data passed to callouts attached to them.
-
-Before we continue with the example, we'll discuss how arguments are
-passed to callouts and information is returned to the server.  We will
-also discuss how information can be moved between callouts.
-
- at subsubsection hooksdgCalloutSignature The Callout Signature
-
-All callouts are declared with the signature:
- at code
-extern "C" {
-int callout(CalloutHandle& handle);
-};
- at 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.
-
- at subsubsection hooksdgArguments 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.
-
- at 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 callouts attached to the hook
-    ...
-
-    // Retrieve the modified values
-    handle.getArgument("data_count", count);
-    handle.getArgument("inpacket", pktptr);
- at endcode
-
-In the callout
-
- at 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);
- at 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 is 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.
-
- at subsubsection hooksdgSkipFlag The "Skip" Flag
-
-When a to callouts attached to a hook returns, the server will usually continue
-its processing.  However, a callout might have done something that means that
-the server should follow another path.  Possible actions a server could take
-include:
-
-- Skip the next stage of processing because the callout has already
-done it.  For example, a hook is located just before the DHCP server
-allocates an address to the client.  A callout may decide to allocate
-special addresses for certain clients, in which case it needs to tell
-the server not to allocate an address in this case.
-- Drop the packet and continue with the next request. A possible scenario
-is a DNS server where a callout inspects the source address of an incoming
-packet and compares it against a black list; if the address is on it,
-the callout notifies the server to drop the packet.
-
-To handle these common cases, the CalloutHandle has a "skip" flag.
-This is set by a callout when it wishes the server to skip normal
-processing.  It is set false by the hooks framework before callouts on a
-hook are called.  If the flag is set on return, the server will take the
-"skip" action relevant for the hook.
-
-The methods to get and set the "skip" flag are getSkip and setSkip. Their
-usage is intuitive:
-
- at code
-    // Get the current setting of the skip flag.
-    bool skip = handle.getSkip();
-
-    // Do some processing...
-        :
-    if (lease_allocated) {
-        // Flag the server to skip the next step of the processing as we
-        // already have an address.
-        handle.setSkip(true);
-    }
-    return;
-    
- at endcode
-
-Like arguments, the "skip" flag is passed to all callouts on a hook.  Callouts
-later in the list are able to examine (and modify) the settings of earlier ones.
-
- at subsubsection hooksdgCalloutContext Per-Request Context
-
-Although many of the BIND 10 modules can be characterised as handling
-singles 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 can effectively
-attach data to a request that 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.
-
-The example in the next section illustrates their use.
-
- at subsection hooksdgExampleCallouts 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 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
-hooksdgCalloutRegistration).  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:
-
- at 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);
-};
- at 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:
-
- at 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.  Note that the argument list for this hook also
-        // contains a pointer to the query: we don't need to access that in this
-        // example.
-        Pkt4Ptr 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);
-}
-
-};
- at endcode
-
- at subsection hooksdgBuild 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:
-
- at 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
- at 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.
-
- at subsection hooksdgConfiguration 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:
-
- at code
-> config add Dhcp4/hook_libraries
-> config set Dhcp4/hook_libraries[0] "/usr/local/lib/example.so"
-> config commit
- at endcode
-
-The DHCPv4 server will load the library and execute the callouts each time a
-request is received.
-
- at section hooksdgAdvancedTopics Advanced Topics
-
- at subsection hooksdgContextCreateDestroy 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.
-
-These approaches are illustrated in the following examples.
-Here it is assumed that the hooks library is performing some form of
-security checking on the packet and needs to maintain information in
-a user-specified "SecurityInformation" object. (The details of this
-fictitious object are of no concern here.) The object is created in
-the context_create callout and used in both the pkt4_rcvd and the
-v4_lease_write_post callouts.
-
- at code
-// Storing information in a "raw" pointer.  Assume that the
-
-#include <hooks/hooks.h>
-   :
-
-extern "C" {
-
-// context_create callout - called when the request is created.
-int context_create(CalloutHandle& handle) {
-    // Create the security information and store it in the context
-    // for this packet.
-    SecurityInformation* si = new SecurityInformation();
-    handle.setContext("security_information", si);
-}
-
-// Callouts that use the context
-int pktv_rcvd(CalloutHandle& handle) {
-    // Retrieve the pointer to the SecurityInformation object
-    SecurityInformation si;
-    handle.getContext("security_information", si);
-        :
-        :
-    // Set the security information
-    si->setSomething(...);
-
-    // The pointed-to information has been updated but the pointer has not been
-    // altered, so there is no need to call setContext() again.
-}
-
-int v4_lease_write_post(CalloutHandle& handle) {
-    // Retrieve the pointer to the SecurityInformation object
-    SecurityInformation si;
-    handle.getContext("security_information", si);
-        :
-        :
-    // Retrieve security information
-    bool active = si->getSomething(...);
-        :
-}
-
-// Context destruction.  We need to delete the pointed-to SecurityInformation
-// object because we will lose the pointer to it when the CalloutHandle is
-// destroyed.
-int context_destroy(CalloutHandle& handle) {
-    // Retrieve the pointer to the SecurityInformation object
-    SecurityInformation si;
-    handle.getContext("security_information", si);
-
-    // Delete the pointed-to memory.
-    delete si;
-}
- at endcode
-
-The requirement for the context_destroy callout can be eliminated if
-a Boost shared ptr is used to point to the allocated memory:
-
- at code
-// Storing information in a "raw" pointer.  Assume that the
-
-#include <hooks/hooks.h>
-#include <boost/shared_ptr.hpp>
-   :
-
-extern "C" {
-
-// context_create callout - called when the request is created.
-
-int context_create(CalloutHandle& handle) {
-    // Create the security information and store it in the context for this
-    // packet.
-    boost::shared_ptr<SecurityInformation> si(new SecurityInformation());
-    handle.setContext("security_information", si);
-}
-
-// Other than the data type, a shared pointer has similar semantics to a "raw"
-// pointer.  Only the code from pkt_rcvd is shown here.
-
-int pktv_rcvd(CalloutHandle& handle) {
-    // Retrieve the pointer to the SecurityInformation object
-    boost::shared_ptr<SecurityInformation> si;
-    handle.setContext("security_information", si);
-        :
-        :
-    // Modify the security information
-    si->setSomething(...);
-
-    // The pointed-to information has been updated but the pointer has not
-    // altered, so theree is no need to reset the context.
-}
-
-// No context_destroy callout is needed to delete the allocated
-// SecurityInformation object.  When the CalloutHandle is destroyed, the shared
-// pointer object will be destroyed.  If that is the last shared pointer to the
-// allocated memory, then it too will be deleted.
- at endcode
-
-(Note that a Boost shared pointer - rather than any other Boost smart pointer -
-should be used, as the pointer objects are copied within the hooks framework and
-only shared pointers have the correct behavior for the copy operation.)
-
-
- at subsection hooksdgCalloutRegistration Registering Callouts
-
-As briefly mentioned in @ref hooksdgExampleCallouts, 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 convention 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 convention 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 the appropriate 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.
-
- at subsubsection hooksdgLibraryHandle 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.
-
- at subsubsection hooksdgNonstandardCalloutNames 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 our 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:
-
- at 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);
-}
- at 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.
-
- at subsubsection hooksdgMultipleCallouts 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.
-
- at code
-    libhandle.registerCallout("pkt_rcvd", classify);
-    libhandle.registerCallout("pkt_rcvd", write_data);
- at 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, "skip" flag, or per-request
-context by the first is visible to the second.
-
- at subsubsection hooksdgDynamicRegistration 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 can 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:
-
- at code
-// pkt_rcvd.cc
-//      :
-
-int pkt_rcvd(CalloutHandle& handle) {
-
-            :
-            :
-
-    // 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());
-
-        // Register the callback to log the data.
-        handle.getLibraryHandle().registerCallout("v4_lease_write", write_data);
-    }
-
-    return (0);
-};
- at endcode
-
- at 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);
-}
- at endcode
-
-Note that the above example used a non-standard name for the callout
-that wrote 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.)
-
-
- at 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 hook's callouts, A and D will run (in that order).
-
- at subsection hooksdgMultipleLibraries Multiple User Libraries
-
-As alluded to in the section @ref hooksdgConfiguration, 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.
-
- at 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.
-
- at 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.
-
- at subsection hooksdgInterLibraryData 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.
-
-
- at subsection hooksdgRegisterMultipleLibraries 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".
-
-*/
diff --git a/src/lib/hooks/hooks_component_developer.dox b/src/lib/hooks/hooks_component_developer.dox
index edf59a8..777ae2e 100644
--- a/src/lib/hooks/hooks_component_developer.dox
+++ b/src/lib/hooks/hooks_component_developer.dox
@@ -31,7 +31,7 @@ BIND 10 component to use hooks.  It shows how the component should be written
 to load a shared library at run-time and how to call functions in it.
 
 For information about writing a hooks library containing functions called by BIND 10
-during its execution, see the document @ref hooksDevelopersGuide.
+during its execution, see the document @ref hooksdgDevelopersGuide.
 
 @subsection hooksComponentTerminology Terminology
 
@@ -53,7 +53,7 @@ to execute a user-written function.
 shared library and loaded by BIND 10 into its address space.  Multiple
 user libraries can be loaded at the same time, each containing callouts for
 the same hooks.  The hooks framework calls these libraries one after the
-other. (See the document @ref hooksDevelopersGuide for more details.)
+other. (See the document @ref hooksdgDevelopersGuide for more details.)
 
 @subsection hooksComponentLanguages Languages
 
@@ -463,7 +463,7 @@ It is possible for a component to register its own functions (i.e. within
 its own address space) as hook callouts.  These functions are called
 in eactly the same way as user callouts, being passed their arguments
 though a CalloutHandle object.  (Guidelines for writing callouts can be
-found in @ref hooksDevelopersGuide.)
+found in @ref hooksdgDevelopersGuide.)
 
 A component can associate with a hook callouts that run either before
 user-registered callouts or after them.  Registration is done via a
@@ -473,7 +473,7 @@ through the methods isc::hooks::HooksManager::preCalloutLibraryHandle()
 callouts) or isc::hooks::HooksManager::postCalloutLibraryHandle() (for
 a handle to register callouts to run after the user callouts).  Use of
 the LibraryHandle to register and deregister callouts is described in
- at ref hooksLibraryHandle.
+ at ref hooksdgLibraryHandle.
 
 Finally, it should be noted that callouts registered in this way only
 remain registered until the next call to isc::hooks::loadLibraries().
diff --git a/src/lib/hooks/hooks_maintenance.dox b/src/lib/hooks/hooks_maintenance.dox
new file mode 100644
index 0000000..c3c4946
--- /dev/null
+++ b/src/lib/hooks/hooks_maintenance.dox
@@ -0,0 +1,274 @@
+// 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.
+
+// Note: the prefix "hooksmg" to all labels is an abbreviation for "Hooks
+// Maintenance Guide" and is used to prevent a clash with symbols in any
+// other Doxygen file.
+
+/**
+ @page hooksmgMaintenanceGuide Hooks Maintenance Guide
+
+ @section hooksmgIntroduction Introduction
+
+ This document is aimed at BIND 10 maintainers responsible for the hooks
+ system.  It provides an overview of the classes that make up the hooks
+ framework and notes important aspects of processing.  More detailed
+ information can be found in the source code.
+
+ It is assumed that the reader is familiar with the contents of the @ref
+ hooksdgDevelopersGuide and the @ref hooksComponentDeveloperGuide.
+
+ @section hooksmgObjects Hooks Framework Objects
+
+ The relationships between the various objects in the hooks framework
+ is shown below:
+
+ @image html HooksUml.png "High-Level Class Diagram of the Hooks Framework"
+
+ (To avoid clutter, the @ref hooksmgServerHooks object, used to pass
+ information about registered hooks to the components, is not shown on
+ the diagram.)
+
+ The hooks framework objects can be split into user-side objects and
+ server-side objects.  The former are those objects used or referenced
+ by user-written hooks libraries.  The latter are those objects used in
+ the hooks framework.
+
+ @subsection hooksmgUserObjects User-Side Objects
+
+ The user-side code is able to access two objects in the framework,
+ the @ref hooksmgCalloutHandle and the @ref hooksmgLibraryHandle.
+ The @ref hooksmgCalloutHandle is used to pass data between the BIND 10
+ component and the loaded library; the @ref hooksmgLibraryHandle is used
+ for registering callouts.
+
+ @subsubsection hooksmgCalloutHandle Callout Handle
+
+ The @ref isc::hooks::CalloutHandle has two functions: passing arguments
+ between the BIND 10 component and the user-written library, and storing
+ per-request context between library calls.  In both cases the data is
+ stored in a std::map structure, keyed by argument (or context item) name.
+ The actual data is stored in a boost::any object, which allows any
+ data type to be stored, although a penalty for this flexibility is
+ the restriction (mentioned in the @ref hooksdgDevelopersGuide) that
+ the type of data retrieved must be identical (and not just compatible)
+ with that stored.
+
+ The storage of context data is slightly complex because there is
+ separate context for each user library.  For this reason, the @ref
+ hooksmgCalloutHandle has multiple maps, one for each library loaded.
+ The maps are stored in another map, the appropriate map being identified
+ by the "current library index" (this index is explained further below).
+ The reason for the second map (rather than a structure such as a vector)
+ is to avoid creating individual context maps unless needed; given the
+ key to the map (in this case the current library index) accessing an
+ element in a map using the operator[] method returns the element in
+ question if it exists, or creates a new one (and stores it in the map)
+ if its doesn't.
+
+ @subsubsection hooksmgLibraryHandle Library Handle
+
+ Little more than a restricted interface to the @ref
+ hooksmgCalloutManager, the @ref isc::hooks::LibraryHandle allows a
+ callout to register and deregister callouts.  However, there are some
+ quirks to callout registration which, although the processing involved
+ is in the @ref hooksmgCalloutManager, are best described here.
+
+ Firstly, a callout can be deregistered by a function within a user
+ library only if it was registered by a function within that library. That
+ is to say, if library A registers the callout A_func() on hook "alpha"
+ and library B registers B_func(), functions within library A are only
+ able to remove A_func() (and functions in library B remove B_func()).
+ The restriction - here to prevent one library interfering with the
+ callouts of another - is enforced by means of the current library index.
+ As described below, each entry in the vector of callouts associated with
+ a hook is a pair object, comprising a pointer to the callout and
+ the index of the library with which it is associated. A callout
+ can only modify entries in that vector where the current library index
+ matches the index element of the pair.
+
+ A second quirk is that when dynamically modifying the list of callouts,
+ the change only takes effect when the current call out from the server
+ completes. To clarify this, suppose that functions A_func(), B_func()
+ and C_func() are registered on a hook, and the server executes a callout
+ on the hook. Suppose also during this call, A_func() removes the callout
+ C_func() and that B_func() adds D_func(). As changes only take effect
+ when the current call out completes, the user callouts executed will be
+ A_func(), B_func() then C_func(). When the server calls the hook callouts
+ again, the functions executed will be A_func(), B_func() and D_func().
+
+ This restriction is down to implementation.  When a set of callouts on a hook
+ is being called, the @ref hooksmgCalloutManager iterates through a
+ vector (the "callout vector") of (index, callout pointer) pairs.  Since
+ registration or deregistration of a callout on that hook would change the
+ vector (and so potentially invalidate the iterators used to access the it),
+ a copy of the vector is taken before the iteration starts.  The @ref
+ hooksmgCalloutManager iterates over this copy while any changes made
+ by the callout registration functions affect the relevant callout vector.
+ Such approach was chosen because of performance considerations.
+
+ @subsection hooksmgServerObjects Server-Side Objects
+
+ Those objects are not accessible by user libraries. Please do not
+ attempt to use them if you are developing user callouts.
+
+ @subsubsection hooksmgServerHooks Server Hooks
+
+ The singleton @ref isc::hooks::ServerHooks object is used to register
+ hooks. It is little more than a wrapper around a map of (hook index,
+ hook name), generating a unique number (the hook index) for each
+ hook registered.  It also handles the registration of the pre-defined
+ context_create and context_destroy hooks.
+
+ In operation, the @ref hooksmgHooksManager provides a thin wrapper
+ around it, so that the BIND 10 component developer does not have to
+ worry about another object.
+
+ @subsubsection hooksmgLibraryManager Library Manager
+
+ An @ref isc::hooks::LibraryManager is created by the @ref
+ hooksmgHooksManager object for each shared library loaded. It
+ controls the loading and unloading of the library and in essence
+ represents the library in the hooks framework. It also handles the
+ registration of the standard callouts (functions in the library with
+ the same name as the hook name).
+
+ Of particular importance is the "library's index", a number associated
+ with the library.  This is passed to the LibraryManager at creation
+ time and is used to tag the callout pointers.  It is discussed
+ further below.
+
+ As the LibraryManager provides all the methods needed to manage the
+ shared library, it is the natural home for the static validateLibrary()
+ method. The function called the parsing of the BIND 10 configuration, when
+ the "hooks-libraries" element is processed. It checks that shared library
+ exists, that it can be opened, that it contains the "version()" function
+ and that that function returns a valid value. It then closes the shared
+ library and returns an appropriate indication as to the library status.
+
+ @subsubsection hooksmgLibraryManagerCollection Library Manager Collection
+
+ The hooks framework can handle multiple libraries and as
+ a result will create a @ref hooksmgLibraryManager for each
+ of them. The collection of LibraryManagers is managed by the
+ @ref isc::hooks::LibraryManagerCollection object which, in most
+ cases has a method corresponding to a @ref hooksmgLibraryManager
+ method, e.g. it has a loadLibraries() that corresponds to the @ref
+ hooksmgLibraryManager's loadLibrary() call. As would be expected, methods
+ on the LibraryManagerCollection iterate through all specified libraries,
+ calling the corresponding LibraryManager method for each library.
+
+ One point of note is that LibraryManagerCollection operates on an "all
+ or none" principle. When loadLibraries() is called, on exit either all
+ libraries have been successfully opened or none of them have. There
+ is no use-case in BIND 10 where, after a user has specified the shared
+ libraries they want to load, the system will operate with only some of
+ them loaded.
+
+ The LibraryManagerCollection is the place where each library's index is set.
+ Each library is assigned a number ranging from 1 through to the number
+ of libraries being loaded.  As mentioned in the previous section, this
+ index is used to tag callout pointers, something that is discussed
+ in the next section.
+
+ (Whilst on the subject of library index numbers, two additional
+ numbers - 0 and INT_MAX - are also valid as "current library index".
+ For flexibility, the BIND 10 component is able to register its own
+ functions as hook callouts.  It does this by obtaining a suitable @ref
+ hooksmgLibraryHandle from the @ref hooksmgHooksManager.  A choice
+ of two is available: one @ref hooksmgLibraryHandle (with an index
+ of 0) can be used to register a callout on a hook to execute before
+ any user-supplied callouts.  The second (with an index of INT_MAX)
+ is used to register a callout to run after user-specified callouts.
+ Apart from the index number, the hooks framework does not treat these
+ callouts any differently from user-supplied ones.)
+
+ @subsubsection hooksmgCalloutManager Callout Manager
+
+ The @ref isc::hooks::CalloutManager is the core of the framework insofar
+ as the registration and calling of callouts is concerned.
+
+ It maintains a "hook vector" - a vector with one element for
+ each registered hook. Each element in this vector is itself a
+ vector (the callout vector), each element of which is a pair of
+ (library index, callback pointer). When a callout is registered, the
+ CalloutManager's current library index is used to supply the "library
+ index" part of the pair. The library index is set explicitly by the
+ @ref hooksmgLibraryManager prior to calling the user library's load()
+ function (and prior to registering the standard callbacks).
+
+ The situation is slightly more complex when a callout is executing. In
+ order to execute a callout, the CalloutManager's callCallouts()
+ method must be called. This iterates through the callout vector for
+ a hook and for each element in the vector, uses the "library index"
+ part of the pair to set the "current library index" before calling the
+ callout function recorded in the second part of the pair. In most cases,
+ the setting of the library index has no effect on the callout. However,
+ if the callout wishes to dynamically register or deregister a callout,
+ the @ref hooksmgLibraryHandle (see above) calls a method on the
+ @ref hooksmgCalloutManager which in turn uses that information.
+
+ @subsubsection hooksmgHooksManager Hooks Manager
+
+ The @ref isc::hooks::HooksManager is the main object insofar as the
+ server is concerned. It controls the creation of the library-related
+ objects and provides the framework in which they interact. It also
+ provides a shell around objects such as @ref hooksmgServerHooks so that all
+ interaction with the hooks framework by the server is through the
+ HooksManager object.  Apart from this, it supplies no functionality to
+ the hooks framework.
+
+ @section hooksmgOtherIssues Other Issues
+
+ @subsection hooksmgMemoryAllocation Memory Allocation
+
+ Unloading a shared library works by unmapping the part of the process's
+ virtual address space in which the library lies. This may lead to
+ problems if there are still references to that address space elsewhere
+ in the process.
+
+ In many operating systems, heap storage allowed by a shared library
+ will lie in the virtual address allocated to the library. This has
+ implications in the hooks framework because:
+
+ - Argument information stored in a @ref hooksmgCalloutHandle by a
+ callout in a library may lie in the library's address space.
+
+ - Data modified in objects passed as arguments may lie in the address
+ space. For example, it is common for a DHCP callout to add "options"
+ to a packet: the memory allocated for those options will most likely
+ lie in library address space.
+
+ The problem really arises because of the extensive use by BIND 10 of
+ boost smart pointers. When the pointer is destroyed, the pointed-to
+ memory is deallocated. If the pointer points to address space that is
+ unmapped because a library has been unloaded, the deletion causes a
+ segmentation fault.
+
+ The hooks framework addresses the issue for the @ref hooksmgCalloutHandle
+ by keeping in that object a shared pointer to the object controlling
+ library unloading (the @ref hooksmgLibraryManagerCollection). Although
+ the libraries can be unloaded at any time, it is only when every
+ @ref hooksmgCalloutHandle that could possibly reference address space in the
+ library have been deleted that the library will actually be unloaded
+ and the address space unmapped.
+
+ The hooks framework cannot solve the second issue as the objects in
+ question are under control of the BIND 10 server incorporating the
+ hooks. It is up to the server developer to ensure that all such objects
+ have been destroyed before libraries are reloaded. In extreme cases
+ this may mean the server suspending all processing of incoming requests
+ until all currently executing requests have completed and data object
+ destroyed, reloading the libraries, then resuming processing.
+*/
diff --git a/src/lib/hooks/hooks_messages.mes b/src/lib/hooks/hooks_messages.mes
index 68b6e3c..ebaed41 100644
--- a/src/lib/hooks/hooks_messages.mes
+++ b/src/lib/hooks/hooks_messages.mes
@@ -44,7 +44,7 @@ is issued.  It identifies the hook to which the callout is attached, the
 index of the library (in the list of loaded libraries) that registered
 it and the address of the callout.  The error is otherwise ignored.
 
-% HOOKS_CALLOUT_EXCEPTION exception thrown by callout on hook %1 registered by library with index %2 (callout address %3)
+% HOOKS_CALLOUT_EXCEPTION exception thrown by callout on hook %1 registered by library with index %2 (callout address %3): %4
 If a callout throws an exception when called, this error message is
 issued.  It identifies the hook to which the callout is attached, the
 index of the library (in the list of loaded libraries) that registered
diff --git a/src/lib/hooks/hooks_user.dox b/src/lib/hooks/hooks_user.dox
new file mode 100644
index 0000000..8aa75c1
--- /dev/null
+++ b/src/lib/hooks/hooks_user.dox
@@ -0,0 +1,1031 @@
+// 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.
+
+// Note: the prefix "hooksdg" to all labels is an abbreviation for "Hooks
+// Developer's Guide" and is used to prevent a clash with symbols in any
+// other Doxygen file.
+
+/**
+ @page hooksdgDevelopersGuide Hooks Developer's Guide
+
+ @section hooksdgIntroduction 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.
+
+- 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, all such code will be referred
+to as "user 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 code can examine and, if required, modify.
+BIND 10 uses the modified data in the remainder of its processing.
+
+In order to minimise the interaction between BIND 10 and the user
+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 the BIND 10 configuration mechanism means that
+as new versions of BIND 10 are released, there is no need to modify
+the user 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.
+
+ at note Although the defined interface should not change, the internals
+of some of the classes and structures referenced by the user code may
+change between versions of BIND 10.  These changes have to be reflected
+in the compiled version of the software, hence the need for a rebuild.
+
+ at subsection hooksdgLanguages Languages
+
+The core of BIND 10 is written in C++.  While it is the intention to
+provide interfaces into user code written in 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.
+
+ at subsection hooksdgTerminology 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 functions is made. Each hook has a name and
+each hook can have any number (including 0) of user functions
+attached to it.
+
+- Callout - a user 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 function.
+
+- Framework function - the functions that a user library needs to
+supply in order for the hooks framework to 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.
+
+
+ at section hooksdgTutorial 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 however, is to illustrate the main features of
+user hook code not to provide an optimal solution.
+
+
+ at subsection hooksdgFrameworkFunctions Framework Functions
+
+Loading and initializing a library holding user 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 in our example, all three
+are used.
+
+ at subsubsection hooksdgVersionFunction 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 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 header files of the version
+of BIND 10 against which it was built.  The hooks framework checks this
+for compatibility with the running version of BIND 10 before loading
+the library.
+
+In this tutorial, we'll put "version" in its own file, version.cc.  The
+contents are:
+
+ at code
+// version.cc
+
+#include <hooks/hooks.h>
+
+extern "C" {
+
+int version() {
+    return (BIND10_HOOKS_VERSION);
+}
+
+};
+ at endcode
+
+The file "hooks/hooks.h" is specified relative to the BIND 10 libraries
+source directory - this is covered later in the section @ref hooksdgBuild.
+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.
+
+ at subsubsection hooksdgLoadUnloadFunctions 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
+(names that are not hook point names) can be registered:
+this is covered further in the section @ref hooksdgCalloutRegistration.
+
+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:
+
+ at 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 // LIBRARY_COMMON_H
+ at endcode
+
+... and one to hold the "load" and "unload" functions:
+
+ at 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);
+}
+
+};
+ at 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, this being used in
+the registration of functions.  As no functions are being registered
+in this example, the argument specification omits the variable name
+(whilst retaining the type) to avoid an "unused variable" compiler
+warning. (The LibraryHandle and its use is discussed in the section
+ at ref hooksdgLibraryHandle.)
+- 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 code by some other means.
+- "load" must 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 must be 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.
+
+ at subsection hooksdgCallouts 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: server-specific documentation
+describes in detail the points in the server at which the hooks are
+present together with the data passed to callouts attached to them.
+
+Before we continue with the example, we'll discuss how arguments are
+passed to callouts and information is returned to the server.  We will
+also discuss how information can be moved between callouts.
+
+ at subsubsection hooksdgCalloutSignature The Callout Signature
+
+All callouts are declared with the signature:
+ at code
+extern "C" {
+int callout(CalloutHandle& handle);
+};
+ at 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.
+
+ at subsubsection hooksdgArguments 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.
+
+ at 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 callouts attached to the hook
+    ...
+
+    // Retrieve the modified values
+    handle.getArgument("data_count", count);
+    handle.getArgument("inpacket", pktptr);
+ at endcode
+
+In the callout
+
+ at 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);
+ at 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 is 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.
+
+ at subsubsection hooksdgSkipFlag The "Skip" Flag
+
+When a to callouts attached to a hook returns, the server will usually continue
+its processing.  However, a callout might have done something that means that
+the server should follow another path.  Possible actions a server could take
+include:
+
+- Skip the next stage of processing because the callout has already
+done it.  For example, a hook is located just before the DHCP server
+allocates an address to the client.  A callout may decide to allocate
+special addresses for certain clients, in which case it needs to tell
+the server not to allocate an address in this case.
+- Drop the packet and continue with the next request. A possible scenario
+is a DNS server where a callout inspects the source address of an incoming
+packet and compares it against a black list; if the address is on it,
+the callout notifies the server to drop the packet.
+
+To handle these common cases, the CalloutHandle has a "skip" flag.
+This is set by a callout when it wishes the server to skip normal
+processing.  It is set false by the hooks framework before callouts on a
+hook are called.  If the flag is set on return, the server will take the
+"skip" action relevant for the hook.
+
+The methods to get and set the "skip" flag are getSkip and setSkip. Their
+usage is intuitive:
+
+ at code
+    // Get the current setting of the skip flag.
+    bool skip = handle.getSkip();
+
+    // Do some processing...
+        :
+    if (lease_allocated) {
+        // Flag the server to skip the next step of the processing as we
+        // already have an address.
+        handle.setSkip(true);
+    }
+    return;
+    
+ at endcode
+
+Like arguments, the "skip" flag is passed to all callouts on a hook.  Callouts
+later in the list are able to examine (and modify) the settings of earlier ones.
+
+ at subsubsection hooksdgCalloutContext Per-Request Context
+
+Although many of the BIND 10 modules can be characterised as handling
+singles 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 can effectively
+attach data to a request that 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.
+
+The example in the next section illustrates their use.
+
+ at subsection hooksdgExampleCallouts 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 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
+hooksdgCalloutRegistration).  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:
+
+ at 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);
+};
+ at 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:
+
+ at 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.  Note that the argument list for this hook also
+        // contains a pointer to the query: we don't need to access that in this
+        // example.
+        Pkt4Ptr 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);
+}
+
+};
+ at endcode
+
+ at subsection hooksdgBuild 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:
+
+ at 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
+ at 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.
+
+ at subsection hooksdgConfiguration 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:
+
+ at code
+> config add Dhcp4/hook_libraries
+> config set Dhcp4/hook_libraries[0] "/usr/local/lib/example.so"
+> config commit
+ at endcode
+
+The DHCPv4 server will load the library and execute the callouts each time a
+request is received.
+
+ at section hooksdgAdvancedTopics Advanced Topics
+
+ at subsection hooksdgContextCreateDestroy 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.
+
+These approaches are illustrated in the following examples.
+Here it is assumed that the hooks library is performing some form of
+security checking on the packet and needs to maintain information in
+a user-specified "SecurityInformation" object. (The details of this
+fictitious object are of no concern here.) The object is created in
+the context_create callout and used in both the pkt4_rcvd and the
+v4_lease_write_post callouts.
+
+ at code
+// Storing information in a "raw" pointer.  Assume that the
+
+#include <hooks/hooks.h>
+   :
+
+extern "C" {
+
+// context_create callout - called when the request is created.
+int context_create(CalloutHandle& handle) {
+    // Create the security information and store it in the context
+    // for this packet.
+    SecurityInformation* si = new SecurityInformation();
+    handle.setContext("security_information", si);
+}
+
+// Callouts that use the context
+int pktv_rcvd(CalloutHandle& handle) {
+    // Retrieve the pointer to the SecurityInformation object
+    SecurityInformation si;
+    handle.getContext("security_information", si);
+        :
+        :
+    // Set the security information
+    si->setSomething(...);
+
+    // The pointed-to information has been updated but the pointer has not been
+    // altered, so there is no need to call setContext() again.
+}
+
+int v4_lease_write_post(CalloutHandle& handle) {
+    // Retrieve the pointer to the SecurityInformation object
+    SecurityInformation si;
+    handle.getContext("security_information", si);
+        :
+        :
+    // Retrieve security information
+    bool active = si->getSomething(...);
+        :
+}
+
+// Context destruction.  We need to delete the pointed-to SecurityInformation
+// object because we will lose the pointer to it when the CalloutHandle is
+// destroyed.
+int context_destroy(CalloutHandle& handle) {
+    // Retrieve the pointer to the SecurityInformation object
+    SecurityInformation si;
+    handle.getContext("security_information", si);
+
+    // Delete the pointed-to memory.
+    delete si;
+}
+ at endcode
+
+The requirement for the context_destroy callout can be eliminated if
+a Boost shared ptr is used to point to the allocated memory:
+
+ at code
+// Storing information in a "raw" pointer.  Assume that the
+
+#include <hooks/hooks.h>
+#include <boost/shared_ptr.hpp>
+   :
+
+extern "C" {
+
+// context_create callout - called when the request is created.
+
+int context_create(CalloutHandle& handle) {
+    // Create the security information and store it in the context for this
+    // packet.
+    boost::shared_ptr<SecurityInformation> si(new SecurityInformation());
+    handle.setContext("security_information", si);
+}
+
+// Other than the data type, a shared pointer has similar semantics to a "raw"
+// pointer.  Only the code from pkt_rcvd is shown here.
+
+int pktv_rcvd(CalloutHandle& handle) {
+    // Retrieve the pointer to the SecurityInformation object
+    boost::shared_ptr<SecurityInformation> si;
+    handle.setContext("security_information", si);
+        :
+        :
+    // Modify the security information
+    si->setSomething(...);
+
+    // The pointed-to information has been updated but the pointer has not
+    // altered, so theree is no need to reset the context.
+}
+
+// No context_destroy callout is needed to delete the allocated
+// SecurityInformation object.  When the CalloutHandle is destroyed, the shared
+// pointer object will be destroyed.  If that is the last shared pointer to the
+// allocated memory, then it too will be deleted.
+ at endcode
+
+(Note that a Boost shared pointer - rather than any other Boost smart pointer -
+should be used, as the pointer objects are copied within the hooks framework and
+only shared pointers have the correct behavior for the copy operation.)
+
+
+ at subsection hooksdgCalloutRegistration Registering Callouts
+
+As briefly mentioned in @ref hooksdgExampleCallouts, 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 convention 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 convention 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 the appropriate 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.
+
+ at subsubsection hooksdgLibraryHandle 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.
+
+ at subsubsection hooksdgNonstandardCalloutNames 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 our 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:
+
+ at 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);
+}
+ at 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.
+
+ at subsubsection hooksdgMultipleCallouts 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.
+
+ at code
+    libhandle.registerCallout("pkt_rcvd", classify);
+    libhandle.registerCallout("pkt_rcvd", write_data);
+ at 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, "skip" flag, or per-request
+context by the first is visible to the second.
+
+ at subsubsection hooksdgDynamicRegistration 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 can 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:
+
+ at code
+// pkt_rcvd.cc
+//      :
+
+int pkt_rcvd(CalloutHandle& handle) {
+
+            :
+            :
+
+    // 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());
+
+        // Register the callback to log the data.
+        handle.getLibraryHandle().registerCallout("v4_lease_write", write_data);
+    }
+
+    return (0);
+};
+ at endcode
+
+ at 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);
+}
+ at endcode
+
+Note that the above example used a non-standard name for the callout
+that wrote 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.)
+
+
+ at 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 hook's callouts, A and D will run (in that order).
+
+ at subsection hooksdgMultipleLibraries Multiple User Libraries
+
+As alluded to in the section @ref hooksdgConfiguration, 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.
+
+ at 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.
+
+ at 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.
+
+ at subsection hooksdgInterLibraryData 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.
+
+
+ at subsection hooksdgRegisterMultipleLibraries 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".
+
+*/
diff --git a/src/lib/hooks/images/HooksUml.dia b/src/lib/hooks/images/HooksUml.dia
new file mode 100644
index 0000000..0972fca
Binary files /dev/null and b/src/lib/hooks/images/HooksUml.dia differ
diff --git a/src/lib/hooks/images/HooksUml.png b/src/lib/hooks/images/HooksUml.png
new file mode 100644
index 0000000..3859e6a
Binary files /dev/null and b/src/lib/hooks/images/HooksUml.png differ
diff --git a/tests/tools/perfdhcp/perf_pkt4.cc b/tests/tools/perfdhcp/perf_pkt4.cc
index 8b7e974..05d1c6c 100644
--- a/tests/tools/perfdhcp/perf_pkt4.cc
+++ b/tests/tools/perfdhcp/perf_pkt4.cc
@@ -40,7 +40,7 @@ PerfPkt4::rawPack() {
                                options_,
                                getTransidOffset(),
                                getTransid(),
-                               bufferOut_));
+                               buffer_out_));
 }
 
 bool



More information about the bind10-changes mailing list