diff options
author | Razvan Becheriu <razvan@isc.org> | 2022-10-10 17:19:18 +0200 |
---|---|---|
committer | Razvan Becheriu <razvan@isc.org> | 2022-10-25 09:05:53 +0200 |
commit | a131e8ea3ffa25be85b36f1cc9df7a0723b916ce (patch) | |
tree | 2c98620437c25928512d30f38c96d09424b895dc /src/bin | |
parent | [#2607] bump lib versions for 2.3.2 (diff) | |
download | kea-a131e8ea3ffa25be85b36f1cc9df7a0723b916ce.tar.xz kea-a131e8ea3ffa25be85b36f1cc9df7a0723b916ce.zip |
[#2548] expire lease on release if affinity is enabled
Diffstat (limited to 'src/bin')
-rw-r--r-- | src/bin/dhcp4/dhcp4_messages.cc | 4 | ||||
-rw-r--r-- | src/bin/dhcp4/dhcp4_messages.h | 2 | ||||
-rw-r--r-- | src/bin/dhcp4/dhcp4_messages.mes | 12 | ||||
-rw-r--r-- | src/bin/dhcp4/dhcp4_srv.cc | 41 | ||||
-rw-r--r-- | src/bin/dhcp4/tests/dhcp4_test_utils.cc | 14 | ||||
-rw-r--r-- | src/bin/dhcp4/tests/dhcp4_test_utils.h | 10 | ||||
-rw-r--r-- | src/bin/dhcp4/tests/fqdn_unittest.cc | 45 | ||||
-rw-r--r-- | src/bin/dhcp4/tests/hooks_unittest.cc | 1204 | ||||
-rw-r--r-- | src/bin/dhcp4/tests/out_of_range_unittest.cc | 290 | ||||
-rw-r--r-- | src/bin/dhcp4/tests/release_unittest.cc | 151 | ||||
-rw-r--r-- | src/bin/dhcp4/tests/shared_network_unittest.cc | 2 | ||||
-rw-r--r-- | src/bin/dhcp6/dhcp6_messages.cc | 8 | ||||
-rw-r--r-- | src/bin/dhcp6/dhcp6_messages.h | 4 | ||||
-rw-r--r-- | src/bin/dhcp6/dhcp6_messages.mes | 31 | ||||
-rw-r--r-- | src/bin/dhcp6/dhcp6_srv.cc | 98 | ||||
-rw-r--r-- | src/bin/dhcp6/tests/dhcp6_srv_unittest.cc | 36 | ||||
-rw-r--r-- | src/bin/dhcp6/tests/dhcp6_test_utils.cc | 66 | ||||
-rw-r--r-- | src/bin/dhcp6/tests/dhcp6_test_utils.h | 38 | ||||
-rw-r--r-- | src/bin/dhcp6/tests/fqdn_unittest.cc | 47 | ||||
-rw-r--r-- | src/bin/dhcp6/tests/hooks_unittest.cc | 2707 |
20 files changed, 2875 insertions, 1935 deletions
diff --git a/src/bin/dhcp4/dhcp4_messages.cc b/src/bin/dhcp4/dhcp4_messages.cc index e93a9b0022..7f2ebf6a9a 100644 --- a/src/bin/dhcp4/dhcp4_messages.cc +++ b/src/bin/dhcp4/dhcp4_messages.cc @@ -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", diff --git a/src/bin/dhcp4/dhcp4_messages.h b/src/bin/dhcp4/dhcp4_messages.h index 1ff14ac5e0..b5ead0792d 100644 --- a/src/bin/dhcp4/dhcp4_messages.h +++ b/src/bin/dhcp4/dhcp4_messages.h @@ -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; diff --git a/src/bin/dhcp4/dhcp4_messages.mes b/src/bin/dhcp4/dhcp4_messages.mes index a5afe6e568..742d4fcd23 100644 --- a/src/bin/dhcp4/dhcp4_messages.mes +++ b/src/bin/dhcp4/dhcp4_messages.mes @@ -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, diff --git a/src/bin/dhcp4/dhcp4_srv.cc b/src/bin/dhcp4/dhcp4_srv.cc index df0b100047..bb29c8fe49 100644 --- a/src/bin/dhcp4/dhcp4_srv.cc +++ b/src/bin/dhcp4/dhcp4_srv.cc @@ -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) diff --git a/src/bin/dhcp4/tests/dhcp4_test_utils.cc b/src/bin/dhcp4/tests/dhcp4_test_utils.cc index 6916da5e78..91f9d42521 100644 --- a/src/bin/dhcp4/tests/dhcp4_test_utils.cc +++ b/src/bin/dhcp4/tests/dhcp4_test_utils.cc @@ -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) { diff --git a/src/bin/dhcp4/tests/dhcp4_test_utils.h b/src/bin/dhcp4/tests/dhcp4_test_utils.h index 17a3d5a75d..5436a67f9e 100644 --- a/src/bin/dhcp4/tests/dhcp4_test_utils.h +++ b/src/bin/dhcp4/tests/dhcp4_test_utils.h @@ -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. /// diff --git a/src/bin/dhcp4/tests/fqdn_unittest.cc b/src/bin/dhcp4/tests/fqdn_unittest.cc index 2a275c6041..2e3ce3e24a 100644 --- a/src/bin/dhcp4/tests/fqdn_unittest.cc +++ b/src/bin/dhcp4/tests/fqdn_unittest.cc @@ -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. diff --git a/src/bin/dhcp4/tests/hooks_unittest.cc b/src/bin/dhcp4/tests/hooks_unittest.cc index 84d37f8d97..4c092e6716 100644 --- a/src/bin/dhcp4/tests/hooks_unittest.cc +++ b/src/bin/dhcp4/tests/hooks_unittest.cc @@ -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); @@ -2213,16 +2242,185 @@ TEST_F(HooksDhcpv4SrvTest, leases4CommittedParkRequests) { 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; + + // Install lease4_renew_skip_callout + EXPECT_NO_THROW(HooksManager::preCalloutsLibraryHandle().registerCallout( + "lease4_renew", 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 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 a 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 a 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 diff --git a/src/bin/dhcp4/tests/out_of_range_unittest.cc b/src/bin/dhcp4/tests/out_of_range_unittest.cc index a11cfe6735..f5769474cb 100644 --- a/src/bin/dhcp4/tests/out_of_range_unittest.cc +++ b/src/bin/dhcp4/tests/out_of_range_unittest.cc @@ -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"; @@ -430,17 +476,44 @@ TEST_F(OutOfRangeTest, dynamicHostOutOfPool) { } // 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); @@ -460,11 +533,28 @@ TEST_F(OutOfRangeTest, dynamicHostReservationRemoved) { } // 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"; @@ -505,10 +623,24 @@ TEST_F(OutOfRangeTest, fixedHostDifferentPool) { } // 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 diff --git a/src/bin/dhcp4/tests/release_unittest.cc b/src/bin/dhcp4/tests/release_unittest.cc index f2f66f32cf..cff11d5ec3 100644 --- a/src/bin/dhcp4/tests/release_unittest.cc +++ b/src/bin/dhcp4/tests/release_unittest.cc @@ -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 diff --git a/src/bin/dhcp4/tests/shared_network_unittest.cc b/src/bin/dhcp4/tests/shared_network_unittest.cc index f9c137c858..10afcc2b14 100644 --- a/src/bin/dhcp4/tests/shared_network_unittest.cc +++ b/src/bin/dhcp4/tests/shared_network_unittest.cc @@ -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); diff --git a/src/bin/dhcp6/dhcp6_messages.cc b/src/bin/dhcp6/dhcp6_messages.cc index 4249c7f6e1..b93c162c7c 100644 --- a/src/bin/dhcp6/dhcp6_messages.cc +++ b/src/bin/dhcp6/dhcp6_messages.cc @@ -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)", diff --git a/src/bin/dhcp6/dhcp6_messages.h b/src/bin/dhcp6/dhcp6_messages.h index 7e88e99a5b..5ec8aa1eed 100644 --- a/src/bin/dhcp6/dhcp6_messages.h +++ b/src/bin/dhcp6/dhcp6_messages.h @@ -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; diff --git a/src/bin/dhcp6/dhcp6_messages.mes b/src/bin/dhcp6/dhcp6_messages.mes index 049c61e161..470e2c1660 100644 --- a/src/bin/dhcp6/dhcp6_messages.mes +++ b/src/bin/dhcp6/dhcp6_messages.mes @@ -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 diff --git a/src/bin/dhcp6/dhcp6_srv.cc b/src/bin/dhcp6/dhcp6_srv.cc index dfad23ba04..31bb3a7251 100644 --- a/src/bin/dhcp6/dhcp6_srv.cc +++ b/src/bin/dhcp6/dhcp6_srv.cc @@ -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); diff --git a/src/bin/dhcp6/tests/dhcp6_srv_unittest.cc b/src/bin/dhcp6/tests/dhcp6_srv_unittest.cc index a3620d5cbb..2a3d71b94a 100644 --- a/src/bin/dhcp6/tests/dhcp6_srv_unittest.cc +++ b/src/bin/dhcp6/tests/dhcp6_srv_unittest.cc @@ -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. // diff --git a/src/bin/dhcp6/tests/dhcp6_test_utils.cc b/src/bin/dhcp6/tests/dhcp6_test_utils.cc index 4d0e79dcb8..d9c6e34df6 100644 --- a/src/bin/dhcp6/tests/dhcp6_test_utils.cc +++ b/src/bin/dhcp6/tests/dhcp6_test_utils.cc @@ -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) { diff --git a/src/bin/dhcp6/tests/dhcp6_test_utils.h b/src/bin/dhcp6/tests/dhcp6_test_utils.h index 856f1a40f6..48953ad39f 100644 --- a/src/bin/dhcp6/tests/dhcp6_test_utils.h +++ b/src/bin/dhcp6/tests/dhcp6_test_utils.h @@ -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 /// diff --git a/src/bin/dhcp6/tests/fqdn_unittest.cc b/src/bin/dhcp6/tests/fqdn_unittest.cc index 683a71cf62..e3757df635 100644 --- a/src/bin/dhcp6/tests/fqdn_unittest.cc +++ b/src/bin/dhcp6/tests/fqdn_unittest.cc @@ -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) { diff --git a/src/bin/dhcp6/tests/hooks_unittest.cc b/src/bin/dhcp6/tests/hooks_unittest.cc index fac2d438ac..99be8bd558 100644 --- a/src/bin/dhcp6/tests/hooks_unittest.cc +++ b/src/bin/dhcp6/tests/hooks_unittest.cc @@ -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> @@ -37,15 +39,16 @@ #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,116 +2647,37 @@ TEST_F(HooksDhcpv6SrvTest, leases6CommittedCachePrefix) { // 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) { - 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 }"; - // Create first client and perform SARR. - Dhcp6Client client1; - client1.setInterface("eth1"); - client1.requestAddress(0xabca, IOAddress("2001:db8:1::28")); + resetCalloutBuffers(); - ASSERT_NO_THROW(configure(config, *client1.getServer())); + // Renew a prefix: this should lead to an error as no prefix pool + // is configured. + client.requestPrefix(0x1122, 64, IOAddress("2001:db8:1000::")); - // 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(client.doRenew()); - ASSERT_NO_THROW(client1.doSARR()); + // Make sure that we received a response + ASSERT_TRUE(client.getContext().response_); - // 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 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_); - // 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(1, callback_new_leases6_->size()); - Lease6Ptr 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 a new allocation. + EXPECT_EQ(3, callback_new_leases6_->size()); 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(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. - Dhcp6Client client2(client1.getServer()); - client2.setInterface("eth1"); - client2.requestAddress(0xabca, IOAddress("2001:db8:1::29")); - ASSERT_NO_THROW(client2.doSARR()); - - // The ADVERTISE should have been returned but not REPLAY, as this - // packet got parked too. - ASSERT_FALSE(client2.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_); + 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 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\": {" @@ -2806,24 +2696,19 @@ TEST_F(HooksDhcpv6SrvTest, leases6CommittedParkRequestsPrefixes) { " } ]," "\"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.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_); @@ -2837,7 +2722,7 @@ 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); @@ -2852,331 +2737,125 @@ TEST_F(HooksDhcpv6SrvTest, leases6CommittedParkRequestsPrefixes) { // 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. - Dhcp6Client client2(client1.getServer()); - client2.setInterface("eth1"); - client2.requestPrefix(0xabca, 64, IOAddress("2001:db8:1:29::")); - ASSERT_NO_THROW(client2.doSARR()); + // Renew the lease and make sure that the callout has been executed. + 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. + // 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()); + // 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_); - // 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_); -} - -// 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); - - // Install lease6_renew_callout - EXPECT_NO_THROW(HooksManager::preCalloutsLibraryHandle().registerCallout( - "lease6_renew", lease6_renew_callout)); - - 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); + checkCalloutHandleReset(client.getContext().query_); - // 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)); + resetCalloutBuffers(); - // 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); + // 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::")); - 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()); + ASSERT_NO_THROW(client.doRenew()); - // 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 that the callback called is indeed the one we installed - 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"); - expected_argument_names.push_back("ia_na"); - - 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); - - OptionPtr tmp = reply->getOption(D6O_IA_NA); - ASSERT_TRUE(tmp); - - // 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 - l = checkLease(duid_, reply->getOption(D6O_IA_NA), addr_opt); - ASSERT_TRUE(l); + EXPECT_EQ("leases6_committed", callback_name_); - // Check that the lease has been returned - ASSERT_TRUE(callback_lease6_); + // 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 returned lease6 in callout is the same as the one in the - // database - EXPECT_TRUE(*callback_lease6_ == *l); + // The old lease is kept. + ASSERT_TRUE(callback_deleted_leases6_); + ASSERT_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); -} - -// 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); - - // Install lease6_renew_update - EXPECT_NO_THROW(HooksManager::preCalloutsLibraryHandle().registerCallout( - "lease6_renew", lease6_renew_update)); - - 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); - - // 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 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()); - - // 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 if we get response at all - checkResponse(reply, DHCPV6_REPLY, 1234); + checkCalloutHandleReset(client.getContext().query_); - OptionPtr tmp = reply->getOption(D6O_IA_NA); - ASSERT_TRUE(tmp); + resetCalloutBuffers(); - // 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)); + // The renewed prefix is just a hint. + client.requestPrefix(0x5577, 64, IOAddress("4000::1")); - 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); + ASSERT_NO_THROW(client.doRenew()); - // Check that we chose the distinct override values - ASSERT_NE(override_preferred_, subnet_->getPreferred()); - EXPECT_NE(override_valid_, subnet_->getValid()); + // Make sure that we received a response + ASSERT_TRUE(client.getContext().response_); - // Check that preferred, valid were overridden the callout - EXPECT_EQ(override_preferred_, l->preferred_lft_); - EXPECT_EQ(override_valid_, l->valid_lft_); + // Check that the callback called is indeed the one we installed + EXPECT_EQ("leases6_committed", callback_name_); - // 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_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()); - 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); -} - -// 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; - - // 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); - - // 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)); + checkCalloutHandleReset(client.getContext().query_); - // 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); + resetCalloutBuffers(); - OptionPtr renewed_addr_opt(new Option6IAAddr(D6O_IAADDR, addr, 300, 500)); - ia->addOption(renewed_addr_opt); - req->addOption(ia); - req->addOption(clientid); + // Renew an address: this should lead to an error as no address pool + // is configured. + client.requestAddress(0x1122, IOAddress("2001:db8:1::28")); - // Server-id is mandatory in RENEW - req->addOption(srv.getServerID()); + ASSERT_NO_THROW(client.doRenew()); - // 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 that our callback was called - EXPECT_EQ("lease6_renew", callback_name_); + // Check the error. + EXPECT_EQ(STATUS_NoAddrsAvail, client.getStatusCode(0x1122)); - l = LeaseMgrFactory::instance().getLease6(Lease::TYPE_NA, addr); + // Check that the callback called is indeed the one we installed + EXPECT_EQ("leases6_committed", callback_name_); - // 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)); + 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(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 +// point is executed as a result of REBIND message sent to allocate new // lease or renew an existing lease. -TEST_F(HooksDhcpv6SrvTest, leases6CommittedRenew) { +TEST_F(HooksDhcpv6SrvTest, leases6CommittedRebind) { IfaceMgrTestConfig test_config(true); string config = "{ \"interfaces-config\": {" @@ -3237,8 +2916,8 @@ TEST_F(HooksDhcpv6SrvTest, leases6CommittedRenew) { resetCalloutBuffers(); - // Renew the lease and make sure that the callout has been executed. - ASSERT_NO_THROW(client.doRenew()); + // 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_); @@ -3246,7 +2925,7 @@ TEST_F(HooksDhcpv6SrvTest, leases6CommittedRenew) { // Check that the callback called is indeed the one we installed EXPECT_EQ("leases6_committed", callback_name_); - // Renewed lease should be returned. + // Rebound lease should be returned. ASSERT_TRUE(callback_new_leases6_); ASSERT_EQ(1, callback_new_leases6_->size()); lease = callback_new_leases6_->at(0); @@ -3260,13 +2939,16 @@ TEST_F(HooksDhcpv6SrvTest, leases6CommittedRenew) { // 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(); - // Let's try to renew again but force the client to renew a different + // 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")); - ASSERT_NO_THROW(client.doRenew()); + ASSERT_NO_THROW(client.doRebind()); // Make sure that we received a response ASSERT_TRUE(client.getContext().response_); @@ -3293,10 +2975,10 @@ TEST_F(HooksDhcpv6SrvTest, leases6CommittedRenew) { resetCalloutBuffers(); - // The renewed address is just a hint. + // The rebound address is just a hint. client.requestAddress(0x5577, IOAddress("4000::2")); - ASSERT_NO_THROW(client.doRenew()); + ASSERT_NO_THROW(client.doRebind()); // Make sure that we received a response ASSERT_TRUE(client.getContext().response_); @@ -3320,11 +3002,11 @@ TEST_F(HooksDhcpv6SrvTest, leases6CommittedRenew) { resetCalloutBuffers(); - // Renew a prefix: this should lead to an error as no prefix pool + // 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.doRenew()); + ASSERT_NO_THROW(client.doRebind()); // Make sure that we received a response ASSERT_TRUE(client.getContext().response_); @@ -3345,9 +3027,9 @@ TEST_F(HooksDhcpv6SrvTest, leases6CommittedRenew) { } // 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 +// point is executed as a result of REBIND message sent to allocate new // lease or renew an existing lease. Prefix variant. -TEST_F(HooksDhcpv6SrvTest, leases6CommittedRenewPrefix) { +TEST_F(HooksDhcpv6SrvTest, leases6CommittedRebindPrefix) { IfaceMgrTestConfig test_config(true); string config = "{ \"interfaces-config\": {" @@ -3407,10 +3089,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()); + // 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_); @@ -3418,7 +3103,7 @@ 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. + // Rebound lease should be returned. ASSERT_TRUE(callback_new_leases6_); ASSERT_EQ(1, callback_new_leases6_->size()); lease = callback_new_leases6_->at(0); @@ -3438,11 +3123,11 @@ TEST_F(HooksDhcpv6SrvTest, leases6CommittedRenewPrefix) { resetCalloutBuffers(); - // Let's try to renew again but force the client to renew a different + // 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::")); - ASSERT_NO_THROW(client.doRenew()); + ASSERT_NO_THROW(client.doRebind()); // Make sure that we received a response ASSERT_TRUE(client.getContext().response_); @@ -3470,10 +3155,10 @@ TEST_F(HooksDhcpv6SrvTest, leases6CommittedRenewPrefix) { resetCalloutBuffers(); - // The renewed prefix is just a hint. + // The rebound prefix is just a hint. client.requestPrefix(0x5577, 64, IOAddress("4000::1")); - ASSERT_NO_THROW(client.doRenew()); + ASSERT_NO_THROW(client.doRebind()); // Make sure that we received a response ASSERT_TRUE(client.getContext().response_); @@ -3498,11 +3183,11 @@ TEST_F(HooksDhcpv6SrvTest, leases6CommittedRenewPrefix) { resetCalloutBuffers(); - // Renew an address: this should lead to an error as no address pool + // Rebind 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()); + ASSERT_NO_THROW(client.doRebind()); // Make sure that we received a response ASSERT_TRUE(client.getContext().response_); @@ -3522,308 +3207,136 @@ TEST_F(HooksDhcpv6SrvTest, leases6CommittedRenewPrefix) { checkCalloutHandleReset(client.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) { - NakedDhcpv6Srv srv(0); - - // Install lease6_release_callout - EXPECT_NO_THROW(HooksManager::preCalloutsLibraryHandle().registerCallout( - "lease6_release", lease6_release_callout)); - - 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)); +// 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); - // 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)); + 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 lease is really in the database - Lease6Ptr l = LeaseMgrFactory::instance().getLease6(Lease::TYPE_NA, - addr); - ASSERT_TRUE(l); + Dhcp6Client client; + client.setInterface("eth1"); + client.requestAddress(0xabca, 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_NA, iaid, 1500, 3000); + ASSERT_NO_THROW(configure(config, *client.getServer())); - OptionPtr released_addr_opt(new Option6IAAddr(D6O_IAADDR, addr, 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); + ASSERT_NO_THROW(client.doDecline()); // 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_); + 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"); - sort(callback_argument_names_.begin(), callback_argument_names_.end()); + 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); - // 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); + // No deleted leases. + ASSERT_TRUE(callback_deleted_leases6_); + ASSERT_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); + // 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(req); + checkCalloutHandleReset(client.getContext().query_); } -// 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)); +// 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); - // Check that the lease is really in the database - Lease6Ptr l = LeaseMgrFactory::instance().getLease6(Lease::TYPE_PD, - prefix); - ASSERT_TRUE(l); + 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 }"; - // 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); + Dhcp6Client client; + client.setInterface("eth1"); + client.requestAddress(0xabca, IOAddress("2001:db8:1::28")); + client.requestAddress(0x2233, IOAddress("2001:db8:1::29")); - 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(configure(config, *client.getServer())); - // Server-id is mandatory in RELEASE - req->addOption(srv.getServerID()); + ASSERT_NO_THROW(client.doSARR()); + // 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(req); + ASSERT_NO_THROW(HooksManager::preCalloutsLibraryHandle().registerCallout( + "leases6_committed", leases6_committed_callout)); - ASSERT_TRUE(reply); + ASSERT_NO_THROW(client.doDecline()); // 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_); + 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"); - sort(callback_argument_names_.begin(), callback_argument_names_.end()); + 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); - // 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); + // No deleted leases. + ASSERT_TRUE(callback_deleted_leases6_); + ASSERT_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); + // 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(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; - - // 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("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 drop flag returned by a callout installed on the -// lease6_release hook point will keep the lease. -TEST_F(HooksDhcpv6SrvTest, dropLease6Release) { - NakedDhcpv6Srv srv(0); - - // Install lease6_release_drop - EXPECT_NO_THROW(HooksManager::preCalloutsLibraryHandle().registerCallout( - "lease6_release", lease6_release_drop)); - - 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("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); + checkCalloutHandleReset(client.getContext().query_); } // This test verifies that the leases6_committed callout is executed @@ -3850,13 +3363,14 @@ TEST_F(HooksDhcpv6SrvTest, leases6CommittedRelease) { 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(HooksManager::preCalloutsLibraryHandle().registerCallout( - "leases6_committed", leases6_committed_callout)); - ASSERT_NO_THROW(client.doRelease()); // Check that the callback called is indeed the one we installed @@ -3917,6 +3431,7 @@ TEST_F(HooksDhcpv6SrvTest, leases6CommittedReleasePrefix) { ASSERT_NO_THROW(configure(config, *client.getServer())); ASSERT_NO_THROW(client.doSARR()); + // Make sure that we received a response ASSERT_TRUE(client.getContext().response_); @@ -4026,9 +3541,684 @@ TEST_F(HooksDhcpv6SrvTest, leases6CommittedReleaseMultiple) { 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. +TEST_F(HooksDhcpv6SrvTest, leases6CommittedCache) { + 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\", " + " \"cache-threshold\": .25 " + " } ]," + "\"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_); + + // 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_); + 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()); + + // 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_); + + resetCalloutBuffers(); + + // 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 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. + 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())); + + 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("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_); + 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_); + + // 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_); + + resetCalloutBuffers(); + + // 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 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. + 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 it is possible to park a packet as a result of +// the leases6_committed callouts. +TEST_F(HooksDhcpv6SrvTest, leases6CommittedParkRequests) { + 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 }"; + + // Create first client and perform SARR. + Dhcp6Client client1; + client1.setInterface("eth1"); + client1.requestAddress(0xabca, 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_); + 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()); + + // 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(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. + Dhcp6Client client2(client1.getServer()); + client2.setInterface("eth1"); + client2.requestAddress(0xabca, IOAddress("2001:db8:1::29")); + ASSERT_NO_THROW(client2.doSARR()); + + // The ADVERTISE should have been returned but not REPLAY, as this + // packet got parked too. + ASSERT_FALSE(client2.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_); + 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_); + + // 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(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. + Dhcp6Client client2(client1.getServer()); + client2.setInterface("eth1"); + client2.requestPrefix(0xabca, 64, IOAddress("2001:db8:1:29::")); + ASSERT_NO_THROW(client2.doSARR()); + + // The ADVERTISE should have been returned but not REPLAY, as this + // packet got parked too. + ASSERT_FALSE(client2.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.hasLeaseForPrefix(IOAddress("2001:db8:1:28::"), 64)); + + // 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(client2.getContext().query_); +} + +// 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_renew_callout + EXPECT_NO_THROW(HooksManager::preCalloutsLibraryHandle().registerCallout( + "lease6_renew", lease6_renew_callout)); + + 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); + + // 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 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()); + + // 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_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"); + expected_argument_names.push_back("ia_na"); + + 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); + + OptionPtr tmp = reply->getOption(D6O_IA_NA); + ASSERT_TRUE(tmp); + + // 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 + 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 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_renew_update + EXPECT_NO_THROW(HooksManager::preCalloutsLibraryHandle().registerCallout( + "lease6_renew", lease6_renew_update)); + + 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); + + // 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 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()); + + // 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 if we get response at all + checkResponse(reply, DHCPV6_REPLY, 1234); + + OptionPtr tmp = reply->getOption(D6O_IA_NA); + ASSERT_TRUE(tmp); + + // 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)); + + 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 that we chose the distinct override values + ASSERT_NE(override_preferred_, subnet_->getPreferred()); + EXPECT_NE(override_valid_, subnet_->getValid()); + + // Check that preferred, valid were overridden the callout + EXPECT_EQ(override_preferred_, l->preferred_lft_); + EXPECT_EQ(override_valid_, l->valid_lft_); + + // 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)); + + 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(req); +} + +// 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); + + // 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; + + // 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); + + // 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)); + + // 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 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()); + + // Pass it to the server and hope for a REPLY + Pkt6Ptr reply = srv.processRenew(req); + ASSERT_TRUE(reply); + + // Check that our callback was called + EXPECT_EQ("lease6_renew", callback_name_); + + l = LeaseMgrFactory::instance().getLease6(Lease::TYPE_NA, addr); + + // 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(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; - // 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()); + // 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 req = Pkt6Ptr(new Pkt6(DHCPV6_RELEASE, 1234)); + req->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); + req->addOption(ia); + req->addOption(clientid); - // 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")); + // Server-id is mandatory in RELEASE + req->addOption(srv.getServerID()); - ASSERT_NO_THROW(client.doRebind()); + // 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_); - // 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 appropriate parameters are passed to the callouts + EXPECT_TRUE(callback_qry_pkt6_); + EXPECT_TRUE(callback_lease6_); - // The old lease is kept. - ASSERT_TRUE(callback_deleted_leases6_); - ASSERT_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_); - - resetCalloutBuffers(); + checkCalloutHandleReset(req); +} - // The rebound address is just a hint. - client.requestAddress(0x5577, IOAddress("4000::2")); +// 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); - 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 prefix("2001:db8:1:2:1::"); + 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()); - ASSERT_TRUE(callback_deleted_leases6_); - EXPECT_TRUE(callback_deleted_leases6_->empty()); + // Check that the prefix we are about to use is indeed in pool + ASSERT_TRUE(subnet_->inPool(Lease::TYPE_PD, prefix)); - // 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_PD, prefix, duid_, iaid, + 501, 502, subnet_->getID(), + HWAddrPtr(), 80)); + 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_PD, + prefix); + ASSERT_TRUE(l); - resetCalloutBuffers(); + // 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); - // Rebind a prefix: this should lead to an error as no prefix pool - // is configured. - client.requestPrefix(0x1122, 64, IOAddress("2001:db8:1000::")); + 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(client.doRebind()); + // Server-id is mandatory in RELEASE + req->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(req); - // Check the error. - EXPECT_EQ(STATUS_NoPrefixAvail, 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 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("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(client.getContext().query_); + 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. Prefix variant. -TEST_F(HooksDhcpv6SrvTest, leases6CommittedRebindPrefix) { - IfaceMgrTestConfig test_config(true); +// 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); - 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 }"; + // Install lease6_release_callout + EXPECT_NO_THROW(HooksManager::preCalloutsLibraryHandle().registerCallout( + "lease6_release", lease6_release_callout)); - Dhcp6Client client; - client.setInterface("eth1"); - client.requestPrefix(0xabca, 64, IOAddress("2001:db8:1:28::")); + const IOAddress prefix("2001:db8:1:2:1::"); + 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 prefix we are about to use is indeed in pool + ASSERT_TRUE(subnet_->inPool(Lease::TYPE_PD, prefix)); - 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_PD, prefix, duid_, iaid, + 501, 502, subnet_->getID(), + HWAddrPtr(), 80)); + 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_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("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 |