]> git.ipfire.org Git - thirdparty/kea.git/commitdiff
[#2548] expire lease on release if affinity is enabled
authorRazvan Becheriu <razvan@isc.org>
Mon, 10 Oct 2022 15:19:18 +0000 (18:19 +0300)
committerRazvan Becheriu <razvan@isc.org>
Tue, 25 Oct 2022 07:05:53 +0000 (10:05 +0300)
20 files changed:
src/bin/dhcp4/dhcp4_messages.cc
src/bin/dhcp4/dhcp4_messages.h
src/bin/dhcp4/dhcp4_messages.mes
src/bin/dhcp4/dhcp4_srv.cc
src/bin/dhcp4/tests/dhcp4_test_utils.cc
src/bin/dhcp4/tests/dhcp4_test_utils.h
src/bin/dhcp4/tests/fqdn_unittest.cc
src/bin/dhcp4/tests/hooks_unittest.cc
src/bin/dhcp4/tests/out_of_range_unittest.cc
src/bin/dhcp4/tests/release_unittest.cc
src/bin/dhcp4/tests/shared_network_unittest.cc
src/bin/dhcp6/dhcp6_messages.cc
src/bin/dhcp6/dhcp6_messages.h
src/bin/dhcp6/dhcp6_messages.mes
src/bin/dhcp6/dhcp6_srv.cc
src/bin/dhcp6/tests/dhcp6_srv_unittest.cc
src/bin/dhcp6/tests/dhcp6_test_utils.cc
src/bin/dhcp6/tests/dhcp6_test_utils.h
src/bin/dhcp6/tests/fqdn_unittest.cc
src/bin/dhcp6/tests/hooks_unittest.cc

index e93a9b0022640203198066a3af87558f003289f7..7f2ebf6a9a8b8f5a90bf2a862c2d8134bbd85fa9 100644 (file)
@@ -140,7 +140,9 @@ extern const isc::log::MessageID DHCP4_POST_ALLOCATION_NAME_UPDATE_FAIL = "DHCP4
 extern const isc::log::MessageID DHCP4_QUERY_DATA = "DHCP4_QUERY_DATA";
 extern const isc::log::MessageID DHCP4_RECLAIM_EXPIRED_LEASES_FAIL = "DHCP4_RECLAIM_EXPIRED_LEASES_FAIL";
 extern const isc::log::MessageID DHCP4_RELEASE = "DHCP4_RELEASE";
+extern const isc::log::MessageID DHCP4_RELEASE_DELETED = "DHCP4_RELEASE_DELETED";
 extern const isc::log::MessageID DHCP4_RELEASE_EXCEPTION = "DHCP4_RELEASE_EXCEPTION";
+extern const isc::log::MessageID DHCP4_RELEASE_EXPIRED = "DHCP4_RELEASE_EXPIRED";
 extern const isc::log::MessageID DHCP4_RELEASE_FAIL = "DHCP4_RELEASE_FAIL";
 extern const isc::log::MessageID DHCP4_RELEASE_FAIL_NO_LEASE = "DHCP4_RELEASE_FAIL_NO_LEASE";
 extern const isc::log::MessageID DHCP4_RELEASE_FAIL_WRONG_CLIENT = "DHCP4_RELEASE_FAIL_WRONG_CLIENT";
@@ -307,7 +309,9 @@ const char* values[] = {
     "DHCP4_QUERY_DATA", "%1, packet details: %2",
     "DHCP4_RECLAIM_EXPIRED_LEASES_FAIL", "failed to reclaim expired leases: %1",
     "DHCP4_RELEASE", "%1: address %2 was released properly.",
+    "DHCP4_RELEASE_DELETED", "%1: address %2 was properly deleted on release.",
     "DHCP4_RELEASE_EXCEPTION", "%1: while trying to release address %2 an exception occurred: %3",
+    "DHCP4_RELEASE_EXPIRED", "%1: address %2 was properly expired on release.",
     "DHCP4_RELEASE_FAIL", "%1: failed to remove lease for address %2",
     "DHCP4_RELEASE_FAIL_NO_LEASE", "%1: client is trying to release non-existing lease %2",
     "DHCP4_RELEASE_FAIL_WRONG_CLIENT", "%1: client is trying to release the lease %2 which belongs to a different client",
index 1ff14ac5e07542fbf2b44dd39b6202b42342e98c..b5ead0792d7307c1750d82d71b40f7fdb4db42f5 100644 (file)
@@ -141,7 +141,9 @@ extern const isc::log::MessageID DHCP4_POST_ALLOCATION_NAME_UPDATE_FAIL;
 extern const isc::log::MessageID DHCP4_QUERY_DATA;
 extern const isc::log::MessageID DHCP4_RECLAIM_EXPIRED_LEASES_FAIL;
 extern const isc::log::MessageID DHCP4_RELEASE;
+extern const isc::log::MessageID DHCP4_RELEASE_DELETED;
 extern const isc::log::MessageID DHCP4_RELEASE_EXCEPTION;
+extern const isc::log::MessageID DHCP4_RELEASE_EXPIRED;
 extern const isc::log::MessageID DHCP4_RELEASE_FAIL;
 extern const isc::log::MessageID DHCP4_RELEASE_FAIL_NO_LEASE;
 extern const isc::log::MessageID DHCP4_RELEASE_FAIL_WRONG_CLIENT;
index a5afe6e568e3226da93ea22a073064030daecc22..742d4fcd2366b9383831f4498ed6fb2d8a99636b 100644 (file)
@@ -791,6 +791,18 @@ is a normal operation during client shutdown. The first argument includes
 the client and transaction identification information. The second argument
 includes the released IPv4 address.
 
+% DHCP4_RELEASE_EXPIRED %1: address %2 was properly expired on release.
+This informational message indicates that an address was properly expired on
+release. It is a normal operation during client shutdown. The first argument
+includes the client and transaction identification information. The second
+argument includes the released IPv4 address.
+
+% DHCP4_RELEASE_DELETED %1: address %2 was properly deleted on release.
+This informational message indicates that an address was properly deleted on
+release. It is a normal operation during client shutdown. The first argument
+includes the client and transaction identification information. The second
+argument includes the released IPv4 address.
+
 % DHCP4_RELEASE_EXCEPTION %1: while trying to release address %2 an exception occurred: %3
 This message is output when an error was encountered during an attempt
 to process a DHCPRELEASE message. The error will not affect the client,
index df0b1000474bd0fc96bd6f97e2d66b228bcc8d38..bb29c8fe492c993d8c09388c3f994f637596c041 100644 (file)
@@ -3427,12 +3427,26 @@ Dhcpv4Srv::processRelease(Pkt4Ptr& release, AllocEngine::ClientContext4Ptr& cont
         }
 
         // Callout didn't indicate to skip the release process. Let's release
-        // the lease.
+        // the address.
         if (!skip) {
-            bool success = LeaseMgrFactory::instance().deleteLease(lease);
+            // Ok, we've passed all checks. Let's release this address.
+            bool success = false; // was the removal operation successful?
+            bool expired = false; // explicitly expired instead of removed?
+            auto expiration_cfg = CfgMgr::instance().getCurrentCfg()->getCfgExpiration();
+
+            // Delete lease only if affinity is disabled.
+            if (expiration_cfg->getFlushReclaimedTimerWaitTime() &&
+                expiration_cfg->getHoldReclaimedTime()) {
+                // Expire the lease.
+                lease->cltt_ -= lease->valid_lft_ + 1;
+                LeaseMgrFactory::instance().updateLease4(lease);
+                expired = true;
+                success = true;
+            } else {
+                success = LeaseMgrFactory::instance().deleteLease(lease);
+            }
 
             if (success) {
-
                 context.reset(new AllocEngine::ClientContext4());
                 context->old_lease_ = lease;
 
@@ -3441,14 +3455,23 @@ Dhcpv4Srv::processRelease(Pkt4Ptr& release, AllocEngine::ClientContext4Ptr& cont
                     .arg(release->getLabel())
                     .arg(lease->addr_.toText());
 
-                // Need to decrease statistic for assigned addresses.
-                StatsMgr::instance().addValue(
-                    StatsMgr::generateName("subnet", lease->subnet_id_, "assigned-addresses"),
-                    static_cast<int64_t>(-1));
+                if (expired) {
+                    LOG_INFO(lease4_logger, DHCP4_RELEASE_EXPIRED)
+                                            .arg(release->getLabel())
+                                            .arg(lease->addr_.toText());
+                } else {
+                    LOG_INFO(lease4_logger, DHCP4_RELEASE_DELETED)
+                        .arg(release->getLabel())
+                        .arg(lease->addr_.toText());
 
-                // Remove existing DNS entries for the lease, if any.
-                queueNCR(CHG_REMOVE, lease);
+                    // Need to decrease statistic for assigned addresses.
+                    StatsMgr::instance().addValue(
+                        StatsMgr::generateName("subnet", lease->subnet_id_, "assigned-addresses"),
+                        static_cast<int64_t>(-1));
 
+                    // Remove existing DNS entries for the lease, if any.
+                    queueNCR(CHG_REMOVE, lease);
+                }
             } else {
                 // Release failed
                 LOG_ERROR(lease4_logger, DHCP4_RELEASE_FAIL)
index 6916da5e780e4588be44c3f4faacd10c5c0cfbc7..91f9d42521c3c160fa2bd4f1741b78244e569fc5 100644 (file)
@@ -802,8 +802,9 @@ Dhcpv4SrvTest::configure(const std::string& config,
                          const bool commit,
                          const bool open_sockets,
                          const bool create_managers,
-                         const bool test) {
-    configure(config, srv_, commit, open_sockets, create_managers, test);
+                         const bool test,
+                         const bool disable_affinity) {
+    configure(config, srv_, commit, open_sockets, create_managers, test, disable_affinity);
 }
 
 void
@@ -812,7 +813,8 @@ Dhcpv4SrvTest::configure(const std::string& config,
                          const bool commit,
                          const bool open_sockets,
                          const bool create_managers,
-                         const bool test) {
+                         const bool test,
+                         const bool disable_affinity) {
     setenv("KEA_LFC_EXECUTABLE", KEA_LFC_EXECUTABLE, 1);
     MultiThreadingCriticalSection cs;
     ConstElementPtr json;
@@ -850,6 +852,12 @@ Dhcpv4SrvTest::configure(const std::string& config,
         } );
     }
 
+    if (disable_affinity) {
+        auto expiration_cfg = CfgMgr::instance().getStagingCfg()->getCfgExpiration();
+        expiration_cfg->setFlushReclaimedTimerWaitTime(0);
+        expiration_cfg->setHoldReclaimedTime(0);
+    }
+
     try {
         CfgMultiThreading::apply(CfgMgr::instance().getStagingCfg()->getDHCPMultiThreading());
     } catch (const std::exception& ex) {
index 17a3d5a75dfb0b1f167032da03e165bfdd2d32aa..5436a67f9e348c927f5553a9432dfab191fb5fc0 100644 (file)
@@ -559,11 +559,14 @@ public:
     /// @param create_managers A boolean flag indicating if managers should be
     /// recreated.
     /// @param test A boolean flag which indicates if only testing config.
+    /// @param disable_affinity A boolean flag which indicates if lease affinity
+    /// should be disabled.
     void configure(const std::string& config,
                    const bool commit = true,
                    const bool open_sockets = true,
                    const bool create_managers = true,
-                   const bool test = false);
+                   const bool test = false,
+                   const bool disable_affinity = true);
 
     /// @brief Configure specified DHCP server using JSON string.
     ///
@@ -576,12 +579,15 @@ public:
     /// @param create_managers A boolean flag indicating if managers should be
     /// recreated.
     /// @param test A boolean flag which indicates if only testing config.
+    /// @param disable_affinity A boolean flag which indicates if lease affinity
+    /// should be disabled.
     void configure(const std::string& config,
                    NakedDhcpv4Srv& srv,
                    const bool commit = true,
                    const bool open_sockets = true,
                    const bool create_managers = true,
-                   const bool test = false);
+                   const bool test = false,
+                   const bool disable_affinity = true);
 
     /// @brief Configure specified DHCP server using JSON string.
     ///
index 2a275c6041177f4c534587e146860a2bea919fbd..2e3ce3e24a561d995d177f293c885c2872c9fe07 100644 (file)
@@ -1520,11 +1520,14 @@ TEST_F(NameDhcpv4SrvTest, processRequestRenewHostname) {
 
 // Test that when a release message is sent for a previously acquired lease,
 // DDNS updates are enabled that the server generates a NameChangeRequest
-// to remove entries corresponding to the released lease.
+// to remove entries corresponding to the released (deleted) lease.
 TEST_F(NameDhcpv4SrvTest, processRequestRelease) {
     IfaceMgrTestConfig test_config(true);
     IfaceMgr::instance().openSockets4();
 
+    CfgMgr::instance().getCurrentCfg()->getCfgExpiration()->setFlushReclaimedTimerWaitTime(0);
+    CfgMgr::instance().getCurrentCfg()->getCfgExpiration()->setHoldReclaimedTime(0);
+
     // Verify the updates are enabled.
     ASSERT_TRUE(CfgMgr::instance().ddnsEnabled());
 
@@ -1563,6 +1566,46 @@ TEST_F(NameDhcpv4SrvTest, processRequestRelease) {
                             time(NULL), subnet_->getValid(), true);
 }
 
+// Test that when a release message is sent for a previously acquired lease,
+// DDNS updates are enabled that the server does not generate a NameChangeRequest
+// to remove entries corresponding to the released (expired) lease.
+TEST_F(NameDhcpv4SrvTest, processRequestReleaseNoDelete) {
+    IfaceMgrTestConfig test_config(true);
+    IfaceMgr::instance().openSockets4();
+
+    // Verify the updates are enabled.
+    ASSERT_TRUE(CfgMgr::instance().ddnsEnabled());
+
+    // Create and process a lease request so we have a lease to release.
+    Pkt4Ptr req = generatePktWithFqdn(DHCPREQUEST, Option4ClientFqdn::FLAG_S |
+                                      Option4ClientFqdn::FLAG_E,
+                                      "myhost.example.com.",
+                                      Option4ClientFqdn::FULL, true);
+    Pkt4Ptr reply;
+    ASSERT_NO_THROW(reply = srv_->processRequest(req));
+    checkResponse(reply, DHCPACK, 1234);
+
+    // Verify that there is one NameChangeRequest generated for lease.
+    ASSERT_EQ(1, d2_mgr_.getQueueSize());
+    verifyNameChangeRequest(isc::dhcp_ddns::CHG_ADD, true, true,
+                            reply->getYiaddr().toText(), "myhost.example.com.",
+                            "00010132E91AA355CFBB753C0F0497A5A940436"
+                            "965B68B6D438D98E680BF10B09F3BCF",
+                            time(NULL), subnet_->getValid(), true);
+
+    // Create and process the Release message.
+    Pkt4Ptr rel = Pkt4Ptr(new Pkt4(DHCPRELEASE, 1234));
+    rel->setCiaddr(reply->getYiaddr());
+    rel->setRemoteAddr(IOAddress("192.0.2.3"));
+    rel->addOption(generateClientId());
+    rel->addOption(srv_->getServerID());
+    ASSERT_NO_THROW(srv_->processRelease(rel));
+
+    // The lease has been expired, so there should not be a NameChangeRequest to
+    // remove corresponding DNS entries.
+    ASSERT_EQ(0, d2_mgr_.getQueueSize());
+}
+
 // Test that when the Release message is sent for a previously acquired lease
 // and DDNS updates are disabled that server does NOT generate a
 // NameChangeRequest to remove entries corresponding to the released lease.
index 84d37f8d97725cd98b10ab6d8ad0b6cecd5b6380..4c092e671653ff314a1a2cd910a9273a6ef5b66e 100644 (file)
@@ -6,76 +6,94 @@
 
 #include <config.h>
 
-#include <dhcp4/tests/dhcp4_test_utils.h>
-#include <dhcp4/ctrl_dhcp4_srv.h>
-#include <dhcp4/json_config_parser.h>
+#include <asiolink/io_address.h>
 #include <asiolink/io_service.h>
 #include <cc/command_interpreter.h>
 #include <config/command_mgr.h>
-#include <hooks/server_hooks.h>
-#include <hooks/hooks_manager.h>
-#include <hooks/callout_manager.h>
-#include <dhcpsrv/cfgmgr.h>
 #include <dhcp/tests/iface_mgr_test_config.h>
 #include <dhcp/option.h>
-#include <asiolink/io_address.h>
+#include <dhcpsrv/cfgmgr.h>
+#include <dhcp4/ctrl_dhcp4_srv.h>
+#include <dhcp4/json_config_parser.h>
 #include <dhcp4/tests/dhcp4_client.h>
+#include <dhcp4/tests/dhcp4_test_utils.h>
 #include <dhcp4/tests/marker_file.h>
 #include <dhcp4/tests/test_libraries.h>
+#include <hooks/server_hooks.h>
+#include <hooks/hooks_manager.h>
+#include <hooks/callout_manager.h>
 #include <stats/stats_mgr.h>
 #include <util/multi_threading_mgr.h>
 
 #include <vector>
 
-using namespace std;
+
 using namespace isc::asiolink;
-using namespace isc::data;
-using namespace isc::hooks;
 using namespace isc::config;
-using namespace isc::dhcp::test;
+using namespace isc::data;
 using namespace isc::dhcp;
-using namespace isc::util;
+using namespace isc::dhcp::test;
+using namespace isc::hooks;
 using namespace isc::stats;
+using namespace isc::util;
+
+using namespace std;
 
-// Checks if hooks are registered properly.
+// namespace has to be named, because friends are defined in Dhcpv6Srv class
+// Maybe it should be isc::test?
+namespace {
+
+// Checks if hooks are implemented properly.
 TEST_F(Dhcpv4SrvTest, Hooks) {
     NakedDhcpv4Srv srv(0);
 
     // check if appropriate hooks are registered
-    int hook_index_buffer4_receive   = -1;
-    int hook_index_pkt4_receive      = -1;
-    int hook_index_select_subnet     = -1;
+    int hook_index_dhcp4_srv_configured = -1;
+    int hook_index_buffer4_receive = -1;
+    int hook_index_buffer4_send = -1;
+    int hook_index_lease4_renew = -1;
+    int hook_index_lease4_release = -1;
+    int hook_index_lease4_decline = -1;
+    int hook_index_pkt4_receive = -1;
+    int hook_index_pkt4_send = -1;
+    int hook_index_select_subnet = -1;
     int hook_index_leases4_committed = -1;
-    int hook_index_lease4_release    = -1;
-    int hook_index_pkt4_send         = -1;
-    int hook_index_buffer4_send      = -1;
-    int hook_index_host4_identifier  = -1;
+    int hook_index_host4_identifier = -1;
 
     // check if appropriate indexes are set
+    EXPECT_NO_THROW(hook_index_dhcp4_srv_configured = ServerHooks::getServerHooks()
+                    .getIndex("dhcp4_srv_configured"));
     EXPECT_NO_THROW(hook_index_buffer4_receive = ServerHooks::getServerHooks()
                     .getIndex("buffer4_receive"));
+    EXPECT_NO_THROW(hook_index_buffer4_send = ServerHooks::getServerHooks()
+                    .getIndex("buffer4_send"));
+    EXPECT_NO_THROW(hook_index_lease4_renew = ServerHooks::getServerHooks()
+                    .getIndex("lease4_renew"));
+    EXPECT_NO_THROW(hook_index_lease4_release = ServerHooks::getServerHooks()
+                    .getIndex("lease4_release"));
+    EXPECT_NO_THROW(hook_index_lease4_decline = ServerHooks::getServerHooks()
+                    .getIndex("lease4_decline"));
     EXPECT_NO_THROW(hook_index_pkt4_receive = ServerHooks::getServerHooks()
                     .getIndex("pkt4_receive"));
+    EXPECT_NO_THROW(hook_index_pkt4_send = ServerHooks::getServerHooks()
+                    .getIndex("pkt4_send"));
     EXPECT_NO_THROW(hook_index_select_subnet = ServerHooks::getServerHooks()
                     .getIndex("subnet4_select"));
     EXPECT_NO_THROW(hook_index_leases4_committed = ServerHooks::getServerHooks()
                     .getIndex("leases4_committed"));
-    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_NO_THROW(hook_index_host4_identifier = ServerHooks::getServerHooks()
                     .getIndex("host4_identifier"));
 
+    EXPECT_TRUE(hook_index_dhcp4_srv_configured > 0);
     EXPECT_TRUE(hook_index_buffer4_receive > 0);
+    EXPECT_TRUE(hook_index_buffer4_send > 0);
+    EXPECT_TRUE(hook_index_lease4_renew > 0);
+    EXPECT_TRUE(hook_index_lease4_release > 0);
+    EXPECT_TRUE(hook_index_lease4_decline > 0);
     EXPECT_TRUE(hook_index_pkt4_receive > 0);
+    EXPECT_TRUE(hook_index_pkt4_send > 0);
     EXPECT_TRUE(hook_index_select_subnet > 0);
     EXPECT_TRUE(hook_index_leases4_committed > 0);
-    EXPECT_TRUE(hook_index_lease4_release > 0);
-    EXPECT_TRUE(hook_index_pkt4_send > 0);
-    EXPECT_TRUE(hook_index_buffer4_send > 0);
     EXPECT_TRUE(hook_index_host4_identifier > 0);
 }
 
@@ -113,20 +131,23 @@ public:
         }
 
         // Allocate new DHCPv4 Server
-        srv_ = new NakedDhcpv4Srv(0);
+        srv_.reset(new NakedDhcpv4Srv(0));
 
-        // clear static buffers
+        // Clear static buffers
         resetCalloutBuffers();
 
         io_service_ = boost::make_shared<IOService>();
 
+        // Reset the hook system in its original state
+        HooksManager::unloadLibraries();
+
         // Clear statistics.
         StatsMgr::instance().removeAll();
     }
 
     /// @brief destructor (deletes Dhcpv4Srv)
     virtual ~HooksDhcpv4SrvTest() {
-        // clear static buffers
+        // Clear static buffers
         resetCalloutBuffers();
 
         HooksManager::preCalloutsLibraryHandle().deregisterAllCallouts("dhcp4_srv_configured");
@@ -135,12 +156,12 @@ public:
         HooksManager::preCalloutsLibraryHandle().deregisterAllCallouts("pkt4_receive");
         HooksManager::preCalloutsLibraryHandle().deregisterAllCallouts("pkt4_send");
         HooksManager::preCalloutsLibraryHandle().deregisterAllCallouts("subnet4_select");
+        HooksManager::preCalloutsLibraryHandle().deregisterAllCallouts("leases4_committed");
         HooksManager::preCalloutsLibraryHandle().deregisterAllCallouts("lease4_renew");
         HooksManager::preCalloutsLibraryHandle().deregisterAllCallouts("lease4_release");
         HooksManager::preCalloutsLibraryHandle().deregisterAllCallouts("lease4_decline");
         HooksManager::preCalloutsLibraryHandle().deregisterAllCallouts("host4_identifier");
 
-        delete srv_;
         HooksManager::setTestMode(false);
         bool status = HooksManager::unloadLibraries();
         if (!status) {
@@ -161,7 +182,7 @@ public:
     /// @return pointer to create option object
     static OptionPtr createOption(uint16_t option_code) {
 
-        char payload[] = {
+        uint8_t payload[] = {
             0xa, 0xb, 0xc, 0xe, 0xf, 0x10, 0x11, 0x12, 0x13, 0x14
         };
 
@@ -257,6 +278,7 @@ public:
         if (callback_qry_pkt4_) {
             callback_qry_options_copy_ = callback_qry_pkt4_->isCopyRetrievedOptions();
         }
+
         return (0);
     }
 
@@ -280,31 +302,31 @@ public:
         return buffer4_receive_callout(callout_handle);
     }
 
-    /// Test callback that sets drop flag
+    /// Test callback that sets skip flag
     /// @param callout_handle handle passed by the hooks framework
     /// @return always 0
     static int
-    buffer4_receive_drop(CalloutHandle& callout_handle) {
+    buffer4_receive_skip(CalloutHandle& callout_handle) {
 
-        callout_handle.setStatus(CalloutHandle::NEXT_STEP_DROP);
+        callout_handle.setStatus(CalloutHandle::NEXT_STEP_SKIP);
 
         // Carry on as usual
         return buffer4_receive_callout(callout_handle);
     }
 
-    /// Test callback that sets skip flag
+    /// Test callback that sets drop flag
     /// @param callout_handle handle passed by the hooks framework
     /// @return always 0
     static int
-    buffer4_receive_skip(CalloutHandle& callout_handle) {
+    buffer4_receive_drop(CalloutHandle& callout_handle) {
 
-        callout_handle.setStatus(CalloutHandle::NEXT_STEP_SKIP);
+        callout_handle.setStatus(CalloutHandle::NEXT_STEP_DROP);
 
         // Carry on as usual
         return buffer4_receive_callout(callout_handle);
     }
 
-    /// test callback that stores received callout name and pkt4 value
+    /// Test callback that stores received callout name and pkt4 value
     /// @param callout_handle handle passed by the hooks framework
     /// @return always 0
     static int
@@ -322,7 +344,7 @@ public:
         return (0);
     }
 
-    /// test callback that changes client-id value
+    /// Test callback that changes client-id value
     /// @param callout_handle handle passed by the hooks framework
     /// @return always 0
     static int
@@ -331,17 +353,17 @@ public:
         Pkt4Ptr pkt;
         callout_handle.getArgument("query4", pkt);
 
-        // get rid of the old client-id
+        // Get rid of the old client-id
         pkt->delOption(DHO_DHCP_CLIENT_IDENTIFIER);
 
-        // add a new option
+        // Add a new option
         pkt->addOption(createOption(DHO_DHCP_CLIENT_IDENTIFIER));
 
-        // carry on as usual
+        // Carry on as usual
         return pkt4_receive_callout(callout_handle);
     }
 
-    /// test callback that deletes client-id
+    /// Test callback that deletes client-id
     /// @param callout_handle handle passed by the hooks framework
     /// @return always 0
     static int
@@ -350,42 +372,42 @@ public:
         Pkt4Ptr pkt;
         callout_handle.getArgument("query4", pkt);
 
-        // get rid of the old client-id (and no HWADDR)
+        // Get rid of the old client-id (and no HWADDR)
         vector<uint8_t> mac;
         pkt->delOption(DHO_DHCP_CLIENT_IDENTIFIER);
         pkt->setHWAddr(1, 0, mac); // HWtype 1, hardware len = 0
 
-        // carry on as usual
+        // Carry on as usual
         return pkt4_receive_callout(callout_handle);
     }
 
-    /// test callback that sets drop flag
+    /// Test callback that sets skip flag
     /// @param callout_handle handle passed by the hooks framework
     /// @return always 0
     static int
-    pkt4_receive_drop(CalloutHandle& callout_handle) {
+    pkt4_receive_skip(CalloutHandle& callout_handle) {
 
         Pkt4Ptr pkt;
         callout_handle.getArgument("query4", pkt);
 
-        callout_handle.setStatus(CalloutHandle::NEXT_STEP_DROP);
+        callout_handle.setStatus(CalloutHandle::NEXT_STEP_SKIP);
 
-        // carry on as usual
+        // Carry on as usual
         return pkt4_receive_callout(callout_handle);
     }
 
-    /// test callback that sets skip flag
+    /// Test callback that sets drop flag
     /// @param callout_handle handle passed by the hooks framework
     /// @return always 0
     static int
-    pkt4_receive_skip(CalloutHandle& callout_handle) {
+    pkt4_receive_drop(CalloutHandle& callout_handle) {
 
         Pkt4Ptr pkt;
         callout_handle.getArgument("query4", pkt);
 
-        callout_handle.setStatus(CalloutHandle::NEXT_STEP_SKIP);
+        callout_handle.setStatus(CalloutHandle::NEXT_STEP_DROP);
 
-        // carry on as usual
+        // Carry on as usual
         return pkt4_receive_callout(callout_handle);
     }
 
@@ -422,17 +444,17 @@ public:
         Pkt4Ptr pkt;
         callout_handle.getArgument("response4", pkt);
 
-        // get rid of the old server-id
+        // Get rid of the old server-id
         pkt->delOption(DHO_DHCP_SERVER_IDENTIFIER);
 
-        // add a new option
+        // Add a new option
         pkt->addOption(createOption(DHO_DHCP_SERVER_IDENTIFIER));
 
-        // carry on as usual
+        // Carry on as usual
         return pkt4_send_callout(callout_handle);
     }
 
-    /// test callback that deletes server-id
+    /// Test callback that deletes server-id
     /// @param callout_handle handle passed by the hooks framework
     /// @return always 0
     static int
@@ -441,10 +463,10 @@ public:
         Pkt4Ptr pkt;
         callout_handle.getArgument("response4", pkt);
 
-        // get rid of the old client-id
+        // Get rid of the old client-id
         pkt->delOption(DHO_DHCP_SERVER_IDENTIFIER);
 
-        // carry on as usual
+        // Carry on as usual
         return pkt4_send_callout(callout_handle);
     }
 
@@ -478,8 +500,8 @@ 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
+    /// Test callback that stores response packet.
+    /// @param callout_handle handle passed by the hooks framework.
     /// @return always 0
     static int
     buffer4_send_callout(CalloutHandle& callout_handle) {
@@ -512,26 +534,28 @@ public:
         return (0);
     }
 
-    /// Test callback that stores received callout name and pkt4 value
+    /// Test callback that sets skip flag
     /// @param callout_handle handle passed by the hooks framework
     /// @return always 0
     static int
-    skip_callout(CalloutHandle& callout_handle) {
+    buffer4_send_skip(CalloutHandle& callout_handle) {
 
         callout_handle.setStatus(CalloutHandle::NEXT_STEP_SKIP);
 
-        return (0);
+        // Carry on as usual
+        return buffer4_send_callout(callout_handle);
     }
 
-    /// Test callback that stores received callout name and pkt4 value
+    /// Test callback that sets drop flag
     /// @param callout_handle handle passed by the hooks framework
     /// @return always 0
     static int
-    drop_callout(CalloutHandle& callout_handle) {
+    buffer4_send_drop(CalloutHandle& callout_handle) {
 
         callout_handle.setStatus(CalloutHandle::NEXT_STEP_DROP);
 
-        return (0);
+        // carry on as usual
+        return buffer4_send_callout(callout_handle);
     }
 
     /// Test callback that stores received callout name and subnet4 values
@@ -558,7 +582,7 @@ public:
     /// @param callout_handle handle passed by the hooks framework
     /// @return always 0
     static int
-    subnet4_select_different_subnet_callout(CalloutHandle& callout_handle) {
+    subnet4_select_different_subnet(CalloutHandle& callout_handle) {
 
         // Call the basic callout to record all passed values
         subnet4_select_callout(callout_handle);
@@ -577,9 +601,23 @@ public:
         return (0);
     }
 
+    /// Test callback that sets skip flag
+    /// @param callout_handle handle passed by the hooks framework
+    /// @return always 0
+    static int
+    subnet4_select_skip(CalloutHandle& callout_handle) {
+
+        callout_handle.setStatus(CalloutHandle::NEXT_STEP_SKIP);
+
+        // Carry on as usual
+        return subnet4_select_callout(callout_handle);
+    }
+
     /// Test callback that sets drop flag
+    /// @param callout_handle handle passed by the hooks framework
+    /// @return always 0
     static int
-    subnet4_select_drop_callout(CalloutHandle& callout_handle) {
+    subnet4_select_drop(CalloutHandle& callout_handle) {
 
         callout_handle.setStatus(CalloutHandle::NEXT_STEP_DROP);
 
@@ -587,15 +625,18 @@ public:
         return subnet4_select_callout(callout_handle);
     }
 
-    /// Test callback that stores received callout name passed parameters
+    /// 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_release_callout(CalloutHandle& callout_handle) {
-        callback_name_ = string("lease4_release");
+    lease4_renew_callout(CalloutHandle& callout_handle) {
+        callback_name_ = string("lease4_renew");
 
         callout_handle.getArgument("query4", callback_qry_pkt4_);
+        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();
 
@@ -606,18 +647,27 @@ public:
         return (0);
     }
 
-    /// Test callback that stores received callout name and subnet4 values
+    /// Test callback that sets the skip flag
     /// @param callout_handle handle passed by the hooks framework
     /// @return always 0
     static int
-    lease4_renew_callout(CalloutHandle& callout_handle) {
+    lease4_renew_skip_callout(CalloutHandle& callout_handle) {
         callback_name_ = string("lease4_renew");
 
+        callout_handle.setStatus(CalloutHandle::NEXT_STEP_SKIP);
+
+        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_qry_pkt4_);
-        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();
 
@@ -628,6 +678,30 @@ public:
         return (0);
     }
 
+    /// Test callback that sets the skip flag
+    /// @param callout_handle handle passed by the hooks framework
+    /// @return always 0
+    static int
+    lease4_release_skip(CalloutHandle& callout_handle) {
+        callback_name_ = string("lease4_release");
+
+        callout_handle.setStatus(CalloutHandle::NEXT_STEP_SKIP);
+
+        return (0);
+    }
+
+    /// Test callback that sets the drop flag
+    /// @param callout_handle handle passed by the hooks framework
+    /// @return always 0
+    static int
+    lease4_release_drop(CalloutHandle& callout_handle) {
+        callback_name_ = string("lease4_release");
+
+        callout_handle.setStatus(CalloutHandle::NEXT_STEP_DROP);
+
+        return (0);
+    }
+
     /// Test lease4_decline callback that stores received parameters.
     ///
     /// @param callout_handle handle passed by the hooks framework
@@ -650,7 +724,7 @@ public:
     /// @param callout_handle handle passed by the hooks framework
     /// @return always 0
     static int
-    lease4_decline_skip_callout(CalloutHandle& callout_handle) {
+    lease4_decline_skip(CalloutHandle& callout_handle) {
         callout_handle.setStatus(CalloutHandle::NEXT_STEP_SKIP);
 
         return (lease4_decline_callout(callout_handle));
@@ -661,7 +735,7 @@ public:
     /// @param callout_handle handle passed by the hooks framework
     /// @return always 0
     static int
-    lease4_decline_drop_callout(CalloutHandle& callout_handle) {
+    lease4_decline_drop(CalloutHandle& callout_handle) {
         callout_handle.setStatus(CalloutHandle::NEXT_STEP_DROP);
 
         return (lease4_decline_callout(callout_handle));
@@ -671,6 +745,7 @@ public:
     static int
     leases4_committed_callout(CalloutHandle& callout_handle) {
         callback_name_ = string("leases4_committed");
+
         callout_handle.getArgument("query4", callback_qry_pkt4_);
 
         Lease4CollectionPtr leases4;
@@ -736,7 +811,7 @@ public:
         return (0);
     }
 
-    /// @brief Test host4_identifier callout by setting identifier to "foo"
+    /// @brief Test host4_identifier callback by setting identifier to "foo"
     ///
     /// @param callout_handle handle passed by the hooks framework
     /// @return always 0
@@ -755,6 +830,7 @@ public:
         std::vector<uint8_t> id_test;
         handle.getArgument("id_value", id_test);
 
+        // Ok, now set the identifier.
         std::vector<uint8_t> id = { 0x66, 0x6f, 0x6f };  // foo
         handle.setArgument("id_value", id);
         handle.setArgument("id_type", Host::IDENT_FLEX);
@@ -791,17 +867,16 @@ public:
         return (0);
     }
 
-
-    /// resets buffers used to store data received by callouts
+    /// Resets buffers used to store data received by callouts
     void resetCalloutBuffers() {
         callback_name_ = string("");
         callback_qry_pkt4_.reset();
-        callback_qry_pkt4_.reset();
+        callback_resp_pkt4_.reset();
+        callback_subnet4_.reset();
         callback_lease4_.reset();
         callback_deleted_lease4_.reset();
         callback_hwaddr_.reset();
         callback_clientid_.reset();
-        callback_subnet4_.reset();
         callback_subnet4collection_ = NULL;
         callback_argument_names_.clear();
         callback_qry_options_copy_ = false;
@@ -821,8 +896,8 @@ public:
         return (stat->getInteger().first);
     }
 
-    /// pointer to Dhcpv4Srv that is used in tests
-    NakedDhcpv4Srv* srv_;
+    /// Pointer to Dhcpv4Srv that is used in tests
+    boost::shared_ptr<NakedDhcpv4Srv> srv_;
 
     /// Pointer to the IO service used in the tests.
     static IOServicePtr io_service_;
@@ -832,16 +907,16 @@ public:
     /// String name of the received callout
     static string callback_name_;
 
-    /// Client/query Pkt4 structure returned in the callout
+    /// Client's query Pkt4 structure returned in the callout
     static Pkt4Ptr callback_qry_pkt4_;
 
-    /// Server/response Pkt4 structure returned in the callout
+    /// Server's response Pkt4 structure returned in the callout
     static Pkt4Ptr callback_resp_pkt4_;
 
-    /// Lease4 structure returned in the leases4_committed callout
+    /// Pointer to lease4 structure returned in the leases4_committed callout
     static Lease4Ptr callback_lease4_;
 
-    /// Lease4 structure returned in the leases4_committed callout
+    /// Pointer to lease4 structure returned in the leases4_committed callout
     static Lease4Ptr callback_deleted_lease4_;
 
     /// Hardware address returned in the callout
@@ -866,7 +941,6 @@ public:
     /// Flag indicating if copying retrieved options was enabled for
     /// a response during callout execution.
     static bool callback_resp_options_copy_;
-
 };
 
 // The following fields are used in testing pkt4_receive_callout.
@@ -876,11 +950,11 @@ string HooksDhcpv4SrvTest::callback_name_;
 Pkt4Ptr HooksDhcpv4SrvTest::callback_qry_pkt4_;
 Pkt4Ptr HooksDhcpv4SrvTest::callback_resp_pkt4_;
 Subnet4Ptr HooksDhcpv4SrvTest::callback_subnet4_;
+const Subnet4Collection* HooksDhcpv4SrvTest::callback_subnet4collection_;
 HWAddrPtr HooksDhcpv4SrvTest::callback_hwaddr_;
 ClientIdPtr HooksDhcpv4SrvTest::callback_clientid_;
 Lease4Ptr HooksDhcpv4SrvTest::callback_lease4_;
 Lease4Ptr HooksDhcpv4SrvTest::callback_deleted_lease4_;
-const Subnet4Collection* HooksDhcpv4SrvTest::callback_subnet4collection_;
 vector<string> HooksDhcpv4SrvTest::callback_argument_names_;
 bool HooksDhcpv4SrvTest::callback_qry_options_copy_;
 bool HooksDhcpv4SrvTest::callback_resp_options_copy_;
@@ -909,35 +983,35 @@ public:
     /// that no libraries are loaded and that any marker files are deleted.
     void reset() {
         // Unload any previously-loaded libraries.
-        HooksManager::unloadLibraries();
+        EXPECT_TRUE(HooksManager::unloadLibraries());
 
         // Get rid of any marker files.
         static_cast<void>(remove(LOAD_MARKER_FILE));
         static_cast<void>(remove(UNLOAD_MARKER_FILE));
         static_cast<void>(remove(SRV_CONFIG_MARKER_FILE));
+
         CfgMgr::instance().clear();
     }
 };
 
-
-// Checks if callouts installed on pkt4_receive are indeed called and the
+// Checks if callouts installed on buffer4_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) {
+TEST_F(HooksDhcpv4SrvTest, buffer4ReceiveSimple) {
     IfaceMgrTestConfig test_config(true);
     IfaceMgr::instance().openSockets4();
 
-    // Install pkt4_receive_callout
+    // Install buffer4_receive_callout
     EXPECT_NO_THROW(HooksManager::preCalloutsLibraryHandle().registerCallout(
                         "buffer4_receive", buffer4_receive_callout));
 
     // Let's create a simple DISCOVER
-    Pkt4Ptr dis = generateSimpleDiscover();
+    Pkt4Ptr discover = generateSimpleDiscover();
 
     // Simulate that we have received that traffic
-    srv_->fakeReceive(dis);
+    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
@@ -949,7 +1023,7 @@ TEST_F(HooksDhcpv4SrvTest, Buffer4ReceiveSimple) {
     EXPECT_EQ("buffer4_receive", callback_name_);
 
     // Check that pkt4 argument passing was successful and returned proper value
-    EXPECT_TRUE(callback_qry_pkt4_.get() == dis.get());
+    EXPECT_TRUE(callback_qry_pkt4_.get() == discover.get());
 
     // Check that all expected parameters are there
     vector<string> expected_argument_names;
@@ -961,7 +1035,7 @@ TEST_F(HooksDhcpv4SrvTest, Buffer4ReceiveSimple) {
     EXPECT_TRUE(callback_qry_options_copy_);
 
     // Check if the callout handle state was reset after the callout.
-    checkCalloutHandleReset(dis);
+    checkCalloutHandleReset(discover);
 }
 
 // Checks if callouts installed on buffer4_receive is able to change
@@ -1007,14 +1081,12 @@ TEST_F(HooksDhcpv4SrvTest, buffer4ReceiveValueChange) {
 }
 
 // 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.
+// will cause the server to not process the packet (drop), even though it is valid.
 TEST_F(HooksDhcpv4SrvTest, buffer4ReceiveSkip) {
     IfaceMgrTestConfig test_config(true);
     IfaceMgr::instance().openSockets4();
 
-    // Install pkt4_receive_callout
+    // Install buffer4_receive_skip
     EXPECT_NO_THROW(HooksManager::preCalloutsLibraryHandle().registerCallout(
                         "buffer4_receive", buffer4_receive_skip));
 
@@ -1027,7 +1099,7 @@ TEST_F(HooksDhcpv4SrvTest, buffer4ReceiveSkip) {
     // 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.
+    // In particular, it should call registered buffer4_receive callback.
     srv_->run();
 
     // Check that the server dropped the packet and did not produce any response
@@ -1038,12 +1110,12 @@ TEST_F(HooksDhcpv4SrvTest, buffer4ReceiveSkip) {
 }
 
 // Checks if callouts installed on buffer4_receive is able to set drop flag that
-// will cause the server to drop the packet.
+// will cause the server to not process the packet (drop), even though it is valid.
 TEST_F(HooksDhcpv4SrvTest, buffer4ReceiveDrop) {
     IfaceMgrTestConfig test_config(true);
     IfaceMgr::instance().openSockets4();
 
-    // Install pkt4_receive_callout
+    // Install buffer4_receive_drop
     EXPECT_NO_THROW(HooksManager::preCalloutsLibraryHandle().registerCallout(
                         "buffer4_receive", buffer4_receive_drop));
 
@@ -1056,7 +1128,7 @@ TEST_F(HooksDhcpv4SrvTest, buffer4ReceiveDrop) {
     // 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.
+    // In particular, it should call registered buffer4_receive callback.
     srv_->run();
 
     // Check that the server dropped the packet and did not produce any response
@@ -1080,10 +1152,10 @@ TEST_F(HooksDhcpv4SrvTest, pkt4ReceiveSimple) {
                         "pkt4_receive", pkt4_receive_callout));
 
     // Let's create a simple DISCOVER
-    Pkt4Ptr sol = generateSimpleDiscover();
+    Pkt4Ptr discover = generateSimpleDiscover();
 
     // Simulate that we have received that traffic
-    srv_->fakeReceive(sol);
+    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
@@ -1091,11 +1163,11 @@ TEST_F(HooksDhcpv4SrvTest, pkt4ReceiveSimple) {
     // In particular, it should call registered pkt4_receive callback.
     srv_->run();
 
-    // check that the callback called is indeed the one we installed
+    // Check that the callback called is indeed the one we installed
     EXPECT_EQ("pkt4_receive", callback_name_);
 
-    // check that pkt4 argument passing was successful and returned proper value
-    EXPECT_TRUE(callback_qry_pkt4_.get() == sol.get());
+    // Check that pkt4 argument passing was successful and returned proper value
+    EXPECT_TRUE(callback_qry_pkt4_.get() == discover.get());
 
     // Check that all expected parameters are there
     vector<string> expected_argument_names;
@@ -1107,12 +1179,12 @@ TEST_F(HooksDhcpv4SrvTest, pkt4ReceiveSimple) {
     EXPECT_TRUE(callback_qry_options_copy_);
 
     // Check if the callout handle state was reset after the callout.
-    checkCalloutHandleReset(sol);
+    checkCalloutHandleReset(discover);
 }
 
 // Checks if callouts installed on pkt4_received is able to change
 // the values and the parameters are indeed used by the server.
-TEST_F(HooksDhcpv4SrvTest, valueChange_pkt4_receive) {
+TEST_F(HooksDhcpv4SrvTest, pkt4ReceiveValueChange) {
     IfaceMgrTestConfig test_config(true);
     IfaceMgr::instance().openSockets4();
 
@@ -1121,10 +1193,10 @@ TEST_F(HooksDhcpv4SrvTest, valueChange_pkt4_receive) {
                         "pkt4_receive", pkt4_receive_change_clientid));
 
     // Let's create a simple DISCOVER
-    Pkt4Ptr sol = generateSimpleDiscover();
+    Pkt4Ptr discover = generateSimpleDiscover();
 
     // Simulate that we have received that traffic
-    srv_->fakeReceive(sol);
+    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
@@ -1132,7 +1204,7 @@ TEST_F(HooksDhcpv4SrvTest, valueChange_pkt4_receive) {
     // In particular, it should call registered pkt4_receive callback.
     srv_->run();
 
-    // check that the server did send a response
+    // Check that the server did send a response
     ASSERT_EQ(1, srv_->fake_sent_.size());
 
     // Make sure that we received a response
@@ -1147,7 +1219,7 @@ TEST_F(HooksDhcpv4SrvTest, valueChange_pkt4_receive) {
     EXPECT_TRUE(clientid->equals(expected));
 
     // Check if the callout handle state was reset after the callout.
-    checkCalloutHandleReset(sol);
+    checkCalloutHandleReset(discover);
 }
 
 // Checks if callouts installed on pkt4_received is able to delete
@@ -1157,15 +1229,15 @@ TEST_F(HooksDhcpv4SrvTest, pkt4ReceiveDeleteClientId) {
     IfaceMgrTestConfig test_config(true);
     IfaceMgr::instance().openSockets4();
 
-    // Install pkt4_receive_callout
+    // Install pkt4_receive_delete_clientid
     EXPECT_NO_THROW(HooksManager::preCalloutsLibraryHandle().registerCallout(
                         "pkt4_receive", pkt4_receive_delete_clientid));
 
     // Let's create a simple DISCOVER
-    Pkt4Ptr sol = generateSimpleDiscover();
+    Pkt4Ptr discover = generateSimpleDiscover();
 
     // Simulate that we have received that traffic
-    srv_->fakeReceive(sol);
+    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
@@ -1177,7 +1249,7 @@ TEST_F(HooksDhcpv4SrvTest, pkt4ReceiveDeleteClientId) {
     ASSERT_EQ(0, srv_->fake_sent_.size());
 
     // Check if the callout handle state was reset after the callout.
-    checkCalloutHandleReset(sol);
+    checkCalloutHandleReset(discover);
 }
 
 // Checks if callouts installed on pkt4_received is able to set skip flag that
@@ -1186,15 +1258,15 @@ TEST_F(HooksDhcpv4SrvTest, pkt4ReceiveSkip) {
     IfaceMgrTestConfig test_config(true);
     IfaceMgr::instance().openSockets4();
 
-    // Install pkt4_receive_callout
+    // Install pkt4_receive_skip
     EXPECT_NO_THROW(HooksManager::preCalloutsLibraryHandle().registerCallout(
                         "pkt4_receive", pkt4_receive_skip));
 
     // Let's create a simple DISCOVER
-    Pkt4Ptr sol = generateSimpleDiscover();
+    Pkt4Ptr discover = generateSimpleDiscover();
 
     // Simulate that we have received that traffic
-    srv_->fakeReceive(sol);
+    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
@@ -1202,11 +1274,11 @@ TEST_F(HooksDhcpv4SrvTest, pkt4ReceiveSkip) {
     // 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 server dropped the packet and did not produce any response
     ASSERT_EQ(0, srv_->fake_sent_.size());
 
     // Check if the callout handle state was reset after the callout.
-    checkCalloutHandleReset(sol);
+    checkCalloutHandleReset(discover);
 }
 
 // Checks if callouts installed on pkt4_received is able to set drop flag that
@@ -1215,15 +1287,15 @@ TEST_F(HooksDhcpv4SrvTest, pkt4ReceiveDrop) {
     IfaceMgrTestConfig test_config(true);
     IfaceMgr::instance().openSockets4();
 
-    // Install pkt4_receive_callout
+    // Install pkt4_receive_drop
     EXPECT_NO_THROW(HooksManager::preCalloutsLibraryHandle().registerCallout(
                         "pkt4_receive", pkt4_receive_drop));
 
     // Let's create a simple DISCOVER
-    Pkt4Ptr sol = generateSimpleDiscover();
+    Pkt4Ptr discover = generateSimpleDiscover();
 
     // Simulate that we have received that traffic
-    srv_->fakeReceive(sol);
+    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
@@ -1231,34 +1303,33 @@ TEST_F(HooksDhcpv4SrvTest, pkt4ReceiveDrop) {
     // 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 server dropped the packet and did not produce any response
     ASSERT_EQ(0, srv_->fake_sent_.size());
 
     // Check if the callout handle state was reset after the callout.
-    checkCalloutHandleReset(sol);
+    checkCalloutHandleReset(discover);
 }
 
-
 // Checks if callouts installed on pkt4_send are indeed called and the
 // all necessary parameters are passed.
 TEST_F(HooksDhcpv4SrvTest, pkt4SendSimple) {
     IfaceMgrTestConfig test_config(true);
     IfaceMgr::instance().openSockets4();
 
-    // Install pkt4_receive_callout
+    // Install pkt4_send_callout
     EXPECT_NO_THROW(HooksManager::preCalloutsLibraryHandle().registerCallout(
                         "pkt4_send", pkt4_send_callout));
 
     // Let's create a simple DISCOVER
-    Pkt4Ptr sol = generateSimpleDiscover();
+    Pkt4Ptr discover = generateSimpleDiscover();
 
     // Simulate that we have received that traffic
-    srv_->fakeReceive(sol);
+    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.
+    // In particular, it should call registered pkt4_send callback.
     srv_->run();
 
     // Check that the callback called is indeed the one we installed
@@ -1268,18 +1339,17 @@ TEST_F(HooksDhcpv4SrvTest, pkt4SendSimple) {
     ASSERT_EQ(1, srv_->fake_sent_.size());
     Pkt4Ptr adv = srv_->fake_sent_.front();
 
-    // Check that pkt4 argument passing was successful and returned proper value
+    // Check that pkt4 argument passing was successful and returned proper
+    // values
+    ASSERT_TRUE(callback_qry_pkt4_);
+    EXPECT_TRUE(callback_qry_pkt4_.get() == discover.get());
     ASSERT_TRUE(callback_resp_pkt4_);
     EXPECT_TRUE(callback_resp_pkt4_.get() == adv.get());
 
-    // That that the query4 argument was correctly set to the Discover we sent.
-    ASSERT_TRUE(callback_qry_pkt4_);
-    EXPECT_TRUE(callback_qry_pkt4_.get() == sol.get());
-
     // Check that all expected parameters are there
     vector<string> expected_argument_names;
-    expected_argument_names.push_back(string("response4"));
     expected_argument_names.push_back(string("query4"));
+    expected_argument_names.push_back(string("response4"));
     sort(callback_argument_names_.begin(), callback_argument_names_.end());
     sort(expected_argument_names.begin(), expected_argument_names.end());
     EXPECT_TRUE(expected_argument_names == callback_argument_names_);
@@ -1289,7 +1359,7 @@ TEST_F(HooksDhcpv4SrvTest, pkt4SendSimple) {
     EXPECT_TRUE(callback_resp_options_copy_);
 
     // Check if the callout handle state was reset after the callout.
-    checkCalloutHandleReset(sol);
+    checkCalloutHandleReset(discover);
 }
 
 // Checks if callouts installed on pkt4_send is able to change
@@ -1298,23 +1368,23 @@ TEST_F(HooksDhcpv4SrvTest, pkt4SendValueChange) {
     IfaceMgrTestConfig test_config(true);
     IfaceMgr::instance().openSockets4();
 
-    // Install pkt4_receive_callout
+    // Install pkt4_send_change_serverid
     EXPECT_NO_THROW(HooksManager::preCalloutsLibraryHandle().registerCallout(
                         "pkt4_send", pkt4_send_change_serverid));
 
     // Let's create a simple DISCOVER
-    Pkt4Ptr sol = generateSimpleDiscover();
+    Pkt4Ptr discover = generateSimpleDiscover();
 
     // Simulate that we have received that traffic
-    srv_->fakeReceive(sol);
+    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.
+    // In particular, it should call registered pkt4_send callback.
     srv_->run();
 
-    // check that the server did send a response
+    // Check that the server did send a response
     ASSERT_EQ(1, srv_->fake_sent_.size());
 
     // Make sure that we received a response
@@ -1329,7 +1399,7 @@ TEST_F(HooksDhcpv4SrvTest, pkt4SendValueChange) {
     EXPECT_TRUE(clientid->equals(expected));
 
     // Check if the callout handle state was reset after the callout.
-    checkCalloutHandleReset(sol);
+    checkCalloutHandleReset(discover);
 }
 
 // Checks if callouts installed on pkt4_send is able to delete
@@ -1340,20 +1410,20 @@ TEST_F(HooksDhcpv4SrvTest, pkt4SendDeleteServerId) {
     IfaceMgrTestConfig test_config(true);
     IfaceMgr::instance().openSockets4();
 
-    // Install pkt4_receive_callout
+    // Install pkt4_send_delete_serverid
     EXPECT_NO_THROW(HooksManager::preCalloutsLibraryHandle().registerCallout(
                         "pkt4_send", pkt4_send_delete_serverid));
 
     // Let's create a simple DISCOVER
-    Pkt4Ptr sol = generateSimpleDiscover();
+    Pkt4Ptr discover = generateSimpleDiscover();
 
     // Simulate that we have received that traffic
-    srv_->fakeReceive(sol);
+    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.
+    // In particular, it should call registered pkt4_send callback.
     srv_->run();
 
     // Check that the server indeed sent a malformed ADVERTISE
@@ -1367,24 +1437,24 @@ TEST_F(HooksDhcpv4SrvTest, pkt4SendDeleteServerId) {
     EXPECT_FALSE(adv->getOption(DHO_DHCP_SERVER_IDENTIFIER));
 
     // Check if the callout handle state was reset after the callout.
-    checkCalloutHandleReset(sol);
+    checkCalloutHandleReset(discover);
 }
 
 // Checks if callouts installed on pkt4_skip 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_send) {
+TEST_F(HooksDhcpv4SrvTest, pkt4SendSkip) {
     IfaceMgrTestConfig test_config(true);
     IfaceMgr::instance().openSockets4();
 
-    // Install pkt4_receive_callout
+    // Install pkt4_send_skip
     EXPECT_NO_THROW(HooksManager::preCalloutsLibraryHandle().registerCallout(
                         "pkt4_send", pkt4_send_skip));
 
     // Let's create a simple REQUEST
-    Pkt4Ptr sol = generateSimpleDiscover();
+    Pkt4Ptr discover = generateSimpleDiscover();
 
     // Simulate that we have received that traffic
-    srv_->fakeReceive(sol);
+    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
@@ -1392,33 +1462,35 @@ TEST_F(HooksDhcpv4SrvTest, skip_pkt4_send) {
     // In particular, it should call registered pkt4_send callback.
     srv_->run();
 
-    // Check that the server sent the message
+    // Check that the server send the packet
     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();
+
+    // The actual size of sent packet should be 0
     EXPECT_EQ(0, sent->getBuffer().getLength());
 
     // Check if the callout handle state was reset after the callout.
-    checkCalloutHandleReset(sol);
+    checkCalloutHandleReset(discover);
 }
 
 // Checks if callouts installed on pkt4_drop is able to set drop flag that
 // will cause the server to not process the packet (drop), even though it is valid.
-TEST_F(HooksDhcpv4SrvTest, drop_pkt4_send) {
+TEST_F(HooksDhcpv4SrvTest, pkt4SendDrop) {
     IfaceMgrTestConfig test_config(true);
     IfaceMgr::instance().openSockets4();
 
-    // Install pkt4_receive_callout
+    // Install pkt4_send_drop
     EXPECT_NO_THROW(HooksManager::preCalloutsLibraryHandle().registerCallout(
                         "pkt4_send", pkt4_send_drop));
 
     // Let's create a simple REQUEST
-    Pkt4Ptr sol = generateSimpleDiscover();
+    Pkt4Ptr discover = generateSimpleDiscover();
 
     // Simulate that we have received that traffic
-    srv_->fakeReceive(sol);
+    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
@@ -1426,11 +1498,11 @@ TEST_F(HooksDhcpv4SrvTest, drop_pkt4_send) {
     // In particular, it should call registered pkt4_send callback.
     srv_->run();
 
-    // Check that the server did not the message
-    ASSERT_EQ(0, srv_->fake_sent_.size());
+    // Check that the server does not send the packet
+    EXPECT_EQ(0, srv_->fake_sent_.size());
 
     // Check if the callout handle state was reset after the callout.
-    checkCalloutHandleReset(sol);
+    checkCalloutHandleReset(discover);
 }
 
 // Checks if callouts installed on buffer4_send are indeed called and the
@@ -1439,7 +1511,7 @@ TEST_F(HooksDhcpv4SrvTest, buffer4SendSimple) {
     IfaceMgrTestConfig test_config(true);
     IfaceMgr::instance().openSockets4();
 
-    // Install pkt4_receive_callout
+    // Install buffer4_send_callout
     EXPECT_NO_THROW(HooksManager::preCalloutsLibraryHandle().registerCallout(
                         "buffer4_send", buffer4_send_callout));
 
@@ -1452,7 +1524,7 @@ TEST_F(HooksDhcpv4SrvTest, buffer4SendSimple) {
     // 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.
+    // In particular, it should call registered buffer4_send callback.
     srv_->run();
 
     // Check that the callback called is indeed the one we installed
@@ -1479,11 +1551,11 @@ TEST_F(HooksDhcpv4SrvTest, buffer4SendSimple) {
 
 // Checks if callouts installed on buffer4_send are indeed called and that
 // the output buffer can be changed.
-TEST_F(HooksDhcpv4SrvTest, buffer4Send) {
+TEST_F(HooksDhcpv4SrvTest, buffer4SendChange) {
     IfaceMgrTestConfig test_config(true);
     IfaceMgr::instance().openSockets4();
 
-    // Install pkt4_receive_callout
+    // Install buffer4_send_change_callout
     EXPECT_NO_THROW(HooksManager::preCalloutsLibraryHandle().registerCallout(
                         "buffer4_send", buffer4_send_change_callout));
 
@@ -1496,7 +1568,7 @@ TEST_F(HooksDhcpv4SrvTest, buffer4Send) {
     // 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.
+    // In particular, it should call registered buffer4_send callback.
     srv_->run();
 
     // Check that there is one packet sent
@@ -1517,9 +1589,9 @@ TEST_F(HooksDhcpv4SrvTest, buffer4SendSkip) {
     IfaceMgrTestConfig test_config(true);
     IfaceMgr::instance().openSockets4();
 
-    // Install pkt4_receive_callout
+    // Install buffer4_send_skip
     EXPECT_NO_THROW(HooksManager::preCalloutsLibraryHandle().registerCallout(
-                        "buffer4_send", skip_callout));
+                        "buffer4_send", buffer4_send_skip));
 
     // Let's create a simple DISCOVER
     Pkt4Ptr discover = generateSimpleDiscover();
@@ -1530,9 +1602,12 @@ TEST_F(HooksDhcpv4SrvTest, buffer4SendSkip) {
     // 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.
+    // In particular, it should call registered buffer4_send callback.
     srv_->run();
 
+    // Check that the callback called is indeed the one we installed
+    EXPECT_EQ("buffer4_send", callback_name_);
+
     // Check that there is no packet sent.
     ASSERT_EQ(0, srv_->fake_sent_.size());
 
@@ -1546,9 +1621,9 @@ TEST_F(HooksDhcpv4SrvTest, buffer4SendDrop) {
     IfaceMgrTestConfig test_config(true);
     IfaceMgr::instance().openSockets4();
 
-    // Install pkt4_receive_callout
+    // Install buffer4_send_drop
     EXPECT_NO_THROW(HooksManager::preCalloutsLibraryHandle().registerCallout(
-                        "buffer4_send", drop_callout));
+                        "buffer4_send", buffer4_send_drop));
 
     // Let's create a simple DISCOVER
     Pkt4Ptr discover = generateSimpleDiscover();
@@ -1559,17 +1634,19 @@ TEST_F(HooksDhcpv4SrvTest, buffer4SendDrop) {
     // 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.
+    // In particular, it should call registered buffer4_send callback.
     srv_->run();
 
-    // Check that there is no packet sent.
-    ASSERT_EQ(0, srv_->fake_sent_.size());
+    // Check that the callback called is indeed the one we installed
+    EXPECT_EQ("buffer4_send", callback_name_);
+
+    // Check that there is no packet sent
+    EXPECT_EQ(0, srv_->fake_sent_.size());
 
     // Check if the callout handle state was reset after the callout.
     checkCalloutHandleReset(discover);
 }
 
-
 // This test checks if subnet4_select callout is triggered and reports
 // valid parameters
 TEST_F(HooksDhcpv4SrvTest, subnet4SelectSimple) {
@@ -1606,29 +1683,29 @@ TEST_F(HooksDhcpv4SrvTest, subnet4SelectSimple) {
     // Commit the config
     CfgMgr::instance().commit();
 
-    // Install pkt4_receive_callout
+    // Install subnet4_select_callout
     EXPECT_NO_THROW(HooksManager::preCalloutsLibraryHandle().registerCallout(
                         "subnet4_select", subnet4_select_callout));
 
     // Prepare discover packet. Server should select first subnet for it
-    Pkt4Ptr sol = Pkt4Ptr(new Pkt4(DHCPDISCOVER, 1234));
-    sol->setRemoteAddr(IOAddress("192.0.2.1"));
-    sol->setIface("eth1");
-    sol->setIndex(ETH1_INDEX);
+    Pkt4Ptr discover = Pkt4Ptr(new Pkt4(DHCPDISCOVER, 1234));
+    discover->setRemoteAddr(IOAddress("192.0.2.1"));
+    discover->setIface("eth1");
+    discover->setIndex(ETH1_INDEX);
     OptionPtr clientid = generateClientId();
-    sol->addOption(clientid);
+    discover->addOption(clientid);
 
     // Pass it to the server and get an advertise
-    Pkt4Ptr adv = srv_->processDiscover(sol);
+    Pkt4Ptr adv = srv_->processDiscover(discover);
 
-    // check if we get response at all
+    // Check if we get response at all
     ASSERT_TRUE(adv);
 
     // Check that the callback called is indeed the one we installed
     EXPECT_EQ("subnet4_select", callback_name_);
 
     // Check that pkt4 argument passing was successful and returned proper value
-    EXPECT_TRUE(callback_qry_pkt4_.get() == sol.get());
+    EXPECT_TRUE(callback_qry_pkt4_.get() == discover.get());
 
     const Subnet4Collection* exp_subnets =
         CfgMgr::instance().getCurrentCfg()->getCfgSubnets4()->getAll();
@@ -1636,7 +1713,7 @@ TEST_F(HooksDhcpv4SrvTest, subnet4SelectSimple) {
     // The server is supposed to pick the first subnet, because of matching
     // interface. Check that the value is reported properly.
     ASSERT_TRUE(callback_subnet4_);
-    EXPECT_EQ(exp_subnets->begin()->get(), callback_subnet4_.get());
+    EXPECT_EQ(callback_subnet4_.get(), exp_subnets->begin()->get());
 
     // Server is supposed to report two subnets
     ASSERT_EQ(exp_subnets->size(), callback_subnet4collection_->size());
@@ -1650,7 +1727,7 @@ TEST_F(HooksDhcpv4SrvTest, subnet4SelectSimple) {
     EXPECT_TRUE(callback_qry_options_copy_);
 
     // Check if the callout handle state was reset after the callout.
-    checkCalloutHandleReset(sol);
+    checkCalloutHandleReset(discover);
 }
 
 // This test checks if callout installed on subnet4_select hook point can pick
@@ -1688,22 +1765,22 @@ TEST_F(HooksDhcpv4SrvTest, subnet4SelectChange) {
 
     CfgMgr::instance().commit();
 
-    // Install a callout
+    // Install subnet4_select_different_subnet
     EXPECT_NO_THROW(HooksManager::preCalloutsLibraryHandle().registerCallout(
-                        "subnet4_select", subnet4_select_different_subnet_callout));
+                        "subnet4_select", subnet4_select_different_subnet));
 
     // Prepare discover packet. Server should select first subnet for it
-    Pkt4Ptr sol = Pkt4Ptr(new Pkt4(DHCPDISCOVER, 1234));
-    sol->setRemoteAddr(IOAddress("192.0.2.1"));
-    sol->setIface("eth0");
-    sol->setIndex(ETH0_INDEX);
+    Pkt4Ptr discover = Pkt4Ptr(new Pkt4(DHCPDISCOVER, 1234));
+    discover->setRemoteAddr(IOAddress("192.0.2.1"));
+    discover->setIface("eth0");
+    discover->setIndex(ETH0_INDEX);
     OptionPtr clientid = generateClientId();
-    sol->addOption(clientid);
+    discover->addOption(clientid);
 
     // Pass it to the server and get an advertise
-    Pkt4Ptr adv = srv_->processDiscover(sol);
+    Pkt4Ptr adv = srv_->processDiscover(discover);
 
-    // check if we get response at all
+    // Check if we get response at all
     ASSERT_TRUE(adv);
 
     // The response should have an address from second pool, so let's check it
@@ -1723,7 +1800,35 @@ TEST_F(HooksDhcpv4SrvTest, subnet4SelectChange) {
     EXPECT_TRUE((*subnet)->inPool(Lease::TYPE_V4, addr));
 
     // Check if the callout handle state was reset after the callout.
-    checkCalloutHandleReset(sol);
+    checkCalloutHandleReset(discover);
+}
+
+// Checks that subnet4_select is able to drop the packet.
+TEST_F(HooksDhcpv4SrvTest, subnet4SelectDrop) {
+    IfaceMgrTestConfig test_config(true);
+    IfaceMgr::instance().openSockets4();
+
+    // Install subnet4_select_drop
+    EXPECT_NO_THROW(HooksManager::preCalloutsLibraryHandle().registerCallout(
+                        "subnet4_select", subnet4_select_drop));
+
+    // 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 subnet4_select callback.
+    srv_->run();
+
+    // Check that the server dropped the packet and did not produce any response
+    ASSERT_EQ(0, srv_->fake_sent_.size());
+
+    // Check if the callout handle state was reset after the callout.
+    checkCalloutHandleReset(discover);
 }
 
 // This test verifies that the leases4_committed hook point is not triggered
@@ -1732,10 +1837,10 @@ TEST_F(HooksDhcpv4SrvTest, leases4CommittedDiscover) {
     IfaceMgrTestConfig test_config(true);
     IfaceMgr::instance().openSockets4();
 
+    // Install leases4_committed callout
     ASSERT_NO_THROW(HooksManager::preCalloutsLibraryHandle().registerCallout(
                     "leases4_committed", leases4_committed_callout));
 
-
     Dhcp4Client client(Dhcp4Client::SELECTING);
     client.setIfaceName("eth1");
     client.setIfaceIndex(ETH1_INDEX);
@@ -1760,7 +1865,6 @@ TEST_F(HooksDhcpv4SrvTest, leases4CommittedInform) {
     ASSERT_NO_THROW(HooksManager::preCalloutsLibraryHandle().registerCallout(
                     "leases4_committed", leases4_committed_callout));
 
-
     Dhcp4Client client(Dhcp4Client::SELECTING);
     client.useRelay();
     ASSERT_NO_THROW(client.doInform());
@@ -1775,172 +1879,6 @@ TEST_F(HooksDhcpv4SrvTest, leases4CommittedInform) {
     checkCalloutHandleReset(client.getContext().query_);
 }
 
-// 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) {
-    IfaceMgrTestConfig test_config(true);
-    IfaceMgr::instance().openSockets4();
-
-    const IOAddress addr("192.0.2.106");
-    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(Lease::TYPE_V4, addr));
-
-    // let's create a lease and put it in the LeaseMgr
-    uint8_t hwaddr2_data[] = { 0, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe};
-    HWAddrPtr hwaddr2(new HWAddr(hwaddr2_data, sizeof(hwaddr2_data), HTYPE_ETHER));
-    Lease4Ptr used(new Lease4(IOAddress("192.0.2.106"), hwaddr2,
-                              &client_id_->getDuid()[0], client_id_->getDuid().size(),
-                              temp_valid, 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->setIface("eth0");
-    req->setIndex(ETH0_INDEX);
-    req->setHWAddr(hwaddr2);
-
-    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 preferred, valid and cltt were really updated
-    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 query4 argument passing was successful and
-    // returned proper value
-    EXPECT_TRUE(callback_qry_pkt4_.get() == req.get());
-
-    // 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("query4");
-    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);
-
-    Lease4Ptr lease = LeaseMgrFactory::instance().getLease4(addr);
-    EXPECT_TRUE(LeaseMgrFactory::instance().deleteLease(lease));
-
-    // Pkt passed to a callout must be configured to copy retrieved options.
-    EXPECT_TRUE(callback_qry_options_copy_);
-
-    // Check if the callout handle state was reset after the callout.
-    checkCalloutHandleReset(req);
-}
-
-// This test verifies that a callout installed on lease4_renew can trigger
-// the server to not renew a lease.
-TEST_F(HooksDhcpv4SrvTest, lease4RenewSkip) {
-    IfaceMgrTestConfig test_config(true);
-    IfaceMgr::instance().openSockets4();
-
-    const IOAddress addr("192.0.2.106");
-    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(Lease::TYPE_V4, addr));
-
-    // let's create a lease and put it in the LeaseMgr
-    uint8_t hwaddr2_data[] = { 0, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe};
-    HWAddrPtr hwaddr2(new HWAddr(hwaddr2_data, sizeof(hwaddr2_data), HTYPE_ETHER));
-    Lease4Ptr used(new Lease4(IOAddress("192.0.2.106"), hwaddr2,
-                              &client_id_->getDuid()[0], client_id_->getDuid().size(),
-                              temp_valid, 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 preferred, valid and cltt really set.
-    // Constructed lease looks as if it was assigned 10 seconds ago
-    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->setIface("eth0");
-    req->setIndex(ETH0_INDEX);
-    req->setHWAddr(hwaddr2);
-
-    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 valid and cltt were NOT updated
-    EXPECT_EQ(temp_valid, l->valid_lft_);
-    EXPECT_EQ(temp_timestamp, l->cltt_);
-
-    Lease4Ptr lease = LeaseMgrFactory::instance().getLease4(addr);
-    EXPECT_TRUE(LeaseMgrFactory::instance().deleteLease(lease));
-
-    // Check if the callout handle state was reset after the callout.
-    checkCalloutHandleReset(req);
-}
-
 // This test verifies that the callout installed on the leases4_committed hook
 // point is executed as a result of DHCPREQUEST message sent to allocate new
 // lease or renew an existing lease.
@@ -1951,7 +1889,6 @@ TEST_F(HooksDhcpv4SrvTest, leases4CommittedRequest) {
     ASSERT_NO_THROW(HooksManager::preCalloutsLibraryHandle().registerCallout(
                     "leases4_committed", leases4_committed_callout));
 
-
     Dhcp4Client client(Dhcp4Client::SELECTING);
     client.setIfaceName("eth1");
     client.setIfaceIndex(ETH1_INDEX);
@@ -2055,9 +1992,102 @@ TEST_F(HooksDhcpv4SrvTest, leases4CommittedRequest) {
     EXPECT_FALSE(callback_deleted_lease4_);
 }
 
+// This test verifies that the leases4_committed callout is executed
+// with declined leases as argument when DHCPDECLINE is processed.
+TEST_F(HooksDhcpv4SrvTest, leases4CommittedDecline) {
+    IfaceMgrTestConfig test_config(true);
+    IfaceMgr::instance().openSockets4();
+
+    ASSERT_NO_THROW(HooksManager::preCalloutsLibraryHandle().registerCallout(
+                    "leases4_committed", leases4_committed_callout));
+
+    Dhcp4Client client(Dhcp4Client::SELECTING);
+    client.useRelay();
+    ASSERT_NO_THROW(client.doDORA(boost::shared_ptr<IOAddress>(new IOAddress("192.0.2.100"))));
+
+    // Make sure that we received a response
+    ASSERT_TRUE(client.getContext().response_);
+
+    resetCalloutBuffers();
+
+    ASSERT_NO_THROW(client.doDecline());
+
+    // Check that the callback called is indeed the one we installed
+    EXPECT_EQ("leases4_committed", callback_name_);
+
+    // Check if all expected parameters were really received
+    vector<string> expected_argument_names;
+    expected_argument_names.push_back("query4");
+    expected_argument_names.push_back("deleted_leases4");
+    expected_argument_names.push_back("leases4");
+
+    sort(expected_argument_names.begin(), expected_argument_names.end());
+    EXPECT_TRUE(callback_argument_names_ == expected_argument_names);
+
+    // No new allocations.
+    ASSERT_TRUE(callback_lease4_);
+    EXPECT_EQ("192.0.2.100", callback_lease4_->addr_.toText());
+    EXPECT_EQ(Lease::STATE_DECLINED, callback_lease4_->state_);
+
+    // Released lease should be returned.
+    EXPECT_FALSE(callback_deleted_lease4_);
+
+    // Pkt passed to a callout must be configured to copy retrieved options.
+    EXPECT_TRUE(callback_qry_options_copy_);
+
+    // Check if the callout handle state was reset after the callout.
+    checkCalloutHandleReset(client.getContext().query_);
+}
+
+// This test verifies that the leases4_committed callout is executed
+// with deleted leases as argument when DHCPRELEASE is processed.
+TEST_F(HooksDhcpv4SrvTest, leases4CommittedRelease) {
+    IfaceMgrTestConfig test_config(true);
+    IfaceMgr::instance().openSockets4();
+
+    ASSERT_NO_THROW(HooksManager::preCalloutsLibraryHandle().registerCallout(
+                    "leases4_committed", leases4_committed_callout));
+
+    Dhcp4Client client(Dhcp4Client::SELECTING);
+    client.setIfaceName("eth1");
+    client.setIfaceIndex(ETH1_INDEX);
+    ASSERT_NO_THROW(client.doDORA(boost::shared_ptr<IOAddress>(new IOAddress("192.0.2.100"))));
+
+    // Make sure that we received a response
+    ASSERT_TRUE(client.getContext().response_);
+
+    resetCalloutBuffers();
+
+    ASSERT_NO_THROW(client.doRelease());
+
+    // Check that the callback called is indeed the one we installed
+    EXPECT_EQ("leases4_committed", callback_name_);
+
+    // Check if all expected parameters were really received
+    vector<string> expected_argument_names;
+    expected_argument_names.push_back("query4");
+    expected_argument_names.push_back("deleted_leases4");
+    expected_argument_names.push_back("leases4");
+
+    sort(expected_argument_names.begin(), expected_argument_names.end());
+    EXPECT_TRUE(callback_argument_names_ == expected_argument_names);
+
+    // No new allocations.
+    EXPECT_FALSE(callback_lease4_);
+
+    // Released lease should be returned.
+    ASSERT_TRUE(callback_deleted_lease4_);
+    EXPECT_EQ("192.0.2.100", callback_deleted_lease4_->addr_.toText());
+
+    // Pkt passed to a callout must be configured to copy retrieved options.
+    EXPECT_TRUE(callback_qry_options_copy_);
+
+    // Check if the callout handle state was reset after the callout.
+    checkCalloutHandleReset(client.getContext().query_);
+}
 // This test verifies that the callout installed on the leases4_committed hook
-// point is executed as a result of DHCPREQUEST message sent to reuse
-// an existing lease.
+// point is executed as a result of DHCPREQUEST message sent to reuse an
+// existing lease.
 TEST_F(HooksDhcpv4SrvTest, leases4CommittedCache) {
     IfaceMgrTestConfig test_config(true);
     IfaceMgr::instance().openSockets4();
@@ -2065,7 +2095,6 @@ TEST_F(HooksDhcpv4SrvTest, leases4CommittedCache) {
     ASSERT_NO_THROW(HooksManager::preCalloutsLibraryHandle().registerCallout(
                     "leases4_committed", leases4_committed_callout));
 
-
     // Modify the subnet to reuse leases.
     subnet_->setCacheThreshold(.25);
 
@@ -2158,71 +2187,240 @@ TEST_F(HooksDhcpv4SrvTest, leases4CommittedParkRequests) {
     expected_argument_names.push_back("deleted_leases4");
     expected_argument_names.push_back("leases4");
 
-    sort(expected_argument_names.begin(), expected_argument_names.end());
-    EXPECT_TRUE(callback_argument_names_ == expected_argument_names);
+    sort(expected_argument_names.begin(), expected_argument_names.end());
+    EXPECT_TRUE(callback_argument_names_ == expected_argument_names);
+
+    // Newly allocated lease should be passed to the callout.
+    ASSERT_TRUE(callback_lease4_);
+    EXPECT_EQ("192.0.2.100", callback_lease4_->addr_.toText());
+
+    // Deleted lease must not be present, because it is a new allocation.
+    EXPECT_FALSE(callback_deleted_lease4_);
+
+    // Pkt passed to a callout must be configured to copy retrieved options.
+    EXPECT_TRUE(callback_qry_options_copy_);
+
+    // Check if the callout handle state was reset after the callout.
+    checkCalloutHandleReset(client1.getContext().query_);
+
+    // Reset all indicators because we'll be now creating a second client.
+    resetCalloutBuffers();
+
+    // Create the second client to test that it may communicate with the
+    // server while the previous packet is parked.
+    Dhcp4Client client2(client1.getServer(), Dhcp4Client::SELECTING);
+    client2.setIfaceName("eth1");
+    client2.setIfaceIndex(ETH1_INDEX);
+    ASSERT_NO_THROW(client2.doDORA(boost::shared_ptr<IOAddress>(new IOAddress("192.0.2.101"))));
+
+    // The DHCPOFFER should have been returned but not DHCPACK, as this
+    // packet got parked too.
+    ASSERT_FALSE(client2.getContext().response_);
+
+    // Check that the callback called is indeed the one we installed.
+    EXPECT_EQ("leases4_committed", callback_name_);
+
+    // There should be now two actions scheduled on our IO service
+    // by the invoked callouts. They unpark both DHCPACK messages.
+    ASSERT_NO_THROW(io_service_->poll());
+
+    // Receive and check the first response.
+    ASSERT_NO_THROW(client1.receiveResponse());
+    ASSERT_TRUE(client1.getContext().response_);
+    Pkt4Ptr rsp = client1.getContext().response_;
+    EXPECT_EQ(DHCPACK, rsp->getType());
+    EXPECT_EQ("192.0.2.100", rsp->getYiaddr().toText());
+
+    // Receive and check the second response.
+    ASSERT_NO_THROW(client2.receiveResponse());
+    ASSERT_TRUE(client2.getContext().response_);
+    rsp = client2.getContext().response_;
+    EXPECT_EQ(DHCPACK, rsp->getType());
+    EXPECT_EQ("192.0.2.101", rsp->getYiaddr().toText());
+
+    // Check if the callout handle state was reset after the callout.
+    checkCalloutHandleReset(client2.getContext().query_);
+}
+
+// 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) {
+    IfaceMgrTestConfig test_config(true);
+    IfaceMgr::instance().openSockets4();
+
+    const IOAddress addr("192.0.2.106");
+    const uint32_t temp_valid = 100;
+    const time_t temp_timestamp = time(NULL) - 10;
+
+    // Install lease4_renew_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(Lease::TYPE_V4, addr));
+
+    // let's create a lease and put it in the LeaseMgr
+    uint8_t hwaddr2_data[] = { 0, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe };
+    HWAddrPtr hwaddr2(new HWAddr(hwaddr2_data, sizeof(hwaddr2_data), HTYPE_ETHER));
+    Lease4Ptr used(new Lease4(IOAddress("192.0.2.106"), hwaddr2,
+                              &client_id_->getDuid()[0], client_id_->getDuid().size(),
+                              temp_valid, 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->setIface("eth0");
+    req->setIndex(ETH0_INDEX);
+    req->setHWAddr(hwaddr2);
+
+    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 preferred, valid and cltt were really updated
+    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 query4 argument passing was successful and
+    // returned proper value
+    EXPECT_TRUE(callback_qry_pkt4_.get() == req.get());
+
+    // 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("query4");
+    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);
+
+    Lease4Ptr lease = LeaseMgrFactory::instance().getLease4(addr);
+    EXPECT_TRUE(LeaseMgrFactory::instance().deleteLease(lease));
+
+    // Pkt passed to a callout must be configured to copy retrieved options.
+    EXPECT_TRUE(callback_qry_options_copy_);
+
+    // Check if the callout handle state was reset after the callout.
+    checkCalloutHandleReset(req);
+}
+
+// This test verifies that a callout installed on lease4_renew can trigger
+// the server to not renew a lease.
+TEST_F(HooksDhcpv4SrvTest, lease4RenewSkip) {
+    IfaceMgrTestConfig test_config(true);
+    IfaceMgr::instance().openSockets4();
+
+    const IOAddress addr("192.0.2.106");
+    const uint32_t temp_valid = 100;
+    const time_t temp_timestamp = time(NULL) - 10;
 
-    // Newly allocated lease should be passed to the callout.
-    ASSERT_TRUE(callback_lease4_);
-    EXPECT_EQ("192.0.2.100", callback_lease4_->addr_.toText());
+    // Install lease4_renew_skip_callout
+    EXPECT_NO_THROW(HooksManager::preCalloutsLibraryHandle().registerCallout(
+                        "lease4_renew", lease4_renew_skip_callout));
 
-    // Deleted lease must not be present, because it is a new allocation.
-    EXPECT_FALSE(callback_deleted_lease4_);
+    // Generate client-id also sets client_id_ member
+    OptionPtr clientid = generateClientId();
 
-    // Pkt passed to a callout must be configured to copy retrieved options.
-    EXPECT_TRUE(callback_qry_options_copy_);
+    // Check that the address we are about to use is indeed in pool
+    ASSERT_TRUE(subnet_->inPool(Lease::TYPE_V4, addr));
 
-    // Check if the callout handle state was reset after the callout.
-    checkCalloutHandleReset(client1.getContext().query_);
+    // let's create a lease and put it in the LeaseMgr
+    uint8_t hwaddr2_data[] = { 0, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe };
+    HWAddrPtr hwaddr2(new HWAddr(hwaddr2_data, sizeof(hwaddr2_data), HTYPE_ETHER));
+    Lease4Ptr used(new Lease4(IOAddress("192.0.2.106"), hwaddr2,
+                              &client_id_->getDuid()[0], client_id_->getDuid().size(),
+                              temp_valid, temp_timestamp, subnet_->getID()));
+    ASSERT_TRUE(LeaseMgrFactory::instance().addLease(used));
 
-    // Reset all indicators because we'll be now creating a second client.
-    resetCalloutBuffers();
+    // Check that the lease is really in the database
+    Lease4Ptr l = LeaseMgrFactory::instance().getLease4(addr);
+    ASSERT_TRUE(l);
 
-    // Create the second client to test that it may communicate with the
-    // server while the previous packet is parked.
-    Dhcp4Client client2(client1.getServer(), Dhcp4Client::SELECTING);
-    client2.setIfaceName("eth1");
-    client2.setIfaceIndex(ETH1_INDEX);
-    ASSERT_NO_THROW(client2.doDORA(boost::shared_ptr<IOAddress>(new IOAddress("192.0.2.101"))));
+    // Check that preferred, valid and cltt really set.
+    // Constructed lease looks as if it was assigned 10 seconds ago
+    EXPECT_EQ(l->valid_lft_, temp_valid);
+    EXPECT_EQ(l->cltt_, temp_timestamp);
 
-    // The DHCPOFFER should have been returned but not DHCPACK, as this
-    // packet got parked too.
-    ASSERT_FALSE(client2.getContext().response_);
+    // 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->setIface("eth0");
+    req->setIndex(ETH0_INDEX);
+    req->setHWAddr(hwaddr2);
 
-    // Check that the callback called is indeed the one we installed.
-    EXPECT_EQ("leases4_committed", callback_name_);
+    req->addOption(clientid);
+    req->addOption(srv_->getServerID());
 
-    // There should be now two actions scheduled on our IO service
-    // by the invoked callouts. They unpark both DHCPACK messages.
-    ASSERT_NO_THROW(io_service_->poll());
+    // Pass it to the server and hope for a REPLY
+    Pkt4Ptr ack = srv_->processRequest(req);
+    ASSERT_TRUE(ack);
 
-    // Receive and check the first response.
-    ASSERT_NO_THROW(client1.receiveResponse());
-    ASSERT_TRUE(client1.getContext().response_);
-    Pkt4Ptr rsp = client1.getContext().response_;
-    EXPECT_EQ(DHCPACK, rsp->getType());
-    EXPECT_EQ("192.0.2.100", rsp->getYiaddr().toText());
+    // Check that the lease is really in the database
+    l = checkLease(ack, clientid, req->getHWAddr(), addr);
+    ASSERT_TRUE(l);
 
-    // Receive and check the second response.
-    ASSERT_NO_THROW(client2.receiveResponse());
-    ASSERT_TRUE(client2.getContext().response_);
-    rsp = client2.getContext().response_;
-    EXPECT_EQ(DHCPACK, rsp->getType());
-    EXPECT_EQ("192.0.2.101", rsp->getYiaddr().toText());
+    // Check that valid and cltt were NOT updated
+    EXPECT_EQ(temp_valid, l->valid_lft_);
+    EXPECT_EQ(temp_timestamp, l->cltt_);
+
+    Lease4Ptr lease = LeaseMgrFactory::instance().getLease4(addr);
+    EXPECT_TRUE(LeaseMgrFactory::instance().deleteLease(lease));
 
     // Check if the callout handle state was reset after the callout.
-    checkCalloutHandleReset(client2.getContext().query_);
+    checkCalloutHandleReset(req);
 }
 
 // This test verifies that valid RELEASE triggers lease4_release callouts
 TEST_F(HooksDhcpv4SrvTest, lease4ReleaseSimple) {
     IfaceMgrTestConfig test_config(true);
+    CfgMgr::instance().getCurrentCfg()->getCfgExpiration()->setFlushReclaimedTimerWaitTime(0);
+    CfgMgr::instance().getCurrentCfg()->getCfgExpiration()->setHoldReclaimedTime(0);
     IfaceMgr::instance().openSockets4();
 
     const IOAddress addr("192.0.2.106");
     const uint32_t temp_valid = 100;
     const time_t temp_timestamp = time(NULL) - 10;
 
-    // Install callout
+    // Install lease4_release_callout
     EXPECT_NO_THROW(HooksManager::preCalloutsLibraryHandle().registerCallout(
                         "lease4_release", lease4_release_callout));
 
@@ -2233,7 +2431,7 @@ TEST_F(HooksDhcpv4SrvTest, lease4ReleaseSimple) {
     ASSERT_TRUE(subnet_->inPool(Lease::TYPE_V4, addr));
 
     // Let's create a lease and put it in the LeaseMgr
-    uint8_t mac_addr[] = { 0, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe};
+    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, hw,
                               &client_id_->getDuid()[0], client_id_->getDuid().size(),
@@ -2262,18 +2460,16 @@ TEST_F(HooksDhcpv4SrvTest, lease4ReleaseSimple) {
     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);
+    Lease4Collection leases = LeaseMgrFactory::instance().getLease4(*hw);
+    EXPECT_EQ(leases.size(), 0);
 
     // Try to get it by hw/subnet_id combination
-    l = LeaseMgrFactory::instance().getLease4(hw->hwaddr_, subnet_->getID());
+    l = LeaseMgrFactory::instance().getLease4(*hw, 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);
+    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());
@@ -2302,9 +2498,8 @@ TEST_F(HooksDhcpv4SrvTest, lease4ReleaseSimple) {
     checkCalloutHandleReset(rel);
 }
 
-// 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) {
+// This test verifies that valid RELEASE triggers lease4_release callouts
+TEST_F(HooksDhcpv4SrvTest, lease4ReleaseSimpleNoDelete) {
     IfaceMgrTestConfig test_config(true);
     IfaceMgr::instance().openSockets4();
 
@@ -2312,9 +2507,9 @@ TEST_F(HooksDhcpv4SrvTest, lease4ReleaseSkip) {
     const uint32_t temp_valid = 100;
     const time_t temp_timestamp = time(NULL) - 10;
 
-    // Install callout
+    // Install lease4_release_callout
     EXPECT_NO_THROW(HooksManager::preCalloutsLibraryHandle().registerCallout(
-                        "lease4_release", skip_callout));
+                        "lease4_release", lease4_release_callout));
 
     // Generate client-id also duid_
     OptionPtr clientid = generateClientId();
@@ -2323,7 +2518,7 @@ TEST_F(HooksDhcpv4SrvTest, lease4ReleaseSkip) {
     ASSERT_TRUE(subnet_->inPool(Lease::TYPE_V4, addr));
 
     // Let's create a lease and put it in the LeaseMgr
-    uint8_t mac_addr[] = { 0, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe};
+    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, hw,
                               &client_id_->getDuid()[0], client_id_->getDuid().size(),
@@ -2338,7 +2533,7 @@ TEST_F(HooksDhcpv4SrvTest, lease4ReleaseSkip) {
     // Generate client-id also duid_
     Pkt4Ptr rel = Pkt4Ptr(new Pkt4(DHCPRELEASE, 1234));
     rel->setRemoteAddr(addr);
-    rel->setYiaddr(addr);
+    rel->setCiaddr(addr);
     rel->addOption(clientid);
     rel->addOption(srv_->getServerID());
     rel->setHWAddr(hw);
@@ -2347,76 +2542,116 @@ TEST_F(HooksDhcpv4SrvTest, lease4ReleaseSkip) {
     // Note: this is no response to RELEASE in DHCPv4
     EXPECT_NO_THROW(srv_->processRelease(rel));
 
-    // The lease should be still there
+    // The lease should not be gone from LeaseMgr
     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, should succeed
+    // Try to get the lease by hardware address
     Lease4Collection leases = LeaseMgrFactory::instance().getLease4(*hw);
     EXPECT_EQ(leases.size(), 1);
 
-    // Try by client-id, should be successful as well.
+    // Try to get it by hw/subnet_id combination
+    l = LeaseMgrFactory::instance().getLease4(*hw, subnet_->getID());
+    EXPECT_TRUE(l);
+
+    // Try by client-id
     leases = LeaseMgrFactory::instance().getLease4(*client_id_);
     EXPECT_EQ(leases.size(), 1);
 
+    // Try by client-id/subnet-id
+    l = LeaseMgrFactory::instance().getLease4(*client_id_, subnet_->getID());
+    EXPECT_TRUE(l);
+
+    // Ok, the lease is *really* 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_qry_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);
+
+    // Pkt passed to a callout must be configured to copy retrieved options.
+    EXPECT_TRUE(callback_qry_options_copy_);
+
     // Check if the callout handle state was reset after the callout.
     checkCalloutHandleReset(rel);
 }
 
-// This test verifies that the leases4_committed callout is executed
-// with deleted leases as argument when DHCPRELEASE is processed.
-TEST_F(HooksDhcpv4SrvTest, leases4CommittedRelease) {
+// 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) {
     IfaceMgrTestConfig test_config(true);
     IfaceMgr::instance().openSockets4();
 
-    ASSERT_NO_THROW(HooksManager::preCalloutsLibraryHandle().registerCallout(
-                    "leases4_committed", leases4_committed_callout));
+    const IOAddress addr("192.0.2.106");
+    const uint32_t temp_valid = 100;
+    const time_t temp_timestamp = time(NULL) - 10;
 
+    // Install lease4_release_skip
+    EXPECT_NO_THROW(HooksManager::preCalloutsLibraryHandle().registerCallout(
+                        "lease4_release", lease4_release_skip));
 
-    Dhcp4Client client(Dhcp4Client::SELECTING);
-    client.setIfaceName("eth1");
-    client.setIfaceIndex(ETH1_INDEX);
-    ASSERT_NO_THROW(client.doDORA(boost::shared_ptr<IOAddress>(new IOAddress("192.0.2.100"))));
+    // Generate client-id also duid_
+    OptionPtr clientid = generateClientId();
 
-    // Make sure that we received a response
-    ASSERT_TRUE(client.getContext().response_);
+    // Check that the address we are about to use is indeed in pool
+    ASSERT_TRUE(subnet_->inPool(Lease::TYPE_V4, addr));
 
-    resetCalloutBuffers();
+    // 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, hw,
+                              &client_id_->getDuid()[0], client_id_->getDuid().size(),
+                              temp_valid, temp_timestamp, subnet_->getID()));
+    ASSERT_TRUE(LeaseMgrFactory::instance().addLease(used));
 
-    ASSERT_NO_THROW(client.doRelease());
+    // Check that the lease is really in the database
+    Lease4Ptr l = LeaseMgrFactory::instance().getLease4(addr);
+    ASSERT_TRUE(l);
 
-    // Check that the callback called is indeed the one we installed
-    EXPECT_EQ("leases4_committed", callback_name_);
+    // 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);
 
-    // Check if all expected parameters were really received
-    vector<string> expected_argument_names;
-    expected_argument_names.push_back("query4");
-    expected_argument_names.push_back("deleted_leases4");
-    expected_argument_names.push_back("leases4");
+    // 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));
 
-    sort(expected_argument_names.begin(), expected_argument_names.end());
-    EXPECT_TRUE(callback_argument_names_ == expected_argument_names);
+    // The lease should be still there
+    l = LeaseMgrFactory::instance().getLease4(addr);
+    EXPECT_TRUE(l);
 
-    // No new allocations.
-    EXPECT_FALSE(callback_lease4_);
+    // Try by client-id/subnet-id
+    l = LeaseMgrFactory::instance().getLease4(*client_id_, subnet_->getID());
+    EXPECT_TRUE(l);
 
-    // Released lease should be returned.
-    ASSERT_TRUE(callback_deleted_lease4_);
-    EXPECT_EQ("192.0.2.100", callback_deleted_lease4_->addr_.toText());
+    // Try to get the lease by hardware address, should succeed
+    Lease4Collection leases = LeaseMgrFactory::instance().getLease4(*hw);
+    EXPECT_EQ(leases.size(), 1);
 
-    // Pkt passed to a callout must be configured to copy retrieved options.
-    EXPECT_TRUE(callback_qry_options_copy_);
+    // Try by client-id, should be successful as well.
+    leases = LeaseMgrFactory::instance().getLease4(*client_id_);
+    EXPECT_EQ(leases.size(), 1);
 
     // Check if the callout handle state was reset after the callout.
-    checkCalloutHandleReset(client.getContext().query_);
+    checkCalloutHandleReset(rel);
 }
 
 // This test verifies that drop flag returned by a callout installed on the
-// lease4_release hook point will keep the lease
+// lease4_release hook point will keep the lease.
 TEST_F(HooksDhcpv4SrvTest, lease4ReleaseDrop) {
     IfaceMgrTestConfig test_config(true);
     IfaceMgr::instance().openSockets4();
@@ -2425,9 +2660,9 @@ TEST_F(HooksDhcpv4SrvTest, lease4ReleaseDrop) {
     const uint32_t temp_valid = 100;
     const time_t temp_timestamp = time(NULL) - 10;
 
-    // Install a callout
+    // Install lease4_release_drop
     EXPECT_NO_THROW(HooksManager::preCalloutsLibraryHandle().registerCallout(
-                        "lease4_release", drop_callout));
+                        "lease4_release", lease4_release_drop));
 
     // Generate client-id also duid_
     OptionPtr clientid = generateClientId();
@@ -2436,7 +2671,7 @@ TEST_F(HooksDhcpv4SrvTest, lease4ReleaseDrop) {
     ASSERT_TRUE(subnet_->inPool(Lease::TYPE_V4, addr));
 
     // Let's create a lease and put it in the LeaseMgr
-    uint8_t mac_addr[] = { 0, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe};
+    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, hw,
                               &client_id_->getDuid()[0], client_id_->getDuid().size(),
@@ -2480,12 +2715,13 @@ TEST_F(HooksDhcpv4SrvTest, lease4ReleaseDrop) {
     checkCalloutHandleReset(rel);
 }
 
-// Checks that decline4 hooks (lease4_decline) are triggered properly.
-TEST_F(HooksDhcpv4SrvTest, HooksDecline) {
+// This test checks that the basic decline hook (lease4_decline) is
+// triggered properly.
+TEST_F(HooksDhcpv4SrvTest, lease4DeclineSimple) {
     IfaceMgrTestConfig test_config(true);
     IfaceMgr::instance().openSockets4();
 
-    // Install a callout
+    // Install lease4_decline callout
     EXPECT_NO_THROW(HooksManager::preCalloutsLibraryHandle().registerCallout(
                         "lease4_decline", lease4_decline_callout));
 
@@ -2531,14 +2767,14 @@ TEST_F(HooksDhcpv4SrvTest, HooksDecline) {
     checkCalloutHandleReset(client.getContext().query_);
 }
 
-// Checks that decline4 hook is able to skip the packet.
-TEST_F(HooksDhcpv4SrvTest, HooksDeclineSkip) {
+// Test that the lease4_decline hook point can handle SKIP status.
+TEST_F(HooksDhcpv4SrvTest, lease4DeclineSkip) {
     IfaceMgrTestConfig test_config(true);
     IfaceMgr::instance().openSockets4();
 
-    // Install a callout
+    // Install lease4_decline_skip callout. It will set the status to skip
     EXPECT_NO_THROW(HooksManager::preCalloutsLibraryHandle().registerCallout(
-                        "lease4_decline", lease4_decline_skip_callout));
+                        "lease4_decline", lease4_decline_skip));
 
     HooksManager::setTestMode(true);
 
@@ -2581,14 +2817,14 @@ TEST_F(HooksDhcpv4SrvTest, HooksDeclineSkip) {
     checkCalloutHandleReset(client.getContext().query_);
 }
 
-// Checks that decline4 hook is able to drop the packet.
-TEST_F(HooksDhcpv4SrvTest, HooksDeclineDrop) {
+// Test that the lease4_decline hook point can handle DROP status.
+TEST_F(HooksDhcpv4SrvTest, lease4DeclineDrop) {
     IfaceMgrTestConfig test_config(true);
     IfaceMgr::instance().openSockets4();
 
-    // Install a callout
+    // Install lease4_decline_drop callout. It will set the status to drop
     EXPECT_NO_THROW(HooksManager::preCalloutsLibraryHandle().registerCallout(
-                        "lease4_decline", lease4_decline_drop_callout));
+                        "lease4_decline", lease4_decline_drop));
 
     HooksManager::setTestMode(true);
 
@@ -2631,57 +2867,9 @@ TEST_F(HooksDhcpv4SrvTest, HooksDeclineDrop) {
     checkCalloutHandleReset(client.getContext().query_);
 }
 
-// This test verifies that the leases4_committed callout is executed
-// with declined leases as argument when DHCPDECLINE is processed.
-TEST_F(HooksDhcpv4SrvTest, leases4CommittedDecline) {
-    IfaceMgrTestConfig test_config(true);
-    IfaceMgr::instance().openSockets4();
-
-    ASSERT_NO_THROW(HooksManager::preCalloutsLibraryHandle().registerCallout(
-                    "leases4_committed", leases4_committed_callout));
-
-
-    Dhcp4Client client(Dhcp4Client::SELECTING);
-    client.useRelay();
-    ASSERT_NO_THROW(client.doDORA(boost::shared_ptr<IOAddress>(new IOAddress("192.0.2.100"))));
-
-    // Make sure that we received a response
-    ASSERT_TRUE(client.getContext().response_);
-
-    resetCalloutBuffers();
-
-    ASSERT_NO_THROW(client.doDecline());
-
-    // Check that the callback called is indeed the one we installed
-    EXPECT_EQ("leases4_committed", callback_name_);
-
-    // Check if all expected parameters were really received
-    vector<string> expected_argument_names;
-    expected_argument_names.push_back("query4");
-    expected_argument_names.push_back("deleted_leases4");
-    expected_argument_names.push_back("leases4");
-
-    sort(expected_argument_names.begin(), expected_argument_names.end());
-    EXPECT_TRUE(callback_argument_names_ == expected_argument_names);
-
-    // No new allocations.
-    ASSERT_TRUE(callback_lease4_);
-    EXPECT_EQ("192.0.2.100", callback_lease4_->addr_.toText());
-    EXPECT_EQ(Lease::STATE_DECLINED, callback_lease4_->state_);
-
-    // Released lease should be returned.
-    EXPECT_FALSE(callback_deleted_lease4_);
-
-    // Pkt passed to a callout must be configured to copy retrieved options.
-    EXPECT_TRUE(callback_qry_options_copy_);
-
-    // Check if the callout handle state was reset after the callout.
-    checkCalloutHandleReset(client.getContext().query_);
-}
-
 // Checks if callout installed on host4_identifier can generate an
 // identifier and whether that identifier is actually used.
-TEST_F(HooksDhcpv4SrvTest, host4_identifier) {
+TEST_F(HooksDhcpv4SrvTest, host4Identifier) {
     IfaceMgrTestConfig test_config(true);
     IfaceMgr::instance().openSockets4();
 
@@ -2725,10 +2913,10 @@ TEST_F(HooksDhcpv4SrvTest, host4_identifier) {
                         "host4_identifier", host4_identifier_foo_callout));
 
     // Let's create a simple DISCOVER
-    Pkt4Ptr sol = generateSimpleDiscover();
+    Pkt4Ptr discover = generateSimpleDiscover();
 
     // Simulate that we have received that traffic
-    srv_->fakeReceive(sol);
+    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
@@ -2747,12 +2935,12 @@ TEST_F(HooksDhcpv4SrvTest, host4_identifier) {
     EXPECT_EQ("192.0.2.201", adv->getYiaddr().toText());
 
     // Check if the callout handle state was reset after the callout.
-    checkCalloutHandleReset(sol);
+    checkCalloutHandleReset(discover);
 }
 
-// Checks if callout installed on host4_identifier can generate identifier of
+// Checks if callout installed on host4_identifier can generate an identifier of
 // other type. This particular callout always returns hwaddr.
-TEST_F(HooksDhcpv4SrvTest, host4_identifier_hwaddr) {
+TEST_F(HooksDhcpv4SrvTest, host4IdentifierHWAddr) {
     IfaceMgrTestConfig test_config(true);
     IfaceMgr::instance().openSockets4();
 
@@ -2796,10 +2984,10 @@ TEST_F(HooksDhcpv4SrvTest, host4_identifier_hwaddr) {
                         "host4_identifier", host4_identifier_hwaddr_callout));
 
     // Let's create a simple DISCOVER
-    Pkt4Ptr sol = generateSimpleDiscover();
+    Pkt4Ptr discover = generateSimpleDiscover();
 
     // Simulate that we have received that traffic
-    srv_->fakeReceive(sol);
+    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
@@ -2818,10 +3006,9 @@ TEST_F(HooksDhcpv4SrvTest, host4_identifier_hwaddr) {
     EXPECT_EQ("192.0.2.201", adv->getYiaddr().toText());
 
     // Check if the callout handle state was reset after the callout.
-    checkCalloutHandleReset(sol);
+    checkCalloutHandleReset(discover);
 }
 
-
 // Verifies that libraries are unloaded by server destruction
 // The callout libraries write their library index number to a marker
 // file upon load and unload, making it simple to test whether or not
@@ -2993,7 +3180,7 @@ TEST_F(LoadUnloadDhcpv4SrvTest, Dhcpv4SrvConfigured) {
 }
 
 // This test verifies that parked-packet-limit is properly enforced.
-TEST_F(HooksDhcpv4SrvTest, parkedPacketLimit) {
+TEST_F(HooksDhcpv4SrvTest, leases4ParkedPacketLimit) {
     IfaceMgrTestConfig test_config(true);
 
     // Configure 1 directly reachable subnet, parked-packet-limit of 1.
@@ -3118,3 +3305,4 @@ TEST_F(HooksDhcpv4SrvTest, parkedPacketLimit) {
     EXPECT_EQ(1, getStatistic("pkt4-receive-drop"));
 }
 
+}  // namespace
index a11cfe67355b27818470630d73ce1c9967057c01..f5769474cbf470b58d4f173defacd33a0df1c5bc 100644 (file)
@@ -106,7 +106,6 @@ const char* OOR_CONFIGS[] = {
         "}"
     "}",
 
-
 // Configuration 3 - different subnet with reservations
     "{ \"interfaces-config\": {"
         "      \"interfaces\": [ \"*\" ]"
@@ -164,35 +163,35 @@ const char* OOR_CONFIGS[] = {
 
 };
 
-/// @brief Enum for indexing into the array of configurations.
-/// These were created to make the test cases easier to follow.
-enum CfgIndex {
-    REF_CFG = 0,
-    DIFF_POOL,
-    DIFF_POOL_NO_HR,
-    DIFF_SUBNET,
-    DIFF_SUBNET_NO_HR,
-    NO_HR
-};
-
-/// @brief Enum for specifying expected response to client renewal attempt
-enum RenewOutcome {
-    DOES_RENEW,
-    DOES_NOT_RENEW,
-    DOES_NOT_NAK
-};
-
-/// @brief Enum for specifying expected response to client release attempt
-enum ReleaseOutcome {
-    DOES_RELEASE,
-    DOES_NOT_RELEASE
-};
-
 /// @brief Test fixture class for testing various exchanges when the client's
 /// leased address is out of range due to configuration changes.
 class OutOfRangeTest : public Dhcpv4SrvTest {
 public:
-    D2ClientMgr& d2_mgr_;
+
+    /// @brief Enum for indexing into the array of configurations.
+    /// These were created to make the test cases easier to follow.
+    enum CfgIndex {
+        REF_CFG = 0,
+        DIFF_POOL,
+        DIFF_POOL_NO_HR,
+        DIFF_SUBNET,
+        DIFF_SUBNET_NO_HR,
+        NO_HR
+    };
+
+    /// @brief Enum for specifying expected response to client renewal attempt.
+    enum RenewOutcome {
+        DOES_RENEW,
+        DOES_NOT_RENEW,
+        DOES_NOT_NAK
+    };
+
+    /// @brief Enum for specifying expected response to client release attempt.
+    enum ReleaseOutcome {
+        DOES_RELEASE_EXPIRE,
+        DOES_RELEASE_DELETE,
+        DOES_NOT_RELEASE
+    };
 
     /// @brief Constructor.
     ///
@@ -209,9 +208,18 @@ public:
     ~OutOfRangeTest() {
     }
 
-    void configure(const std::string& config, Dhcp4Client& client) {
+    /// @brief Configure specified DHCP server using JSON string.
+    ///
+    /// @param config String holding server configuration in JSON format.
+    /// @param client Instance of the client.
+    /// @param disable_affinity A boolean flag which indicates if lease affinity
+    /// should be disabled.
+    void configure(const std::string& config, Dhcp4Client& client,
+                   const bool disable_affinity = true) {
         NakedDhcpv4Srv& server = *client.getServer();
-        ASSERT_NO_FATAL_FAILURE(Dhcpv4SrvTest::configure(config, server));
+        ASSERT_NO_FATAL_FAILURE(Dhcpv4SrvTest::configure(config, server, true,
+                                                         true, true, false,
+                                                         disable_affinity));
         if (d2_mgr_.ddnsEnabled()) {
             ASSERT_NO_THROW(server.startD2());
         }
@@ -266,32 +274,39 @@ public:
     /// @param renew_outcome - expected server reaction in response to the
     /// client's stage two renewal attempt.
     /// @param release_outcome - expected server reaction in response to the
-    /// client's stage two release attempt.  Currently defaults to DOES_RELEASE
+    /// client's stage two release attempt.  Currently defaults to DOES_RELEASE_DELETE
     /// as no cases have been identified which do otherwise.
-    void oorRenewReleaseTest(enum CfgIndex cfg_idx,
+    /// @param disable_affinity A boolean flag which indicates if lease affinity
+    /// should be disabled. In the case lease affinity is enabled, the lease is
+    /// not removed, but instead it is expired, and no DNS update is performed.
+    void oorRenewReleaseTest(CfgIndex cfg_idx,
                              const std::string& hwaddress,
                              const std::string& expected_address,
-                             enum RenewOutcome renew_outcome,
-                             enum ReleaseOutcome release_outcome = DOES_RELEASE);
+                             RenewOutcome renew_outcome,
+                             ReleaseOutcome release_outcome = DOES_RELEASE_DELETE,
+                             const bool disable_affinity = true);
+
+    /// @brief D2 client manager.
+    D2ClientMgr& d2_mgr_;
 
     /// @brief Interface Manager's fake configuration control.
     IfaceMgrTestConfig iface_mgr_test_config_;
-
 };
 
 void
-OutOfRangeTest::oorRenewReleaseTest(enum CfgIndex cfg_idx,
-                             const std::string& hwaddress,
-                             const std::string& expected_address,
-                             enum RenewOutcome renew_outcome,
-                             enum ReleaseOutcome release_outcome) {
+OutOfRangeTest::oorRenewReleaseTest(CfgIndex cfg_idx,
+                                    const std::string& hwaddress,
+                                    const std::string& expected_address,
+                                    RenewOutcome renew_outcome,
+                                    ReleaseOutcome release_outcome,
+                                    const bool disable_affinity) {
     // STAGE ONE:
 
     // Step 1 is to acquire the lease
     Dhcp4Client client(Dhcp4Client::SELECTING);
 
     // Configure DHCP server.
-    configure(OOR_CONFIGS[REF_CFG], client);
+    configure(OOR_CONFIGS[REF_CFG], client, disable_affinity);
 
     // Set the host name so DNS updates will be performed
     client.includeHostname("test.example.com");
@@ -338,7 +353,7 @@ OutOfRangeTest::oorRenewReleaseTest(enum CfgIndex cfg_idx,
     // STAGE TWO:
 
     // Now reconfigure which should render our leased address out-of-range
-    configure(OOR_CONFIGS[cfg_idx], client);
+    configure(OOR_CONFIGS[cfg_idx], client, disable_affinity);
 
     // Try to renew after the configuration change..
     ASSERT_NO_THROW(client.doRequest());
@@ -372,55 +387,86 @@ OutOfRangeTest::oorRenewReleaseTest(enum CfgIndex cfg_idx,
 
     lease = LeaseMgrFactory::instance().getLease4(leased_address);
 
-    if (release_outcome == DOES_RELEASE) {
+    if (release_outcome == DOES_RELEASE_DELETE) {
         EXPECT_FALSE(lease);
         // Verify the DNS remove was queued.
         verifyNameChangeRequest(isc::dhcp_ddns::CHG_REMOVE,
                                 leased_address.toText());
     } else {
         // Lease should still exist, and no NCR should be queued.
-        EXPECT_TRUE(lease);
+        ASSERT_TRUE(lease);
         EXPECT_EQ(0, d2_mgr_.getQueueSize());
+        if (release_outcome == DOES_RELEASE_EXPIRE) {
+            EXPECT_TRUE(lease->expired());
+        } else {
+            EXPECT_FALSE(lease->expired());
+        }
     }
 }
 
-
 // Verifies that once-valid lease, whose address is no longer
 // within the subnet's pool:
 //
 // a: Is NAKed upon a renewal attempt
-// b: Is released properly upon release, including DNS removal
+// b: Is deleted properly upon release, including DNS removal
 //
 TEST_F(OutOfRangeTest, dynamicOutOfPool) {
 
     std::string hwaddress = "";
     std::string expected_address = "";
 
-    oorRenewReleaseTest(DIFF_POOL, hwaddress, expected_address,
-                        DOES_NOT_NAK);
+    oorRenewReleaseTest(DIFF_POOL, hwaddress, expected_address, DOES_NOT_NAK);
+}
+
+// Verifies that once-valid lease, whose address is no longer
+// within the subnet's pool:
+//
+// a: Is NAKed upon a renewal attempt
+// b: Is expired properly upon release, including no DNS removal
+//
+TEST_F(OutOfRangeTest, dynamicOutOfPoolNoDelete) {
+
+    std::string hwaddress = "";
+    std::string expected_address = "";
 
+    oorRenewReleaseTest(DIFF_POOL, hwaddress, expected_address, DOES_NOT_NAK,
+                        DOES_RELEASE_EXPIRE, false);
 }
 
 // Verifies that once-valid lease whose address is no longer
 // within any configured subnet:
 //
 // a: Is NAKed upon a renewal attempt
-// b: Is released properly upon release, including DNS removal
+// b: Is deleted properly upon release, including DNS removal
 //
 TEST_F(OutOfRangeTest, dynamicOutOfSubnet) {
 
     std::string hwaddress = "";
     std::string expected_address = "";
 
-    oorRenewReleaseTest(DIFF_SUBNET, hwaddress, expected_address,
-                        DOES_NOT_RENEW);
+    oorRenewReleaseTest(DIFF_SUBNET, hwaddress, expected_address, DOES_NOT_RENEW);
+}
+
+// Verifies that once-valid lease whose address is no longer
+// within any configured subnet:
+//
+// a: Is NAKed upon a renewal attempt
+// b: Is expired properly upon release, including no DNS removal
+//
+TEST_F(OutOfRangeTest, dynamicOutOfSubnetNoDelete) {
+
+    std::string hwaddress = "";
+    std::string expected_address = "";
+
+    oorRenewReleaseTest(DIFF_SUBNET, hwaddress, expected_address, DOES_NOT_RENEW,
+                        DOES_RELEASE_EXPIRE, false);
 }
 
 // Test verifies that once-valid dynamic address host reservation,
 // whose address is no longer within the subnet's pool:
 //
 // a: Is NAKed upon a renewal attempt
-// b: Is released properly upon release, including DNS removal
+// b: Is deleted properly upon release, including DNS removal
 //
 TEST_F(OutOfRangeTest, dynamicHostOutOfPool) {
     std::string hwaddress = "dd:dd:dd:dd:dd:01";
@@ -429,18 +475,45 @@ TEST_F(OutOfRangeTest, dynamicHostOutOfPool) {
     oorRenewReleaseTest(DIFF_POOL, hwaddress, expected_address, DOES_NOT_NAK);
 }
 
+// Test verifies that once-valid dynamic address host reservation,
+// whose address is no longer within the subnet's pool:
+//
+// a: Is NAKed upon a renewal attempt
+// b: Is expired properly upon release, including no DNS removal
+//
+TEST_F(OutOfRangeTest, dynamicHostOutOfPoolNoDelete) {
+    std::string hwaddress = "dd:dd:dd:dd:dd:01";
+    std::string expected_address = "";
+
+    oorRenewReleaseTest(DIFF_POOL, hwaddress, expected_address, DOES_NOT_NAK,
+                        DOES_RELEASE_EXPIRE, false);
+}
+
 // Test verifies that once-valid dynamic address host reservation,
 // whose address is no longer within any configured subnet:
 //
 // a: Is NAKed upon a renewal attempt
-// b: Is released properly upon release, including DNS removal
+// b: Is deleted properly upon release, including DNS removal
 //
 TEST_F(OutOfRangeTest, dynamicHostOutOfSubnet) {
     std::string hwaddress = "dd:dd:dd:dd:dd:01";
     std::string expected_address = "";
 
-    oorRenewReleaseTest(DIFF_SUBNET, hwaddress, expected_address,
-                        DOES_NOT_RENEW);
+    oorRenewReleaseTest(DIFF_SUBNET, hwaddress, expected_address, DOES_NOT_RENEW);
+}
+
+// Test verifies that once-valid dynamic address host reservation,
+// whose address is no longer within any configured subnet:
+//
+// a: Is NAKed upon a renewal attempt
+// b: Is expired properly upon release, including no DNS removal
+//
+TEST_F(OutOfRangeTest, dynamicHostOutOfSubnetNoDelete) {
+    std::string hwaddress = "dd:dd:dd:dd:dd:01";
+    std::string expected_address = "";
+
+    oorRenewReleaseTest(DIFF_SUBNET, hwaddress, expected_address, DOES_NOT_RENEW,
+                        DOES_RELEASE_EXPIRE, false);
 }
 
 // Test verifies that once-valid dynamic address host reservation,
@@ -448,7 +521,7 @@ TEST_F(OutOfRangeTest, dynamicHostOutOfSubnet) {
 // reservation has been removed:
 //
 // a: Is allowed to renew
-// b: Is released properly upon release, including DNS removal
+// b: Is deleted properly upon release, including DNS removal
 //
 TEST_F(OutOfRangeTest, dynamicHostReservationRemoved) {
     Dhcp4Client client(Dhcp4Client::SELECTING);
@@ -459,12 +532,29 @@ TEST_F(OutOfRangeTest, dynamicHostReservationRemoved) {
     oorRenewReleaseTest(NO_HR, hwaddress, expected_address, DOES_RENEW);
 }
 
+// Test verifies that once-valid dynamic address host reservation,
+// whose address is within the configured subnet and pool, but whose
+// reservation has been removed:
+//
+// a: Is allowed to renew
+// b: Is expired properly upon release, including no DNS removal
+//
+TEST_F(OutOfRangeTest, dynamicHostReservationRemovedNoDelete) {
+    Dhcp4Client client(Dhcp4Client::SELECTING);
+
+    std::string hwaddress = "dd:dd:dd:dd:dd:01";
+    std::string expected_address = "";
+
+    oorRenewReleaseTest(NO_HR, hwaddress, expected_address, DOES_RENEW,
+                        DOES_RELEASE_EXPIRE, false);
+}
+
 // Test verifies that once-valid dynamic address host reservation,
 // whose address is no longer within any configured subnet, and which
 // no longer has reservation defined:
 //
 // a: Is NAKed upon a renewal attempt
-// b: Is released properly upon release, including DNS removal
+// b: Is deleted properly upon release, including DNS removal
 //
 TEST_F(OutOfRangeTest, dynamicHostOutOfSubnetReservationRemoved) {
     Dhcp4Client client(Dhcp4Client::SELECTING);
@@ -472,30 +562,58 @@ TEST_F(OutOfRangeTest, dynamicHostOutOfSubnetReservationRemoved) {
     std::string hwaddress = "dd:dd:dd:dd:dd:01";
     std::string expected_address = "";
 
-    oorRenewReleaseTest(DIFF_SUBNET_NO_HR, hwaddress, expected_address,
-                        DOES_NOT_RENEW);
+    oorRenewReleaseTest(DIFF_SUBNET_NO_HR, hwaddress, expected_address, DOES_NOT_RENEW);
+}
+
+// Test verifies that once-valid dynamic address host reservation,
+// whose address is no longer within any configured subnet, and which
+// no longer has reservation defined:
+//
+// a: Is NAKed upon a renewal attempt
+// b: Is expired properly upon release, including no DNS removal
+//
+TEST_F(OutOfRangeTest, dynamicHostOutOfSubnetReservationRemovedNoDelete) {
+    Dhcp4Client client(Dhcp4Client::SELECTING);
+
+    std::string hwaddress = "dd:dd:dd:dd:dd:01";
+    std::string expected_address = "";
+
+    oorRenewReleaseTest(DIFF_SUBNET_NO_HR, hwaddress, expected_address, DOES_NOT_RENEW,
+                        DOES_RELEASE_EXPIRE, false);
 }
 
 // Test verifies that once-valid in-subnet fixed-address host reservation,
 // after the subnet pool changes:
 //
 // a: Is NAK'd upon a renewal attempt
-// b: Is released properly upon release, including DNS removal
+// b: Is deleted properly upon release, including DNS removal
 //
 TEST_F(OutOfRangeTest, fixedHostOutOfSubnet) {
     std::string hwaddress = "ff:ff:ff:ff:ff:01";
     std::string expected_address = "10.0.0.7";
 
-    oorRenewReleaseTest(DIFF_SUBNET, hwaddress, expected_address,
-                        DOES_NOT_RENEW);
+    oorRenewReleaseTest(DIFF_SUBNET, hwaddress, expected_address, DOES_NOT_RENEW);
 }
 
+// Test verifies that once-valid in-subnet fixed-address host reservation,
+// after the subnet pool changes:
+//
+// a: Is NAK'd upon a renewal attempt
+// b: Is expired properly upon release, including no DNS removal
+//
+TEST_F(OutOfRangeTest, fixedHostOutOfSubnetNoDelete) {
+    std::string hwaddress = "ff:ff:ff:ff:ff:01";
+    std::string expected_address = "10.0.0.7";
+
+    oorRenewReleaseTest(DIFF_SUBNET, hwaddress, expected_address, DOES_NOT_RENEW,
+                        DOES_RELEASE_EXPIRE, false);
+}
 
 // Test verifies that once-valid in-subnet fixed-address host reservation,
 // after the subnet pool is changed:
 //
 // a: Is ACK'd upon a renewal attempt
-// b: Is released properly upon release, including DNS removal
+// b: Is deleted properly upon release, including DNS removal
 //
 TEST_F(OutOfRangeTest, fixedHostDifferentPool) {
     std::string hwaddress = "ff:ff:ff:ff:ff:01";
@@ -504,11 +622,25 @@ TEST_F(OutOfRangeTest, fixedHostDifferentPool) {
     oorRenewReleaseTest(DIFF_POOL, hwaddress, expected_address, DOES_RENEW);
 }
 
+// Test verifies that once-valid in-subnet fixed-address host reservation,
+// after the subnet pool is changed:
+//
+// a: Is ACK'd upon a renewal attempt
+// b: Is expired properly upon release, including no DNS removal
+//
+TEST_F(OutOfRangeTest, fixedHostDifferentPoolNoDelete) {
+    std::string hwaddress = "ff:ff:ff:ff:ff:01";
+    std::string expected_address = "10.0.0.7";
+
+    oorRenewReleaseTest(DIFF_POOL, hwaddress, expected_address, DOES_RENEW,
+                        DOES_RELEASE_EXPIRE, false);
+}
+
 // Test verifies that once-valid in-subnet fixed-address host reservation,
 // whose reservation has been removed from the configuration
 //
 // a: Is NAK'd upon a renewal attempt
-// b: Is released properly upon release, including DNS removal
+// b: Is deleted properly upon release, including DNS removal
 //
 TEST_F(OutOfRangeTest, fixedHostReservationRemoved) {
     std::string hwaddress = "ff:ff:ff:ff:ff:01";
@@ -517,18 +649,46 @@ TEST_F(OutOfRangeTest, fixedHostReservationRemoved) {
     oorRenewReleaseTest(NO_HR, hwaddress, expected_address, DOES_NOT_NAK);
 }
 
+
+// Test verifies that once-valid in-subnet fixed-address host reservation,
+// whose reservation has been removed from the configuration
+//
+// a: Is NAK'd upon a renewal attempt
+// b: Is expired properly upon release, including no DNS removal
+//
+TEST_F(OutOfRangeTest, fixedHostReservationRemovedNoDelete) {
+    std::string hwaddress = "ff:ff:ff:ff:ff:01";
+    std::string expected_address = "10.0.0.7";
+
+    oorRenewReleaseTest(NO_HR, hwaddress, expected_address, DOES_NOT_NAK,
+                        DOES_RELEASE_EXPIRE, false);
+}
+
 // Test verifies that once-valid fixed address host reservation,
 // whose address is no longer within any configured subnet
 //
 // a: Is NAKed upon a renewal attempt
-// b: Is released properly upon release, including DNS removal
+// b: Is deleted properly upon release, including DNS removal
 //
 TEST_F(OutOfRangeTest, fixedHostOutOfSubnetReservationRemoved) {
     std::string hwaddress = "ff:ff:ff:ff:ff:01";
     std::string expected_address = "10.0.0.7";
 
-    oorRenewReleaseTest(DIFF_SUBNET_NO_HR, hwaddress, expected_address,
-                        DOES_NOT_RENEW);
+    oorRenewReleaseTest(DIFF_SUBNET_NO_HR, hwaddress, expected_address, DOES_NOT_RENEW);
+}
+
+// Test verifies that once-valid fixed address host reservation,
+// whose address is no longer within any configured subnet
+//
+// a: Is NAKed upon a renewal attempt
+// b: Is expired properly upon release, including no DNS removal
+//
+TEST_F(OutOfRangeTest, fixedHostOutOfSubnetReservationRemovedNoDelete) {
+    std::string hwaddress = "ff:ff:ff:ff:ff:01";
+    std::string expected_address = "10.0.0.7";
+
+    oorRenewReleaseTest(DIFF_SUBNET_NO_HR, hwaddress, expected_address, DOES_NOT_RENEW,
+                        DOES_RELEASE_EXPIRE, false);
 }
 
 } // end of anonymous namespace
index f2f66f32cf9f7baac2ae70d5bb75c4b693d87ab0..cff11d5ec3b7f080ca2c4c86a9dd7c66a8a6d105 100644 (file)
@@ -61,7 +61,8 @@ class ReleaseTest : public Dhcpv4SrvTest {
 public:
 
     enum ExpectedResult {
-        SHOULD_PASS,
+        SHOULD_PASS_EXPIRED,
+        SHOULD_PASS_DELETED,
         SHOULD_FAIL
     };
 
@@ -69,8 +70,7 @@ public:
     ///
     /// Sets up fake interfaces.
     ReleaseTest()
-        : Dhcpv4SrvTest(),
-          iface_mgr_test_config_(true) {
+        : Dhcpv4SrvTest(), iface_mgr_test_config_(true) {
     }
 
     /// @brief Performs 4-way exchange to obtain new lease.
@@ -84,18 +84,21 @@ public:
     /// @param client_id_1 Client id to be used to acquire the lease.
     /// @param hw_address_2 HW Address to be used to release the lease.
     /// @param client_id_2 Client id to be used to release the lease.
-    /// @param expected_result SHOULD_PASS if the lease is expected to
-    /// be successfully released, or SHOULD_FAIL if the lease is expected
-    /// to not be released.
+    /// @param expected_result SHOULD_PASS_EXPIRED if the lease is expected to
+    /// be successfully released and expired, SHOULD_PASS_DELETED if the lease
+    /// is expected to be successfully released and deleted, or SHOULD_FAIL if
+    /// the lease is expected to not be released.
+    /// @param disable_affinity A boolean flag which indicates if lease affinity
+    /// should be disabled.
     void acquireAndRelease(const std::string& hw_address_1,
                            const std::string& client_id_1,
                            const std::string& hw_address_2,
                            const std::string& client_id_2,
-                           ExpectedResult expected_result);
+                           ExpectedResult expected_result,
+                           const bool disable_affinity = true);
 
     /// @brief Interface Manager's fake configuration control.
     IfaceMgrTestConfig iface_mgr_test_config_;
-
 };
 
 void
@@ -123,11 +126,12 @@ ReleaseTest::acquireAndRelease(const std::string& hw_address_1,
                                const std::string& client_id_1,
                                const std::string& hw_address_2,
                                const std::string& client_id_2,
-                               ExpectedResult expected_result) {
+                               ExpectedResult expected_result,
+                               const bool disable_affinity) {
     CfgMgr::instance().clear();
     Dhcp4Client client(Dhcp4Client::SELECTING);
     // Configure DHCP server.
-    configure(RELEASE_CONFIGS[0], *client.getServer());
+    configure(RELEASE_CONFIGS[0], *client.getServer(), true, true, true, false, disable_affinity);
     // Explicitly set the client id.
     client.includeClientId(client_id_1);
     // Explicitly set the HW address.
@@ -167,7 +171,16 @@ ReleaseTest::acquireAndRelease(const std::string& hw_address_1,
     // We check if the release process was successful by checking if the
     // lease is in the database. It is expected that it is not present,
     // i.e. has been deleted with the release.
-    if (expected_result == SHOULD_PASS) {
+    if (expected_result == SHOULD_PASS_EXPIRED) {
+        ASSERT_TRUE(lease);
+
+        // The update succeeded, so the assigned-address should be expired
+        EXPECT_TRUE(lease->expired());
+
+        // No lease has been removed, so the assigned-address should be the same
+        // as before
+        EXPECT_EQ(before, after);
+    } else if (expected_result == SHOULD_PASS_DELETED) {
         EXPECT_FALSE(lease);
 
         // The removal succeeded, so the assigned-addresses statistic should
@@ -186,7 +199,14 @@ ReleaseTest::acquireAndRelease(const std::string& hw_address_1,
 TEST_F(ReleaseTest, releaseNoIdentifierChange) {
     acquireAndRelease("01:02:03:04:05:06", "12:14",
                       "01:02:03:04:05:06", "12:14",
-                      SHOULD_PASS);
+                      SHOULD_PASS_DELETED);
+}
+
+// This test checks that the client can acquire and release the lease.
+TEST_F(ReleaseTest, releaseNoDeleteNoIdentifierChange) {
+    acquireAndRelease("01:02:03:04:05:06", "12:14",
+                      "01:02:03:04:05:06", "12:14",
+                      SHOULD_PASS_EXPIRED, false);
 }
 
 // This test verifies the release correctness in the following case:
@@ -197,7 +217,18 @@ TEST_F(ReleaseTest, releaseNoIdentifierChange) {
 TEST_F(ReleaseTest, releaseHWAddressOnly) {
     acquireAndRelease("01:02:03:04:05:06", "",
                       "01:02:03:04:05:06", "",
-                      SHOULD_PASS);
+                      SHOULD_PASS_DELETED);
+}
+
+// This test verifies the release correctness in the following case:
+// - Client acquires new lease using HW address only
+// - Client sends the DHCPRELEASE with valid HW address and without
+//   client identifier.
+// - The server successfully releases the lease.
+TEST_F(ReleaseTest, releaseNoDeleteHWAddressOnly) {
+    acquireAndRelease("01:02:03:04:05:06", "",
+                      "01:02:03:04:05:06", "",
+                      SHOULD_PASS_EXPIRED, false);
 }
 
 // This test verifies the release correctness in the following case:
@@ -208,7 +239,18 @@ TEST_F(ReleaseTest, releaseHWAddressOnly) {
 TEST_F(ReleaseTest, releaseNoClientId) {
     acquireAndRelease("01:02:03:04:05:06", "12:14",
                       "01:02:03:04:05:06", "",
-                      SHOULD_PASS);
+                      SHOULD_PASS_DELETED);
+}
+
+// This test verifies the release correctness in the following case:
+// - Client acquires new lease using the client identifier and HW address
+// - Client sends the DHCPRELEASE with valid HW address but with no
+//   client identifier.
+// - The server successfully releases the lease.
+TEST_F(ReleaseTest, releaseNoDeleteNoClientId) {
+    acquireAndRelease("01:02:03:04:05:06", "12:14",
+                      "01:02:03:04:05:06", "",
+                      SHOULD_PASS_EXPIRED, false);
 }
 
 // This test verifies the release correctness in the following case:
@@ -220,7 +262,19 @@ TEST_F(ReleaseTest, releaseNoClientId) {
 TEST_F(ReleaseTest, releaseNoClientId2) {
     acquireAndRelease("01:02:03:04:05:06", "",
                       "01:02:03:04:05:06", "12:14",
-                      SHOULD_PASS);
+                      SHOULD_PASS_DELETED);
+}
+
+// This test verifies the release correctness in the following case:
+// - Client acquires new lease using HW address
+// - Client sends the DHCPRELEASE with valid HW address and some
+//   client identifier.
+// - The server identifies the lease using HW address and releases
+//   this lease.
+TEST_F(ReleaseTest, releaseNoDeleteNoClientId2) {
+    acquireAndRelease("01:02:03:04:05:06", "",
+                      "01:02:03:04:05:06", "12:14",
+                      SHOULD_PASS_EXPIRED, false);
 }
 
 // This test checks the server's behavior in the following case:
@@ -243,7 +297,19 @@ TEST_F(ReleaseTest, releaseNonMatchingClientId) {
 TEST_F(ReleaseTest, releaseNonMatchingHWAddress) {
     acquireAndRelease("01:02:03:04:05:06", "12:14",
                       "06:06:06:06:06:06", "12:14",
-                      SHOULD_PASS);
+                      SHOULD_PASS_DELETED);
+}
+
+// This test checks the server's behavior in the following case:
+// - Client acquires new lease using client identifier and HW address
+// - Client sends the DHCPRELEASE with the same client identifier but
+//   different HW address.
+// - The server uses client identifier to find the client's lease and
+//   releases it.
+TEST_F(ReleaseTest, releaseNoDeleteNonMatchingHWAddress) {
+    acquireAndRelease("01:02:03:04:05:06", "12:14",
+                      "06:06:06:06:06:06", "12:14",
+                      SHOULD_PASS_EXPIRED, false);
 }
 
 // This test checks the server's behavior in the following case:
@@ -273,6 +339,33 @@ TEST_F(ReleaseTest, releaseNonMatchingIPAddress) {
     ASSERT_TRUE(lease);
 }
 
+// This test checks the server's behavior in the following case:
+// - Client acquires new lease.
+// - Client sends DHCPRELEASE with the ciaddr set to a different
+//   address than it has acquired from the server.
+// - Server determines that the client is trying to release a
+//   wrong address and will refuse to release.
+TEST_F(ReleaseTest, releaseNoDeleteNonMatchingIPAddress) {
+    Dhcp4Client client(Dhcp4Client::SELECTING);
+    // Configure DHCP server.
+    configure(RELEASE_CONFIGS[0], *client.getServer(), true, true, true, false, false);
+    // Perform 4-way exchange to obtain a new lease.
+    acquireLease(client);
+
+    // Remember the acquired address.
+    IOAddress leased_address = client.config_.lease_.addr_;
+
+    // Modify the client's address to force it to release a different address
+    // than it has obtained from the server.
+    client.config_.lease_.addr_ = IOAddress(leased_address.toUint32() + 1);
+
+    // Send DHCPRELEASE and make sure it was unsuccessful, i.e. the lease
+    // remains in the database.
+    ASSERT_NO_THROW(client.doRelease());
+    Lease4Ptr lease = LeaseMgrFactory::instance().getLease4(leased_address);
+    ASSERT_TRUE(lease);
+}
+
 // This test verifies that an incoming RELEASE for an address within
 // a subnet that has been removed can still be released.
 TEST_F(ReleaseTest, releaseNoSubnet) {
@@ -296,4 +389,30 @@ TEST_F(ReleaseTest, releaseNoSubnet) {
     EXPECT_FALSE(lease);
 }
 
+// This test verifies that an incoming RELEASE for an address within
+// a subnet that has been removed can still be released.
+TEST_F(ReleaseTest, releaseNoDeleteNoSubnet) {
+    Dhcp4Client client(Dhcp4Client::SELECTING);
+    // Configure DHCP server.
+    configure(RELEASE_CONFIGS[0], *client.getServer(), true, true, true, false, false);
+    // Perform 4-way exchange to obtain a new lease.
+    acquireLease(client);
+
+    // Remember the acquired address.
+    IOAddress leased_address = client.config_.lease_.addr_;
+
+    // Release is as it was relayed
+    client.useRelay(true);
+
+    // Send the release
+    ASSERT_NO_THROW(client.doRelease());
+
+    // Check that the lease was not removed
+    Lease4Ptr lease = LeaseMgrFactory::instance().getLease4(leased_address);
+    ASSERT_TRUE(lease);
+
+    // Check That the lease has been expired
+    EXPECT_TRUE(lease->expired());
+}
+
 } // end of anonymous namespace
index f9c137c85812bcf049065398a56792260aea870c..10afcc2b1403962412ba8ef040d4adaf4d01829b 100644 (file)
@@ -2122,7 +2122,7 @@ TEST_F(Dhcpv4SharedNetworkTest, matchClientId) {
 
 // Shared network is selected based on the client class specified.
 TEST_F(Dhcpv4SharedNetworkTest, sharedNetworkSelectedByClass) {
-   // Create client #1.
+    // Create client #1.
     Dhcp4Client client1(Dhcp4Client::SELECTING);
     client1.setIfaceName("eth1");
     client1.setIfaceIndex(ETH1_INDEX);
index 4249c7f6e1c127052aa24af0b7cadd8b194598b3..b93c162c7cd380f1e541784957df00a66d9b13a7 100644 (file)
@@ -136,10 +136,14 @@ extern const isc::log::MessageID DHCP6_QUERY_DATA = "DHCP6_QUERY_DATA";
 extern const isc::log::MessageID DHCP6_RAPID_COMMIT = "DHCP6_RAPID_COMMIT";
 extern const isc::log::MessageID DHCP6_RECLAIM_EXPIRED_LEASES_FAIL = "DHCP6_RECLAIM_EXPIRED_LEASES_FAIL";
 extern const isc::log::MessageID DHCP6_RELEASE_NA = "DHCP6_RELEASE_NA";
+extern const isc::log::MessageID DHCP6_RELEASE_NA_DELETED = "DHCP6_RELEASE_NA_DELETED";
+extern const isc::log::MessageID DHCP6_RELEASE_NA_EXPIRED = "DHCP6_RELEASE_NA_EXPIRED";
 extern const isc::log::MessageID DHCP6_RELEASE_NA_FAIL = "DHCP6_RELEASE_NA_FAIL";
 extern const isc::log::MessageID DHCP6_RELEASE_NA_FAIL_WRONG_DUID = "DHCP6_RELEASE_NA_FAIL_WRONG_DUID";
 extern const isc::log::MessageID DHCP6_RELEASE_NA_FAIL_WRONG_IAID = "DHCP6_RELEASE_NA_FAIL_WRONG_IAID";
 extern const isc::log::MessageID DHCP6_RELEASE_PD = "DHCP6_RELEASE_PD";
+extern const isc::log::MessageID DHCP6_RELEASE_PD_DELETED = "DHCP6_RELEASE_PD_DELETED";
+extern const isc::log::MessageID DHCP6_RELEASE_PD_EXPIRED = "DHCP6_RELEASE_PD_EXPIRED";
 extern const isc::log::MessageID DHCP6_RELEASE_PD_FAIL = "DHCP6_RELEASE_PD_FAIL";
 extern const isc::log::MessageID DHCP6_RELEASE_PD_FAIL_WRONG_DUID = "DHCP6_RELEASE_PD_FAIL_WRONG_DUID";
 extern const isc::log::MessageID DHCP6_RELEASE_PD_FAIL_WRONG_IAID = "DHCP6_RELEASE_PD_FAIL_WRONG_IAID";
@@ -299,10 +303,14 @@ const char* values[] = {
     "DHCP6_RAPID_COMMIT", "%1: Rapid Commit option received, following 2-way exchange",
     "DHCP6_RECLAIM_EXPIRED_LEASES_FAIL", "failed to reclaim expired leases: %1",
     "DHCP6_RELEASE_NA", "%1: binding for address %2 and iaid=%3 was released properly",
+    "DHCP6_RELEASE_NA_DELETED", "%1: binding for address %2 and iaid=%3 was properly deleted on release",
+    "DHCP6_RELEASE_NA_EXPIRED", "%1: binding for address %2 and iaid=%3 was properly expired on release",
     "DHCP6_RELEASE_NA_FAIL", "%1: failed to remove address lease for address %2 and iaid=%3",
     "DHCP6_RELEASE_NA_FAIL_WRONG_DUID", "%1: client tried to release address %2, but it belongs to another client using duid=%3",
     "DHCP6_RELEASE_NA_FAIL_WRONG_IAID", "%1: client tried to release address %2, but it used wrong IAID (expected %3, but got %4)",
     "DHCP6_RELEASE_PD", "%1: prefix %2/%3 for iaid=%4 was released properly",
+    "DHCP6_RELEASE_PD_DELETED", "%1: prefix %2/%3 for iaid=%4 was properly deleted on release",
+    "DHCP6_RELEASE_PD_EXPIRED", "%1: prefix %2/%3 for iaid=%4 was properly expired on release",
     "DHCP6_RELEASE_PD_FAIL", "%1: failed to release prefix %2/%3 for iaid=%4",
     "DHCP6_RELEASE_PD_FAIL_WRONG_DUID", "%1: client tried to release prefix %2/%3, but it belongs to another client (duid=%4)",
     "DHCP6_RELEASE_PD_FAIL_WRONG_IAID", "%1: client tried to release prefix %2/%3, but it used wrong IAID (expected %4, but got %5)",
index 7e88e99a5bbd6d842da28fc4556cbff1935b0918..5ec8aa1eed97d17dbdd974240f910556fb20159b 100644 (file)
@@ -137,10 +137,14 @@ extern const isc::log::MessageID DHCP6_QUERY_DATA;
 extern const isc::log::MessageID DHCP6_RAPID_COMMIT;
 extern const isc::log::MessageID DHCP6_RECLAIM_EXPIRED_LEASES_FAIL;
 extern const isc::log::MessageID DHCP6_RELEASE_NA;
+extern const isc::log::MessageID DHCP6_RELEASE_NA_DELETED;
+extern const isc::log::MessageID DHCP6_RELEASE_NA_EXPIRED;
 extern const isc::log::MessageID DHCP6_RELEASE_NA_FAIL;
 extern const isc::log::MessageID DHCP6_RELEASE_NA_FAIL_WRONG_DUID;
 extern const isc::log::MessageID DHCP6_RELEASE_NA_FAIL_WRONG_IAID;
 extern const isc::log::MessageID DHCP6_RELEASE_PD;
+extern const isc::log::MessageID DHCP6_RELEASE_PD_DELETED;
+extern const isc::log::MessageID DHCP6_RELEASE_PD_EXPIRED;
 extern const isc::log::MessageID DHCP6_RELEASE_PD_FAIL;
 extern const isc::log::MessageID DHCP6_RELEASE_PD_FAIL_WRONG_DUID;
 extern const isc::log::MessageID DHCP6_RELEASE_PD_FAIL_WRONG_IAID;
index 049c61e1613f0f93636c91133ccc27d40d3cc2c2..470e2c1660e66668d8a7147673e24943621df9e6 100644 (file)
@@ -795,7 +795,21 @@ and provides the cause of failure.
 
 % DHCP6_RELEASE_NA %1: binding for address %2 and iaid=%3 was released properly
 This informational message indicates that an address was released properly. It
-is a normal operation during client shutdown.
+is a normal operation during client shutdown. The first argument includes
+the client and transaction identification information. The second and third
+argument hold the released IPv6 address and IAID respectively.
+
+% DHCP6_RELEASE_NA_EXPIRED %1: binding for address %2 and iaid=%3 was properly expired on release
+This informational message indicates that an address was properly expired on
+release. It is a normal operation during client shutdown. The first argument
+includes the client and transaction identification information. The second and
+third argument hold the released IPv6 address and IAID respectively.
+
+% DHCP6_RELEASE_NA_DELETED %1: binding for address %2 and iaid=%3 was properly deleted on release
+This informational message indicates that an address was properly deleted on
+release. It is a normal operation during client shutdown. The first argument
+includes the client and transaction identification information. The second and
+third argument hold the released IPv6 address and IAID respectively.
 
 % DHCP6_RELEASE_NA_FAIL %1: failed to remove address lease for address %2 and iaid=%3
 This error message indicates that the software failed to remove an address
@@ -825,8 +839,19 @@ support for multiple addresses is flawed.
 This informational message indicates that a prefix was released properly. It
 is a normal operation during client shutdown. The first argument holds
 the client and transaction identification information. The second and
-third argument define the prefix and its length. The fourth argument
-holds IAID.
+third argument hold the prefix and its length. The fourth argument holds IAID.
+
+% DHCP6_RELEASE_PD_EXPIRED %1: prefix %2/%3 for iaid=%4 was properly expired on release
+This informational message indicates that a prefix was properly expired on
+release. It is a normal operation during client shutdown. The first argument
+holds the client and transaction identification information. The second and
+third argument hold the prefix and its length. The fourth argument holds IAID.
+
+% DHCP6_RELEASE_PD_DELETED %1: prefix %2/%3 for iaid=%4 was properly deleted on release
+This informational message indicates that a prefix was properly deleted on
+release. It is a normal operation during client shutdown. The first argument
+holds the client and transaction identification information. The second and
+third argument hold the prefix and its length. The fourth argument holds IAID.
 
 % DHCP6_RELEASE_PD_FAIL %1: failed to release prefix %2/%3 for iaid=%4
 This error message indicates that the software failed to remove a prefix
index dfad23ba045d3f58557c9735151952bac63cc9f0..31bb3a7251887ce0799d9d92779cd295854c771a 100644 (file)
@@ -3127,9 +3127,23 @@ Dhcpv6Srv::releaseIA_NA(const DuidPtr& duid, const Pkt6Ptr& query,
 
     // Ok, we've passed all checks. Let's release this address.
     bool success = false; // was the removal operation successful?
+    bool expired = false; // explicitly expired instead of removed?
+    auto expiration_cfg = CfgMgr::instance().getCurrentCfg()->getCfgExpiration();
 
+    // Callout didn't indicate to skip the release process. Let's release
+    // the address.
     if (!skip) {
-        success = LeaseMgrFactory::instance().deleteLease(lease);
+        // Delete lease only if affinity is disabled.
+        if (expiration_cfg->getFlushReclaimedTimerWaitTime() &&
+            expiration_cfg->getHoldReclaimedTime()) {
+            // Expire the lease.
+            lease->cltt_ -= lease->valid_lft_ + 1;
+            LeaseMgrFactory::instance().updateLease6(lease);
+            expired = true;
+            success = true;
+        } else {
+            success = LeaseMgrFactory::instance().deleteLease(lease);
+        }
     }
 
     // Here the success should be true if we removed lease successfully
@@ -3157,15 +3171,27 @@ Dhcpv6Srv::releaseIA_NA(const DuidPtr& duid, const Pkt6Ptr& query,
         ia_rsp->addOption(createStatusCode(*query, *ia_rsp, STATUS_Success,
                           "Lease released. Thank you, please come again."));
 
-        // Need to decrease statistic for assigned addresses.
-        StatsMgr::instance().addValue(
-            StatsMgr::generateName("subnet", lease->subnet_id_, "assigned-nas"),
-            static_cast<int64_t>(-1));
+        if (expired) {
+            LOG_INFO(lease6_logger, DHCP6_RELEASE_NA_EXPIRED)
+                .arg(query->getLabel())
+                .arg(lease->addr_.toText())
+                .arg(lease->iaid_);
+        } else {
+            LOG_INFO(lease6_logger, DHCP6_RELEASE_NA_DELETED)
+                .arg(query->getLabel())
+                .arg(lease->addr_.toText())
+                .arg(lease->iaid_);
+
+            // Need to decrease statistic for assigned addresses.
+            StatsMgr::instance().addValue(
+                StatsMgr::generateName("subnet", lease->subnet_id_, "assigned-nas"),
+                static_cast<int64_t>(-1));
 
-        // Check if a lease has flags indicating that the FQDN update has
-        // been performed. If so, create NameChangeRequest which removes
-        // the entries.
-        queueNCR(CHG_REMOVE, lease);
+            // Check if a lease has flags indicating that the FQDN update has
+            // been performed. If so, create NameChangeRequest which removes
+            // the entries.
+            queueNCR(CHG_REMOVE, lease);
+        }
 
         return (ia_rsp);
     }
@@ -3280,20 +3306,36 @@ Dhcpv6Srv::releaseIA_PD(const DuidPtr& duid, const Pkt6Ptr& query,
         // Call all installed callouts
         HooksManager::callCallouts(Hooks.hook_index_lease6_release_, *callout_handle);
 
-        skip = callout_handle->getStatus() == CalloutHandle::NEXT_STEP_SKIP;
+        // 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->getStatus() == CalloutHandle::NEXT_STEP_SKIP) ||
+            (callout_handle->getStatus() == CalloutHandle::NEXT_STEP_DROP)) {
+            skip = true;
+            LOG_DEBUG(hooks_logger, DBG_DHCP6_HOOKS, DHCP6_HOOK_LEASE6_RELEASE_PD_SKIP)
+                .arg(query->getLabel());
+        }
     }
 
     // Ok, we've passed all checks. Let's release this prefix.
     bool success = false; // was the removal operation successful?
+    bool expired = false; // explicitly expired instead of removed?
+    auto expiration_cfg = CfgMgr::instance().getCurrentCfg()->getCfgExpiration();
 
+    // Callout didn't indicate to skip the release process. Let's release
+    // the prefix.
     if (!skip) {
-        success = LeaseMgrFactory::instance().deleteLease(lease);
-    } else {
-        // 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".
-        LOG_DEBUG(hooks_logger, DBG_DHCP6_HOOKS, DHCP6_HOOK_LEASE6_RELEASE_PD_SKIP)
-            .arg(query->getLabel());
+        // Delete lease only if affinity is disabled.
+        if (expiration_cfg->getFlushReclaimedTimerWaitTime() &&
+            expiration_cfg->getHoldReclaimedTime()) {
+            // Expire the lease.
+            lease->cltt_ -= lease->valid_lft_ + 1;
+            LeaseMgrFactory::instance().updateLease6(lease);
+            expired = true;
+            success = true;
+        } else {
+            success = LeaseMgrFactory::instance().deleteLease(lease);
+        }
     }
 
     // Here the success should be true if we removed lease successfully
@@ -3322,10 +3364,24 @@ Dhcpv6Srv::releaseIA_PD(const DuidPtr& duid, const Pkt6Ptr& query,
         ia_rsp->addOption(createStatusCode(*query, *ia_rsp, STATUS_Success,
                           "Lease released. Thank you, please come again."));
 
-        // Need to decrease statistic for assigned prefixes.
-        StatsMgr::instance().addValue(
-            StatsMgr::generateName("subnet", lease->subnet_id_, "assigned-pds"),
-            static_cast<int64_t>(-1));
+        if (expired) {
+            LOG_INFO(lease6_logger, DHCP6_RELEASE_PD_EXPIRED)
+                .arg(query->getLabel())
+                .arg(lease->addr_.toText())
+                .arg(static_cast<int>(lease->prefixlen_))
+                .arg(lease->iaid_);
+        } else {
+            LOG_INFO(lease6_logger, DHCP6_RELEASE_PD_DELETED)
+                .arg(query->getLabel())
+                .arg(lease->addr_.toText())
+                .arg(static_cast<int>(lease->prefixlen_))
+                .arg(lease->iaid_);
+
+            // Need to decrease statistic for assigned prefixes.
+            StatsMgr::instance().addValue(
+                StatsMgr::generateName("subnet", lease->subnet_id_, "assigned-pds"),
+                static_cast<int64_t>(-1));
+        }
     }
 
     return (ia_rsp);
index a3620d5cbbc712ad9eccb669a141bb8f9eedc30b..2a3d71b94a0d278cb48fe11b57dbe8a5dfaa0b5e 100644 (file)
@@ -2020,10 +2020,28 @@ TEST_F(Dhcpv6SrvTest, pdRenewCache) {
 // - lease is actually removed from LeaseMgr
 // - assigned-nas stats counter is properly decremented
 TEST_F(Dhcpv6SrvTest, ReleaseBasic) {
+    CfgMgr::instance().getCurrentCfg()->getCfgExpiration()->setFlushReclaimedTimerWaitTime(0);
+    CfgMgr::instance().getCurrentCfg()->getCfgExpiration()->setHoldReclaimedTime(0);
+
     testReleaseBasic(Lease::TYPE_NA, IOAddress("2001:db8:1:1::cafe:babe"),
                      IOAddress("2001:db8:1:1::cafe:babe"));
 }
 
+// This test verifies that incoming (positive) RELEASE with address can be
+// handled properly, that a REPLY is generated, that the response has status
+// code and that the lease is expired and not removed from the database.
+//
+// expected:
+// - returned REPLY message has copy of client-id
+// - returned REPLY message has server-id
+// - returned REPLY message has IA_NA that does not include an IAADDR
+// - lease is actually expired instead of being removed from LeaseMgr
+// - assigned-nas stats counter is unchanged
+TEST_F(Dhcpv6SrvTest, ReleaseBasicNoDelete) {
+    testReleaseBasic(Lease::TYPE_NA, IOAddress("2001:db8:1:1::cafe:babe"),
+                     IOAddress("2001:db8:1:1::cafe:babe"), false);
+}
+
 // This test verifies that incoming (positive) RELEASE with prefix can be
 // handled properly, that a REPLY is generated, that the response has
 // status code and that the lease is indeed removed from the database.
@@ -2035,10 +2053,28 @@ TEST_F(Dhcpv6SrvTest, ReleaseBasic) {
 // - lease is actually removed from LeaseMgr
 // - assigned-pds stats counter is properly decremented
 TEST_F(Dhcpv6SrvTest, pdReleaseBasic) {
+    CfgMgr::instance().getCurrentCfg()->getCfgExpiration()->setFlushReclaimedTimerWaitTime(0);
+    CfgMgr::instance().getCurrentCfg()->getCfgExpiration()->setHoldReclaimedTime(0);
+
     testReleaseBasic(Lease::TYPE_PD, IOAddress("2001:db8:1:2::"),
                      IOAddress("2001:db8:1:2::"));
 }
 
+// This test verifies that incoming (positive) RELEASE with prefix can be
+// handled properly, that a REPLY is generated, that the response has
+// status code and that the lease is expired and not removed from the database.
+//
+// expected:
+// - returned REPLY message has copy of client-id
+// - returned REPLY message has server-id
+// - returned REPLY message has IA_PD that does not include an IAPREFIX
+// - lease is actually expired instead of being removed from LeaseMgr
+// - assigned-pds stats counter is unchanged
+TEST_F(Dhcpv6SrvTest, pdReleaseBasicNoDelete) {
+    testReleaseBasic(Lease::TYPE_PD, IOAddress("2001:db8:1:2::"),
+                     IOAddress("2001:db8:1:2::"), false);
+}
+
 // This test verifies that incoming (invalid) RELEASE with an address
 // can be handled properly.
 //
index 4d0e79dcb8daf1b7b281b356bfeed5dc101e9572..d9c6e34df6d41a15cf0521013627c50a94cb94d2 100644 (file)
@@ -558,7 +558,8 @@ Dhcpv6SrvTest::testRenewSomeoneElsesLease(Lease::Type type, const IOAddress& add
 
 void
 Dhcpv6SrvTest::testReleaseBasic(Lease::Type type, const IOAddress& existing,
-                                const IOAddress& release_addr) {
+                                const IOAddress& release_addr,
+                                const bool disable_affinity) {
     NakedDhcpv6Srv srv(0);
 
     const uint32_t iaid = 234;
@@ -597,6 +598,10 @@ Dhcpv6SrvTest::testReleaseBasic(Lease::Type type, const IOAddress& existing,
                                               "assigned-pds");
     StatsMgr::instance().setValue(name, static_cast<int64_t>(1));
 
+    ObservationPtr stat = StatsMgr::instance().getObservation(name);
+    ASSERT_TRUE(stat);
+    uint64_t before = stat->getInteger().first;
+
     // Let's create a RELEASE
     Pkt6Ptr rel = createMessage(DHCPV6_RELEASE, type, release_addr, prefix_len,
                                 iaid);
@@ -626,20 +631,41 @@ Dhcpv6SrvTest::testReleaseBasic(Lease::Type type, const IOAddress& existing,
     checkServerId(reply, srv.getServerID());
     checkClientId(reply, clientid);
 
-    // Check that the lease is really gone in the database
-    // get lease by address
-    l = LeaseMgrFactory::instance().getLease6(type, release_addr);
-    ASSERT_FALSE(l);
+    if (disable_affinity) {
+        // Check that the lease is really gone in the database
+        // get lease by address
+        l = LeaseMgrFactory::instance().getLease6(type, release_addr);
+        ASSERT_FALSE(l);
+
+        // get lease by subnetid/duid/iaid combination
+        l = LeaseMgrFactory::instance().getLease6(type, *duid_, iaid,
+                                                  subnet_->getID());
+        ASSERT_FALSE(l);
+
+        // We should have decremented the address counter
+        stat = StatsMgr::instance().getObservation(name);
+        ASSERT_TRUE(stat);
+        EXPECT_EQ(0, stat->getInteger().first);
+    } else {
+        // Check that the lease is really gone in the database
+        // get lease by address
+        l = LeaseMgrFactory::instance().getLease6(type, release_addr);
+        ASSERT_TRUE(l);
 
-    // get lease by subnetid/duid/iaid combination
-    l = LeaseMgrFactory::instance().getLease6(type, *duid_, iaid,
-                                              subnet_->getID());
-    ASSERT_FALSE(l);
+        EXPECT_TRUE(l->expired());
 
-    // We should have decremented the address counter
-    ObservationPtr stat = StatsMgr::instance().getObservation(name);
-    ASSERT_TRUE(stat);
-    EXPECT_EQ(0, stat->getInteger().first);
+        // get lease by subnetid/duid/iaid combination
+        l = LeaseMgrFactory::instance().getLease6(type, *duid_, iaid,
+                                                  subnet_->getID());
+        ASSERT_TRUE(l);
+
+        EXPECT_TRUE(l->expired());
+
+        // We should have decremented the address counter
+        stat = StatsMgr::instance().getObservation(name);
+        ASSERT_TRUE(stat);
+        EXPECT_EQ(before, stat->getInteger().first);
+    }
 }
 
 void
@@ -815,8 +841,9 @@ Dhcpv6SrvTest::configure(const std::string& config,
                          const bool commit,
                          const bool open_sockets,
                          const bool create_managers,
-                         const bool test) {
-    configure(config, srv_, commit, open_sockets, create_managers, test);
+                         const bool test,
+                         const bool disable_affinity) {
+    configure(config, srv_, commit, open_sockets, create_managers, test, disable_affinity);
 }
 
 void
@@ -825,7 +852,8 @@ Dhcpv6SrvTest::configure(const std::string& config,
                          const bool commit,
                          const bool open_sockets,
                          const bool create_managers,
-                         const bool test) {
+                         const bool test,
+                         const bool disable_affinity) {
     setenv("KEA_LFC_EXECUTABLE", KEA_LFC_EXECUTABLE, 1);
     MultiThreadingCriticalSection cs;
     ConstElementPtr json;
@@ -863,6 +891,12 @@ Dhcpv6SrvTest::configure(const std::string& config,
         } );
     }
 
+    if (disable_affinity) {
+        auto expiration_cfg = CfgMgr::instance().getStagingCfg()->getCfgExpiration();
+        expiration_cfg->setFlushReclaimedTimerWaitTime(0);
+        expiration_cfg->setHoldReclaimedTime(0);
+    }
+
     try {
         CfgMultiThreading::apply(CfgMgr::instance().getStagingCfg()->getDHCPMultiThreading());
     } catch (const std::exception& ex) {
index 856f1a40f6aa05b5b9da542eec4d98b0051e43fc..48953ad39f28b8ffd859bf3c2355cec84e791850 100644 (file)
@@ -339,8 +339,8 @@ public:
     NakedDhcpv6SrvTest();
 
     // Generate IA_NA or IA_PD option with specified parameters
-    boost::shared_ptr<isc::dhcp::Option6IA> generateIA
-        (uint16_t type, uint32_t iaid, uint32_t t1, uint32_t t2);
+    boost::shared_ptr<isc::dhcp::Option6IA> generateIA(uint16_t type, uint32_t iaid,
+                                                       uint32_t t1, uint32_t t2);
 
     /// @brief generates interface-id option, based on text
     ///
@@ -400,8 +400,7 @@ public:
     // Checks if server response (ADVERTISE or REPLY) includes proper
     // server-id.
     void checkServerId(const isc::dhcp::Pkt6Ptr& rsp,
-                       const isc::dhcp::OptionPtr& expected_srvid)
-    {
+                       const isc::dhcp::OptionPtr& expected_srvid) {
         // check that server included its server-id
         isc::dhcp::OptionPtr tmp = rsp->getOption(D6O_SERVERID);
         EXPECT_EQ(tmp->getType(), expected_srvid->getType() );
@@ -412,8 +411,7 @@ public:
     // Checks if server response (ADVERTISE or REPLY) includes proper
     // client-id.
     void checkClientId(const isc::dhcp::Pkt6Ptr& rsp,
-                       const isc::dhcp::OptionPtr& expected_clientid)
-    {
+                       const isc::dhcp::OptionPtr& expected_clientid) {
         // check that server included our own client-id
         isc::dhcp::OptionPtr tmp = rsp->getOption(D6O_CLIENTID);
         ASSERT_TRUE(tmp);
@@ -429,8 +427,7 @@ public:
                           uint8_t expected_message_type,
                           uint32_t expected_transid,
                           uint16_t expected_status_code,
-                          uint32_t expected_t1, uint32_t expected_t2)
-    {
+                          uint32_t expected_t1, uint32_t expected_t2) {
         // Check if we get response at all
         checkResponse(rsp, expected_message_type, expected_transid);
 
@@ -463,14 +460,12 @@ public:
     /// @param expected_t1 expected T1 in IA_NA option
     /// @param expected_t2 expected T2 in IA_NA option
     /// @param check_addr whether to check for address with 0 lifetimes
-    void checkIA_NAStatusCode
-        (const boost::shared_ptr<isc::dhcp::Option6IA>& ia,
-         uint16_t expected_status_code, uint32_t expected_t1,
-         uint32_t expected_t2, bool check_addr = true);
+    void checkIA_NAStatusCode(const boost::shared_ptr<isc::dhcp::Option6IA>& ia,
+                              uint16_t expected_status_code, uint32_t expected_t1,
+                              uint32_t expected_t2, bool check_addr = true);
 
     void checkMsgStatusCode(const isc::dhcp::Pkt6Ptr& msg,
-                            uint16_t expected_status)
-    {
+                            uint16_t expected_status) {
         isc::dhcp::Option6StatusCodePtr status =
             boost::dynamic_pointer_cast<isc::dhcp::Option6StatusCode>
                 (msg->getOption(D6O_STATUS_CODE));
@@ -571,11 +566,14 @@ public:
     /// @param create_managers A boolean flag indicating if managers should be
     /// recreated.
     /// @param test A boolean flag which indicates if only testing config.
+    /// @param disable_affinity A boolean flag which indicates if lease affinity
+    /// should be disabled.
     void configure(const std::string& config,
                    const bool commit = true,
                    const bool open_sockets = false,
                    const bool create_managers = true,
-                   const bool test = false);
+                   const bool test = false,
+                   const bool disable_affinity = true);
 
     /// @brief Configure the DHCPv6 server using the JSON string.
     ///
@@ -588,12 +586,15 @@ public:
     /// @param create_managers A boolean flag indicating if managers should be
     /// recreated.
     /// @param test A boolean flag which indicates if only testing config.
+    /// @param disable_affinity A boolean flag which indicates if lease affinity
+    /// should be disabled.
     void configure(const std::string& config,
                    NakedDhcpv6Srv& srv,
                    const bool commit = true,
                    const bool open_sockets = false,
                    const bool create_managers = true,
-                   const bool test = false);
+                   const bool test = false,
+                   const bool disable_affinity = true);
 
     /// @brief Checks that server response (ADVERTISE or REPLY) contains proper
     ///        IA_NA option
@@ -810,10 +811,13 @@ public:
     /// @param type type (TYPE_NA or TYPE_PD)
     /// @param existing address to be preinserted into the database
     /// @param release_addr address being sent in RELEASE
+    /// @param disable_affinity A boolean flag which indicates if lease affinity
+    /// should be disabled.
     void
     testReleaseBasic(isc::dhcp::Lease::Type type,
                      const isc::asiolink::IOAddress& existing,
-                     const isc::asiolink::IOAddress& release_addr);
+                     const isc::asiolink::IOAddress& release_addr,
+                     const bool disable_affinity = true);
 
     /// @brief Performs negative RELEASE test
     ///
index 683a71cf6275146e06f0d0bfee7d851e9bffadbc..e3757df6354cfc44423d5e27775cb712bb28188f 100644 (file)
@@ -261,7 +261,6 @@ public:
         pkt->addOption(opt_ia);
     }
 
-
     /// @brief Adds IA option to the message.
     ///
     /// Added option holds status code.
@@ -484,7 +483,6 @@ public:
         }
     }
 
-
     /// @brief Tests that the client's message holding an FQDN is processed
     /// and that lease is acquired.
     ///
@@ -523,18 +521,18 @@ public:
 
         ASSERT_FALSE(drop);
         if (msg_type == DHCPV6_SOLICIT) {
-          ASSERT_NO_THROW(reply = srv_->processSolicit(ctx));
+            ASSERT_NO_THROW(reply = srv_->processSolicit(ctx));
 
         } else if (msg_type == DHCPV6_REQUEST) {
-          ASSERT_NO_THROW(reply = srv_->processRequest(ctx));
+            ASSERT_NO_THROW(reply = srv_->processRequest(ctx));
 
         } else if (msg_type == DHCPV6_RENEW) {
-          ASSERT_NO_THROW(reply = srv_->processRenew(ctx));
+            ASSERT_NO_THROW(reply = srv_->processRenew(ctx));
 
         } else if (msg_type == DHCPV6_RELEASE) {
             // For Release no lease will be acquired so we have to leave
             // function here.
-          ASSERT_NO_THROW(reply = srv_->processRelease(ctx));
+            ASSERT_NO_THROW(reply = srv_->processRelease(ctx));
             return;
         } else {
             // We are not interested in testing other message types.
@@ -944,7 +942,6 @@ TEST_F(FqdnDhcpv6SrvTest, noRemovalsWhenDisabled) {
     ASSERT_NO_THROW(queueNCR(CHG_REMOVE, lease_));
 }
 
-
 // Test creation of the NameChangeRequest to remove reverse mapping for the
 // given lease.
 TEST_F(FqdnDhcpv6SrvTest, createRemovalNameChangeRequestRev) {
@@ -1038,7 +1035,6 @@ TEST_F(FqdnDhcpv6SrvTest, processTwoRequestsDiffFqdn) {
                             "FAAAA3EBD29826B5C907B2C9268A6F52",
                             0, 4000);
 
-
     // Client may send another request message with a new domain-name. In this
     // case the same lease will be returned. The existing DNS entry needs to
     // be replaced with a new one. Server should determine that the different
@@ -1085,7 +1081,6 @@ TEST_F(FqdnDhcpv6SrvTest, processTwoRequestsSameFqdn) {
                             "FAAAA3EBD29826B5C907B2C9268A6F52",
                             0, 4000);
 
-
     // Client may send another request message with a same domain-name. In this
     // case the same lease will be returned. The existing DNS entry should be
     // left alone, so we expect no NameChangeRequests queued..
@@ -1123,7 +1118,6 @@ TEST_F(FqdnDhcpv6SrvTest, processRequestSolicit) {
 
 }
 
-
 // Test that client may send Request followed by the Renew, both holding
 // FQDN options, but each option holding different domain-name. The Renew
 // should result in generation of the two NameChangeRequests, one to remove
@@ -1236,7 +1230,6 @@ TEST_F(FqdnDhcpv6SrvTest, processRequestRenewFqdnFlags) {
     // Queue should be empty.
     ASSERT_EQ(0, d2_mgr_.getQueueSize());
 
-
     // Renew with both N and S flags = 0. This tells the server to update
     // both directions, which should change forward flag to true. This should
     // generate a reverse only remove and a dual add.
@@ -1276,8 +1269,10 @@ TEST_F(FqdnDhcpv6SrvTest, processRequestRenewFqdnFlags) {
                             lease_->cltt_, lease_->valid_lft_);
 }
 
-
 TEST_F(FqdnDhcpv6SrvTest, processRequestRelease) {
+    CfgMgr::instance().getCurrentCfg()->getCfgExpiration()->setFlushReclaimedTimerWaitTime(0);
+    CfgMgr::instance().getCurrentCfg()->getCfgExpiration()->setHoldReclaimedTime(0);
+
     // Create a Request message with FQDN option and generate server's
     // response using processRequest function. This will result in the
     // creation of a new lease and the appropriate NameChangeRequest
@@ -1311,6 +1306,33 @@ TEST_F(FqdnDhcpv6SrvTest, processRequestRelease) {
                             lease_->cltt_, lease_->valid_lft_);
 }
 
+TEST_F(FqdnDhcpv6SrvTest, processRequestReleaseNoDelete) {
+    // Create a Request message with FQDN option and generate server's
+    // response using processRequest function. This will result in the
+    // creation of a new lease and the appropriate NameChangeRequest
+    // to add both reverse and forward mapping to DNS.
+    testProcessMessage(DHCPV6_REQUEST, "myhost.example.com",
+                       "myhost.example.com.");
+
+    // The lease should have been recorded in the database.
+    lease_ = LeaseMgrFactory::instance().getLease6(Lease::TYPE_NA,
+                                                   IOAddress("2001:db8:1:1::dead:beef"));
+    ASSERT_TRUE(lease_);
+
+    ASSERT_EQ(1, d2_mgr_.getQueueSize());
+    verifyNameChangeRequest(isc::dhcp_ddns::CHG_ADD, true, true,
+                            "2001:db8:1:1::dead:beef",
+                            "000201415AA33D1187D148275136FA30300478"
+                            "FAAAA3EBD29826B5C907B2C9268A6F52",
+                            0, lease_->valid_lft_);
+
+    // Client may send Release message. In this case the lease should be
+    // expired and no NameChangeRequest to remove DNS entries is generated.
+    testProcessMessage(DHCPV6_RELEASE, "otherhost.example.com",
+                       "otherhost.example.com.");
+    ASSERT_EQ(0, d2_mgr_.getQueueSize());
+}
+
 // Checks that the server include DHCPv6 Client FQDN option in its
 // response even when client doesn't request this option using ORO.
 TEST_F(FqdnDhcpv6SrvTest, processRequestWithoutFqdn) {
@@ -1607,7 +1629,6 @@ TEST_F(FqdnDhcpv6SrvTest, replaceClientNameModeTest) {
                               CLIENT_NAME_PRESENT, NAME_NOT_REPLACED);
 }
 
-
 // Verifies that setting hostname-char-set sanitizes FQDN option
 // values received from clients.
 TEST_F(FqdnDhcpv6SrvTest, sanitizeFqdn) {
index fac2d438ac813a83e88388eb2818a1b3f55b7cd2..99be8bd5584c283c33260cadd30eae503e85cdcf 100644 (file)
@@ -8,26 +8,28 @@
 
 #include <asiolink/io_address.h>
 #include <asiolink/io_service.h>
+#include <cc/command_interpreter.h>
+#include <config/command_mgr.h>
 #include <dhcp/dhcp6.h>
 #include <dhcp/duid.h>
-#include <dhcp6/json_config_parser.h>
+#include <dhcp/tests/iface_mgr_test_config.h>
+#include <dhcp/tests/pkt_captures.h>
 #include <dhcpsrv/cfgmgr.h>
 #include <dhcpsrv/lease_mgr.h>
 #include <dhcpsrv/lease_mgr_factory.h>
 #include <dhcpsrv/utils.h>
-#include <util/buffer.h>
-#include <util/range_utilities.h>
-#include <hooks/server_hooks.h>
-#include <hooks/callout_manager.h>
-#include <dhcp6/tests/dhcp6_test_utils.h>
-#include <dhcp6/tests/dhcp6_client.h>
 #include <dhcp6/ctrl_dhcp6_srv.h>
-#include <dhcp/tests/iface_mgr_test_config.h>
-#include <dhcp/tests/pkt_captures.h>
-#include <cc/command_interpreter.h>
+#include <dhcp6/json_config_parser.h>
+#include <dhcp6/tests/dhcp6_client.h>
+#include <dhcp6/tests/dhcp6_test_utils.h>
 #include <dhcp6/tests/marker_file.h>
 #include <dhcp6/tests/test_libraries.h>
+#include <hooks/server_hooks.h>
+#include <hooks/hooks_manager.h>
+#include <hooks/callout_manager.h>
 #include <stats/stats_mgr.h>
+#include <util/buffer.h>
+#include <util/range_utilities.h>
 #include <util/multi_threading_mgr.h>
 
 #include <boost/scoped_ptr.hpp>
 #include <iostream>
 #include <sstream>
 
-using namespace isc;
+
+using namespace isc::asiolink;
 using namespace isc::config;
 using namespace isc::data;
-using namespace isc::dhcp::test;
-using namespace isc::asiolink;
 using namespace isc::dhcp;
-using namespace isc::util;
+using namespace isc::dhcp::test;
 using namespace isc::hooks;
 using namespace isc::stats;
+using namespace isc::util;
+
 using namespace std;
 
 // namespace has to be named, because friends are defined in Dhcpv6Srv class
@@ -57,19 +60,22 @@ TEST_F(Dhcpv6SrvTest, Hooks) {
     NakedDhcpv6Srv srv(0);
 
     // check if appropriate hooks are registered
-    int hook_index_buffer6_receive   = -1;
-    int hook_index_buffer6_send      = -1;
-    int hook_index_lease6_renew      = -1;
-    int hook_index_lease6_release    = -1;
-    int hook_index_lease6_rebind     = -1;
-    int hook_index_lease6_decline    = -1;
-    int hook_index_pkt6_received     = -1;
-    int hook_index_select_subnet     = -1;
+    int hook_index_dhcp6_srv_configured = -1;
+    int hook_index_buffer6_receive = -1;
+    int hook_index_buffer6_send = -1;
+    int hook_index_lease6_renew = -1;
+    int hook_index_lease6_release = -1;
+    int hook_index_lease6_rebind = -1;
+    int hook_index_lease6_decline = -1;
+    int hook_index_pkt6_receive = -1;
+    int hook_index_pkt6_send = -1;
+    int hook_index_select_subnet = -1;
     int hook_index_leases6_committed = -1;
-    int hook_index_pkt6_send         = -1;
-    int hook_index_host6_identifier  = -1;
+    int hook_index_host6_identifier = -1;
 
     // check if appropriate indexes are set
+    EXPECT_NO_THROW(hook_index_dhcp6_srv_configured = ServerHooks::getServerHooks()
+                    .getIndex("dhcp6_srv_configured"));
     EXPECT_NO_THROW(hook_index_buffer6_receive = ServerHooks::getServerHooks()
                     .getIndex("buffer6_receive"));
     EXPECT_NO_THROW(hook_index_buffer6_send = ServerHooks::getServerHooks()
@@ -82,29 +88,29 @@ TEST_F(Dhcpv6SrvTest, Hooks) {
                     .getIndex("lease6_rebind"));
     EXPECT_NO_THROW(hook_index_lease6_decline = ServerHooks::getServerHooks()
                     .getIndex("lease6_decline"));
-    EXPECT_NO_THROW(hook_index_pkt6_received = ServerHooks::getServerHooks()
+    EXPECT_NO_THROW(hook_index_pkt6_receive = ServerHooks::getServerHooks()
                     .getIndex("pkt6_receive"));
+    EXPECT_NO_THROW(hook_index_pkt6_send = ServerHooks::getServerHooks()
+                    .getIndex("pkt6_send"));
     EXPECT_NO_THROW(hook_index_select_subnet = ServerHooks::getServerHooks()
                     .getIndex("subnet6_select"));
     EXPECT_NO_THROW(hook_index_leases6_committed = ServerHooks::getServerHooks()
                     .getIndex("leases6_committed"));
-    EXPECT_NO_THROW(hook_index_pkt6_send = ServerHooks::getServerHooks()
-                    .getIndex("pkt6_send"));
     EXPECT_NO_THROW(hook_index_host6_identifier = ServerHooks::getServerHooks()
                     .getIndex("host6_identifier"));
 
-
-    EXPECT_TRUE(hook_index_pkt6_received     > 0);
-    EXPECT_TRUE(hook_index_select_subnet     > 0);
+    EXPECT_TRUE(hook_index_dhcp6_srv_configured > 0);
+    EXPECT_TRUE(hook_index_buffer6_receive > 0);
+    EXPECT_TRUE(hook_index_buffer6_send > 0);
+    EXPECT_TRUE(hook_index_lease6_renew > 0);
+    EXPECT_TRUE(hook_index_lease6_release > 0);
+    EXPECT_TRUE(hook_index_lease6_rebind > 0);
+    EXPECT_TRUE(hook_index_lease6_decline > 0);
+    EXPECT_TRUE(hook_index_pkt6_receive > 0);
+    EXPECT_TRUE(hook_index_pkt6_send > 0);
+    EXPECT_TRUE(hook_index_select_subnet > 0);
     EXPECT_TRUE(hook_index_leases6_committed > 0);
-    EXPECT_TRUE(hook_index_pkt6_send         > 0);
-    EXPECT_TRUE(hook_index_buffer6_receive   > 0);
-    EXPECT_TRUE(hook_index_buffer6_send      > 0);
-    EXPECT_TRUE(hook_index_lease6_renew      > 0);
-    EXPECT_TRUE(hook_index_lease6_release    > 0);
-    EXPECT_TRUE(hook_index_lease6_rebind     > 0);
-    EXPECT_TRUE(hook_index_lease6_decline    > 0);
-    EXPECT_TRUE(hook_index_host6_identifier  > 0);
+    EXPECT_TRUE(hook_index_host6_identifier > 0);
 }
 
 /// @brief a class dedicated to Hooks testing in DHCPv6 server
@@ -120,9 +126,7 @@ class HooksDhcpv6SrvTest : public Dhcpv6SrvTest {
 public:
 
     /// @brief creates Dhcpv6Srv and prepares buffers for callouts
-    HooksDhcpv6SrvTest()
-        : Dhcpv6SrvTest() {
-
+    HooksDhcpv6SrvTest() : Dhcpv6SrvTest() {
         HooksManager::setTestMode(false);
         bool status = HooksManager::unloadLibraries();
         if (!status) {
@@ -149,10 +153,11 @@ public:
         // Clear static buffers
         resetCalloutBuffers();
 
+        HooksManager::preCalloutsLibraryHandle().deregisterAllCallouts("dhcp6_srv_configured");
         HooksManager::preCalloutsLibraryHandle().deregisterAllCallouts("buffer6_receive");
+        HooksManager::preCalloutsLibraryHandle().deregisterAllCallouts("buffer6_send");
         HooksManager::preCalloutsLibraryHandle().deregisterAllCallouts("pkt6_receive");
         HooksManager::preCalloutsLibraryHandle().deregisterAllCallouts("pkt6_send");
-        HooksManager::preCalloutsLibraryHandle().deregisterAllCallouts("buffer6_send");
         HooksManager::preCalloutsLibraryHandle().deregisterAllCallouts("subnet6_select");
         HooksManager::preCalloutsLibraryHandle().deregisterAllCallouts("leases6_committed");
         HooksManager::preCalloutsLibraryHandle().deregisterAllCallouts("lease6_renew");
@@ -161,20 +166,20 @@ public:
         HooksManager::preCalloutsLibraryHandle().deregisterAllCallouts("lease6_decline");
         HooksManager::preCalloutsLibraryHandle().deregisterAllCallouts("host6_identifier");
 
-        // Clear statistics.
-        StatsMgr::instance().removeAll();
-
         HooksManager::setTestMode(false);
         bool status = HooksManager::unloadLibraries();
         if (!status) {
             cerr << "(fixture dtor) unloadLibraries failed" << endl;
         }
+
+        // Clear statistics.
+        StatsMgr::instance().removeAll();
     }
 
     /// @brief creates an option with specified option code
     ///
     /// This method is static, because it is used from callouts
-    /// that do not have a pointer to HooksDhcpv6SSrvTest object
+    /// that do not have a pointer to HooksDhcpv6SrvTest object
     ///
     /// @param option_code code of option to be created
     ///
@@ -204,12 +209,12 @@ public:
         EXPECT_TRUE(callout_handle->getArgumentNames().empty());
     }
 
-    /// test callback that stores received callout name and pkt6 value
+    /// Test callback that stores received callout name and pkt6 value
     /// @param callout_handle handle passed by the hooks framework
     /// @return always 0
     static int
-    pkt6_receive_callout(CalloutHandle& callout_handle) {
-        callback_name_ = string("pkt6_receive");
+    buffer6_receive_callout(CalloutHandle& callout_handle) {
+        callback_name_ = string("buffer6_receive");
 
         callout_handle.getArgument("query6", callback_qry_pkt6_);
 
@@ -222,77 +227,84 @@ public:
         return (0);
     }
 
-    /// test callback that changes client-id value
+    /// Test callback that changes first byte of client-id value
     /// @param callout_handle handle passed by the hooks framework
     /// @return always 0
     static int
-    pkt6_receive_change_clientid(CalloutHandle& callout_handle) {
+    buffer6_receive_change_clientid(CalloutHandle& callout_handle) {
 
         Pkt6Ptr pkt;
         callout_handle.getArgument("query6", pkt);
 
-        // Get rid of the old client-id
-        pkt->delOption(D6O_CLIENTID);
-
-        // Add a new option
-        pkt->addOption(createOption(D6O_CLIENTID));
+        // If there is at least one option with data
+        if (pkt->data_.size() > Pkt6::DHCPV6_PKT_HDR_LEN + Option::OPTION6_HDR_LEN) {
+            // Offset of the first byte of the first option. Let's set this byte
+            // to some new value that we could later check
+            pkt->data_[Pkt6::DHCPV6_PKT_HDR_LEN + Option::OPTION6_HDR_LEN] = 0xff;
+        }
 
         // Carry on as usual
-        return pkt6_receive_callout(callout_handle);
+        return buffer6_receive_callout(callout_handle);
     }
 
     /// Test callback that deletes client-id
     /// @param callout_handle handle passed by the hooks framework
     /// @return always 0
     static int
-    pkt6_receive_delete_clientid(CalloutHandle& callout_handle) {
+    buffer6_receive_delete_clientid(CalloutHandle& callout_handle) {
 
         Pkt6Ptr pkt;
         callout_handle.getArgument("query6", pkt);
 
-        // Get rid of the old client-id
-        pkt->delOption(D6O_CLIENTID);
+        // this is modified SOLICIT (with missing mandatory client-id)
+        uint8_t data[] = {
+            1,  // type 1 = SOLICIT
+            0xca, 0xfe, 0x01, // trans-id = 0xcafe01
+            0, 3, // option type 3 (IA_NA)
+            0, 12, // option length 12
+            0, 0, 0, 1, // iaid = 1
+            0, 0, 0, 0, // T1 = 0
+            0, 0, 0, 0  // T2 = 0
+        };
+
+        OptionBuffer modifiedMsg(data, data + sizeof(data));
+
+        pkt->data_ = modifiedMsg;
 
         // Carry on as usual
-        return pkt6_receive_callout(callout_handle);
+        return buffer6_receive_callout(callout_handle);
     }
 
     /// Test callback that sets skip flag
     /// @param callout_handle handle passed by the hooks framework
     /// @return always 0
     static int
-    pkt6_receive_skip(CalloutHandle& callout_handle) {
-
-        Pkt6Ptr pkt;
-        callout_handle.getArgument("query6", pkt);
+    buffer6_receive_skip(CalloutHandle& callout_handle) {
 
         callout_handle.setStatus(CalloutHandle::NEXT_STEP_SKIP);
 
         // Carry on as usual
-        return pkt6_receive_callout(callout_handle);
+        return buffer6_receive_callout(callout_handle);
     }
 
     /// Test callback that sets drop flag
     /// @param callout_handle handle passed by the hooks framework
     /// @return always 0
     static int
-    pkt6_receive_drop(CalloutHandle& callout_handle) {
-
-        Pkt6Ptr pkt;
-        callout_handle.getArgument("query6", pkt);
+    buffer6_receive_drop(CalloutHandle& callout_handle) {
 
         callout_handle.setStatus(CalloutHandle::NEXT_STEP_DROP);
 
         // Carry on as usual
-        return pkt6_receive_callout(callout_handle);
+        return buffer6_receive_callout(callout_handle);
     }
 
     /// Test callback that stores received callout name and pkt6 value
     /// @param callout_handle handle passed by the hooks framework
     /// @return always 0
     static int
-    buffer6_receive_callout(CalloutHandle& callout_handle) {
-        callback_name_ = string("buffer6_receive");
+    pkt6_receive_callout(CalloutHandle& callout_handle) {
+        callback_name_ = string("pkt6_receive");
 
         callout_handle.getArgument("query6", callback_qry_pkt6_);
 
@@ -305,74 +317,69 @@ public:
         return (0);
     }
 
-    /// Test callback that changes first byte of client-id value
+    /// Test callback that changes client-id value
     /// @param callout_handle handle passed by the hooks framework
     /// @return always 0
     static int
-    buffer6_receive_change_clientid(CalloutHandle& callout_handle) {
+    pkt6_receive_change_clientid(CalloutHandle& callout_handle) {
 
         Pkt6Ptr pkt;
         callout_handle.getArgument("query6", pkt);
 
-        // If there is at least one option with data
-        if (pkt->data_.size() > Pkt6::DHCPV6_PKT_HDR_LEN + Option::OPTION6_HDR_LEN) {
-            // Offset of the first byte of the first option. Let's set this byte
-            // to some new value that we could later check
-            pkt->data_[Pkt6::DHCPV6_PKT_HDR_LEN + Option::OPTION6_HDR_LEN] = 0xff;
-        }
+        // Get rid of the old client-id
+        pkt->delOption(D6O_CLIENTID);
+
+        // Add a new option
+        pkt->addOption(createOption(D6O_CLIENTID));
 
         // Carry on as usual
-        return buffer6_receive_callout(callout_handle);
+        return pkt6_receive_callout(callout_handle);
     }
 
     /// Test callback that deletes client-id
     /// @param callout_handle handle passed by the hooks framework
     /// @return always 0
     static int
-    buffer6_receive_delete_clientid(CalloutHandle& callout_handle) {
+    pkt6_receive_delete_clientid(CalloutHandle& callout_handle) {
 
         Pkt6Ptr pkt;
         callout_handle.getArgument("query6", pkt);
 
-        // this is modified SOLICIT (with missing mandatory client-id)
-        uint8_t data[] = {
-        1,  // type 1 = SOLICIT
-        0xca, 0xfe, 0x01, // trans-id = 0xcafe01
-        0, 3, // option type 3 (IA_NA)
-        0, 12, // option length 12
-        0, 0, 0, 1, // iaid = 1
-        0, 0, 0, 0, // T1 = 0
-        0, 0, 0, 0  // T2 = 0
-        };
-
-        OptionBuffer modifiedMsg(data, data + sizeof(data));
-
-        pkt->data_ = modifiedMsg;
+        // Get rid of the old client-id
+        pkt->delOption(D6O_CLIENTID);
 
-        // carry on as usual
-        return buffer6_receive_callout(callout_handle);
+        // Carry on as usual
+        return pkt6_receive_callout(callout_handle);
     }
 
     /// Test callback that sets skip flag
     /// @param callout_handle handle passed by the hooks framework
     /// @return always 0
     static int
-    buffer6_receive_skip(CalloutHandle& callout_handle) {
+    pkt6_receive_skip(CalloutHandle& callout_handle) {
+
+        Pkt6Ptr pkt;
+        callout_handle.getArgument("query6", pkt);
+
         callout_handle.setStatus(CalloutHandle::NEXT_STEP_SKIP);
 
         // Carry on as usual
-        return buffer6_receive_callout(callout_handle);
+        return pkt6_receive_callout(callout_handle);
     }
 
     /// Test callback that sets drop flag
     /// @param callout_handle handle passed by the hooks framework
     /// @return always 0
     static int
-    buffer6_receive_drop(CalloutHandle& callout_handle) {
+    pkt6_receive_drop(CalloutHandle& callout_handle) {
+
+        Pkt6Ptr pkt;
+        callout_handle.getArgument("query6", pkt);
+
         callout_handle.setStatus(CalloutHandle::NEXT_STEP_DROP);
 
         // Carry on as usual
-        return buffer6_receive_callout(callout_handle);
+        return pkt6_receive_callout(callout_handle);
     }
 
     /// Test callback that stores received callout name and pkt6 value
@@ -464,17 +471,17 @@ public:
         return pkt6_send_callout(callout_handle);
     }
 
-    /// @brief Test callback that stores response packet.
+    /// Test callback that stores response packet.
     /// @param callout_handle handle passed by the hooks framework.
     /// @return always 0
     static int
     buffer6_send_callout(CalloutHandle& callout_handle) {
         callback_name_ = string("buffer6_send");
 
-        callback_argument_names_ = callout_handle.getArgumentNames();
-
         callout_handle.getArgument("response6", callback_resp_pkt6_);
 
+        callback_argument_names_ = callout_handle.getArgumentNames();
+
         if (callback_resp_pkt6_) {
             callback_resp_options_copy_ = callback_resp_pkt6_->isCopyRetrievedOptions();
         }
@@ -490,7 +497,7 @@ public:
 
         callout_handle.setStatus(CalloutHandle::NEXT_STEP_SKIP);
 
-        // carry on as usual
+        // Carry on as usual
         return buffer6_send_callout(callout_handle);
     }
 
@@ -573,7 +580,7 @@ public:
         return subnet6_select_callout(callout_handle);
     }
 
-    /// Test callback that stores received callout name and pkt6 value
+    /// Test callback that stores received callout name and subnet6 values
     /// @param callout_handle handle passed by the hooks framework
     /// @return always 0
     static int
@@ -709,7 +716,6 @@ public:
         return (lease6_rebind_callout(callout_handle));
     }
 
-
     /// Test callback that stores received callout name passed parameters
     /// @param callout_handle handle passed by the hooks framework
     /// @return always 0
@@ -753,9 +759,7 @@ public:
         return (0);
     }
 
-    /// Lease6_decline test callback
-    ///
-    /// Stores all parameters in callback_* fields.
+    /// Test lease6_decline callback that stores received parameters.
     ///
     /// @param callout_handle handle passed by the hooks framework
     /// @return always 0
@@ -772,7 +776,7 @@ public:
         return (0);
     }
 
-    /// Lease6_decline callout that sets status to SKIP
+    /// Test lease6_decline callback that sets next step to SKIP.
     ///
     /// @param callout_handle handle passed by the hooks framework
     /// @return always 0
@@ -783,7 +787,7 @@ public:
         return (lease6_decline_callout(callout_handle));
     }
 
-    /// Lease6_decline callout that sets status to DROP
+    /// Test lease6_decline callback that sets next step to DROP.
     ///
     /// @param callout_handle handle passed by the hooks framework
     /// @return always 0
@@ -848,7 +852,7 @@ public:
         return (0);
     }
 
-    /// @brief Test host6_identifier by setting identifier to "foo"
+    /// @brief Test host6_identifier callback by setting identifier to "foo"
     ///
     /// @param callout_handle handle passed by the hooks framework
     /// @return always 0
@@ -904,7 +908,6 @@ public:
         return (0);
     }
 
-
     /// Resets buffers used to store data received by callouts
     void resetCalloutBuffers() {
         callback_name_ = string("");
@@ -954,8 +957,10 @@ public:
     /// Pointer to lease6 structure returned in the leases6_committed callout
     static Lease6Ptr callback_lease6_;
 
-    /// Pointers to lease6 structures returned in the leases6_committed callout
+    /// Pointer to lease6 structure returned in the leases6_committed callout
     static Lease6CollectionPtr callback_new_leases6_;
+
+    /// Pointer to lease6 structure returned in the leases6_committed callout
     static Lease6CollectionPtr callback_deleted_leases6_;
 
     /// Pointer to IA_NA option being renewed or rebound
@@ -993,11 +998,11 @@ Pkt6Ptr HooksDhcpv6SrvTest::callback_qry_pkt6_;
 Pkt6Ptr HooksDhcpv6SrvTest::callback_resp_pkt6_;
 Subnet6Ptr HooksDhcpv6SrvTest::callback_subnet6_;
 const Subnet6Collection* HooksDhcpv6SrvTest::callback_subnet6collection_;
-vector<string> HooksDhcpv6SrvTest::callback_argument_names_;
 Lease6Ptr HooksDhcpv6SrvTest::callback_lease6_;
 Lease6CollectionPtr HooksDhcpv6SrvTest::callback_new_leases6_;
 Lease6CollectionPtr HooksDhcpv6SrvTest::callback_deleted_leases6_;
 boost::shared_ptr<Option6IA> HooksDhcpv6SrvTest::callback_ia_na_;
+vector<string> HooksDhcpv6SrvTest::callback_argument_names_;
 bool HooksDhcpv6SrvTest::callback_qry_options_copy_;
 bool HooksDhcpv6SrvTest::callback_resp_options_copy_;
 
@@ -1007,8 +1012,7 @@ public:
     /// @brief Pointer to the tested server object
     boost::shared_ptr<NakedDhcpv6Srv> server_;
 
-    LoadUnloadDhcpv6SrvTest()
-        : Dhcpv6SrvTest() {
+    LoadUnloadDhcpv6SrvTest() : Dhcpv6SrvTest() {
         reset();
         MultiThreadingMgr::instance().setMode(false);
     }
@@ -1037,13 +1041,12 @@ public:
     }
 };
 
-
 // Checks if callouts installed on buffer6_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 "buffer6_receive".
-TEST_F(HooksDhcpv6SrvTest, simpleBuffer6Receive) {
+TEST_F(HooksDhcpv6SrvTest, buffer6ReceiveSimple) {
 
     // Install buffer6_receive_callout
     EXPECT_NO_THROW(HooksManager::preCalloutsLibraryHandle().registerCallout(
@@ -1082,7 +1085,7 @@ TEST_F(HooksDhcpv6SrvTest, simpleBuffer6Receive) {
 
 // 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, valueChangeBuffer6Receive) {
+TEST_F(HooksDhcpv6SrvTest, buffer6ReceiveValueChange) {
 
     // Install buffer6_receive_change_clientid
     EXPECT_NO_THROW(HooksManager::preCalloutsLibraryHandle().registerCallout(
@@ -1122,7 +1125,7 @@ TEST_F(HooksDhcpv6SrvTest, valueChangeBuffer6Receive) {
 // Checks if callouts installed on buffer6_receive 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(HooksDhcpv6SrvTest, deleteClientIdBuffer6Receive) {
+TEST_F(HooksDhcpv6SrvTest, buffer6ReceiveDeleteClientId) {
 
     // Install buffer6_receive_delete_clientid
     EXPECT_NO_THROW(HooksManager::preCalloutsLibraryHandle().registerCallout(
@@ -1147,9 +1150,9 @@ TEST_F(HooksDhcpv6SrvTest, deleteClientIdBuffer6Receive) {
     checkCalloutHandleReset(sol);
 }
 
-// Checks if callouts installed on buffer6_received is able to set skip flag that
+// Checks if callouts installed on buffer6_receive is able to set skip flag that
 // will cause the server to not process the packet (drop), even though it is valid.
-TEST_F(HooksDhcpv6SrvTest, skipBuffer6Receive) {
+TEST_F(HooksDhcpv6SrvTest, buffer6ReceiveSkip) {
 
     // Install buffer6_receive_skip
     EXPECT_NO_THROW(HooksManager::preCalloutsLibraryHandle().registerCallout(
@@ -1174,9 +1177,9 @@ TEST_F(HooksDhcpv6SrvTest, skipBuffer6Receive) {
     checkCalloutHandleReset(sol);
 }
 
-// Checks if callouts installed on buffer6_received is able to set drop flag that
+// Checks if callouts installed on buffer6_receive is able to set drop flag that
 // will cause the server to not process the packet (drop), even though it is valid.
-TEST_F(HooksDhcpv6SrvTest, dropBuffer6Receive) {
+TEST_F(HooksDhcpv6SrvTest, buffer6ReceiveDrop) {
 
     // Install buffer6_receive_drop
     EXPECT_NO_THROW(HooksManager::preCalloutsLibraryHandle().registerCallout(
@@ -1206,7 +1209,7 @@ TEST_F(HooksDhcpv6SrvTest, dropBuffer6Receive) {
 //
 // Note that the test name does not follow test naming convention,
 // but the proper hook name is "pkt6_receive".
-TEST_F(HooksDhcpv6SrvTest, simplePkt6Receive) {
+TEST_F(HooksDhcpv6SrvTest, pkt6ReceiveSimple) {
 
     // Install pkt6_receive_callout
     EXPECT_NO_THROW(HooksManager::preCalloutsLibraryHandle().registerCallout(
@@ -1245,7 +1248,7 @@ TEST_F(HooksDhcpv6SrvTest, simplePkt6Receive) {
 
 // Checks if callouts installed on pkt6_received is able to change
 // the values and the parameters are indeed used by the server.
-TEST_F(HooksDhcpv6SrvTest, valueChangePkt6Receive) {
+TEST_F(HooksDhcpv6SrvTest, pkt6ReceiveValueChange) {
 
     // Install pkt6_receive_change_clientid
     EXPECT_NO_THROW(HooksManager::preCalloutsLibraryHandle().registerCallout(
@@ -1284,7 +1287,7 @@ TEST_F(HooksDhcpv6SrvTest, valueChangePkt6Receive) {
 // Checks if callouts installed on pkt6_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(HooksDhcpv6SrvTest, deleteClientIdPkt6Receive) {
+TEST_F(HooksDhcpv6SrvTest, pkt6ReceiveDeleteClientId) {
 
     // Install pkt6_receive_delete_clientid
     EXPECT_NO_THROW(HooksManager::preCalloutsLibraryHandle().registerCallout(
@@ -1311,7 +1314,7 @@ TEST_F(HooksDhcpv6SrvTest, deleteClientIdPkt6Receive) {
 
 // Checks if callouts installed on pkt6_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(HooksDhcpv6SrvTest, skipPkt6Receive) {
+TEST_F(HooksDhcpv6SrvTest, pkt6ReceiveSkip) {
 
     // Install pkt6_receive_skip
     EXPECT_NO_THROW(HooksManager::preCalloutsLibraryHandle().registerCallout(
@@ -1338,7 +1341,7 @@ TEST_F(HooksDhcpv6SrvTest, skipPkt6Receive) {
 
 // Checks if callouts installed on pkt6_received is able to set drop flag that
 // will cause the server to not process the packet (drop), even though it is valid.
-TEST_F(HooksDhcpv6SrvTest, dropPkt6Receive) {
+TEST_F(HooksDhcpv6SrvTest, pkt6ReceiveDrop) {
 
     // Install pkt6_receive_drop
     EXPECT_NO_THROW(HooksManager::preCalloutsLibraryHandle().registerCallout(
@@ -1363,10 +1366,9 @@ TEST_F(HooksDhcpv6SrvTest, dropPkt6Receive) {
     checkCalloutHandleReset(sol);
 }
 
-
 // Checks if callouts installed on pkt6_send are indeed called and the
 // all necessary parameters are passed.
-TEST_F(HooksDhcpv6SrvTest, simplePkt6Send) {
+TEST_F(HooksDhcpv6SrvTest, pkt6SendSimple) {
 
     // Install pkt6_send_callout
     EXPECT_NO_THROW(HooksManager::preCalloutsLibraryHandle().registerCallout(
@@ -1393,7 +1395,9 @@ TEST_F(HooksDhcpv6SrvTest, simplePkt6Send) {
 
     // Check that pkt6 argument passing was successful and returned proper
     // values
+    ASSERT_TRUE(callback_qry_pkt6_);
     EXPECT_TRUE(callback_qry_pkt6_.get() == sol.get());
+    ASSERT_TRUE(callback_resp_pkt6_);
     EXPECT_TRUE(callback_resp_pkt6_.get() == adv.get());
 
     // Check that all expected parameters are there
@@ -1412,7 +1416,7 @@ TEST_F(HooksDhcpv6SrvTest, simplePkt6Send) {
 
 // Checks if callouts installed on pkt6_send is able to change
 // the values and the packet sent contains those changes
-TEST_F(HooksDhcpv6SrvTest, valueChangePkt6Send) {
+TEST_F(HooksDhcpv6SrvTest, pkt6SendValueChange) {
 
     // Install pkt6_send_change_serverid
     EXPECT_NO_THROW(HooksManager::preCalloutsLibraryHandle().registerCallout(
@@ -1452,7 +1456,7 @@ TEST_F(HooksDhcpv6SrvTest, valueChangePkt6Send) {
 // 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(HooksDhcpv6SrvTest, deleteServerIdPkt6Send) {
+TEST_F(HooksDhcpv6SrvTest, pkt6SendDeleteServerId) {
 
     // Install pkt6_send_delete_serverid
     EXPECT_NO_THROW(HooksManager::preCalloutsLibraryHandle().registerCallout(
@@ -1486,7 +1490,7 @@ TEST_F(HooksDhcpv6SrvTest, deleteServerIdPkt6Send) {
 
 // Checks if callouts installed on pkt6_skip is able to set skip flag that
 // will cause the server to send an empty response.
-TEST_F(HooksDhcpv6SrvTest, skipPkt6Send) {
+TEST_F(HooksDhcpv6SrvTest, pkt6SendSkip) {
 
     // Install pkt6_send_skip
     EXPECT_NO_THROW(HooksManager::preCalloutsLibraryHandle().registerCallout(
@@ -1507,8 +1511,8 @@ TEST_F(HooksDhcpv6SrvTest, skipPkt6Send) {
     // Check that the server send the packet
     ASSERT_EQ(1, srv_->fake_sent_.size());
 
-    // But the sent packet should have 0 length (we told the server to
-    // skip pack(), but did not do packing outselves)
+    // Get the first packet and check that it has zero length (i.e. the server
+    // did not do packing on its own)
     Pkt6Ptr sent = srv_->fake_sent_.front();
 
     // The actual size of sent packet should be 0
@@ -1520,7 +1524,7 @@ TEST_F(HooksDhcpv6SrvTest, skipPkt6Send) {
 
 // Checks if callouts installed on pkt6_drop is able to set drop flag that
 // will cause the server to not process the packet (drop), even though it is valid.
-TEST_F(HooksDhcpv6SrvTest, dropPkt6Send) {
+TEST_F(HooksDhcpv6SrvTest, pkt6SendDrop) {
 
     // Install pkt6_send_drop
     EXPECT_NO_THROW(HooksManager::preCalloutsLibraryHandle().registerCallout(
@@ -1547,7 +1551,7 @@ TEST_F(HooksDhcpv6SrvTest, dropPkt6Send) {
 
 // Checks if callouts installed on buffer6_send are indeed called and the
 // all necessary parameters are passed.
-TEST_F(HooksDhcpv6SrvTest, simpleBuffer6Send) {
+TEST_F(HooksDhcpv6SrvTest, buffer6SendSimple) {
 
     // Install buffer6_send_callout
     EXPECT_NO_THROW(HooksManager::preCalloutsLibraryHandle().registerCallout(
@@ -1572,8 +1576,7 @@ TEST_F(HooksDhcpv6SrvTest, simpleBuffer6Send) {
     ASSERT_EQ(1, srv_->fake_sent_.size());
     Pkt6Ptr adv = srv_->fake_sent_.front();
 
-    // Check that pkt6 argument passing was successful and returned proper
-    // values
+    // Check that pkt6 argument passing was successful and returned proper value
     EXPECT_TRUE(callback_resp_pkt6_.get() == adv.get());
 
     // Check that all expected parameters are there
@@ -1611,8 +1614,8 @@ TEST_F(HooksDhcpv6SrvTest, buffer6SendSkip) {
     // Check that the callback called is indeed the one we installed
     EXPECT_EQ("buffer6_send", callback_name_);
 
-    // Check that there is no packet sent
-    EXPECT_EQ(0, srv_->fake_sent_.size());
+    // Check that there is no packet sent.
+    ASSERT_EQ(0, srv_->fake_sent_.size());
 
     // Check if the callout handle state was reset after the callout.
     checkCalloutHandleReset(sol);
@@ -1650,7 +1653,7 @@ TEST_F(HooksDhcpv6SrvTest, buffer6SendDrop) {
 
 // This test checks if subnet6_select callout is triggered and reports
 // valid parameters
-TEST_F(HooksDhcpv6SrvTest, subnet6Select) {
+TEST_F(HooksDhcpv6SrvTest, subnet6SelectSimple) {
 
     // Configure 2 subnets, both directly reachable over local interface
     // (let's not complicate the matter with relays)
@@ -1680,6 +1683,7 @@ TEST_F(HooksDhcpv6SrvTest, subnet6Select) {
     comment_ = isc::config::parseAnswer(rcode_, status);
     ASSERT_EQ(0, rcode_);
 
+    // Commit the config
     CfgMgr::instance().commit();
 
     // Install subnet6_select_callout
@@ -1722,6 +1726,7 @@ TEST_F(HooksDhcpv6SrvTest, subnet6Select) {
 
     // Server is supposed to report two subnets
     ASSERT_EQ(exp_subnets->size(), callback_subnet6collection_->size());
+    ASSERT_GE(exp_subnets->size(), 2);
 
     // Compare that the available subnets are reported as expected
     EXPECT_TRUE((*exp_subnets->begin())->get() == (*callback_subnet6collection_->begin())->get());
@@ -2499,9 +2504,9 @@ TEST_F(HooksDhcpv6SrvTest, leases6CommittedRequestPrefix) {
 }
 
 // This test verifies that the callout installed on the leases6_committed hook
-// point is executed as a result of REQUEST message sent to reuse an
-// existing lease.
-TEST_F(HooksDhcpv6SrvTest, leases6CommittedCache) {
+// point is executed as a result of RENEW message sent to allocate new
+// lease or renew an existing lease.
+TEST_F(HooksDhcpv6SrvTest, leases6CommittedRenew) {
     IfaceMgrTestConfig test_config(true);
 
     string config = "{ \"interfaces-config\": {"
@@ -2513,8 +2518,7 @@ TEST_F(HooksDhcpv6SrvTest, leases6CommittedCache) {
         "\"subnet6\": [ { "
         "    \"pools\": [ { \"pool\": \"2001:db8:1::/64\" } ],"
         "    \"subnet\": \"2001:db8:1::/48\", "
-        "    \"interface\": \"eth1\", "
-        "    \"cache-threshold\": .25 "
+        "    \"interface\": \"eth1\" "
         " } ],"
         "\"valid-lifetime\": 4000 }";
 
@@ -2563,8 +2567,8 @@ TEST_F(HooksDhcpv6SrvTest, leases6CommittedCache) {
 
     resetCalloutBuffers();
 
-    // Request the lease and make sure that the callout has been executed.
-    ASSERT_NO_THROW(client.doRequest());
+    // Renew the lease and make sure that the callout has been executed.
+    ASSERT_NO_THROW(client.doRenew());
 
     // Make sure that we received a response
     ASSERT_TRUE(client.getContext().response_);
@@ -2572,54 +2576,27 @@ TEST_F(HooksDhcpv6SrvTest, leases6CommittedCache) {
     // Check that the callback called is indeed the one we installed
     EXPECT_EQ("leases6_committed", callback_name_);
 
-    // Requested lease should not be present, because it is reused.
+    // Renewed lease should be returned.
     ASSERT_TRUE(callback_new_leases6_);
-    EXPECT_TRUE(callback_new_leases6_->empty());
+    ASSERT_EQ(1, callback_new_leases6_->size());
+    lease = callback_new_leases6_->at(0);
+    ASSERT_TRUE(lease);
+    EXPECT_EQ("2001:db8:1::28", lease->addr_.toText());
 
-    // Deleted lease must not be present, because it is renewed.
+    // Deleted lease must not be present, because it is a new allocation.
     ASSERT_TRUE(callback_deleted_leases6_);
     EXPECT_TRUE(callback_deleted_leases6_->empty());
 
     // Pkt passed to a callout must be configured to copy retrieved options.
     EXPECT_TRUE(callback_qry_options_copy_);
 
-    // Check if the callout handle state was reset after the callout.
-    checkCalloutHandleReset(client.getContext().query_);
-}
-
-// This test verifies that the callout installed on the leases6_committed hook
-// point is executed as a result of REQUEST message sent to reuse an
-// existing lease. Prefix variant.
-TEST_F(HooksDhcpv6SrvTest, leases6CommittedCachePrefix) {
-    IfaceMgrTestConfig test_config(true);
-
-    string config = "{ \"interfaces-config\": {"
-        "  \"interfaces\": [ \"*\" ]"
-        "},"
-        "\"preferred-lifetime\": 3000,"
-        "\"rebind-timer\": 2000, "
-        "\"renew-timer\": 1000, "
-        "\"subnet6\": [ { "
-        "    \"pd-pools\": [ {"
-        "        \"prefix\": \"2001:db8:1::\", "
-        "        \"prefix-len\": 56, "
-        "        \"delegated-len\": 64 } ], "
-        "    \"subnet\": \"2001:db8:1::/48\", "
-        "    \"interface\": \"eth1\", "
-        "    \"cache-threshold\": .25 "
-        " } ],"
-        "\"valid-lifetime\": 4000 }";
-
-    Dhcp6Client client;
-    client.setInterface("eth1");
-    client.requestPrefix(0xabca, 64, IOAddress("2001:db8:1:28::"));
-
-    ASSERT_NO_THROW(configure(config, *client.getServer()));
+    resetCalloutBuffers();
 
-    ASSERT_NO_THROW(HooksManager::preCalloutsLibraryHandle().registerCallout(
-                    "leases6_committed", leases6_committed_callout));
+    // Let's try to renew again but force the client to renew a different
+    // address with a different IAID.
+    client.requestAddress(0x2233, IOAddress("2001:db8:1::29"));
 
-    ASSERT_NO_THROW(client.doSARR());
+    ASSERT_NO_THROW(client.doRenew());
 
     // Make sure that we received a response
     ASSERT_TRUE(client.getContext().response_);
@@ -2627,26 +2604,16 @@ TEST_F(HooksDhcpv6SrvTest, leases6CommittedCachePrefix) {
     // Check that the callback called is indeed the one we installed
     EXPECT_EQ("leases6_committed", callback_name_);
 
-    // Check if all expected parameters were really received
-    vector<string> expected_argument_names;
-    expected_argument_names.push_back("query6");
-    expected_argument_names.push_back("deleted_leases6");
-    expected_argument_names.push_back("leases6");
-
-    sort(expected_argument_names.begin(), expected_argument_names.end());
-    EXPECT_TRUE(callback_argument_names_ == expected_argument_names);
-
-    // Newly allocated lease should be returned.
+    // New lease should be returned.
     ASSERT_TRUE(callback_new_leases6_);
-    ASSERT_EQ(1, callback_new_leases6_->size());
-    Lease6Ptr lease = callback_new_leases6_->at(0);
+    ASSERT_EQ(2, callback_new_leases6_->size());
+    lease = callback_new_leases6_->at(1);
     ASSERT_TRUE(lease);
-    EXPECT_EQ("2001:db8:1:28::", lease->addr_.toText());
-    EXPECT_EQ(64, lease->prefixlen_);
+    EXPECT_EQ("2001:db8:1::29", lease->addr_.toText());
 
-    // Deleted lease must not be present, because it is a new allocation.
+    // The old lease is kept.
     ASSERT_TRUE(callback_deleted_leases6_);
-    EXPECT_TRUE(callback_deleted_leases6_->empty());
+    ASSERT_TRUE(callback_deleted_leases6_->empty());
 
     // Pkt passed to a callout must be configured to copy retrieved options.
     EXPECT_TRUE(callback_qry_options_copy_);
@@ -2656,8 +2623,10 @@ TEST_F(HooksDhcpv6SrvTest, leases6CommittedCachePrefix) {
 
     resetCalloutBuffers();
 
-    // Request the lease and make sure that the callout has been executed.
-    ASSERT_NO_THROW(client.doRequest());
+    // The renewed address is just a hint.
+    client.requestAddress(0x5577, IOAddress("4000::2"));
+
+    ASSERT_NO_THROW(client.doRenew());
 
     // Make sure that we received a response
     ASSERT_TRUE(client.getContext().response_);
@@ -2665,11 +2634,11 @@ TEST_F(HooksDhcpv6SrvTest, leases6CommittedCachePrefix) {
     // Check that the callback called is indeed the one we installed
     EXPECT_EQ("leases6_committed", callback_name_);
 
-    // Requested lease should not be present, because it is reused.
     ASSERT_TRUE(callback_new_leases6_);
-    EXPECT_TRUE(callback_new_leases6_->empty());
-
-    // Deleted lease must not be present, because it is renewed.
+    EXPECT_EQ(3, callback_new_leases6_->size());
+    lease = callback_new_leases6_->at(2);
+    ASSERT_TRUE(lease);
+    EXPECT_EQ("2001:db8:1::", lease->addr_.toText());
     ASSERT_TRUE(callback_deleted_leases6_);
     EXPECT_TRUE(callback_deleted_leases6_->empty());
 
@@ -2678,11 +2647,37 @@ TEST_F(HooksDhcpv6SrvTest, leases6CommittedCachePrefix) {
 
     // Check if the callout handle state was reset after the callout.
     checkCalloutHandleReset(client.getContext().query_);
+
+    resetCalloutBuffers();
+
+    // Renew a prefix: this should lead to an error as no prefix pool
+    // is configured.
+    client.requestPrefix(0x1122, 64, IOAddress("2001:db8:1000::"));
+
+    ASSERT_NO_THROW(client.doRenew());
+
+    // Make sure that we received a response
+    ASSERT_TRUE(client.getContext().response_);
+
+    // Check the error.
+    EXPECT_EQ(STATUS_NoPrefixAvail, client.getStatusCode(0x1122));
+
+    // Check that the callback called is indeed the one we installed
+    EXPECT_EQ("leases6_committed", callback_name_);
+
+    ASSERT_TRUE(callback_new_leases6_);
+    EXPECT_EQ(3, callback_new_leases6_->size());
+    ASSERT_TRUE(callback_deleted_leases6_);
+    EXPECT_TRUE(callback_deleted_leases6_->empty());
+
+    // Check if the callout handle state was reset after the callout.
+    checkCalloutHandleReset(client.getContext().query_);
 }
 
-// This test verifies that it is possible to park a packet as a result of
-// the leases6_committed callouts.
-TEST_F(HooksDhcpv6SrvTest, leases6CommittedParkRequests) {
+// This test verifies that the callout installed on the leases6_committed hook
+// point is executed as a result of RENEW message sent to allocate new
+// lease or renew an existing lease. Prefix variant.
+TEST_F(HooksDhcpv6SrvTest, leases6CommittedRenewPrefix) {
     IfaceMgrTestConfig test_config(true);
 
     string config = "{ \"interfaces-config\": {"
@@ -2692,30 +2687,28 @@ TEST_F(HooksDhcpv6SrvTest, leases6CommittedParkRequests) {
         "\"rebind-timer\": 2000, "
         "\"renew-timer\": 1000, "
         "\"subnet6\": [ { "
-        "    \"pools\": [ { \"pool\": \"2001:db8:1::/64\" } ],"
+        "    \"pd-pools\": [ {"
+        "        \"prefix\": \"2001:db8:1::\", "
+        "        \"prefix-len\": 56, "
+        "        \"delegated-len\": 64 } ], "
         "    \"subnet\": \"2001:db8:1::/48\", "
         "    \"interface\": \"eth1\" "
         " } ],"
         "\"valid-lifetime\": 4000 }";
 
-    // Create first client and perform SARR.
-    Dhcp6Client client1;
-    client1.setInterface("eth1");
-    client1.requestAddress(0xabca, IOAddress("2001:db8:1::28"));
+    Dhcp6Client client;
+    client.setInterface("eth1");
+    client.requestPrefix(0xabca, 64, IOAddress("2001:db8:1:28::"));
 
-    ASSERT_NO_THROW(configure(config, *client1.getServer()));
+    ASSERT_NO_THROW(configure(config, *client.getServer()));
 
-    // This callout uses provided IO service object to post a function
-    // that unparks the packet. The packet is parked and can be unparked
-    // by simply calling IOService::poll.
     ASSERT_NO_THROW(HooksManager::preCalloutsLibraryHandle().registerCallout(
-                    "leases6_committed", leases6_committed_park_callout));
+                    "leases6_committed", leases6_committed_callout));
 
-    ASSERT_NO_THROW(client1.doSARR());
+    ASSERT_NO_THROW(client.doSARR());
 
-    // We should be offered an address but the REPLY should not arrive
-    // at this point, because the packet is parked.
-    ASSERT_FALSE(client1.getContext().response_);
+    // Make sure that we received a response
+    ASSERT_TRUE(client.getContext().response_);
 
     // Check that the callback called is indeed the one we installed
     EXPECT_EQ("leases6_committed", callback_name_);
@@ -2729,12 +2722,39 @@ TEST_F(HooksDhcpv6SrvTest, leases6CommittedParkRequests) {
     sort(expected_argument_names.begin(), expected_argument_names.end());
     EXPECT_TRUE(callback_argument_names_ == expected_argument_names);
 
-    // Newly allocated lease should be passed to the callout.
+    // Newly allocated lease should be returned.
     ASSERT_TRUE(callback_new_leases6_);
     ASSERT_EQ(1, callback_new_leases6_->size());
     Lease6Ptr lease = callback_new_leases6_->at(0);
     ASSERT_TRUE(lease);
-    EXPECT_EQ("2001:db8:1::28", lease->addr_.toText());
+    EXPECT_EQ("2001:db8:1:28::", lease->addr_.toText());
+    EXPECT_EQ(64, lease->prefixlen_);
+
+    // Deleted lease must not be present, because it is a new allocation.
+    ASSERT_TRUE(callback_deleted_leases6_);
+    EXPECT_TRUE(callback_deleted_leases6_->empty());
+
+    // Pkt passed to a callout must be configured to copy retrieved options.
+    EXPECT_TRUE(callback_qry_options_copy_);
+
+    resetCalloutBuffers();
+
+    // Renew the lease and make sure that the callout has been executed.
+    ASSERT_NO_THROW(client.doRenew());
+
+    // Make sure that we received a response
+    ASSERT_TRUE(client.getContext().response_);
+
+    // Check that the callback called is indeed the one we installed
+    EXPECT_EQ("leases6_committed", callback_name_);
+
+    // Renewed lease should be returned.
+    ASSERT_TRUE(callback_new_leases6_);
+    ASSERT_EQ(1, callback_new_leases6_->size());
+    lease = callback_new_leases6_->at(0);
+    ASSERT_TRUE(lease);
+    EXPECT_EQ("2001:db8:1:28::", lease->addr_.toText());
+    EXPECT_EQ(64, lease->prefixlen_);
 
     // Deleted lease must not be present, because it is a new allocation.
     ASSERT_TRUE(callback_deleted_leases6_);
@@ -2744,50 +2764,98 @@ TEST_F(HooksDhcpv6SrvTest, leases6CommittedParkRequests) {
     EXPECT_TRUE(callback_qry_options_copy_);
 
     // Check if the callout handle state was reset after the callout.
-    checkCalloutHandleReset(client1.getContext().query_);
+    checkCalloutHandleReset(client.getContext().query_);
 
-    // Reset all indicators because we'll be now creating a second client.
     resetCalloutBuffers();
 
-    // Create the second client to test that it may communicate with the
-    // server while the previous packet is parked.
-    Dhcp6Client client2(client1.getServer());
-    client2.setInterface("eth1");
-    client2.requestAddress(0xabca, IOAddress("2001:db8:1::29"));
-    ASSERT_NO_THROW(client2.doSARR());
+    // Let's try to renew again but force the client to renew a different
+    // prefix with a different IAID.
+    client.requestPrefix(0x2233, 64, IOAddress("2001:db8:1:29::"));
 
-    // The ADVERTISE should have been returned but not REPLAY, as this
-    // packet got parked too.
-    ASSERT_FALSE(client2.getContext().response_);
+    ASSERT_NO_THROW(client.doRenew());
 
-    // Check that the callback called is indeed the one we installed.
+    // Make sure that we received a response
+    ASSERT_TRUE(client.getContext().response_);
+
+    // Check that the callback called is indeed the one we installed
     EXPECT_EQ("leases6_committed", callback_name_);
 
-    // There should be now two actions scheduled on our IO service
-    // by the invoked callouts. They unpark both REPLY messages.
-    ASSERT_NO_THROW(io_service_->poll());
+    // New lease should be returned.
+    ASSERT_TRUE(callback_new_leases6_);
+    ASSERT_EQ(2, callback_new_leases6_->size());
+    lease = callback_new_leases6_->at(1);
+    ASSERT_TRUE(lease);
+    EXPECT_EQ("2001:db8:1:29::", lease->addr_.toText());
+    EXPECT_EQ(64, lease->prefixlen_);
 
-    // Receive and check the first response.
-    ASSERT_NO_THROW(client1.receiveResponse());
-    ASSERT_TRUE(client1.getContext().response_);
-    Pkt6Ptr rsp = client1.getContext().response_;
-    EXPECT_EQ(DHCPV6_REPLY, rsp->getType());
-    EXPECT_TRUE(client1.hasLeaseForAddress(IOAddress("2001:db8:1::28")));
+    // The old lease is kept.
+    ASSERT_TRUE(callback_deleted_leases6_);
+    ASSERT_TRUE(callback_deleted_leases6_->empty());
 
-    // Receive and check the second response.
-    ASSERT_NO_THROW(client2.receiveResponse());
-    ASSERT_TRUE(client2.getContext().response_);
-    rsp = client2.getContext().response_;
-    EXPECT_EQ(DHCPV6_REPLY, rsp->getType());
-    EXPECT_TRUE(client2.hasLeaseForAddress(IOAddress("2001:db8:1::29")));
+    // Pkt passed to a callout must be configured to copy retrieved options.
+    EXPECT_TRUE(callback_qry_options_copy_);
 
     // Check if the callout handle state was reset after the callout.
-    checkCalloutHandleReset(client2.getContext().query_);
+    checkCalloutHandleReset(client.getContext().query_);
+
+    resetCalloutBuffers();
+
+    // The renewed prefix is just a hint.
+    client.requestPrefix(0x5577, 64, IOAddress("4000::1"));
+
+    ASSERT_NO_THROW(client.doRenew());
+
+    // Make sure that we received a response
+    ASSERT_TRUE(client.getContext().response_);
+
+    // Check that the callback called is indeed the one we installed
+    EXPECT_EQ("leases6_committed", callback_name_);
+
+    ASSERT_TRUE(callback_new_leases6_);
+    EXPECT_EQ(3, callback_new_leases6_->size());
+    lease = callback_new_leases6_->at(2);
+    ASSERT_TRUE(lease);
+    EXPECT_EQ("2001:db8:1::", lease->addr_.toText());
+    EXPECT_EQ(64, lease->prefixlen_);
+    ASSERT_TRUE(callback_deleted_leases6_);
+    EXPECT_TRUE(callback_deleted_leases6_->empty());
+
+    // Pkt passed to a callout must be configured to copy retrieved options.
+    EXPECT_TRUE(callback_qry_options_copy_);
+
+    // Check if the callout handle state was reset after the callout.
+    checkCalloutHandleReset(client.getContext().query_);
+
+    resetCalloutBuffers();
+
+    // Renew an address: this should lead to an error as no address pool
+    // is configured.
+    client.requestAddress(0x1122, IOAddress("2001:db8:1::28"));
+
+    ASSERT_NO_THROW(client.doRenew());
+
+    // Make sure that we received a response
+    ASSERT_TRUE(client.getContext().response_);
+
+    // Check the error.
+    EXPECT_EQ(STATUS_NoAddrsAvail, client.getStatusCode(0x1122));
+
+    // Check that the callback called is indeed the one we installed
+    EXPECT_EQ("leases6_committed", callback_name_);
+
+    ASSERT_TRUE(callback_new_leases6_);
+    EXPECT_EQ(3, callback_new_leases6_->size());
+    ASSERT_TRUE(callback_deleted_leases6_);
+    EXPECT_TRUE(callback_deleted_leases6_->empty());
+
+    // Check if the callout handle state was reset after the callout.
+    checkCalloutHandleReset(client.getContext().query_);
 }
 
-// This test verifies that it is possible to park a packet as a result of
-// the leases6_committed callouts. Prefix variant.
-TEST_F(HooksDhcpv6SrvTest, leases6CommittedParkRequestsPrefixes) {
+// This test verifies that the callout installed on the leases6_committed hook
+// point is executed as a result of REBIND message sent to allocate new
+// lease or renew an existing lease.
+TEST_F(HooksDhcpv6SrvTest, leases6CommittedRebind) {
     IfaceMgrTestConfig test_config(true);
 
     string config = "{ \"interfaces-config\": {"
@@ -2797,33 +2865,25 @@ TEST_F(HooksDhcpv6SrvTest, leases6CommittedParkRequestsPrefixes) {
         "\"rebind-timer\": 2000, "
         "\"renew-timer\": 1000, "
         "\"subnet6\": [ { "
-        "    \"pd-pools\": [ {"
-        "        \"prefix\": \"2001:db8:1::\", "
-        "        \"prefix-len\": 56, "
-        "        \"delegated-len\": 64 } ], "
+        "    \"pools\": [ { \"pool\": \"2001:db8:1::/64\" } ],"
         "    \"subnet\": \"2001:db8:1::/48\", "
         "    \"interface\": \"eth1\" "
         " } ],"
         "\"valid-lifetime\": 4000 }";
 
-    // Create first client and perform SARR.
-    Dhcp6Client client1;
-    client1.setInterface("eth1");
-    client1.requestPrefix(0xabca, 64, IOAddress("2001:db8:1:28::"));
+    Dhcp6Client client;
+    client.setInterface("eth1");
+    client.requestAddress(0xabca, IOAddress("2001:db8:1::28"));
 
-    ASSERT_NO_THROW(configure(config, *client1.getServer()));
+    ASSERT_NO_THROW(configure(config, *client.getServer()));
 
-    // This callout uses provided IO service object to post a function
-    // that unparks the packet. The packet is parked and can be unparked
-    // by simply calling IOService::poll.
     ASSERT_NO_THROW(HooksManager::preCalloutsLibraryHandle().registerCallout(
-                    "leases6_committed", leases6_committed_park_callout));
+                    "leases6_committed", leases6_committed_callout));
 
-    ASSERT_NO_THROW(client1.doSARR());
+    ASSERT_NO_THROW(client.doSARR());
 
-    // We should be offered an address but the REPLY should not arrive
-    // at this point, because the packet is parked.
-    ASSERT_FALSE(client1.getContext().response_);
+    // Make sure that we received a response
+    ASSERT_TRUE(client.getContext().response_);
 
     // Check that the callback called is indeed the one we installed
     EXPECT_EQ("leases6_committed", callback_name_);
@@ -2837,13 +2897,12 @@ TEST_F(HooksDhcpv6SrvTest, leases6CommittedParkRequestsPrefixes) {
     sort(expected_argument_names.begin(), expected_argument_names.end());
     EXPECT_TRUE(callback_argument_names_ == expected_argument_names);
 
-    // Newly allocated lease should be passed to the callout.
+    // Newly allocated lease should be returned.
     ASSERT_TRUE(callback_new_leases6_);
     ASSERT_EQ(1, callback_new_leases6_->size());
     Lease6Ptr lease = callback_new_leases6_->at(0);
     ASSERT_TRUE(lease);
-    EXPECT_EQ("2001:db8:1:28::", lease->addr_.toText());
-    EXPECT_EQ(64, lease->prefixlen_);
+    EXPECT_EQ("2001:db8:1::28", lease->addr_.toText());
 
     // Deleted lease must not be present, because it is a new allocation.
     ASSERT_TRUE(callback_deleted_leases6_);
@@ -2853,330 +2912,436 @@ TEST_F(HooksDhcpv6SrvTest, leases6CommittedParkRequestsPrefixes) {
     EXPECT_TRUE(callback_qry_options_copy_);
 
     // Check if the callout handle state was reset after the callout.
-    checkCalloutHandleReset(client1.getContext().query_);
+    checkCalloutHandleReset(client.getContext().query_);
 
-    // Reset all indicators because we'll be now creating a second client.
     resetCalloutBuffers();
 
-    // Create the second client to test that it may communicate with the
-    // server while the previous packet is parked.
-    Dhcp6Client client2(client1.getServer());
-    client2.setInterface("eth1");
-    client2.requestPrefix(0xabca, 64, IOAddress("2001:db8:1:29::"));
-    ASSERT_NO_THROW(client2.doSARR());
+    // Rebind the lease and make sure that the callout has been executed.
+    ASSERT_NO_THROW(client.doRebind());
 
-    // The ADVERTISE should have been returned but not REPLAY, as this
-    // packet got parked too.
-    ASSERT_FALSE(client2.getContext().response_);
+    // Make sure that we received a response
+    ASSERT_TRUE(client.getContext().response_);
 
-    // Check that the callback called is indeed the one we installed.
+    // Check that the callback called is indeed the one we installed
     EXPECT_EQ("leases6_committed", callback_name_);
 
-    // There should be now two actions scheduled on our IO service
-    // by the invoked callouts. They unpark both REPLY messages.
-    ASSERT_NO_THROW(io_service_->poll());
+    // Rebound lease should be returned.
+    ASSERT_TRUE(callback_new_leases6_);
+    ASSERT_EQ(1, callback_new_leases6_->size());
+    lease = callback_new_leases6_->at(0);
+    ASSERT_TRUE(lease);
+    EXPECT_EQ("2001:db8:1::28", lease->addr_.toText());
 
-    // Receive and check the first response.
-    ASSERT_NO_THROW(client1.receiveResponse());
-    ASSERT_TRUE(client1.getContext().response_);
-    Pkt6Ptr rsp = client1.getContext().response_;
-    EXPECT_EQ(DHCPV6_REPLY, rsp->getType());
-    EXPECT_TRUE(client1.hasLeaseForPrefix(IOAddress("2001:db8:1:28::"), 64));
+    // Deleted lease must not be present, because it is a new allocation.
+    ASSERT_TRUE(callback_deleted_leases6_);
+    EXPECT_TRUE(callback_deleted_leases6_->empty());
 
-    // Receive and check the second response.
-    ASSERT_NO_THROW(client2.receiveResponse());
-    ASSERT_TRUE(client2.getContext().response_);
-    rsp = client2.getContext().response_;
-    EXPECT_EQ(DHCPV6_REPLY, rsp->getType());
-    EXPECT_TRUE(client2.hasLeaseForPrefix(IOAddress("2001:db8:1:29::"), 64));
+    // Pkt passed to a callout must be configured to copy retrieved options.
+    EXPECT_TRUE(callback_qry_options_copy_);
 
     // Check if the callout handle state was reset after the callout.
-    checkCalloutHandleReset(client2.getContext().query_);
-}
+    checkCalloutHandleReset(client.getContext().query_);
 
-// This test verifies that incoming (positive) RENEW can be handled properly,
-// and the lease6_renew callouts are triggered.
-TEST_F(HooksDhcpv6SrvTest, basicLease6Renew) {
-    NakedDhcpv6Srv srv(0);
+    resetCalloutBuffers();
 
-    // Install lease6_renew_callout
-    EXPECT_NO_THROW(HooksManager::preCalloutsLibraryHandle().registerCallout(
-                        "lease6_renew", lease6_renew_callout));
+    // Let's try to rebind again but force the client to rebind a different
+    // address with a different IAID.
+    client.requestAddress(0x2233, IOAddress("2001:db8:1::29"));
 
-    const IOAddress addr("2001:db8:1:1::cafe:babe");
-    const uint32_t iaid = 234;
+    ASSERT_NO_THROW(client.doRebind());
 
-    // Generate client-id also duid_
-    OptionPtr clientid = generateClientId();
+    // Make sure that we received a response
+    ASSERT_TRUE(client.getContext().response_);
 
-    // Check that the address we are about to use is indeed in pool
-    ASSERT_TRUE(subnet_->inPool(Lease::TYPE_NA, addr));
+    // Check that the callback called is indeed the one we installed
+    EXPECT_EQ("leases6_committed", callback_name_);
 
-    // Note that preferred, valid, T1 and T2 timers and CLTT are set to invalid
-    // value on purpose. They should be updated during RENEW.
-    Lease6Ptr lease(new Lease6(Lease::TYPE_NA, addr, duid_, iaid,
-                               501, 502, subnet_->getID(),
-                               HWAddrPtr(), 0));
-    lease->cltt_ = 1234;
-    ASSERT_TRUE(LeaseMgrFactory::instance().addLease(lease));
+    // New lease should be returned.
+    ASSERT_TRUE(callback_new_leases6_);
+    ASSERT_EQ(2, callback_new_leases6_->size());
+    lease = callback_new_leases6_->at(1);
+    ASSERT_TRUE(lease);
+    EXPECT_EQ("2001:db8:1::29", lease->addr_.toText());
 
-    // Check that the lease is really in the database
-    Lease6Ptr l = LeaseMgrFactory::instance().getLease6(Lease::TYPE_NA,
-                                                        addr);
-    ASSERT_TRUE(l);
+    // The old lease is kept.
+    ASSERT_TRUE(callback_deleted_leases6_);
+    ASSERT_TRUE(callback_deleted_leases6_->empty());
 
-    // Check that preferred, valid and cltt really set and not using
-    // previous (500, 501, etc.) values
-    EXPECT_NE(l->preferred_lft_, subnet_->getPreferred());
-    EXPECT_NE(l->valid_lft_, subnet_->getValid());
-    EXPECT_NE(l->cltt_, time(NULL));
+    // Pkt passed to a callout must be configured to copy retrieved options.
+    EXPECT_TRUE(callback_qry_options_copy_);
 
-    // Let's create a RENEW
-    Pkt6Ptr req = Pkt6Ptr(new Pkt6(DHCPV6_RENEW, 1234));
-    req->setRemoteAddr(IOAddress("fe80::abcd"));
-    req->setIface("eth0");
-    req->setIndex(ETH0_INDEX);
-    boost::shared_ptr<Option6IA> ia = generateIA(D6O_IA_NA, iaid, 1500, 3000);
+    // Check if the callout handle state was reset after the callout.
+    checkCalloutHandleReset(client.getContext().query_);
 
-    OptionPtr renewed_addr_opt(new Option6IAAddr(D6O_IAADDR, addr, 300, 500));
-    ia->addOption(renewed_addr_opt);
-    req->addOption(ia);
-    req->addOption(clientid);
-    // Server-id is mandatory in RENEW
-    req->addOption(srv.getServerID());
+    resetCalloutBuffers();
 
-    // Pass it to the server and hope for a REPLY
-    Pkt6Ptr reply = srv.processRenew(req);
-    ASSERT_TRUE(reply);
+    // The rebound address is just a hint.
+    client.requestAddress(0x5577, IOAddress("4000::2"));
+
+    ASSERT_NO_THROW(client.doRebind());
+
+    // Make sure that we received a response
+    ASSERT_TRUE(client.getContext().response_);
 
     // Check that the callback called is indeed the one we installed
-    EXPECT_EQ("lease6_renew", callback_name_);
+    EXPECT_EQ("leases6_committed", callback_name_);
 
-    // Check that appropriate parameters are passed to the callouts
-    EXPECT_TRUE(callback_qry_pkt6_);
-    EXPECT_TRUE(callback_lease6_);
-    EXPECT_TRUE(callback_ia_na_);
+    ASSERT_TRUE(callback_new_leases6_);
+    EXPECT_EQ(3, callback_new_leases6_->size());
+    lease = callback_new_leases6_->at(2);
+    ASSERT_TRUE(lease);
+    EXPECT_EQ("2001:db8:1::", lease->addr_.toText());
+    ASSERT_TRUE(callback_deleted_leases6_);
+    EXPECT_TRUE(callback_deleted_leases6_->empty());
+
+    // Pkt passed to a callout must be configured to copy retrieved options.
+    EXPECT_TRUE(callback_qry_options_copy_);
+
+    // Check if the callout handle state was reset after the callout.
+    checkCalloutHandleReset(client.getContext().query_);
+
+    resetCalloutBuffers();
+
+    // Rebind a prefix: this should lead to an error as no prefix pool
+    // is configured.
+    client.requestPrefix(0x1122, 64, IOAddress("2001:db8:1000::"));
+
+    ASSERT_NO_THROW(client.doRebind());
+
+    // Make sure that we received a response
+    ASSERT_TRUE(client.getContext().response_);
+
+    // Check the error.
+    EXPECT_EQ(STATUS_NoPrefixAvail, client.getStatusCode(0x1122));
+
+    // Check that the callback called is indeed the one we installed
+    EXPECT_EQ("leases6_committed", callback_name_);
+
+    ASSERT_TRUE(callback_new_leases6_);
+    EXPECT_EQ(3, callback_new_leases6_->size());
+    ASSERT_TRUE(callback_deleted_leases6_);
+    EXPECT_TRUE(callback_deleted_leases6_->empty());
+
+    // Check if the callout handle state was reset after the callout.
+    checkCalloutHandleReset(client.getContext().query_);
+}
+
+// This test verifies that the callout installed on the leases6_committed hook
+// point is executed as a result of REBIND message sent to allocate new
+// lease or renew an existing lease. Prefix variant.
+TEST_F(HooksDhcpv6SrvTest, leases6CommittedRebindPrefix) {
+    IfaceMgrTestConfig test_config(true);
+
+    string config = "{ \"interfaces-config\": {"
+        "  \"interfaces\": [ \"*\" ]"
+        "},"
+        "\"preferred-lifetime\": 3000,"
+        "\"rebind-timer\": 2000, "
+        "\"renew-timer\": 1000, "
+        "\"subnet6\": [ { "
+        "    \"pd-pools\": [ {"
+        "        \"prefix\": \"2001:db8:1::\", "
+        "        \"prefix-len\": 56, "
+        "        \"delegated-len\": 64 } ], "
+        "    \"subnet\": \"2001:db8:1::/48\", "
+        "    \"interface\": \"eth1\" "
+        " } ],"
+        "\"valid-lifetime\": 4000 }";
+
+    Dhcp6Client client;
+    client.setInterface("eth1");
+    client.requestPrefix(0xabca, 64, IOAddress("2001:db8:1:28::"));
+
+    ASSERT_NO_THROW(configure(config, *client.getServer()));
+
+    ASSERT_NO_THROW(HooksManager::preCalloutsLibraryHandle().registerCallout(
+                    "leases6_committed", leases6_committed_callout));
+
+    ASSERT_NO_THROW(client.doSARR());
+
+    // Make sure that we received a response
+    ASSERT_TRUE(client.getContext().response_);
+
+    // Check that the callback called is indeed the one we installed
+    EXPECT_EQ("leases6_committed", callback_name_);
 
     // Check if all expected parameters were really received
     vector<string> expected_argument_names;
     expected_argument_names.push_back("query6");
-    expected_argument_names.push_back("lease6");
-    expected_argument_names.push_back("ia_na");
+    expected_argument_names.push_back("deleted_leases6");
+    expected_argument_names.push_back("leases6");
 
-    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);
 
-    // Check if we get response at all
-    checkResponse(reply, DHCPV6_REPLY, 1234);
+    // Newly allocated lease should be returned.
+    ASSERT_TRUE(callback_new_leases6_);
+    ASSERT_EQ(1, callback_new_leases6_->size());
+    Lease6Ptr lease = callback_new_leases6_->at(0);
+    ASSERT_TRUE(lease);
+    EXPECT_EQ("2001:db8:1:28::", lease->addr_.toText());
+    EXPECT_EQ(64, lease->prefixlen_);
 
-    OptionPtr tmp = reply->getOption(D6O_IA_NA);
-    ASSERT_TRUE(tmp);
+    // Deleted lease must not be present, because it is a new allocation.
+    ASSERT_TRUE(callback_deleted_leases6_);
+    EXPECT_TRUE(callback_deleted_leases6_->empty());
 
-    // Check that IA_NA was returned and that there's an address included
-    boost::shared_ptr<Option6IAAddr> addr_opt;
-    ASSERT_NO_FATAL_FAILURE(addr_opt = checkIA_NA(reply, 234, subnet_->getT1(),
-                                                  subnet_->getT2()));
+    // Pkt passed to a callout must be configured to copy retrieved options.
+    EXPECT_TRUE(callback_qry_options_copy_);
 
-    ASSERT_TRUE(addr_opt);
-    // Check that the lease is really in the database
-    l = checkLease(duid_, reply->getOption(D6O_IA_NA), addr_opt);
-    ASSERT_TRUE(l);
+    // Check if the callout handle state was reset after the callout.
+    checkCalloutHandleReset(client.getContext().query_);
 
-    // Check that the lease has been returned
-    ASSERT_TRUE(callback_lease6_);
+    resetCalloutBuffers();
 
-    // Check that the returned lease6 in callout is the same as the one in the
-    // database
-    EXPECT_TRUE(*callback_lease6_ == *l);
+    // Rebind the lease and make sure that the callout has been executed.
+    ASSERT_NO_THROW(client.doRebind());
+
+    // Make sure that we received a response
+    ASSERT_TRUE(client.getContext().response_);
+
+    // Check that the callback called is indeed the one we installed
+    EXPECT_EQ("leases6_committed", callback_name_);
+
+    // Rebound lease should be returned.
+    ASSERT_TRUE(callback_new_leases6_);
+    ASSERT_EQ(1, callback_new_leases6_->size());
+    lease = callback_new_leases6_->at(0);
+    ASSERT_TRUE(lease);
+    EXPECT_EQ("2001:db8:1:28::", lease->addr_.toText());
+    EXPECT_EQ(64, lease->prefixlen_);
+
+    // Deleted lease must not be present, because it is a new allocation.
+    ASSERT_TRUE(callback_deleted_leases6_);
+    EXPECT_TRUE(callback_deleted_leases6_->empty());
 
     // Pkt passed to a callout must be configured to copy retrieved options.
     EXPECT_TRUE(callback_qry_options_copy_);
 
     // Check if the callout handle state was reset after the callout.
-    checkCalloutHandleReset(req);
-}
+    checkCalloutHandleReset(client.getContext().query_);
 
-// This test verifies that incoming (positive) RENEW can be handled properly,
-// and the lease6_renew callouts are able to change the lease being updated.
-TEST_F(HooksDhcpv6SrvTest, leaseUpdateLease6Renew) {
-    NakedDhcpv6Srv srv(0);
+    resetCalloutBuffers();
 
-    // Install lease6_renew_update
-    EXPECT_NO_THROW(HooksManager::preCalloutsLibraryHandle().registerCallout(
-                        "lease6_renew", lease6_renew_update));
+    // Let's try to rebind again but force the client to rebind a different
+    // prefix with a different IAID.
+    client.requestPrefix(0x2233, 64, IOAddress("2001:db8:1:29::"));
 
-    const IOAddress addr("2001:db8:1:1::cafe:babe");
-    const uint32_t iaid = 234;
+    ASSERT_NO_THROW(client.doRebind());
 
-    // Generate client-id also duid_
-    OptionPtr clientid = generateClientId();
+    // Make sure that we received a response
+    ASSERT_TRUE(client.getContext().response_);
 
-    // Check that the address we are about to use is indeed in pool
-    ASSERT_TRUE(subnet_->inPool(Lease::TYPE_NA, addr));
+    // Check that the callback called is indeed the one we installed
+    EXPECT_EQ("leases6_committed", callback_name_);
 
-    // Note that preferred, valid, T1 and T2 timers and CLTT are set to invalid
-    // value on purpose. They should be updated during RENEW.
-    Lease6Ptr lease(new Lease6(Lease::TYPE_NA, addr, duid_, iaid,
-                               501, 502, subnet_->getID(),
-                               HWAddrPtr(), 0));
-    lease->cltt_ = 1234;
-    ASSERT_TRUE(LeaseMgrFactory::instance().addLease(lease));
+    // New lease should be returned.
+    ASSERT_TRUE(callback_new_leases6_);
+    ASSERT_EQ(2, callback_new_leases6_->size());
+    lease = callback_new_leases6_->at(1);
+    ASSERT_TRUE(lease);
+    EXPECT_EQ("2001:db8:1:29::", lease->addr_.toText());
+    EXPECT_EQ(64, lease->prefixlen_);
 
-    // Check that the lease is really in the database
-    Lease6Ptr l = LeaseMgrFactory::instance().getLease6(Lease::TYPE_NA,
-                                                        addr);
-    ASSERT_TRUE(l);
+    // The old lease is kept.
+    ASSERT_TRUE(callback_deleted_leases6_);
+    ASSERT_TRUE(callback_deleted_leases6_->empty());
 
-    // Check that preferred, valid and cltt really set and not using
-    // previous (500, 501, etc.) values
-    EXPECT_NE(l->preferred_lft_, subnet_->getPreferred());
-    EXPECT_NE(l->valid_lft_, subnet_->getValid());
-    EXPECT_NE(l->cltt_, time(NULL));
+    // Pkt passed to a callout must be configured to copy retrieved options.
+    EXPECT_TRUE(callback_qry_options_copy_);
 
-    // Let's create a RENEW
-    Pkt6Ptr req = Pkt6Ptr(new Pkt6(DHCPV6_RENEW, 1234));
-    req->setRemoteAddr(IOAddress("fe80::abcd"));
-    req->setIface("eth0");
-    req->setIndex(ETH0_INDEX);
-    boost::shared_ptr<Option6IA> ia = generateIA(D6O_IA_NA, iaid, 1500, 3000);
+    // Check if the callout handle state was reset after the callout.
+    checkCalloutHandleReset(client.getContext().query_);
 
-    OptionPtr renewed_addr_opt(new Option6IAAddr(D6O_IAADDR, addr, 300, 500));
-    ia->addOption(renewed_addr_opt);
-    req->addOption(ia);
-    req->addOption(clientid);
+    resetCalloutBuffers();
 
-    // Server-id is mandatory in RENEW
-    req->addOption(srv.getServerID());
+    // The rebound prefix is just a hint.
+    client.requestPrefix(0x5577, 64, IOAddress("4000::1"));
 
-    // Turn on tee time calculation so we can see the effect of overriding
-    // the lease life time.
-    subnet_->setCalculateTeeTimes(true);
-    Triplet<uint32_t> unspecified;
-    subnet_->setT1(unspecified);
-    subnet_->setT2(unspecified);
-    subnet_->setT1Percent(0.60);
-    subnet_->setT2Percent(0.80);
+    ASSERT_NO_THROW(client.doRebind());
 
-    // Pass it to the server and hope for a REPLY
-    Pkt6Ptr reply = srv.processRenew(req);
-    ASSERT_TRUE(reply);
+    // Make sure that we received a response
+    ASSERT_TRUE(client.getContext().response_);
 
-    // Check if we get response at all
-    checkResponse(reply, DHCPV6_REPLY, 1234);
+    // Check that the callback called is indeed the one we installed
+    EXPECT_EQ("leases6_committed", callback_name_);
 
-    OptionPtr tmp = reply->getOption(D6O_IA_NA);
-    ASSERT_TRUE(tmp);
+    ASSERT_TRUE(callback_new_leases6_);
+    EXPECT_EQ(3, callback_new_leases6_->size());
+    lease = callback_new_leases6_->at(2);
+    ASSERT_TRUE(lease);
+    EXPECT_EQ("2001:db8:1::", lease->addr_.toText());
+    EXPECT_EQ(64, lease->prefixlen_);
+    ASSERT_TRUE(callback_deleted_leases6_);
+    EXPECT_TRUE(callback_deleted_leases6_->empty());
 
-    // Check that IA_NA was returned and that there's an address included
-    boost::shared_ptr<Option6IAAddr> addr_opt;
-    ASSERT_NO_FATAL_FAILURE(addr_opt = checkIA_NA(reply, 1000, 602, 802));
+    // Pkt passed to a callout must be configured to copy retrieved options.
+    EXPECT_TRUE(callback_qry_options_copy_);
 
-    ASSERT_TRUE(addr_opt);
-    // Check that the lease is really in the database
-    l = checkLease(duid_, reply->getOption(D6O_IA_NA), addr_opt);
-    ASSERT_TRUE(l);
+    // Check if the callout handle state was reset after the callout.
+    checkCalloutHandleReset(client.getContext().query_);
 
-    // Check that we chose the distinct override values
-    ASSERT_NE(override_preferred_, subnet_->getPreferred());
-    EXPECT_NE(override_valid_,     subnet_->getValid());
+    resetCalloutBuffers();
 
-    // Check that preferred, valid were overridden the callout
-    EXPECT_EQ(override_preferred_, l->preferred_lft_);
-    EXPECT_EQ(override_valid_, l->valid_lft_);
+    // Rebind an address: this should lead to an error as no address pool
+    // is configured.
+    client.requestAddress(0x1122, IOAddress("2001:db8:1::28"));
 
-    // Checking for CLTT is a bit tricky if we want to avoid off by 1 errors
-    int32_t cltt = static_cast<int32_t>(l->cltt_);
-    int32_t expected = static_cast<int32_t>(time(NULL));
-    // Equality or difference by 1 between cltt and expected is ok.
-    EXPECT_GE(1, abs(cltt - expected));
+    ASSERT_NO_THROW(client.doRebind());
+
+    // Make sure that we received a response
+    ASSERT_TRUE(client.getContext().response_);
+
+    // Check the error.
+    EXPECT_EQ(STATUS_NoAddrsAvail, client.getStatusCode(0x1122));
+
+    // Check that the callback called is indeed the one we installed
+    EXPECT_EQ("leases6_committed", callback_name_);
+
+    ASSERT_TRUE(callback_new_leases6_);
+    EXPECT_EQ(3, callback_new_leases6_->size());
+    ASSERT_TRUE(callback_deleted_leases6_);
+    EXPECT_TRUE(callback_deleted_leases6_->empty());
+
+    // Check if the callout handle state was reset after the callout.
+    checkCalloutHandleReset(client.getContext().query_);
+}
+
+// This test verifies that the leases6_committed callout is executed
+// when DECLINE is processed. The declined lease is expected to be passed
+// in leases6 argument to the callout.
+TEST_F(HooksDhcpv6SrvTest, leases6CommittedDecline) {
+    IfaceMgrTestConfig test_config(true);
+
+    string config = "{ \"interfaces-config\": {"
+        "  \"interfaces\": [ \"*\" ]"
+        "},"
+        "\"preferred-lifetime\": 3000,"
+        "\"rebind-timer\": 2000, "
+        "\"renew-timer\": 1000, "
+        "\"subnet6\": [ { "
+        "    \"pools\": [ { \"pool\": \"2001:db8:1::/64\" } ],"
+        "    \"subnet\": \"2001:db8:1::/48\", "
+        "    \"interface\": \"eth1\" "
+        " } ],"
+        "\"valid-lifetime\": 4000 }";
+
+    Dhcp6Client client;
+    client.setInterface("eth1");
+    client.requestAddress(0xabca, IOAddress("2001:db8:1::28"));
+
+    ASSERT_NO_THROW(configure(config, *client.getServer()));
+
+    ASSERT_NO_THROW(HooksManager::preCalloutsLibraryHandle().registerCallout(
+                    "leases6_committed", leases6_committed_callout));
+
+    ASSERT_NO_THROW(client.doSARR());
+
+    // Make sure that we received a response
+    ASSERT_TRUE(client.getContext().response_);
+
+    ASSERT_NO_THROW(client.doDecline());
+
+    // Check that the callback called is indeed the one we installed
+    EXPECT_EQ("leases6_committed", callback_name_);
+
+    // Check if all expected parameters were really received
+    vector<string> expected_argument_names;
+    expected_argument_names.push_back("query6");
+    expected_argument_names.push_back("deleted_leases6");
+    expected_argument_names.push_back("leases6");
+
+    sort(expected_argument_names.begin(), expected_argument_names.end());
+    EXPECT_TRUE(callback_argument_names_ == expected_argument_names);
+
+    // No deleted leases.
+    ASSERT_TRUE(callback_deleted_leases6_);
+    ASSERT_TRUE(callback_deleted_leases6_->empty());
+
+    // Declined lease should be returned.
+    ASSERT_TRUE(callback_new_leases6_);
+    ASSERT_EQ(1, callback_new_leases6_->size());
+    Lease6Ptr lease = callback_new_leases6_->at(0);
+    ASSERT_TRUE(lease);
+    EXPECT_EQ("2001:db8:1::28", lease->addr_.toText());
 
-    Lease6Ptr deleted_lease =
-        LeaseMgrFactory::instance().getLease6(Lease::TYPE_NA,
-                                              addr_opt->getAddress());
-    EXPECT_TRUE(LeaseMgrFactory::instance().deleteLease(deleted_lease));
+    // Pkt passed to a callout must be configured to copy retrieved options.
+    EXPECT_TRUE(callback_qry_options_copy_);
 
     // Check if the callout handle state was reset after the callout.
-    checkCalloutHandleReset(req);
+    checkCalloutHandleReset(client.getContext().query_);
 }
 
-// This test verifies that incoming (positive) RENEW can be handled properly,
-// and the lease6_renew callouts are able to set the skip flag that will
-// reject the renewal
-TEST_F(HooksDhcpv6SrvTest, skipLease6Renew) {
-    NakedDhcpv6Srv srv(0);
-
-    // Install lease6_renew_skip_callout
-    EXPECT_NO_THROW(HooksManager::preCalloutsLibraryHandle().registerCallout(
-                        "lease6_renew", lease6_renew_skip_callout));
-
-    const IOAddress addr("2001:db8:1:1::cafe:babe");
-    const uint32_t iaid = 234;
+// This test verifies that the leases6_committed callout is executed
+// when DECLINE is processed. Variant with 2 IA_NAs.
+TEST_F(HooksDhcpv6SrvTest, leases6CommittedDeclineTwoNAs) {
+    IfaceMgrTestConfig test_config(true);
 
-    // Generate client-id also duid_
-    OptionPtr clientid = generateClientId();
+    string config = "{ \"interfaces-config\": {"
+        "  \"interfaces\": [ \"*\" ]"
+        "},"
+        "\"preferred-lifetime\": 3000,"
+        "\"rebind-timer\": 2000, "
+        "\"renew-timer\": 1000, "
+        "\"subnet6\": [ { "
+        "    \"pools\": [ { \"pool\": \"2001:db8:1::/64\" } ],"
+        "    \"subnet\": \"2001:db8:1::/48\", "
+        "    \"interface\": \"eth1\" "
+        " } ],"
+        "\"valid-lifetime\": 4000 }";
 
-    // Check that the address we are about to use is indeed in pool
-    ASSERT_TRUE(subnet_->inPool(Lease::TYPE_NA, addr));
+    Dhcp6Client client;
+    client.setInterface("eth1");
+    client.requestAddress(0xabca, IOAddress("2001:db8:1::28"));
+    client.requestAddress(0x2233, IOAddress("2001:db8:1::29"));
 
-    // Note that preferred, valid, T1 and T2 timers and CLTT are set to invalid
-    // value on purpose. They should be updated during RENEW.
-    Lease6Ptr lease(new Lease6(Lease::TYPE_NA, addr, duid_, iaid,
-                               501, 502, subnet_->getID(),
-                               HWAddrPtr(), 0));
-    lease->cltt_ = 1234;
-    ASSERT_TRUE(LeaseMgrFactory::instance().addLease(lease));
+    ASSERT_NO_THROW(configure(config, *client.getServer()));
 
-    // Check that the lease is really in the database
-    Lease6Ptr l = LeaseMgrFactory::instance().getLease6(Lease::TYPE_NA,
-                                                        addr);
-    ASSERT_TRUE(l);
+    ASSERT_NO_THROW(client.doSARR());
+    // Make sure that we received a response
+    ASSERT_TRUE(client.getContext().response_);
 
-    // Check that preferred, valid and cltt are really set and not using
-    // previous (500, 501, etc.) values
-    EXPECT_NE(l->preferred_lft_, subnet_->getPreferred());
-    EXPECT_NE(l->valid_lft_, subnet_->getValid());
-    EXPECT_NE(l->cltt_, time(NULL));
+    ASSERT_NO_THROW(HooksManager::preCalloutsLibraryHandle().registerCallout(
+                    "leases6_committed", leases6_committed_callout));
 
-    // Let's create a RENEW
-    Pkt6Ptr req = Pkt6Ptr(new Pkt6(DHCPV6_RENEW, 1234));
-    req->setRemoteAddr(IOAddress("fe80::abcd"));
-    req->setIface("eth0");
-    req->setIndex(ETH0_INDEX);
-    boost::shared_ptr<Option6IA> ia = generateIA(D6O_IA_NA, iaid, 1500, 3000);
+    ASSERT_NO_THROW(client.doDecline());
 
-    OptionPtr renewed_addr_opt(new Option6IAAddr(D6O_IAADDR, addr, 300, 500));
-    ia->addOption(renewed_addr_opt);
-    req->addOption(ia);
-    req->addOption(clientid);
+    // Check that the callback called is indeed the one we installed
+    EXPECT_EQ("leases6_committed", callback_name_);
 
-    // Server-id is mandatory in RENEW
-    req->addOption(srv.getServerID());
+    // Check if all expected parameters were really received
+    vector<string> expected_argument_names;
+    expected_argument_names.push_back("query6");
+    expected_argument_names.push_back("deleted_leases6");
+    expected_argument_names.push_back("leases6");
 
-    // Pass it to the server and hope for a REPLY
-    Pkt6Ptr reply = srv.processRenew(req);
-    ASSERT_TRUE(reply);
+    sort(expected_argument_names.begin(), expected_argument_names.end());
+    EXPECT_TRUE(callback_argument_names_ == expected_argument_names);
 
-    // Check that our callback was called
-    EXPECT_EQ("lease6_renew", callback_name_);
+    // No deleted leases.
+    ASSERT_TRUE(callback_deleted_leases6_);
+    ASSERT_TRUE(callback_deleted_leases6_->empty());
 
-    l = LeaseMgrFactory::instance().getLease6(Lease::TYPE_NA, addr);
+    // Declined lease should be returned.
+    ASSERT_TRUE(callback_new_leases6_);
+    ASSERT_EQ(2, callback_new_leases6_->size());
+    Lease6Ptr lease = callback_new_leases6_->at(0);
+    ASSERT_TRUE(lease);
+    EXPECT_EQ("2001:db8:1::28", lease->addr_.toText());
+    lease = callback_new_leases6_->at(1);
+    ASSERT_TRUE(lease);
+    EXPECT_EQ("2001:db8:1::29", lease->addr_.toText());
 
-    // Check that the old values are still there and they were not
-    // updated by the renewal
-    EXPECT_NE(l->preferred_lft_, subnet_->getPreferred());
-    EXPECT_NE(l->valid_lft_, subnet_->getValid());
-    EXPECT_NE(l->cltt_, time(NULL));
+    // Pkt passed to a callout must be configured to copy retrieved options.
+    EXPECT_TRUE(callback_qry_options_copy_);
 
     // Check if the callout handle state was reset after the callout.
-    checkCalloutHandleReset(req);
+    checkCalloutHandleReset(client.getContext().query_);
 }
 
-// This test verifies that the callout installed on the leases6_committed hook
-// point is executed as a result of RENEW message sent to allocate new
-// lease or renew an existing lease.
-TEST_F(HooksDhcpv6SrvTest, leases6CommittedRenew) {
+// This test verifies that the leases6_committed callout is executed
+// with deleted leases as argument when RELEASE is processed.
+TEST_F(HooksDhcpv6SrvTest, leases6CommittedRelease) {
     IfaceMgrTestConfig test_config(true);
 
     string config = "{ \"interfaces-config\": {"
@@ -3206,6 +3371,8 @@ TEST_F(HooksDhcpv6SrvTest, leases6CommittedRenew) {
     // Make sure that we received a response
     ASSERT_TRUE(client.getContext().response_);
 
+    ASSERT_NO_THROW(client.doRelease());
+
     // Check that the callback called is indeed the one we installed
     EXPECT_EQ("leases6_committed", callback_name_);
 
@@ -3218,85 +3385,192 @@ TEST_F(HooksDhcpv6SrvTest, leases6CommittedRenew) {
     sort(expected_argument_names.begin(), expected_argument_names.end());
     EXPECT_TRUE(callback_argument_names_ == expected_argument_names);
 
-    // Newly allocated lease should be returned.
+    // No new allocations.
     ASSERT_TRUE(callback_new_leases6_);
-    ASSERT_EQ(1, callback_new_leases6_->size());
-    Lease6Ptr lease = callback_new_leases6_->at(0);
-    ASSERT_TRUE(lease);
-    EXPECT_EQ("2001:db8:1::28", lease->addr_.toText());
+    ASSERT_TRUE(callback_new_leases6_->empty());
 
-    // Deleted lease must not be present, because it is a new allocation.
+    // Released lease should be returned.
     ASSERT_TRUE(callback_deleted_leases6_);
-    EXPECT_TRUE(callback_deleted_leases6_->empty());
+    EXPECT_EQ(1, callback_deleted_leases6_->size());
+    Lease6Ptr lease = callback_deleted_leases6_->at(0);
+    ASSERT_TRUE(lease);
+    EXPECT_EQ("2001:db8:1::28", lease->addr_.toText());
 
     // Pkt passed to a callout must be configured to copy retrieved options.
     EXPECT_TRUE(callback_qry_options_copy_);
 
     // Check if the callout handle state was reset after the callout.
     checkCalloutHandleReset(client.getContext().query_);
+}
 
-    resetCalloutBuffers();
+// This test verifies that the leases6_committed callout is executed
+// with deleted leases as argument when RELEASE is processed. Prefix variant.
+TEST_F(HooksDhcpv6SrvTest, leases6CommittedReleasePrefix) {
+    IfaceMgrTestConfig test_config(true);
 
-    // Renew the lease and make sure that the callout has been executed.
-    ASSERT_NO_THROW(client.doRenew());
+    string config = "{ \"interfaces-config\": {"
+        "  \"interfaces\": [ \"*\" ]"
+        "},"
+        "\"preferred-lifetime\": 3000,"
+        "\"rebind-timer\": 2000, "
+        "\"renew-timer\": 1000, "
+        "\"subnet6\": [ { "
+        "    \"pd-pools\": [ {"
+        "        \"prefix\": \"2001:db8:1::\", "
+        "        \"prefix-len\": 56, "
+        "        \"delegated-len\": 64 } ], "
+        "    \"subnet\": \"2001:db8:1::/48\", "
+        "    \"interface\": \"eth1\" "
+        " } ],"
+        "\"valid-lifetime\": 4000 }";
+
+    Dhcp6Client client;
+    client.setInterface("eth1");
+    client.requestPrefix(0xabca, 64, IOAddress("2001:db8:1:28::"));
+
+    ASSERT_NO_THROW(configure(config, *client.getServer()));
+
+    ASSERT_NO_THROW(client.doSARR());
 
     // Make sure that we received a response
     ASSERT_TRUE(client.getContext().response_);
 
+    ASSERT_NO_THROW(HooksManager::preCalloutsLibraryHandle().registerCallout(
+                    "leases6_committed", leases6_committed_callout));
+
+    ASSERT_NO_THROW(client.doRelease());
+
     // Check that the callback called is indeed the one we installed
     EXPECT_EQ("leases6_committed", callback_name_);
 
-    // Renewed lease should be returned.
+    // Check if all expected parameters were really received
+    vector<string> expected_argument_names;
+    expected_argument_names.push_back("query6");
+    expected_argument_names.push_back("deleted_leases6");
+    expected_argument_names.push_back("leases6");
+
+    sort(expected_argument_names.begin(), expected_argument_names.end());
+    EXPECT_TRUE(callback_argument_names_ == expected_argument_names);
+
+    // No new allocations.
     ASSERT_TRUE(callback_new_leases6_);
-    ASSERT_EQ(1, callback_new_leases6_->size());
-    lease = callback_new_leases6_->at(0);
-    ASSERT_TRUE(lease);
-    EXPECT_EQ("2001:db8:1::28", lease->addr_.toText());
+    ASSERT_TRUE(callback_new_leases6_->empty());
 
-    // Deleted lease must not be present, because it is a new allocation.
+    // Released lease should be returned.
     ASSERT_TRUE(callback_deleted_leases6_);
-    EXPECT_TRUE(callback_deleted_leases6_->empty());
+    EXPECT_EQ(1, callback_deleted_leases6_->size());
+    Lease6Ptr lease = callback_deleted_leases6_->at(0);
+    ASSERT_TRUE(lease);
+    EXPECT_EQ("2001:db8:1:28::", lease->addr_.toText());
+    EXPECT_EQ(64, lease->prefixlen_);
 
     // Pkt passed to a callout must be configured to copy retrieved options.
     EXPECT_TRUE(callback_qry_options_copy_);
 
-    resetCalloutBuffers();
+    // Check if the callout handle state was reset after the callout.
+    checkCalloutHandleReset(client.getContext().query_);
+}
 
-    // Let's try to renew again but force the client to renew a different
-    // address with a different IAID.
+// This test verifies that the leases6_committed callout is executed
+// with deleted leases as argument when RELEASE is processed.
+// Variant with two addresses and two prefixes.
+TEST_F(HooksDhcpv6SrvTest, leases6CommittedReleaseMultiple) {
+    IfaceMgrTestConfig test_config(true);
+
+    string config = "{ \"interfaces-config\": {"
+        "  \"interfaces\": [ \"*\" ]"
+        "},"
+        "\"preferred-lifetime\": 3000,"
+        "\"rebind-timer\": 2000, "
+        "\"renew-timer\": 1000, "
+        "\"subnet6\": [ { "
+        "    \"pools\": [ { \"pool\": \"2001:db8:1::/64\" } ],"
+        "    \"pd-pools\": [ {"
+        "        \"prefix\": \"2001:db8:2::\", "
+        "        \"prefix-len\": 56, "
+        "        \"delegated-len\": 64 } ], "
+        "    \"subnet\": \"2001:db8::/32\", "
+        "    \"interface\": \"eth1\" "
+        " } ],"
+        "\"valid-lifetime\": 4000 }";
+
+    Dhcp6Client client;
+    client.setInterface("eth1");
+    // In theory we can reuse the IAID but copyIAsFromLeases() copies
+    // only one lease...
+    client.requestAddress(0xabca, IOAddress("2001:db8:1::28"));
+    client.requestPrefix(0xabcb, 64, IOAddress("2001:db8:2:28::"));
     client.requestAddress(0x2233, IOAddress("2001:db8:1::29"));
+    client.requestPrefix(0x2234, 64, IOAddress("2001:db8:2:29::"));
 
-    ASSERT_NO_THROW(client.doRenew());
+    ASSERT_NO_THROW(configure(config, *client.getServer()));
 
+    ASSERT_NO_THROW(client.doSARR());
     // Make sure that we received a response
     ASSERT_TRUE(client.getContext().response_);
 
+    ASSERT_NO_THROW(HooksManager::preCalloutsLibraryHandle().registerCallout(
+                    "leases6_committed", leases6_committed_callout));
+
+    ASSERT_NO_THROW(client.doRelease());
+
     // Check that the callback called is indeed the one we installed
     EXPECT_EQ("leases6_committed", callback_name_);
 
-    // New lease should be returned.
+    // Check if all expected parameters were really received
+    vector<string> expected_argument_names;
+    expected_argument_names.push_back("query6");
+    expected_argument_names.push_back("deleted_leases6");
+    expected_argument_names.push_back("leases6");
+
+    sort(expected_argument_names.begin(), expected_argument_names.end());
+    EXPECT_TRUE(callback_argument_names_ == expected_argument_names);
+
+    // No new allocations.
     ASSERT_TRUE(callback_new_leases6_);
-    ASSERT_EQ(2, callback_new_leases6_->size());
-    lease = callback_new_leases6_->at(1);
-    ASSERT_TRUE(lease);
-    EXPECT_EQ("2001:db8:1::29", lease->addr_.toText());
+    ASSERT_TRUE(callback_new_leases6_->empty());
 
-    // The old lease is kept.
+    // Released lease should be returned.
     ASSERT_TRUE(callback_deleted_leases6_);
-    ASSERT_TRUE(callback_deleted_leases6_->empty());
+    EXPECT_EQ(4, callback_deleted_leases6_->size());
 
     // Pkt passed to a callout must be configured to copy retrieved options.
     EXPECT_TRUE(callback_qry_options_copy_);
 
     // Check if the callout handle state was reset after the callout.
     checkCalloutHandleReset(client.getContext().query_);
+}
 
-    resetCalloutBuffers();
+// This test verifies that the callout installed on the leases6_committed hook
+// point is executed as a result of REQUEST message sent to reuse an
+// existing lease.
+TEST_F(HooksDhcpv6SrvTest, leases6CommittedCache) {
+    IfaceMgrTestConfig test_config(true);
 
-    // The renewed address is just a hint.
-    client.requestAddress(0x5577, IOAddress("4000::2"));
+    string config = "{ \"interfaces-config\": {"
+        "  \"interfaces\": [ \"*\" ]"
+        "},"
+        "\"preferred-lifetime\": 3000,"
+        "\"rebind-timer\": 2000, "
+        "\"renew-timer\": 1000, "
+        "\"subnet6\": [ { "
+        "    \"pools\": [ { \"pool\": \"2001:db8:1::/64\" } ],"
+        "    \"subnet\": \"2001:db8:1::/48\", "
+        "    \"interface\": \"eth1\", "
+        "    \"cache-threshold\": .25 "
+        " } ],"
+        "\"valid-lifetime\": 4000 }";
 
-    ASSERT_NO_THROW(client.doRenew());
+    Dhcp6Client client;
+    client.setInterface("eth1");
+    client.requestAddress(0xabca, IOAddress("2001:db8:1::28"));
+
+    ASSERT_NO_THROW(configure(config, *client.getServer()));
+
+    ASSERT_NO_THROW(HooksManager::preCalloutsLibraryHandle().registerCallout(
+                    "leases6_committed", leases6_committed_callout));
+
+    ASSERT_NO_THROW(client.doSARR());
 
     // Make sure that we received a response
     ASSERT_TRUE(client.getContext().response_);
@@ -3304,11 +3578,23 @@ TEST_F(HooksDhcpv6SrvTest, leases6CommittedRenew) {
     // Check that the callback called is indeed the one we installed
     EXPECT_EQ("leases6_committed", callback_name_);
 
+    // Check if all expected parameters were really received
+    vector<string> expected_argument_names;
+    expected_argument_names.push_back("query6");
+    expected_argument_names.push_back("deleted_leases6");
+    expected_argument_names.push_back("leases6");
+
+    sort(expected_argument_names.begin(), expected_argument_names.end());
+    EXPECT_TRUE(callback_argument_names_ == expected_argument_names);
+
+    // Newly allocated lease should be returned.
     ASSERT_TRUE(callback_new_leases6_);
-    EXPECT_EQ(3, callback_new_leases6_->size());
-    lease = callback_new_leases6_->at(2);
+    ASSERT_EQ(1, callback_new_leases6_->size());
+    Lease6Ptr lease = callback_new_leases6_->at(0);
     ASSERT_TRUE(lease);
-    EXPECT_EQ("2001:db8:1::", lease->addr_.toText());
+    EXPECT_EQ("2001:db8:1::28", lease->addr_.toText());
+
+    // Deleted lease must not be present, because it is a new allocation.
     ASSERT_TRUE(callback_deleted_leases6_);
     EXPECT_TRUE(callback_deleted_leases6_->empty());
 
@@ -3320,34 +3606,34 @@ TEST_F(HooksDhcpv6SrvTest, leases6CommittedRenew) {
 
     resetCalloutBuffers();
 
-    // Renew a prefix: this should lead to an error as no prefix pool
-    // is configured.
-    client.requestPrefix(0x1122, 64, IOAddress("2001:db8:1000::"));
-
-    ASSERT_NO_THROW(client.doRenew());
+    // Request the lease and make sure that the callout has been executed.
+    ASSERT_NO_THROW(client.doRequest());
 
     // Make sure that we received a response
     ASSERT_TRUE(client.getContext().response_);
 
-    // Check the error.
-    EXPECT_EQ(STATUS_NoPrefixAvail, client.getStatusCode(0x1122));
-
     // Check that the callback called is indeed the one we installed
     EXPECT_EQ("leases6_committed", callback_name_);
 
+    // Requested lease should not be present, because it is reused.
     ASSERT_TRUE(callback_new_leases6_);
-    EXPECT_EQ(3, callback_new_leases6_->size());
+    EXPECT_TRUE(callback_new_leases6_->empty());
+
+    // Deleted lease must not be present, because it is renewed.
     ASSERT_TRUE(callback_deleted_leases6_);
     EXPECT_TRUE(callback_deleted_leases6_->empty());
 
+    // Pkt passed to a callout must be configured to copy retrieved options.
+    EXPECT_TRUE(callback_qry_options_copy_);
+
     // Check if the callout handle state was reset after the callout.
     checkCalloutHandleReset(client.getContext().query_);
 }
 
 // This test verifies that the callout installed on the leases6_committed hook
-// point is executed as a result of RENEW message sent to allocate new
-// lease or renew an existing lease. Prefix variant.
-TEST_F(HooksDhcpv6SrvTest, leases6CommittedRenewPrefix) {
+// point is executed as a result of REQUEST message sent to reuse an
+// existing lease. Prefix variant.
+TEST_F(HooksDhcpv6SrvTest, leases6CommittedCachePrefix) {
     IfaceMgrTestConfig test_config(true);
 
     string config = "{ \"interfaces-config\": {"
@@ -3362,7 +3648,8 @@ TEST_F(HooksDhcpv6SrvTest, leases6CommittedRenewPrefix) {
         "        \"prefix-len\": 56, "
         "        \"delegated-len\": 64 } ], "
         "    \"subnet\": \"2001:db8:1::/48\", "
-        "    \"interface\": \"eth1\" "
+        "    \"interface\": \"eth1\", "
+        "    \"cache-threshold\": .25 "
         " } ],"
         "\"valid-lifetime\": 4000 }";
 
@@ -3407,10 +3694,13 @@ TEST_F(HooksDhcpv6SrvTest, leases6CommittedRenewPrefix) {
     // Pkt passed to a callout must be configured to copy retrieved options.
     EXPECT_TRUE(callback_qry_options_copy_);
 
+    // Check if the callout handle state was reset after the callout.
+    checkCalloutHandleReset(client.getContext().query_);
+
     resetCalloutBuffers();
 
-    // Renew the lease and make sure that the callout has been executed.
-    ASSERT_NO_THROW(client.doRenew());
+    // Request the lease and make sure that the callout has been executed.
+    ASSERT_NO_THROW(client.doRequest());
 
     // Make sure that we received a response
     ASSERT_TRUE(client.getContext().response_);
@@ -3418,15 +3708,11 @@ TEST_F(HooksDhcpv6SrvTest, leases6CommittedRenewPrefix) {
     // Check that the callback called is indeed the one we installed
     EXPECT_EQ("leases6_committed", callback_name_);
 
-    // Renewed lease should be returned.
+    // Requested lease should not be present, because it is reused.
     ASSERT_TRUE(callback_new_leases6_);
-    ASSERT_EQ(1, callback_new_leases6_->size());
-    lease = callback_new_leases6_->at(0);
-    ASSERT_TRUE(lease);
-    EXPECT_EQ("2001:db8:1:28::", lease->addr_.toText());
-    EXPECT_EQ(64, lease->prefixlen_);
+    EXPECT_TRUE(callback_new_leases6_->empty());
 
-    // Deleted lease must not be present, because it is a new allocation.
+    // Deleted lease must not be present, because it is renewed.
     ASSERT_TRUE(callback_deleted_leases6_);
     EXPECT_TRUE(callback_deleted_leases6_->empty());
 
@@ -3435,58 +3721,174 @@ TEST_F(HooksDhcpv6SrvTest, leases6CommittedRenewPrefix) {
 
     // Check if the callout handle state was reset after the callout.
     checkCalloutHandleReset(client.getContext().query_);
+}
 
-    resetCalloutBuffers();
+// This test verifies that it is possible to park a packet as a result of
+// the leases6_committed callouts.
+TEST_F(HooksDhcpv6SrvTest, leases6CommittedParkRequests) {
+    IfaceMgrTestConfig test_config(true);
 
-    // Let's try to renew again but force the client to renew a different
-    // prefix with a different IAID.
-    client.requestPrefix(0x2233, 64, IOAddress("2001:db8:1:29::"));
+    string config = "{ \"interfaces-config\": {"
+        "  \"interfaces\": [ \"*\" ]"
+        "},"
+        "\"preferred-lifetime\": 3000,"
+        "\"rebind-timer\": 2000, "
+        "\"renew-timer\": 1000, "
+        "\"subnet6\": [ { "
+        "    \"pools\": [ { \"pool\": \"2001:db8:1::/64\" } ],"
+        "    \"subnet\": \"2001:db8:1::/48\", "
+        "    \"interface\": \"eth1\" "
+        " } ],"
+        "\"valid-lifetime\": 4000 }";
 
-    ASSERT_NO_THROW(client.doRenew());
+    // Create first client and perform SARR.
+    Dhcp6Client client1;
+    client1.setInterface("eth1");
+    client1.requestAddress(0xabca, IOAddress("2001:db8:1::28"));
 
-    // Make sure that we received a response
-    ASSERT_TRUE(client.getContext().response_);
+    ASSERT_NO_THROW(configure(config, *client1.getServer()));
+
+    // This callout uses provided IO service object to post a function
+    // that unparks the packet. The packet is parked and can be unparked
+    // by simply calling IOService::poll.
+    ASSERT_NO_THROW(HooksManager::preCalloutsLibraryHandle().registerCallout(
+                    "leases6_committed", leases6_committed_park_callout));
+
+    ASSERT_NO_THROW(client1.doSARR());
+
+    // We should be offered an address but the REPLY should not arrive
+    // at this point, because the packet is parked.
+    ASSERT_FALSE(client1.getContext().response_);
 
     // Check that the callback called is indeed the one we installed
     EXPECT_EQ("leases6_committed", callback_name_);
 
-    // New lease should be returned.
+    // Check if all expected parameters were really received
+    vector<string> expected_argument_names;
+    expected_argument_names.push_back("query6");
+    expected_argument_names.push_back("deleted_leases6");
+    expected_argument_names.push_back("leases6");
+
+    sort(expected_argument_names.begin(), expected_argument_names.end());
+    EXPECT_TRUE(callback_argument_names_ == expected_argument_names);
+
+    // Newly allocated lease should be passed to the callout.
     ASSERT_TRUE(callback_new_leases6_);
-    ASSERT_EQ(2, callback_new_leases6_->size());
-    lease = callback_new_leases6_->at(1);
+    ASSERT_EQ(1, callback_new_leases6_->size());
+    Lease6Ptr lease = callback_new_leases6_->at(0);
     ASSERT_TRUE(lease);
-    EXPECT_EQ("2001:db8:1:29::", lease->addr_.toText());
-    EXPECT_EQ(64, lease->prefixlen_);
+    EXPECT_EQ("2001:db8:1::28", lease->addr_.toText());
 
-    // The old lease is kept.
+    // Deleted lease must not be present, because it is a new allocation.
     ASSERT_TRUE(callback_deleted_leases6_);
-    ASSERT_TRUE(callback_deleted_leases6_->empty());
+    EXPECT_TRUE(callback_deleted_leases6_->empty());
 
     // Pkt passed to a callout must be configured to copy retrieved options.
     EXPECT_TRUE(callback_qry_options_copy_);
 
     // Check if the callout handle state was reset after the callout.
-    checkCalloutHandleReset(client.getContext().query_);
+    checkCalloutHandleReset(client1.getContext().query_);
 
+    // Reset all indicators because we'll be now creating a second client.
     resetCalloutBuffers();
 
-    // The renewed prefix is just a hint.
-    client.requestPrefix(0x5577, 64, IOAddress("4000::1"));
+    // Create the second client to test that it may communicate with the
+    // server while the previous packet is parked.
+    Dhcp6Client client2(client1.getServer());
+    client2.setInterface("eth1");
+    client2.requestAddress(0xabca, IOAddress("2001:db8:1::29"));
+    ASSERT_NO_THROW(client2.doSARR());
 
-    ASSERT_NO_THROW(client.doRenew());
+    // The ADVERTISE should have been returned but not REPLAY, as this
+    // packet got parked too.
+    ASSERT_FALSE(client2.getContext().response_);
 
-    // Make sure that we received a response
-    ASSERT_TRUE(client.getContext().response_);
+    // Check that the callback called is indeed the one we installed.
+    EXPECT_EQ("leases6_committed", callback_name_);
+
+    // There should be now two actions scheduled on our IO service
+    // by the invoked callouts. They unpark both REPLY messages.
+    ASSERT_NO_THROW(io_service_->poll());
+
+    // Receive and check the first response.
+    ASSERT_NO_THROW(client1.receiveResponse());
+    ASSERT_TRUE(client1.getContext().response_);
+    Pkt6Ptr rsp = client1.getContext().response_;
+    EXPECT_EQ(DHCPV6_REPLY, rsp->getType());
+    EXPECT_TRUE(client1.hasLeaseForAddress(IOAddress("2001:db8:1::28")));
+
+    // Receive and check the second response.
+    ASSERT_NO_THROW(client2.receiveResponse());
+    ASSERT_TRUE(client2.getContext().response_);
+    rsp = client2.getContext().response_;
+    EXPECT_EQ(DHCPV6_REPLY, rsp->getType());
+    EXPECT_TRUE(client2.hasLeaseForAddress(IOAddress("2001:db8:1::29")));
+
+    // Check if the callout handle state was reset after the callout.
+    checkCalloutHandleReset(client2.getContext().query_);
+}
+
+// This test verifies that it is possible to park a packet as a result of
+// the leases6_committed callouts. Prefix variant.
+TEST_F(HooksDhcpv6SrvTest, leases6CommittedParkRequestsPrefixes) {
+    IfaceMgrTestConfig test_config(true);
+
+    string config = "{ \"interfaces-config\": {"
+        "  \"interfaces\": [ \"*\" ]"
+        "},"
+        "\"preferred-lifetime\": 3000,"
+        "\"rebind-timer\": 2000, "
+        "\"renew-timer\": 1000, "
+        "\"subnet6\": [ { "
+        "    \"pd-pools\": [ {"
+        "        \"prefix\": \"2001:db8:1::\", "
+        "        \"prefix-len\": 56, "
+        "        \"delegated-len\": 64 } ], "
+        "    \"subnet\": \"2001:db8:1::/48\", "
+        "    \"interface\": \"eth1\" "
+        " } ],"
+        "\"valid-lifetime\": 4000 }";
+
+    // Create first client and perform SARR.
+    Dhcp6Client client1;
+    client1.setInterface("eth1");
+    client1.requestPrefix(0xabca, 64, IOAddress("2001:db8:1:28::"));
+
+    ASSERT_NO_THROW(configure(config, *client1.getServer()));
+
+    // This callout uses provided IO service object to post a function
+    // that unparks the packet. The packet is parked and can be unparked
+    // by simply calling IOService::poll.
+    ASSERT_NO_THROW(HooksManager::preCalloutsLibraryHandle().registerCallout(
+                    "leases6_committed", leases6_committed_park_callout));
+
+    ASSERT_NO_THROW(client1.doSARR());
+
+    // We should be offered an address but the REPLY should not arrive
+    // at this point, because the packet is parked.
+    ASSERT_FALSE(client1.getContext().response_);
 
     // Check that the callback called is indeed the one we installed
     EXPECT_EQ("leases6_committed", callback_name_);
 
+    // Check if all expected parameters were really received
+    vector<string> expected_argument_names;
+    expected_argument_names.push_back("query6");
+    expected_argument_names.push_back("deleted_leases6");
+    expected_argument_names.push_back("leases6");
+
+    sort(expected_argument_names.begin(), expected_argument_names.end());
+    EXPECT_TRUE(callback_argument_names_ == expected_argument_names);
+
+    // Newly allocated lease should be passed to the callout.
     ASSERT_TRUE(callback_new_leases6_);
-    EXPECT_EQ(3, callback_new_leases6_->size());
-    lease = callback_new_leases6_->at(2);
+    ASSERT_EQ(1, callback_new_leases6_->size());
+    Lease6Ptr lease = callback_new_leases6_->at(0);
     ASSERT_TRUE(lease);
-    EXPECT_EQ("2001:db8:1::", lease->addr_.toText());
+    EXPECT_EQ("2001:db8:1:28::", lease->addr_.toText());
     EXPECT_EQ(64, lease->prefixlen_);
+
+    // Deleted lease must not be present, because it is a new allocation.
     ASSERT_TRUE(callback_deleted_leases6_);
     EXPECT_TRUE(callback_deleted_leases6_->empty());
 
@@ -3494,49 +3896,55 @@ TEST_F(HooksDhcpv6SrvTest, leases6CommittedRenewPrefix) {
     EXPECT_TRUE(callback_qry_options_copy_);
 
     // Check if the callout handle state was reset after the callout.
-    checkCalloutHandleReset(client.getContext().query_);
+    checkCalloutHandleReset(client1.getContext().query_);
 
+    // Reset all indicators because we'll be now creating a second client.
     resetCalloutBuffers();
 
-    // Renew an address: this should lead to an error as no address pool
-    // is configured.
-    client.requestAddress(0x1122, IOAddress("2001:db8:1::28"));
+    // Create the second client to test that it may communicate with the
+    // server while the previous packet is parked.
+    Dhcp6Client client2(client1.getServer());
+    client2.setInterface("eth1");
+    client2.requestPrefix(0xabca, 64, IOAddress("2001:db8:1:29::"));
+    ASSERT_NO_THROW(client2.doSARR());
 
-    ASSERT_NO_THROW(client.doRenew());
+    // The ADVERTISE should have been returned but not REPLAY, as this
+    // packet got parked too.
+    ASSERT_FALSE(client2.getContext().response_);
 
-    // Make sure that we received a response
-    ASSERT_TRUE(client.getContext().response_);
+    // Check that the callback called is indeed the one we installed.
+    EXPECT_EQ("leases6_committed", callback_name_);
 
-    // Check the error.
-    EXPECT_EQ(STATUS_NoAddrsAvail, client.getStatusCode(0x1122));
+    // There should be now two actions scheduled on our IO service
+    // by the invoked callouts. They unpark both REPLY messages.
+    ASSERT_NO_THROW(io_service_->poll());
 
-    // Check that the callback called is indeed the one we installed
-    EXPECT_EQ("leases6_committed", callback_name_);
+    // Receive and check the first response.
+    ASSERT_NO_THROW(client1.receiveResponse());
+    ASSERT_TRUE(client1.getContext().response_);
+    Pkt6Ptr rsp = client1.getContext().response_;
+    EXPECT_EQ(DHCPV6_REPLY, rsp->getType());
+    EXPECT_TRUE(client1.hasLeaseForPrefix(IOAddress("2001:db8:1:28::"), 64));
 
-    ASSERT_TRUE(callback_new_leases6_);
-    EXPECT_EQ(3, callback_new_leases6_->size());
-    ASSERT_TRUE(callback_deleted_leases6_);
-    EXPECT_TRUE(callback_deleted_leases6_->empty());
+    // Receive and check the second response.
+    ASSERT_NO_THROW(client2.receiveResponse());
+    ASSERT_TRUE(client2.getContext().response_);
+    rsp = client2.getContext().response_;
+    EXPECT_EQ(DHCPV6_REPLY, rsp->getType());
+    EXPECT_TRUE(client2.hasLeaseForPrefix(IOAddress("2001:db8:1:29::"), 64));
 
     // Check if the callout handle state was reset after the callout.
-    checkCalloutHandleReset(client.getContext().query_);
+    checkCalloutHandleReset(client2.getContext().query_);
 }
 
-// This test verifies that incoming (positive) RELEASE can be handled properly,
-// that a REPLY is generated, that the response has status code and that the
-// lease is indeed removed from the database.
-//
-// expected:
-// - returned REPLY message has copy of client-id
-// - returned REPLY message has server-id
-// - returned REPLY message has IA that does not include an IAADDR
-// - lease is actually removed from LeaseMgr
-TEST_F(HooksDhcpv6SrvTest, basicLease6Release) {
+// This test verifies that incoming (positive) RENEW can be handled properly,
+// and the lease6_renew callouts are triggered.
+TEST_F(HooksDhcpv6SrvTest, lease6RenewSimple) {
     NakedDhcpv6Srv srv(0);
 
-    // Install lease6_release_callout
+    // Install lease6_renew_callout
     EXPECT_NO_THROW(HooksManager::preCalloutsLibraryHandle().registerCallout(
-                        "lease6_release", lease6_release_callout));
+                        "lease6_renew", lease6_renew_callout));
 
     const IOAddress addr("2001:db8:1:1::cafe:babe");
     const uint32_t iaid = 234;
@@ -3560,213 +3968,87 @@ TEST_F(HooksDhcpv6SrvTest, basicLease6Release) {
                                                         addr);
     ASSERT_TRUE(l);
 
-    // Let's create a RELEASE
-    Pkt6Ptr req = Pkt6Ptr(new Pkt6(DHCPV6_RELEASE, 1234));
+    // Check that preferred, valid and cltt really set and not using
+    // previous (500, 501, etc.) values
+    EXPECT_NE(l->preferred_lft_, subnet_->getPreferred());
+    EXPECT_NE(l->valid_lft_, subnet_->getValid());
+    EXPECT_NE(l->cltt_, time(NULL));
+
+    // Let's create a RENEW
+    Pkt6Ptr req = Pkt6Ptr(new Pkt6(DHCPV6_RENEW, 1234));
     req->setRemoteAddr(IOAddress("fe80::abcd"));
+    req->setIface("eth0");
+    req->setIndex(ETH0_INDEX);
     boost::shared_ptr<Option6IA> ia = generateIA(D6O_IA_NA, iaid, 1500, 3000);
 
-    OptionPtr released_addr_opt(new Option6IAAddr(D6O_IAADDR, addr, 300, 500));
-    ia->addOption(released_addr_opt);
+    OptionPtr renewed_addr_opt(new Option6IAAddr(D6O_IAADDR, addr, 300, 500));
+    ia->addOption(renewed_addr_opt);
     req->addOption(ia);
     req->addOption(clientid);
-
-    // Server-id is mandatory in RELEASE
+    // Server-id is mandatory in RENEW
     req->addOption(srv.getServerID());
 
     // Pass it to the server and hope for a REPLY
-    Pkt6Ptr reply = srv.processRelease(req);
-
+    Pkt6Ptr reply = srv.processRenew(req);
     ASSERT_TRUE(reply);
 
     // Check that the callback called is indeed the one we installed
-    EXPECT_EQ("lease6_release", callback_name_);
+    EXPECT_EQ("lease6_renew", callback_name_);
 
     // Check that appropriate parameters are passed to the callouts
     EXPECT_TRUE(callback_qry_pkt6_);
     EXPECT_TRUE(callback_lease6_);
+    EXPECT_TRUE(callback_ia_na_);
 
     // Check if all expected parameters were really received
     vector<string> expected_argument_names;
     expected_argument_names.push_back("query6");
     expected_argument_names.push_back("lease6");
-    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);
-
-    // Check that the lease is really gone in the database
-    // get lease by address
-    l = LeaseMgrFactory::instance().getLease6(Lease::TYPE_NA, addr);
-    ASSERT_FALSE(l);
-
-    // Get lease by subnetid/duid/iaid combination
-    l = LeaseMgrFactory::instance().getLease6(Lease::TYPE_NA, *duid_, iaid,
-                                              subnet_->getID());
-    ASSERT_FALSE(l);
-
-    // Pkt passed to a callout must be configured to copy retrieved options.
-    EXPECT_TRUE(callback_qry_options_copy_);
-
-    // Check if the callout handle state was reset after the callout.
-    checkCalloutHandleReset(req);
-}
-
-// This is a variant of the previous test that tests that callouts are
-// properly invoked for the prefix release case.
-TEST_F(HooksDhcpv6SrvTest, basicLease6ReleasePD) {
-    NakedDhcpv6Srv srv(0);
-
-    // Install lease6_release_callout
-    EXPECT_NO_THROW(HooksManager::preCalloutsLibraryHandle().registerCallout(
-                        "lease6_release", lease6_release_callout));
-
-    const IOAddress prefix("2001:db8:1:2:1::");
-    const uint32_t iaid = 234;
-
-    // Generate client-id also duid_
-    OptionPtr clientid = generateClientId();
-
-    // Check that the prefix we are about to use is indeed in pool
-    ASSERT_TRUE(subnet_->inPool(Lease::TYPE_PD, prefix));
-
-    // Note that preferred, valid, T1 and T2 timers and CLTT are set to invalid
-    // value on purpose. They should be updated during RENEW.
-    Lease6Ptr lease(new Lease6(Lease::TYPE_PD, prefix, duid_, iaid,
-                               501, 502, subnet_->getID(),
-                               HWAddrPtr(), 80));
-    lease->cltt_ = 1234;
-    ASSERT_TRUE(LeaseMgrFactory::instance().addLease(lease));
-
-    // Check that the lease is really in the database
-    Lease6Ptr l = LeaseMgrFactory::instance().getLease6(Lease::TYPE_PD,
-                                                        prefix);
-    ASSERT_TRUE(l);
-
-    // Let's create a RELEASE
-    Pkt6Ptr req = Pkt6Ptr(new Pkt6(DHCPV6_RELEASE, 1234));
-    req->setRemoteAddr(IOAddress("fe80::abcd"));
-    boost::shared_ptr<Option6IA> ia = generateIA(D6O_IA_PD, iaid, 1500, 3000);
-
-    OptionPtr released_addr_opt(new Option6IAPrefix(D6O_IAPREFIX, prefix, 80,
-                                                    300, 500));
-    ia->addOption(released_addr_opt);
-    req->addOption(ia);
-    req->addOption(clientid);
-
-    // Server-id is mandatory in RELEASE
-    req->addOption(srv.getServerID());
-
-    // Pass it to the server and hope for a REPLY
-    Pkt6Ptr reply = srv.processRelease(req);
-
-    ASSERT_TRUE(reply);
-
-    // Check that the callback called is indeed the one we installed
-    EXPECT_EQ("lease6_release", callback_name_);
-
-    // Check that appropriate parameters are passed to the callouts
-    EXPECT_TRUE(callback_qry_pkt6_);
-    EXPECT_TRUE(callback_lease6_);
+    expected_argument_names.push_back("ia_na");
 
-    // Check if all expected parameters were really received
-    vector<string> expected_argument_names;
-    expected_argument_names.push_back("query6");
-    expected_argument_names.push_back("lease6");
     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);
-
-    // Check that the lease is really gone in the database
-    // get lease by address
-    l = LeaseMgrFactory::instance().getLease6(Lease::TYPE_PD, prefix);
-    ASSERT_FALSE(l);
-
-    // Get lease by subnetid/duid/iaid combination
-    l = LeaseMgrFactory::instance().getLease6(Lease::TYPE_PD, *duid_, iaid,
-                                              subnet_->getID());
-    ASSERT_FALSE(l);
-
-    // Pkt passed to a callout must be configured to copy retrieved options.
-    EXPECT_TRUE(callback_qry_options_copy_);
-
-    // Check if the callout handle state was reset after the callout.
-    checkCalloutHandleReset(req);
-}
-
-// This test verifies that skip flag returned by a callout installed on the
-// lease6_release hook point will keep the lease.
-TEST_F(HooksDhcpv6SrvTest, skipLease6Release) {
-    NakedDhcpv6Srv srv(0);
-
-    // Install lease6_release_skip
-    EXPECT_NO_THROW(HooksManager::preCalloutsLibraryHandle().registerCallout(
-                        "lease6_release", lease6_release_skip));
 
-    const IOAddress addr("2001:db8:1:1::cafe:babe");
-    const uint32_t iaid = 234;
+    EXPECT_TRUE(callback_argument_names_ == expected_argument_names);
 
-    // Generate client-id also duid_
-    OptionPtr clientid = generateClientId();
+    // Check if we get response at all
+    checkResponse(reply, DHCPV6_REPLY, 1234);
 
-    // Check that the address we are about to use is indeed in pool
-    ASSERT_TRUE(subnet_->inPool(Lease::TYPE_NA, addr));
+    OptionPtr tmp = reply->getOption(D6O_IA_NA);
+    ASSERT_TRUE(tmp);
 
-    // Note that preferred, valid, T1 and T2 timers and CLTT are set to invalid
-    // value on purpose. They should be updated during RENEW.
-    Lease6Ptr lease(new Lease6(Lease::TYPE_NA, addr, duid_, iaid,
-                               501, 502, subnet_->getID(),
-                               HWAddrPtr(), 0));
-    lease->cltt_ = 1234;
-    ASSERT_TRUE(LeaseMgrFactory::instance().addLease(lease));
+    // Check that IA_NA was returned and that there's an address included
+    boost::shared_ptr<Option6IAAddr> addr_opt;
+    ASSERT_NO_FATAL_FAILURE(addr_opt = checkIA_NA(reply, 234, subnet_->getT1(),
+                                                  subnet_->getT2()));
 
+    ASSERT_TRUE(addr_opt);
     // Check that the lease is really in the database
-    Lease6Ptr l = LeaseMgrFactory::instance().getLease6(Lease::TYPE_NA,
-                                                        addr);
-    ASSERT_TRUE(l);
-
-    // Let's create a RELEASE
-    Pkt6Ptr req = Pkt6Ptr(new Pkt6(DHCPV6_RELEASE, 1234));
-    req->setRemoteAddr(IOAddress("fe80::abcd"));
-    boost::shared_ptr<Option6IA> ia = generateIA(D6O_IA_NA, iaid, 1500, 3000);
-
-    OptionPtr released_addr_opt(new Option6IAAddr(D6O_IAADDR, addr, 300, 500));
-    ia->addOption(released_addr_opt);
-    req->addOption(ia);
-    req->addOption(clientid);
-
-    // Server-id is mandatory in RELEASE
-    req->addOption(srv.getServerID());
-
-    // Pass it to the server and hope for a REPLY
-    Pkt6Ptr reply = srv.processRelease(req);
-
-    ASSERT_TRUE(reply);
-
-    // Check that the callback called is indeed the one we installed
-    EXPECT_EQ("lease6_release", callback_name_);
-
-    // Check that the lease is still there
-    // get lease by address
-    l = LeaseMgrFactory::instance().getLease6(Lease::TYPE_NA,
-                                              addr);
-    ASSERT_TRUE(l);
-
-    // Get lease by subnetid/duid/iaid combination
-    l = LeaseMgrFactory::instance().getLease6(Lease::TYPE_NA, *duid_, iaid,
-                                              subnet_->getID());
+    l = checkLease(duid_, reply->getOption(D6O_IA_NA), addr_opt);
     ASSERT_TRUE(l);
 
+    // Check that the lease has been returned
+    ASSERT_TRUE(callback_lease6_);
+
+    // Check that the returned lease6 in callout is the same as the one in the
+    // database
+    EXPECT_TRUE(*callback_lease6_ == *l);
+
+    // Pkt passed to a callout must be configured to copy retrieved options.
+    EXPECT_TRUE(callback_qry_options_copy_);
+
     // Check if the callout handle state was reset after the callout.
     checkCalloutHandleReset(req);
 }
 
-// This test verifies that drop flag returned by a callout installed on the
-// lease6_release hook point will keep the lease.
-TEST_F(HooksDhcpv6SrvTest, dropLease6Release) {
+// This test verifies that incoming (positive) RENEW can be handled properly,
+// and the lease6_renew callouts are able to change the lease being updated.
+TEST_F(HooksDhcpv6SrvTest, lease6RenewLeaseUpdate) {
     NakedDhcpv6Srv srv(0);
 
-    // Install lease6_release_drop
+    // Install lease6_renew_update
     EXPECT_NO_THROW(HooksManager::preCalloutsLibraryHandle().registerCallout(
-                        "lease6_release", lease6_release_drop));
+                        "lease6_renew", lease6_renew_update));
 
     const IOAddress addr("2001:db8:1:1::cafe:babe");
     const uint32_t iaid = 234;
@@ -3790,245 +4072,153 @@ TEST_F(HooksDhcpv6SrvTest, dropLease6Release) {
                                                         addr);
     ASSERT_TRUE(l);
 
-    // Let's create a RELEASE
-    Pkt6Ptr req = Pkt6Ptr(new Pkt6(DHCPV6_RELEASE, 1234));
+    // Check that preferred, valid and cltt really set and not using
+    // previous (500, 501, etc.) values
+    EXPECT_NE(l->preferred_lft_, subnet_->getPreferred());
+    EXPECT_NE(l->valid_lft_, subnet_->getValid());
+    EXPECT_NE(l->cltt_, time(NULL));
+
+    // Let's create a RENEW
+    Pkt6Ptr req = Pkt6Ptr(new Pkt6(DHCPV6_RENEW, 1234));
     req->setRemoteAddr(IOAddress("fe80::abcd"));
+    req->setIface("eth0");
+    req->setIndex(ETH0_INDEX);
     boost::shared_ptr<Option6IA> ia = generateIA(D6O_IA_NA, iaid, 1500, 3000);
 
-    OptionPtr released_addr_opt(new Option6IAAddr(D6O_IAADDR, addr, 300, 500));
-    ia->addOption(released_addr_opt);
+    OptionPtr renewed_addr_opt(new Option6IAAddr(D6O_IAADDR, addr, 300, 500));
+    ia->addOption(renewed_addr_opt);
     req->addOption(ia);
     req->addOption(clientid);
 
-    // Server-id is mandatory in RELEASE
+    // Server-id is mandatory in RENEW
     req->addOption(srv.getServerID());
 
-    // Pass it to the server and hope for a REPLY
-    Pkt6Ptr reply = srv.processRelease(req);
+    // Turn on tee time calculation so we can see the effect of overriding
+    // the lease life time.
+    subnet_->setCalculateTeeTimes(true);
+    Triplet<uint32_t> unspecified;
+    subnet_->setT1(unspecified);
+    subnet_->setT2(unspecified);
+    subnet_->setT1Percent(0.60);
+    subnet_->setT2Percent(0.80);
 
+    // Pass it to the server and hope for a REPLY
+    Pkt6Ptr reply = srv.processRenew(req);
     ASSERT_TRUE(reply);
 
-    // Check that the callback called is indeed the one we installed
-    EXPECT_EQ("lease6_release", callback_name_);
-
-    // Check that the lease is still there
-    // get lease by address
-    l = LeaseMgrFactory::instance().getLease6(Lease::TYPE_NA,
-                                              addr);
-    ASSERT_TRUE(l);
-
-    // Get lease by subnetid/duid/iaid combination
-    l = LeaseMgrFactory::instance().getLease6(Lease::TYPE_NA, *duid_, iaid,
-                                              subnet_->getID());
-    ASSERT_TRUE(l);
-
-    // Check if the callout handle state was reset after the callout.
-    checkCalloutHandleReset(req);
-}
-
-// This test verifies that the leases6_committed callout is executed
-// with deleted leases as argument when RELEASE is processed.
-TEST_F(HooksDhcpv6SrvTest, leases6CommittedRelease) {
-    IfaceMgrTestConfig test_config(true);
-
-    string config = "{ \"interfaces-config\": {"
-        "  \"interfaces\": [ \"*\" ]"
-        "},"
-        "\"preferred-lifetime\": 3000,"
-        "\"rebind-timer\": 2000, "
-        "\"renew-timer\": 1000, "
-        "\"subnet6\": [ { "
-        "    \"pools\": [ { \"pool\": \"2001:db8:1::/64\" } ],"
-        "    \"subnet\": \"2001:db8:1::/48\", "
-        "    \"interface\": \"eth1\" "
-        " } ],"
-        "\"valid-lifetime\": 4000 }";
-
-    Dhcp6Client client;
-    client.setInterface("eth1");
-    client.requestAddress(0xabca, IOAddress("2001:db8:1::28"));
-
-    ASSERT_NO_THROW(configure(config, *client.getServer()));
-
-    ASSERT_NO_THROW(client.doSARR());
-    // Make sure that we received a response
-    ASSERT_TRUE(client.getContext().response_);
-
-    ASSERT_NO_THROW(HooksManager::preCalloutsLibraryHandle().registerCallout(
-                    "leases6_committed", leases6_committed_callout));
+    // Check if we get response at all
+    checkResponse(reply, DHCPV6_REPLY, 1234);
 
-    ASSERT_NO_THROW(client.doRelease());
+    OptionPtr tmp = reply->getOption(D6O_IA_NA);
+    ASSERT_TRUE(tmp);
 
-    // Check that the callback called is indeed the one we installed
-    EXPECT_EQ("leases6_committed", callback_name_);
+    // Check that IA_NA was returned and that there's an address included
+    boost::shared_ptr<Option6IAAddr> addr_opt;
+    ASSERT_NO_FATAL_FAILURE(addr_opt = checkIA_NA(reply, 1000, 602, 802));
 
-    // Check if all expected parameters were really received
-    vector<string> expected_argument_names;
-    expected_argument_names.push_back("query6");
-    expected_argument_names.push_back("deleted_leases6");
-    expected_argument_names.push_back("leases6");
+    ASSERT_TRUE(addr_opt);
+    // Check that the lease is really in the database
+    l = checkLease(duid_, reply->getOption(D6O_IA_NA), addr_opt);
+    ASSERT_TRUE(l);
 
-    sort(expected_argument_names.begin(), expected_argument_names.end());
-    EXPECT_TRUE(callback_argument_names_ == expected_argument_names);
+    // Check that we chose the distinct override values
+    ASSERT_NE(override_preferred_, subnet_->getPreferred());
+    EXPECT_NE(override_valid_,     subnet_->getValid());
 
-    // No new allocations.
-    ASSERT_TRUE(callback_new_leases6_);
-    ASSERT_TRUE(callback_new_leases6_->empty());
+    // Check that preferred, valid were overridden the callout
+    EXPECT_EQ(override_preferred_, l->preferred_lft_);
+    EXPECT_EQ(override_valid_, l->valid_lft_);
 
-    // Released lease should be returned.
-    ASSERT_TRUE(callback_deleted_leases6_);
-    EXPECT_EQ(1, callback_deleted_leases6_->size());
-    Lease6Ptr lease = callback_deleted_leases6_->at(0);
-    ASSERT_TRUE(lease);
-    EXPECT_EQ("2001:db8:1::28", lease->addr_.toText());
+    // Checking for CLTT is a bit tricky if we want to avoid off by 1 errors
+    int32_t cltt = static_cast<int32_t>(l->cltt_);
+    int32_t expected = static_cast<int32_t>(time(NULL));
+    // Equality or difference by 1 between cltt and expected is ok.
+    EXPECT_GE(1, abs(cltt - expected));
 
-    // Pkt passed to a callout must be configured to copy retrieved options.
-    EXPECT_TRUE(callback_qry_options_copy_);
+    Lease6Ptr deleted_lease =
+        LeaseMgrFactory::instance().getLease6(Lease::TYPE_NA,
+                                              addr_opt->getAddress());
+    EXPECT_TRUE(LeaseMgrFactory::instance().deleteLease(deleted_lease));
 
     // Check if the callout handle state was reset after the callout.
-    checkCalloutHandleReset(client.getContext().query_);
+    checkCalloutHandleReset(req);
 }
 
-// This test verifies that the leases6_committed callout is executed
-// with deleted leases as argument when RELEASE is processed. Prefix variant.
-TEST_F(HooksDhcpv6SrvTest, leases6CommittedReleasePrefix) {
-    IfaceMgrTestConfig test_config(true);
-
-    string config = "{ \"interfaces-config\": {"
-        "  \"interfaces\": [ \"*\" ]"
-        "},"
-        "\"preferred-lifetime\": 3000,"
-        "\"rebind-timer\": 2000, "
-        "\"renew-timer\": 1000, "
-        "\"subnet6\": [ { "
-        "    \"pd-pools\": [ {"
-        "        \"prefix\": \"2001:db8:1::\", "
-        "        \"prefix-len\": 56, "
-        "        \"delegated-len\": 64 } ], "
-        "    \"subnet\": \"2001:db8:1::/48\", "
-        "    \"interface\": \"eth1\" "
-        " } ],"
-        "\"valid-lifetime\": 4000 }";
-
-    Dhcp6Client client;
-    client.setInterface("eth1");
-    client.requestPrefix(0xabca, 64, IOAddress("2001:db8:1:28::"));
-
-    ASSERT_NO_THROW(configure(config, *client.getServer()));
-
-    ASSERT_NO_THROW(client.doSARR());
-    // Make sure that we received a response
-    ASSERT_TRUE(client.getContext().response_);
-
-    ASSERT_NO_THROW(HooksManager::preCalloutsLibraryHandle().registerCallout(
-                    "leases6_committed", leases6_committed_callout));
-
-    ASSERT_NO_THROW(client.doRelease());
-
-    // Check that the callback called is indeed the one we installed
-    EXPECT_EQ("leases6_committed", callback_name_);
-
-    // Check if all expected parameters were really received
-    vector<string> expected_argument_names;
-    expected_argument_names.push_back("query6");
-    expected_argument_names.push_back("deleted_leases6");
-    expected_argument_names.push_back("leases6");
-
-    sort(expected_argument_names.begin(), expected_argument_names.end());
-    EXPECT_TRUE(callback_argument_names_ == expected_argument_names);
-
-    // No new allocations.
-    ASSERT_TRUE(callback_new_leases6_);
-    ASSERT_TRUE(callback_new_leases6_->empty());
-
-    // Released lease should be returned.
-    ASSERT_TRUE(callback_deleted_leases6_);
-    EXPECT_EQ(1, callback_deleted_leases6_->size());
-    Lease6Ptr lease = callback_deleted_leases6_->at(0);
-    ASSERT_TRUE(lease);
-    EXPECT_EQ("2001:db8:1:28::", lease->addr_.toText());
-    EXPECT_EQ(64, lease->prefixlen_);
-
-    // Pkt passed to a callout must be configured to copy retrieved options.
-    EXPECT_TRUE(callback_qry_options_copy_);
+// This test verifies that incoming (positive) RENEW can be handled properly,
+// and the lease6_renew callouts are able to set the skip flag that will
+// reject the renewal
+TEST_F(HooksDhcpv6SrvTest, lease6RenewSkip) {
+    NakedDhcpv6Srv srv(0);
 
-    // Check if the callout handle state was reset after the callout.
-    checkCalloutHandleReset(client.getContext().query_);
-}
+    // Install lease6_renew_skip_callout
+    EXPECT_NO_THROW(HooksManager::preCalloutsLibraryHandle().registerCallout(
+                        "lease6_renew", lease6_renew_skip_callout));
 
-// This test verifies that the leases6_committed callout is executed
-// with deleted leases as argument when RELEASE is processed.
-// Variant with two addresses and two prefixes.
-TEST_F(HooksDhcpv6SrvTest, leases6CommittedReleaseMultiple) {
-    IfaceMgrTestConfig test_config(true);
+    const IOAddress addr("2001:db8:1:1::cafe:babe");
+    const uint32_t iaid = 234;
 
-    string config = "{ \"interfaces-config\": {"
-        "  \"interfaces\": [ \"*\" ]"
-        "},"
-        "\"preferred-lifetime\": 3000,"
-        "\"rebind-timer\": 2000, "
-        "\"renew-timer\": 1000, "
-        "\"subnet6\": [ { "
-        "    \"pools\": [ { \"pool\": \"2001:db8:1::/64\" } ],"
-        "    \"pd-pools\": [ {"
-        "        \"prefix\": \"2001:db8:2::\", "
-        "        \"prefix-len\": 56, "
-        "        \"delegated-len\": 64 } ], "
-        "    \"subnet\": \"2001:db8::/32\", "
-        "    \"interface\": \"eth1\" "
-        " } ],"
-        "\"valid-lifetime\": 4000 }";
+    // Generate client-id also duid_
+    OptionPtr clientid = generateClientId();
 
-    Dhcp6Client client;
-    client.setInterface("eth1");
-    // In theory we can reuse the IAID but copyIAsFromLeases() copies
-    // only one lease...
-    client.requestAddress(0xabca, IOAddress("2001:db8:1::28"));
-    client.requestPrefix(0xabcb, 64, IOAddress("2001:db8:2:28::"));
-    client.requestAddress(0x2233, IOAddress("2001:db8:1::29"));
-    client.requestPrefix(0x2234, 64, IOAddress("2001:db8:2:29::"));
+    // Check that the address we are about to use is indeed in pool
+    ASSERT_TRUE(subnet_->inPool(Lease::TYPE_NA, addr));
 
-    ASSERT_NO_THROW(configure(config, *client.getServer()));
+    // Note that preferred, valid, T1 and T2 timers and CLTT are set to invalid
+    // value on purpose. They should be updated during RENEW.
+    Lease6Ptr lease(new Lease6(Lease::TYPE_NA, addr, duid_, iaid,
+                               501, 502, subnet_->getID(),
+                               HWAddrPtr(), 0));
+    lease->cltt_ = 1234;
+    ASSERT_TRUE(LeaseMgrFactory::instance().addLease(lease));
 
-    ASSERT_NO_THROW(client.doSARR());
-    // Make sure that we received a response
-    ASSERT_TRUE(client.getContext().response_);
+    // Check that the lease is really in the database
+    Lease6Ptr l = LeaseMgrFactory::instance().getLease6(Lease::TYPE_NA,
+                                                        addr);
+    ASSERT_TRUE(l);
 
-    ASSERT_NO_THROW(HooksManager::preCalloutsLibraryHandle().registerCallout(
-                    "leases6_committed", leases6_committed_callout));
+    // Check that preferred, valid and cltt are really set and not using
+    // previous (500, 501, etc.) values
+    EXPECT_NE(l->preferred_lft_, subnet_->getPreferred());
+    EXPECT_NE(l->valid_lft_, subnet_->getValid());
+    EXPECT_NE(l->cltt_, time(NULL));
 
-    ASSERT_NO_THROW(client.doRelease());
+    // Let's create a RENEW
+    Pkt6Ptr req = Pkt6Ptr(new Pkt6(DHCPV6_RENEW, 1234));
+    req->setRemoteAddr(IOAddress("fe80::abcd"));
+    req->setIface("eth0");
+    req->setIndex(ETH0_INDEX);
+    boost::shared_ptr<Option6IA> ia = generateIA(D6O_IA_NA, iaid, 1500, 3000);
 
-    // Check that the callback called is indeed the one we installed
-    EXPECT_EQ("leases6_committed", callback_name_);
+    OptionPtr renewed_addr_opt(new Option6IAAddr(D6O_IAADDR, addr, 300, 500));
+    ia->addOption(renewed_addr_opt);
+    req->addOption(ia);
+    req->addOption(clientid);
 
-    // Check if all expected parameters were really received
-    vector<string> expected_argument_names;
-    expected_argument_names.push_back("query6");
-    expected_argument_names.push_back("deleted_leases6");
-    expected_argument_names.push_back("leases6");
+    // Server-id is mandatory in RENEW
+    req->addOption(srv.getServerID());
 
-    sort(expected_argument_names.begin(), expected_argument_names.end());
-    EXPECT_TRUE(callback_argument_names_ == expected_argument_names);
+    // Pass it to the server and hope for a REPLY
+    Pkt6Ptr reply = srv.processRenew(req);
+    ASSERT_TRUE(reply);
 
-    // No new allocations.
-    ASSERT_TRUE(callback_new_leases6_);
-    ASSERT_TRUE(callback_new_leases6_->empty());
+    // Check that our callback was called
+    EXPECT_EQ("lease6_renew", callback_name_);
 
-    // Released lease should be returned.
-    ASSERT_TRUE(callback_deleted_leases6_);
-    EXPECT_EQ(4, callback_deleted_leases6_->size());
+    l = LeaseMgrFactory::instance().getLease6(Lease::TYPE_NA, addr);
 
-    // Pkt passed to a callout must be configured to copy retrieved options.
-    EXPECT_TRUE(callback_qry_options_copy_);
+    // Check that the old values are still there and they were not
+    // updated by the renewal
+    EXPECT_NE(l->preferred_lft_, subnet_->getPreferred());
+    EXPECT_NE(l->valid_lft_, subnet_->getValid());
+    EXPECT_NE(l->cltt_, time(NULL));
 
     // Check if the callout handle state was reset after the callout.
-    checkCalloutHandleReset(client.getContext().query_);
+    checkCalloutHandleReset(req);
 }
 
 // This test verifies that incoming (positive) REBIND can be handled properly,
 // and the lease6_rebind callouts are triggered.
-TEST_F(HooksDhcpv6SrvTest, basicLease6Rebind) {
+TEST_F(HooksDhcpv6SrvTest, lease6RebindSimple) {
     NakedDhcpv6Srv srv(0);
 
     // Install lease6_rebind_callout
@@ -4127,7 +4317,7 @@ TEST_F(HooksDhcpv6SrvTest, basicLease6Rebind) {
 
 // This test verifies that incoming (positive) REBIND can be handled properly,
 // and the lease6_rebind callouts are able to change the lease being updated.
-TEST_F(HooksDhcpv6SrvTest, leaseUpdateLease6Rebind) {
+TEST_F(HooksDhcpv6SrvTest, lease6RebindLeaseUpdate) {
     NakedDhcpv6Srv srv(0);
 
     // Install lease6_rebind_update
@@ -4229,7 +4419,7 @@ TEST_F(HooksDhcpv6SrvTest, leaseUpdateLease6Rebind) {
 // This test verifies that incoming (positive) REBIND can be handled properly,
 // and the lease6_rebind callouts are able to set the skip flag that will
 // reject the rebinding
-TEST_F(HooksDhcpv6SrvTest, skipLease6Rebind) {
+TEST_F(HooksDhcpv6SrvTest, lease6RebindSkip) {
     NakedDhcpv6Srv srv(0);
 
     // Install lease6_rebind_skip
@@ -4295,364 +4485,495 @@ TEST_F(HooksDhcpv6SrvTest, skipLease6Rebind) {
     checkCalloutHandleReset(req);
 }
 
-// This test verifies that the callout installed on the leases6_committed hook
-// point is executed as a result of REBIND message sent to allocate new
-// lease or renew an existing lease.
-TEST_F(HooksDhcpv6SrvTest, leases6CommittedRebind) {
-    IfaceMgrTestConfig test_config(true);
+// This test verifies that incoming (positive) RELEASE can be handled properly,
+// that a REPLY is generated, that the response has status code and that the
+// lease is indeed removed from the database.
+//
+// expected:
+// - returned REPLY message has copy of client-id
+// - returned REPLY message has server-id
+// - returned REPLY message has IA that does not include an IAADDR
+// - lease is actually removed from LeaseMgr
+TEST_F(HooksDhcpv6SrvTest, lease6ReleaseSimple) {
+    NakedDhcpv6Srv srv(0);
+    CfgMgr::instance().getCurrentCfg()->getCfgExpiration()->setFlushReclaimedTimerWaitTime(0);
+    CfgMgr::instance().getCurrentCfg()->getCfgExpiration()->setHoldReclaimedTime(0);
 
-    string config = "{ \"interfaces-config\": {"
-        "  \"interfaces\": [ \"*\" ]"
-        "},"
-        "\"preferred-lifetime\": 3000,"
-        "\"rebind-timer\": 2000, "
-        "\"renew-timer\": 1000, "
-        "\"subnet6\": [ { "
-        "    \"pools\": [ { \"pool\": \"2001:db8:1::/64\" } ],"
-        "    \"subnet\": \"2001:db8:1::/48\", "
-        "    \"interface\": \"eth1\" "
-        " } ],"
-        "\"valid-lifetime\": 4000 }";
+    // Install lease6_release_callout
+    EXPECT_NO_THROW(HooksManager::preCalloutsLibraryHandle().registerCallout(
+                        "lease6_release", lease6_release_callout));
 
-    Dhcp6Client client;
-    client.setInterface("eth1");
-    client.requestAddress(0xabca, IOAddress("2001:db8:1::28"));
+    const IOAddress addr("2001:db8:1:1::cafe:babe");
+    const uint32_t iaid = 234;
 
-    ASSERT_NO_THROW(configure(config, *client.getServer()));
+    // Generate client-id also duid_
+    OptionPtr clientid = generateClientId();
 
-    ASSERT_NO_THROW(HooksManager::preCalloutsLibraryHandle().registerCallout(
-                    "leases6_committed", leases6_committed_callout));
+    // Check that the address we are about to use is indeed in pool
+    ASSERT_TRUE(subnet_->inPool(Lease::TYPE_NA, addr));
 
-    ASSERT_NO_THROW(client.doSARR());
+    // Note that preferred, valid, T1 and T2 timers and CLTT are set to invalid
+    // value on purpose. They should be updated during RENEW.
+    Lease6Ptr lease(new Lease6(Lease::TYPE_NA, addr, duid_, iaid,
+                               501, 502, subnet_->getID(),
+                               HWAddrPtr(), 0));
+    lease->cltt_ = 1234;
+    ASSERT_TRUE(LeaseMgrFactory::instance().addLease(lease));
 
-    // Make sure that we received a response
-    ASSERT_TRUE(client.getContext().response_);
+    // Check that the lease is really in the database
+    Lease6Ptr l = LeaseMgrFactory::instance().getLease6(Lease::TYPE_NA,
+                                                        addr);
+    ASSERT_TRUE(l);
+
+    // Let's create a RELEASE
+    Pkt6Ptr rel = Pkt6Ptr(new Pkt6(DHCPV6_RELEASE, 1234));
+    rel->setRemoteAddr(IOAddress("fe80::abcd"));
+    boost::shared_ptr<Option6IA> ia = generateIA(D6O_IA_NA, iaid, 1500, 3000);
+
+    OptionPtr released_addr_opt(new Option6IAAddr(D6O_IAADDR, addr, 300, 500));
+    ia->addOption(released_addr_opt);
+    rel->addOption(ia);
+    rel->addOption(clientid);
+
+    // Server-id is mandatory in RELEASE
+    rel->addOption(srv.getServerID());
+
+    // Pass it to the server and hope for a REPLY
+    Pkt6Ptr reply = srv.processRelease(rel);
+
+    ASSERT_TRUE(reply);
 
     // Check that the callback called is indeed the one we installed
-    EXPECT_EQ("leases6_committed", callback_name_);
+    EXPECT_EQ("lease6_release", callback_name_);
+
+    // Check that appropriate parameters are passed to the callouts
+    EXPECT_TRUE(callback_qry_pkt6_);
+    EXPECT_TRUE(callback_lease6_);
 
     // Check if all expected parameters were really received
     vector<string> expected_argument_names;
     expected_argument_names.push_back("query6");
-    expected_argument_names.push_back("deleted_leases6");
-    expected_argument_names.push_back("leases6");
-
+    expected_argument_names.push_back("lease6");
+    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);
 
-    // Newly allocated lease should be returned.
-    ASSERT_TRUE(callback_new_leases6_);
-    ASSERT_EQ(1, callback_new_leases6_->size());
-    Lease6Ptr lease = callback_new_leases6_->at(0);
-    ASSERT_TRUE(lease);
-    EXPECT_EQ("2001:db8:1::28", lease->addr_.toText());
+    // Check that the lease is really gone in the database
+    // get lease by address
+    l = LeaseMgrFactory::instance().getLease6(Lease::TYPE_NA, addr);
+    ASSERT_FALSE(l);
 
-    // Deleted lease must not be present, because it is a new allocation.
-    ASSERT_TRUE(callback_deleted_leases6_);
-    EXPECT_TRUE(callback_deleted_leases6_->empty());
+    // Get lease by subnetid/duid/iaid combination
+    l = LeaseMgrFactory::instance().getLease6(Lease::TYPE_NA, *duid_, iaid,
+                                              subnet_->getID());
+    ASSERT_FALSE(l);
 
     // Pkt passed to a callout must be configured to copy retrieved options.
     EXPECT_TRUE(callback_qry_options_copy_);
 
     // Check if the callout handle state was reset after the callout.
-    checkCalloutHandleReset(client.getContext().query_);
+    checkCalloutHandleReset(rel);
+}
 
-    resetCalloutBuffers();
+// This test verifies that incoming (positive) RELEASE can be handled properly,
+// that a REPLY is generated, that the response has status code and that the
+// lease is indeed removed from the database.
+//
+// expected:
+// - returned REPLY message has copy of client-id
+// - returned REPLY message has server-id
+// - returned REPLY message has IA that does not include an IAADDR
+// - lease is actually removed from LeaseMgr
+TEST_F(HooksDhcpv6SrvTest, lease6ReleaseSimpleNoDelete) {
+    NakedDhcpv6Srv srv(0);
 
-    // Rebind the lease and make sure that the callout has been executed.
-    ASSERT_NO_THROW(client.doRebind());
+    // Install lease6_release_callout
+    EXPECT_NO_THROW(HooksManager::preCalloutsLibraryHandle().registerCallout(
+                        "lease6_release", lease6_release_callout));
 
-    // Make sure that we received a response
-    ASSERT_TRUE(client.getContext().response_);
+    const IOAddress addr("2001:db8:1:1::cafe:babe");
+    const uint32_t iaid = 234;
+
+    // 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(Lease::TYPE_NA, addr));
+
+    // Note that preferred, valid, T1 and T2 timers and CLTT are set to invalid
+    // value on purpose. They should be updated during RENEW.
+    Lease6Ptr lease(new Lease6(Lease::TYPE_NA, addr, duid_, iaid,
+                               501, 502, subnet_->getID(),
+                               HWAddrPtr(), 0));
+    lease->cltt_ = 1234;
+    ASSERT_TRUE(LeaseMgrFactory::instance().addLease(lease));
+
+    // Check that the lease is really in the database
+    Lease6Ptr l = LeaseMgrFactory::instance().getLease6(Lease::TYPE_NA,
+                                                        addr);
+    ASSERT_TRUE(l);
+
+    // Let's create a RELEASE
+    Pkt6Ptr req = Pkt6Ptr(new Pkt6(DHCPV6_RELEASE, 1234));
+    req->setRemoteAddr(IOAddress("fe80::abcd"));
+    boost::shared_ptr<Option6IA> ia = generateIA(D6O_IA_NA, iaid, 1500, 3000);
+
+    OptionPtr released_addr_opt(new Option6IAAddr(D6O_IAADDR, addr, 300, 500));
+    ia->addOption(released_addr_opt);
+    req->addOption(ia);
+    req->addOption(clientid);
+
+    // Server-id is mandatory in RELEASE
+    req->addOption(srv.getServerID());
+
+    // Pass it to the server and hope for a REPLY
+    Pkt6Ptr reply = srv.processRelease(req);
+
+    ASSERT_TRUE(reply);
 
     // Check that the callback called is indeed the one we installed
-    EXPECT_EQ("leases6_committed", callback_name_);
+    EXPECT_EQ("lease6_release", callback_name_);
 
-    // Rebound lease should be returned.
-    ASSERT_TRUE(callback_new_leases6_);
-    ASSERT_EQ(1, callback_new_leases6_->size());
-    lease = callback_new_leases6_->at(0);
-    ASSERT_TRUE(lease);
-    EXPECT_EQ("2001:db8:1::28", lease->addr_.toText());
+    // Check that appropriate parameters are passed to the callouts
+    EXPECT_TRUE(callback_qry_pkt6_);
+    EXPECT_TRUE(callback_lease6_);
 
-    // Deleted lease must not be present, because it is a new allocation.
-    ASSERT_TRUE(callback_deleted_leases6_);
-    EXPECT_TRUE(callback_deleted_leases6_->empty());
+    // Check if all expected parameters were really received
+    vector<string> expected_argument_names;
+    expected_argument_names.push_back("query6");
+    expected_argument_names.push_back("lease6");
+    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);
+
+    // Check that the lease is not gone in the database
+    // get lease by address
+    l = LeaseMgrFactory::instance().getLease6(Lease::TYPE_NA, addr);
+    ASSERT_TRUE(l);
+
+    EXPECT_TRUE(l->expired());
+
+    // Get lease by subnetid/duid/iaid combination
+    l = LeaseMgrFactory::instance().getLease6(Lease::TYPE_NA, *duid_, iaid,
+                                              subnet_->getID());
+    ASSERT_TRUE(l);
+
+    EXPECT_TRUE(l->expired());
 
     // Pkt passed to a callout must be configured to copy retrieved options.
     EXPECT_TRUE(callback_qry_options_copy_);
 
-    // Check if the callout handle state was reset after the callout.
-    checkCalloutHandleReset(client.getContext().query_);
+    // Check if the callout handle state was reset after the callout.
+    checkCalloutHandleReset(req);
+}
+
+// This is a variant of the previous test that tests that callouts are
+// properly invoked for the prefix release case.
+TEST_F(HooksDhcpv6SrvTest, lease6ReleasePrefixSimple) {
+    NakedDhcpv6Srv srv(0);
+    CfgMgr::instance().getCurrentCfg()->getCfgExpiration()->setFlushReclaimedTimerWaitTime(0);
+    CfgMgr::instance().getCurrentCfg()->getCfgExpiration()->setHoldReclaimedTime(0);
+
+    // Install lease6_release_callout
+    EXPECT_NO_THROW(HooksManager::preCalloutsLibraryHandle().registerCallout(
+                        "lease6_release", lease6_release_callout));
 
-    resetCalloutBuffers();
+    const IOAddress prefix("2001:db8:1:2:1::");
+    const uint32_t iaid = 234;
 
-    // Let's try to rebind again but force the client to rebind a different
-    // address with a different IAID.
-    client.requestAddress(0x2233, IOAddress("2001:db8:1::29"));
+    // Generate client-id also duid_
+    OptionPtr clientid = generateClientId();
 
-    ASSERT_NO_THROW(client.doRebind());
+    // Check that the prefix we are about to use is indeed in pool
+    ASSERT_TRUE(subnet_->inPool(Lease::TYPE_PD, prefix));
 
-    // Make sure that we received a response
-    ASSERT_TRUE(client.getContext().response_);
+    // Note that preferred, valid, T1 and T2 timers and CLTT are set to invalid
+    // value on purpose. They should be updated during RENEW.
+    Lease6Ptr lease(new Lease6(Lease::TYPE_PD, prefix, duid_, iaid,
+                               501, 502, subnet_->getID(),
+                               HWAddrPtr(), 80));
+    lease->cltt_ = 1234;
+    ASSERT_TRUE(LeaseMgrFactory::instance().addLease(lease));
 
-    // Check that the callback called is indeed the one we installed
-    EXPECT_EQ("leases6_committed", callback_name_);
+    // Check that the lease is really in the database
+    Lease6Ptr l = LeaseMgrFactory::instance().getLease6(Lease::TYPE_PD,
+                                                        prefix);
+    ASSERT_TRUE(l);
 
-    // New lease should be returned.
-    ASSERT_TRUE(callback_new_leases6_);
-    ASSERT_EQ(2, callback_new_leases6_->size());
-    lease = callback_new_leases6_->at(1);
-    ASSERT_TRUE(lease);
-    EXPECT_EQ("2001:db8:1::29", lease->addr_.toText());
+    // Let's create a RELEASE
+    Pkt6Ptr req = Pkt6Ptr(new Pkt6(DHCPV6_RELEASE, 1234));
+    req->setRemoteAddr(IOAddress("fe80::abcd"));
+    boost::shared_ptr<Option6IA> ia = generateIA(D6O_IA_PD, iaid, 1500, 3000);
 
-    // The old lease is kept.
-    ASSERT_TRUE(callback_deleted_leases6_);
-    ASSERT_TRUE(callback_deleted_leases6_->empty());
+    OptionPtr released_addr_opt(new Option6IAPrefix(D6O_IAPREFIX, prefix, 80,
+                                                    300, 500));
+    ia->addOption(released_addr_opt);
+    req->addOption(ia);
+    req->addOption(clientid);
 
-    // Pkt passed to a callout must be configured to copy retrieved options.
-    EXPECT_TRUE(callback_qry_options_copy_);
+    // Server-id is mandatory in RELEASE
+    req->addOption(srv.getServerID());
 
-    // Check if the callout handle state was reset after the callout.
-    checkCalloutHandleReset(client.getContext().query_);
+    // Pass it to the server and hope for a REPLY
+    Pkt6Ptr reply = srv.processRelease(req);
 
-    resetCalloutBuffers();
+    ASSERT_TRUE(reply);
 
-    // The rebound address is just a hint.
-    client.requestAddress(0x5577, IOAddress("4000::2"));
+    // Check that the callback called is indeed the one we installed
+    EXPECT_EQ("lease6_release", callback_name_);
 
-    ASSERT_NO_THROW(client.doRebind());
+    // Check that appropriate parameters are passed to the callouts
+    EXPECT_TRUE(callback_qry_pkt6_);
+    EXPECT_TRUE(callback_lease6_);
 
-    // Make sure that we received a response
-    ASSERT_TRUE(client.getContext().response_);
+    // Check if all expected parameters were really received
+    vector<string> expected_argument_names;
+    expected_argument_names.push_back("query6");
+    expected_argument_names.push_back("lease6");
+    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);
 
-    // Check that the callback called is indeed the one we installed
-    EXPECT_EQ("leases6_committed", callback_name_);
+    // Check that the lease is really gone in the database
+    // get lease by address
+    l = LeaseMgrFactory::instance().getLease6(Lease::TYPE_PD, prefix);
+    ASSERT_FALSE(l);
 
-    ASSERT_TRUE(callback_new_leases6_);
-    EXPECT_EQ(3, callback_new_leases6_->size());
-    lease = callback_new_leases6_->at(2);
-    ASSERT_TRUE(lease);
-    EXPECT_EQ("2001:db8:1::", lease->addr_.toText());
-    ASSERT_TRUE(callback_deleted_leases6_);
-    EXPECT_TRUE(callback_deleted_leases6_->empty());
+    // Get lease by subnetid/duid/iaid combination
+    l = LeaseMgrFactory::instance().getLease6(Lease::TYPE_PD, *duid_, iaid,
+                                              subnet_->getID());
+    ASSERT_FALSE(l);
 
     // Pkt passed to a callout must be configured to copy retrieved options.
     EXPECT_TRUE(callback_qry_options_copy_);
 
     // Check if the callout handle state was reset after the callout.
-    checkCalloutHandleReset(client.getContext().query_);
-
-    resetCalloutBuffers();
-
-    // Rebind a prefix: this should lead to an error as no prefix pool
-    // is configured.
-    client.requestPrefix(0x1122, 64, IOAddress("2001:db8:1000::"));
-
-    ASSERT_NO_THROW(client.doRebind());
+    checkCalloutHandleReset(req);
+}
 
-    // Make sure that we received a response
-    ASSERT_TRUE(client.getContext().response_);
+// This is a variant of the previous test that tests that callouts are
+// properly invoked for the prefix release case.
+TEST_F(HooksDhcpv6SrvTest, lease6ReleasePrefixSimpleNoDelete) {
+    NakedDhcpv6Srv srv(0);
 
-    // Check the error.
-    EXPECT_EQ(STATUS_NoPrefixAvail, client.getStatusCode(0x1122));
+    // Install lease6_release_callout
+    EXPECT_NO_THROW(HooksManager::preCalloutsLibraryHandle().registerCallout(
+                        "lease6_release", lease6_release_callout));
 
-    // Check that the callback called is indeed the one we installed
-    EXPECT_EQ("leases6_committed", callback_name_);
+    const IOAddress prefix("2001:db8:1:2:1::");
+    const uint32_t iaid = 234;
 
-    ASSERT_TRUE(callback_new_leases6_);
-    EXPECT_EQ(3, callback_new_leases6_->size());
-    ASSERT_TRUE(callback_deleted_leases6_);
-    EXPECT_TRUE(callback_deleted_leases6_->empty());
+    // Generate client-id also duid_
+    OptionPtr clientid = generateClientId();
 
-    // Check if the callout handle state was reset after the callout.
-    checkCalloutHandleReset(client.getContext().query_);
-}
+    // Check that the prefix we are about to use is indeed in pool
+    ASSERT_TRUE(subnet_->inPool(Lease::TYPE_PD, prefix));
 
-// This test verifies that the callout installed on the leases6_committed hook
-// point is executed as a result of REBIND message sent to allocate new
-// lease or renew an existing lease. Prefix variant.
-TEST_F(HooksDhcpv6SrvTest, leases6CommittedRebindPrefix) {
-    IfaceMgrTestConfig test_config(true);
+    // Note that preferred, valid, T1 and T2 timers and CLTT are set to invalid
+    // value on purpose. They should be updated during RENEW.
+    Lease6Ptr lease(new Lease6(Lease::TYPE_PD, prefix, duid_, iaid,
+                               501, 502, subnet_->getID(),
+                               HWAddrPtr(), 80));
+    lease->cltt_ = 1234;
+    ASSERT_TRUE(LeaseMgrFactory::instance().addLease(lease));
 
-    string config = "{ \"interfaces-config\": {"
-        "  \"interfaces\": [ \"*\" ]"
-        "},"
-        "\"preferred-lifetime\": 3000,"
-        "\"rebind-timer\": 2000, "
-        "\"renew-timer\": 1000, "
-        "\"subnet6\": [ { "
-        "    \"pd-pools\": [ {"
-        "        \"prefix\": \"2001:db8:1::\", "
-        "        \"prefix-len\": 56, "
-        "        \"delegated-len\": 64 } ], "
-        "    \"subnet\": \"2001:db8:1::/48\", "
-        "    \"interface\": \"eth1\" "
-        " } ],"
-        "\"valid-lifetime\": 4000 }";
+    // Check that the lease is really in the database
+    Lease6Ptr l = LeaseMgrFactory::instance().getLease6(Lease::TYPE_PD,
+                                                        prefix);
+    ASSERT_TRUE(l);
 
-    Dhcp6Client client;
-    client.setInterface("eth1");
-    client.requestPrefix(0xabca, 64, IOAddress("2001:db8:1:28::"));
+    // Let's create a RELEASE
+    Pkt6Ptr req = Pkt6Ptr(new Pkt6(DHCPV6_RELEASE, 1234));
+    req->setRemoteAddr(IOAddress("fe80::abcd"));
+    boost::shared_ptr<Option6IA> ia = generateIA(D6O_IA_PD, iaid, 1500, 3000);
 
-    ASSERT_NO_THROW(configure(config, *client.getServer()));
+    OptionPtr released_addr_opt(new Option6IAPrefix(D6O_IAPREFIX, prefix, 80,
+                                                    300, 500));
+    ia->addOption(released_addr_opt);
+    req->addOption(ia);
+    req->addOption(clientid);
 
-    ASSERT_NO_THROW(HooksManager::preCalloutsLibraryHandle().registerCallout(
-                    "leases6_committed", leases6_committed_callout));
+    // Server-id is mandatory in RELEASE
+    req->addOption(srv.getServerID());
 
-    ASSERT_NO_THROW(client.doSARR());
+    // Pass it to the server and hope for a REPLY
+    Pkt6Ptr reply = srv.processRelease(req);
 
-    // Make sure that we received a response
-    ASSERT_TRUE(client.getContext().response_);
+    ASSERT_TRUE(reply);
 
     // Check that the callback called is indeed the one we installed
-    EXPECT_EQ("leases6_committed", callback_name_);
+    EXPECT_EQ("lease6_release", callback_name_);
+
+    // Check that appropriate parameters are passed to the callouts
+    EXPECT_TRUE(callback_qry_pkt6_);
+    EXPECT_TRUE(callback_lease6_);
 
     // Check if all expected parameters were really received
     vector<string> expected_argument_names;
     expected_argument_names.push_back("query6");
-    expected_argument_names.push_back("deleted_leases6");
-    expected_argument_names.push_back("leases6");
-
+    expected_argument_names.push_back("lease6");
+    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);
 
-    // Newly allocated lease should be returned.
-    ASSERT_TRUE(callback_new_leases6_);
-    ASSERT_EQ(1, callback_new_leases6_->size());
-    Lease6Ptr lease = callback_new_leases6_->at(0);
-    ASSERT_TRUE(lease);
-    EXPECT_EQ("2001:db8:1:28::", lease->addr_.toText());
-    EXPECT_EQ(64, lease->prefixlen_);
+    // Check that the lease is not gone in the database
+    // get lease by address
+    l = LeaseMgrFactory::instance().getLease6(Lease::TYPE_PD, prefix);
+    ASSERT_TRUE(l);
 
-    // Deleted lease must not be present, because it is a new allocation.
-    ASSERT_TRUE(callback_deleted_leases6_);
-    EXPECT_TRUE(callback_deleted_leases6_->empty());
+    EXPECT_TRUE(l->expired());
+
+    // Get lease by subnetid/duid/iaid combination
+    l = LeaseMgrFactory::instance().getLease6(Lease::TYPE_PD, *duid_, iaid,
+                                              subnet_->getID());
+    ASSERT_TRUE(l);
+
+    EXPECT_TRUE(l->expired());
 
     // Pkt passed to a callout must be configured to copy retrieved options.
     EXPECT_TRUE(callback_qry_options_copy_);
 
     // Check if the callout handle state was reset after the callout.
-    checkCalloutHandleReset(client.getContext().query_);
+    checkCalloutHandleReset(req);
+}
 
-    resetCalloutBuffers();
+// This test verifies that skip flag returned by a callout installed on the
+// lease6_release hook point will keep the lease.
+TEST_F(HooksDhcpv6SrvTest, lease6ReleaseSkip) {
+    NakedDhcpv6Srv srv(0);
 
-    // Rebind the lease and make sure that the callout has been executed.
-    ASSERT_NO_THROW(client.doRebind());
+    // Install lease6_release_skip
+    EXPECT_NO_THROW(HooksManager::preCalloutsLibraryHandle().registerCallout(
+                        "lease6_release", lease6_release_skip));
 
-    // Make sure that we received a response
-    ASSERT_TRUE(client.getContext().response_);
+    const IOAddress addr("2001:db8:1:1::cafe:babe");
+    const uint32_t iaid = 234;
 
-    // Check that the callback called is indeed the one we installed
-    EXPECT_EQ("leases6_committed", callback_name_);
+    // Generate client-id also duid_
+    OptionPtr clientid = generateClientId();
 
-    // Rebound lease should be returned.
-    ASSERT_TRUE(callback_new_leases6_);
-    ASSERT_EQ(1, callback_new_leases6_->size());
-    lease = callback_new_leases6_->at(0);
-    ASSERT_TRUE(lease);
-    EXPECT_EQ("2001:db8:1:28::", lease->addr_.toText());
-    EXPECT_EQ(64, lease->prefixlen_);
+    // Check that the address we are about to use is indeed in pool
+    ASSERT_TRUE(subnet_->inPool(Lease::TYPE_NA, addr));
 
-    // Deleted lease must not be present, because it is a new allocation.
-    ASSERT_TRUE(callback_deleted_leases6_);
-    EXPECT_TRUE(callback_deleted_leases6_->empty());
+    // Note that preferred, valid, T1 and T2 timers and CLTT are set to invalid
+    // value on purpose. They should be updated during RENEW.
+    Lease6Ptr lease(new Lease6(Lease::TYPE_NA, addr, duid_, iaid,
+                               501, 502, subnet_->getID(),
+                               HWAddrPtr(), 0));
+    lease->cltt_ = 1234;
+    ASSERT_TRUE(LeaseMgrFactory::instance().addLease(lease));
 
-    // Pkt passed to a callout must be configured to copy retrieved options.
-    EXPECT_TRUE(callback_qry_options_copy_);
+    // Check that the lease is really in the database
+    Lease6Ptr l = LeaseMgrFactory::instance().getLease6(Lease::TYPE_NA,
+                                                        addr);
+    ASSERT_TRUE(l);
 
-    // Check if the callout handle state was reset after the callout.
-    checkCalloutHandleReset(client.getContext().query_);
+    // Let's create a RELEASE
+    Pkt6Ptr rel = Pkt6Ptr(new Pkt6(DHCPV6_RELEASE, 1234));
+    rel->setRemoteAddr(IOAddress("fe80::abcd"));
+    boost::shared_ptr<Option6IA> ia = generateIA(D6O_IA_NA, iaid, 1500, 3000);
 
-    resetCalloutBuffers();
+    OptionPtr released_addr_opt(new Option6IAAddr(D6O_IAADDR, addr, 300, 500));
+    ia->addOption(released_addr_opt);
+    rel->addOption(ia);
+    rel->addOption(clientid);
 
-    // Let's try to rebind again but force the client to rebind a different
-    // prefix with a different IAID.
-    client.requestPrefix(0x2233, 64, IOAddress("2001:db8:1:29::"));
+    // Server-id is mandatory in RELEASE
+    rel->addOption(srv.getServerID());
 
-    ASSERT_NO_THROW(client.doRebind());
+    // Pass it to the server and hope for a REPLY
+    Pkt6Ptr reply = srv.processRelease(rel);
 
-    // Make sure that we received a response
-    ASSERT_TRUE(client.getContext().response_);
+    ASSERT_TRUE(reply);
 
     // Check that the callback called is indeed the one we installed
-    EXPECT_EQ("leases6_committed", callback_name_);
-
-    // New lease should be returned.
-    ASSERT_TRUE(callback_new_leases6_);
-    ASSERT_EQ(2, callback_new_leases6_->size());
-    lease = callback_new_leases6_->at(1);
-    ASSERT_TRUE(lease);
-    EXPECT_EQ("2001:db8:1:29::", lease->addr_.toText());
-    EXPECT_EQ(64, lease->prefixlen_);
+    EXPECT_EQ("lease6_release", callback_name_);
 
-    // The old lease is kept.
-    ASSERT_TRUE(callback_deleted_leases6_);
-    ASSERT_TRUE(callback_deleted_leases6_->empty());
+    // Check that the lease is still there
+    // get lease by address
+    l = LeaseMgrFactory::instance().getLease6(Lease::TYPE_NA,
+                                              addr);
+    ASSERT_TRUE(l);
 
-    // Pkt passed to a callout must be configured to copy retrieved options.
-    EXPECT_TRUE(callback_qry_options_copy_);
+    // Get lease by subnetid/duid/iaid combination
+    l = LeaseMgrFactory::instance().getLease6(Lease::TYPE_NA, *duid_, iaid,
+                                              subnet_->getID());
+    ASSERT_TRUE(l);
 
     // Check if the callout handle state was reset after the callout.
-    checkCalloutHandleReset(client.getContext().query_);
-
-    resetCalloutBuffers();
+    checkCalloutHandleReset(rel);
+}
 
-    // The rebound prefix is just a hint.
-    client.requestPrefix(0x5577, 64, IOAddress("4000::1"));
+// This test verifies that drop flag returned by a callout installed on the
+// lease6_release hook point will keep the lease.
+TEST_F(HooksDhcpv6SrvTest, lease6ReleaseDrop) {
+    NakedDhcpv6Srv srv(0);
 
-    ASSERT_NO_THROW(client.doRebind());
+    // Install lease6_release_drop
+    EXPECT_NO_THROW(HooksManager::preCalloutsLibraryHandle().registerCallout(
+                        "lease6_release", lease6_release_drop));
 
-    // Make sure that we received a response
-    ASSERT_TRUE(client.getContext().response_);
+    const IOAddress addr("2001:db8:1:1::cafe:babe");
+    const uint32_t iaid = 234;
 
-    // Check that the callback called is indeed the one we installed
-    EXPECT_EQ("leases6_committed", callback_name_);
+    // Generate client-id also duid_
+    OptionPtr clientid = generateClientId();
 
-    ASSERT_TRUE(callback_new_leases6_);
-    EXPECT_EQ(3, callback_new_leases6_->size());
-    lease = callback_new_leases6_->at(2);
-    ASSERT_TRUE(lease);
-    EXPECT_EQ("2001:db8:1::", lease->addr_.toText());
-    EXPECT_EQ(64, lease->prefixlen_);
-    ASSERT_TRUE(callback_deleted_leases6_);
-    EXPECT_TRUE(callback_deleted_leases6_->empty());
+    // Check that the address we are about to use is indeed in pool
+    ASSERT_TRUE(subnet_->inPool(Lease::TYPE_NA, addr));
 
-    // Pkt passed to a callout must be configured to copy retrieved options.
-    EXPECT_TRUE(callback_qry_options_copy_);
+    // Note that preferred, valid, T1 and T2 timers and CLTT are set to invalid
+    // value on purpose. They should be updated during RENEW.
+    Lease6Ptr lease(new Lease6(Lease::TYPE_NA, addr, duid_, iaid,
+                               501, 502, subnet_->getID(),
+                               HWAddrPtr(), 0));
+    lease->cltt_ = 1234;
+    ASSERT_TRUE(LeaseMgrFactory::instance().addLease(lease));
 
-    // Check if the callout handle state was reset after the callout.
-    checkCalloutHandleReset(client.getContext().query_);
+    // Check that the lease is really in the database
+    Lease6Ptr l = LeaseMgrFactory::instance().getLease6(Lease::TYPE_NA,
+                                                        addr);
+    ASSERT_TRUE(l);
 
-    resetCalloutBuffers();
+    // Let's create a RELEASE
+    Pkt6Ptr rel = Pkt6Ptr(new Pkt6(DHCPV6_RELEASE, 1234));
+    rel->setRemoteAddr(IOAddress("fe80::abcd"));
+    boost::shared_ptr<Option6IA> ia = generateIA(D6O_IA_NA, iaid, 1500, 3000);
 
-    // Rebind an address: this should lead to an error as no address pool
-    // is configured.
-    client.requestAddress(0x1122, IOAddress("2001:db8:1::28"));
+    OptionPtr released_addr_opt(new Option6IAAddr(D6O_IAADDR, addr, 300, 500));
+    ia->addOption(released_addr_opt);
+    rel->addOption(ia);
+    rel->addOption(clientid);
 
-    ASSERT_NO_THROW(client.doRebind());
+    // Server-id is mandatory in RELEASE
+    rel->addOption(srv.getServerID());
 
-    // Make sure that we received a response
-    ASSERT_TRUE(client.getContext().response_);
+    // Pass it to the server and hope for a REPLY
+    Pkt6Ptr reply = srv.processRelease(rel);
 
-    // Check the error.
-    EXPECT_EQ(STATUS_NoAddrsAvail, client.getStatusCode(0x1122));
+    ASSERT_TRUE(reply);
 
     // Check that the callback called is indeed the one we installed
-    EXPECT_EQ("leases6_committed", callback_name_);
+    EXPECT_EQ("lease6_release", callback_name_);
 
-    ASSERT_TRUE(callback_new_leases6_);
-    EXPECT_EQ(3, callback_new_leases6_->size());
-    ASSERT_TRUE(callback_deleted_leases6_);
-    EXPECT_TRUE(callback_deleted_leases6_->empty());
+    // Check that the lease is still there
+    // get lease by address
+    l = LeaseMgrFactory::instance().getLease6(Lease::TYPE_NA,
+                                              addr);
+    ASSERT_TRUE(l);
+
+    // Get lease by subnetid/duid/iaid combination
+    l = LeaseMgrFactory::instance().getLease6(Lease::TYPE_NA, *duid_, iaid,
+                                              subnet_->getID());
+    ASSERT_TRUE(l);
 
     // Check if the callout handle state was reset after the callout.
-    checkCalloutHandleReset(client.getContext().query_);
+    checkCalloutHandleReset(rel);
 }
 
 // This test checks that the basic decline hook (lease6_decline) is
 // triggered properly.
-TEST_F(HooksDhcpv6SrvTest, basicLease6Decline) {
+TEST_F(HooksDhcpv6SrvTest, lease6DeclineSimple) {
     IfaceMgrTestConfig test_config(true);
 
     // Install lease6_decline callout
@@ -4799,139 +5120,7 @@ TEST_F(HooksDhcpv6SrvTest, lease6DeclineDrop) {
     checkCalloutHandleReset(client.getContext().query_);
 }
 
-// This test verifies that the leases6_committed callout is executed
-// when DECLINE is processed. The declined lease is expected to be passed
-// in leases6 argument to the callout.
-TEST_F(HooksDhcpv6SrvTest, leases6CommittedDecline) {
-    IfaceMgrTestConfig test_config(true);
-
-    string config = "{ \"interfaces-config\": {"
-        "  \"interfaces\": [ \"*\" ]"
-        "},"
-        "\"preferred-lifetime\": 3000,"
-        "\"rebind-timer\": 2000, "
-        "\"renew-timer\": 1000, "
-        "\"subnet6\": [ { "
-        "    \"pools\": [ { \"pool\": \"2001:db8:1::/64\" } ],"
-        "    \"subnet\": \"2001:db8:1::/48\", "
-        "    \"interface\": \"eth1\" "
-        " } ],"
-        "\"valid-lifetime\": 4000 }";
-
-    Dhcp6Client client;
-    client.setInterface("eth1");
-    client.requestAddress(0xabca, IOAddress("2001:db8:1::28"));
-
-    ASSERT_NO_THROW(configure(config, *client.getServer()));
-
-    ASSERT_NO_THROW(client.doSARR());
-    // Make sure that we received a response
-    ASSERT_TRUE(client.getContext().response_);
-
-    ASSERT_NO_THROW(HooksManager::preCalloutsLibraryHandle().registerCallout(
-                    "leases6_committed", leases6_committed_callout));
-
-    ASSERT_NO_THROW(client.doDecline());
-
-    // Check that the callback called is indeed the one we installed
-    EXPECT_EQ("leases6_committed", callback_name_);
-
-    // Check if all expected parameters were really received
-    vector<string> expected_argument_names;
-    expected_argument_names.push_back("query6");
-    expected_argument_names.push_back("deleted_leases6");
-    expected_argument_names.push_back("leases6");
-
-    sort(expected_argument_names.begin(), expected_argument_names.end());
-    EXPECT_TRUE(callback_argument_names_ == expected_argument_names);
-
-    // No deleted leases.
-    ASSERT_TRUE(callback_deleted_leases6_);
-    ASSERT_TRUE(callback_deleted_leases6_->empty());
-
-    // Declined lease should be returned.
-    ASSERT_TRUE(callback_new_leases6_);
-    ASSERT_EQ(1, callback_new_leases6_->size());
-    Lease6Ptr lease = callback_new_leases6_->at(0);
-    ASSERT_TRUE(lease);
-    EXPECT_EQ("2001:db8:1::28", lease->addr_.toText());
-
-    // Pkt passed to a callout must be configured to copy retrieved options.
-    EXPECT_TRUE(callback_qry_options_copy_);
-
-    // Check if the callout handle state was reset after the callout.
-    checkCalloutHandleReset(client.getContext().query_);
-}
-
-// This test verifies that the leases6_committed callout is executed
-// when DECLINE is processed. Variant with 2 IA_NAs.
-TEST_F(HooksDhcpv6SrvTest, leases6CommittedDeclineTwoNAs) {
-    IfaceMgrTestConfig test_config(true);
-
-    string config = "{ \"interfaces-config\": {"
-        "  \"interfaces\": [ \"*\" ]"
-        "},"
-        "\"preferred-lifetime\": 3000,"
-        "\"rebind-timer\": 2000, "
-        "\"renew-timer\": 1000, "
-        "\"subnet6\": [ { "
-        "    \"pools\": [ { \"pool\": \"2001:db8:1::/64\" } ],"
-        "    \"subnet\": \"2001:db8:1::/48\", "
-        "    \"interface\": \"eth1\" "
-        " } ],"
-        "\"valid-lifetime\": 4000 }";
-
-    Dhcp6Client client;
-    client.setInterface("eth1");
-    client.requestAddress(0xabca, IOAddress("2001:db8:1::28"));
-    client.requestAddress(0x2233, IOAddress("2001:db8:1::29"));
-
-    ASSERT_NO_THROW(configure(config, *client.getServer()));
-
-    ASSERT_NO_THROW(client.doSARR());
-    // Make sure that we received a response
-    ASSERT_TRUE(client.getContext().response_);
-
-    ASSERT_NO_THROW(HooksManager::preCalloutsLibraryHandle().registerCallout(
-                    "leases6_committed", leases6_committed_callout));
-
-    ASSERT_NO_THROW(client.doDecline());
-
-    // Check that the callback called is indeed the one we installed
-    EXPECT_EQ("leases6_committed", callback_name_);
-
-    // Check if all expected parameters were really received
-    vector<string> expected_argument_names;
-    expected_argument_names.push_back("query6");
-    expected_argument_names.push_back("deleted_leases6");
-    expected_argument_names.push_back("leases6");
-
-    sort(expected_argument_names.begin(), expected_argument_names.end());
-    EXPECT_TRUE(callback_argument_names_ == expected_argument_names);
-
-    // No deleted leases.
-    ASSERT_TRUE(callback_deleted_leases6_);
-    ASSERT_TRUE(callback_deleted_leases6_->empty());
-
-    // Declined lease should be returned.
-    ASSERT_TRUE(callback_new_leases6_);
-    ASSERT_EQ(2, callback_new_leases6_->size());
-    Lease6Ptr lease = callback_new_leases6_->at(0);
-    ASSERT_TRUE(lease);
-    EXPECT_EQ("2001:db8:1::28", lease->addr_.toText());
-    lease = callback_new_leases6_->at(1);
-    ASSERT_TRUE(lease);
-    EXPECT_EQ("2001:db8:1::29", lease->addr_.toText());
-
-    // Pkt passed to a callout must be configured to copy retrieved options.
-    EXPECT_TRUE(callback_qry_options_copy_);
-
-    // Check if the callout handle state was reset after the callout.
-    checkCalloutHandleReset(client.getContext().query_);
-}
-
 // Should test with one NA and two addresses but need an example first...
-
 // Checks if callout installed on host6_identifier can generate an
 // identifier and whether that identifier is actually used.
 TEST_F(HooksDhcpv6SrvTest, host6Identifier) {
@@ -5015,9 +5204,9 @@ TEST_F(HooksDhcpv6SrvTest, host6Identifier) {
     checkCalloutHandleReset(sol);
 }
 
-// Checks if callout installed on host6_identifier can generate an identifier
+// Checks if callout installed on host6_identifier can generate an identifier of
 // other type. This particular callout always returns hwaddr.
-TEST_F(HooksDhcpv6SrvTest, host6Identifier_hwaddr) {
+TEST_F(HooksDhcpv6SrvTest, host6IdentifierHWAddr) {
 
     // Configure 2 subnets, both directly reachable over local interface
     // (let's not complicate the matter with relays)
@@ -5053,7 +5242,7 @@ TEST_F(HooksDhcpv6SrvTest, host6Identifier_hwaddr) {
 
     CfgMgr::instance().commit();
 
-    // Install host6_identifier_foo_callout
+    // Install host6_identifier_hwaddr_callout
     EXPECT_NO_THROW(HooksManager::preCalloutsLibraryHandle().registerCallout(
                         "host6_identifier", host6_identifier_hwaddr_callout));
 
@@ -5098,7 +5287,6 @@ TEST_F(HooksDhcpv6SrvTest, host6Identifier_hwaddr) {
     checkCalloutHandleReset(sol);
 }
 
-
 // Verifies that libraries are unloaded by server destruction
 // The callout libraries write their library index number to a marker
 // file upon load and unload, making it simple to test whether or not
@@ -5270,7 +5458,7 @@ TEST_F(LoadUnloadDhcpv6SrvTest, Dhcpv6SrvConfigured) {
     }
 }
 
-// This test verifies that parked-packet-limit is enforced.
+// This test verifies that parked-packet-limit is properly enforced.
 TEST_F(HooksDhcpv6SrvTest, leases6ParkedPacketLimit) {
     IfaceMgrTestConfig test_config(true);
 
@@ -5400,5 +5588,4 @@ TEST_F(HooksDhcpv6SrvTest, leases6ParkedPacketLimit) {
     EXPECT_EQ(1, getStatistic("pkt6-receive-drop"));
 }
 
-
 }  // namespace