From de78b098d9f1cc0dc4b99f3d30c0f40c2e87b50c Mon Sep 17 00:00:00 2001 From: Slawek Figiel Date: Thu, 6 Apr 2023 17:00:08 +0200 Subject: [PATCH] [#2815] Implement del6 function --- src/lib/dhcpsrv/cfg_hosts.cc | 41 +++++++++++--- src/lib/dhcpsrv/host_container.h | 29 ++++++++-- src/lib/dhcpsrv/hosts_messages.cc | 2 + src/lib/dhcpsrv/hosts_messages.h | 1 + src/lib/dhcpsrv/hosts_messages.mes | 9 ++- src/lib/dhcpsrv/tests/cfg_hosts_unittest.cc | 61 ++++++++++++++++++++- 6 files changed, 130 insertions(+), 13 deletions(-) diff --git a/src/lib/dhcpsrv/cfg_hosts.cc b/src/lib/dhcpsrv/cfg_hosts.cc index e1da797ff9..2ea5ad0fd7 100644 --- a/src/lib/dhcpsrv/cfg_hosts.cc +++ b/src/lib/dhcpsrv/cfg_hosts.cc @@ -1179,13 +1179,40 @@ CfgHosts::delAll6(const SubnetID& subnet_id) { } bool -CfgHosts::del6(const SubnetID& /*subnet_id*/, - const Host::IdentifierType& /*identifier_type*/, - const uint8_t* /*identifier_begin*/, - const size_t /*identifier_len*/) { - /// @todo: Implement host removal - isc_throw(NotImplemented, "sorry, not implemented"); - return (false); +CfgHosts::del6(const SubnetID& subnet_id, + const Host::IdentifierType& identifier_type, + const uint8_t* identifier_begin, + const size_t identifier_len) { + HostContainerIndex0& idx = hosts_.get<0>(); + HostContainer6Index3& idx6 = hosts6_.get<3>(); + + const auto t = boost::make_tuple(std::vector(identifier_begin, + identifier_begin + identifier_len), + identifier_type); + const auto& range = idx.equal_range(t); + size_t erased_hosts = 0; + size_t erased_reservations = 0; + for (auto key = range.first; key != range.second;) { + if ((*key)->getIPv6SubnetID() != subnet_id) { + ++key; + // Skip hosts from other subnets. + continue; + } + + // Delete host. + key = idx.erase(key); + erased_hosts++; + // Delete reservations. + erased_reservations += idx6.erase((*key)->getHostId()); + } + + LOG_DEBUG(hosts_logger, HOSTS_DBG_TRACE, HOSTS_CFG_DEL6) + .arg(erased_hosts) + .arg(erased_reservations) + .arg(subnet_id) + .arg(Host::getIdentifierAsText(identifier_type, identifier_begin, identifier_len)); + + return (erased_hosts != 0); } bool diff --git a/src/lib/dhcpsrv/host_container.h b/src/lib/dhcpsrv/host_container.h index 7cf9c647be..cb106adc24 100644 --- a/src/lib/dhcpsrv/host_container.h +++ b/src/lib/dhcpsrv/host_container.h @@ -179,10 +179,15 @@ struct HostResrv6Tuple { /// @brief Value of the IPv6 Subnet-id const SubnetID subnet_id_; - /// @brief Key extractor (used in the second composite key) - const asiolink::IOAddress& getKey() const { + /// @brief Key extractor used in the second composite key + const asiolink::IOAddress& getPrefix() const { return (resrv_.getPrefix()); } + + /// @brief Key extractor used in the fourth composite key + HostID getHostId() const { + return (host_->getHostId()); + } }; /// @brief Multi-index container holding IPv6 reservations. @@ -204,7 +209,7 @@ typedef boost::multi_index_container< // Address is extracted by calling IPv6Resrv::getPrefix() // and it will return an IOAddress object. boost::multi_index::const_mem_fun< - HostResrv6Tuple, const asiolink::IOAddress&, &HostResrv6Tuple::getKey> + HostResrv6Tuple, const asiolink::IOAddress&, &HostResrv6Tuple::getPrefix> >, // Second index is used to search by (subnet_id, address) pair. @@ -227,7 +232,7 @@ typedef boost::multi_index_container< // IPv6Resrv::getPrefix() and it will return an IOAddress object. boost::multi_index::const_mem_fun< HostResrv6Tuple, const asiolink::IOAddress&, - &HostResrv6Tuple::getKey + &HostResrv6Tuple::getPrefix > > >, @@ -237,6 +242,13 @@ typedef boost::multi_index_container< // Index using values returned by the @c Host::getIPv6SubnetID boost::multi_index::member + >, + + // Fourth index is used to search by increasing host id + boost::multi_index::ordered_non_unique< + // Index using values returned by the @c Host::getHostId + boost::multi_index::const_mem_fun > > > HostContainer6; @@ -271,6 +283,15 @@ typedef HostContainer6::nth_index<2>::type HostContainer6Index2; typedef std::pair HostContainer6Index2Range; +/// @brief Fourth index type in the @c HostContainer6. +/// +/// This index allows for searching for @c Host objects using a host id. +typedef HostContainer6::nth_index<3>::type HostContainer6Index3; + +/// @brief Results range returned using the @c HostContainer6Index3. +typedef std::pair HostContainer6Index3Range; + }; // end of isc::dhcp namespace }; // end of isc namespace diff --git a/src/lib/dhcpsrv/hosts_messages.cc b/src/lib/dhcpsrv/hosts_messages.cc index c9d0b3f28c..4b5b75f46d 100644 --- a/src/lib/dhcpsrv/hosts_messages.cc +++ b/src/lib/dhcpsrv/hosts_messages.cc @@ -15,6 +15,7 @@ extern const isc::log::MessageID HOSTS_CFG_CACHE_HOST_DATA_SOURCE = "HOSTS_CFG_C extern const isc::log::MessageID HOSTS_CFG_CLOSE_HOST_DATA_SOURCE = "HOSTS_CFG_CLOSE_HOST_DATA_SOURCE"; extern const isc::log::MessageID HOSTS_CFG_DEL = "HOSTS_CFG_DEL"; extern const isc::log::MessageID HOSTS_CFG_DEL4 = "HOSTS_CFG_DEL4"; +extern const isc::log::MessageID HOSTS_CFG_DEL6 = "HOSTS_CFG_DEL6"; extern const isc::log::MessageID HOSTS_CFG_DEL_ALL_SUBNET4 = "HOSTS_CFG_DEL_ALL_SUBNET4"; extern const isc::log::MessageID HOSTS_CFG_DEL_ALL_SUBNET6 = "HOSTS_CFG_DEL_ALL_SUBNET6"; extern const isc::log::MessageID HOSTS_CFG_GET_ALL = "HOSTS_CFG_GET_ALL"; @@ -89,6 +90,7 @@ const char* values[] = { "HOSTS_CFG_CLOSE_HOST_DATA_SOURCE", "Closing host data source: %1", "HOSTS_CFG_DEL", "deleted %1 host(s) having %2 IPv6 reservation(s) for subnet id %3 and address %4", "HOSTS_CFG_DEL4", "deleted %1 host(s) for subnet id %2 and identifier %3", + "HOSTS_CFG_DEL6", "deleted %1 host(s) having %2 IPv6 reservation(s) for subnet id %3 and identifier %4", "HOSTS_CFG_DEL_ALL_SUBNET4", "deleted all %1 host(s) for subnet id %2", "HOSTS_CFG_DEL_ALL_SUBNET6", "deleted all %1 host(s) having %2 IPv6 reservation(s) for subnet id %3", "HOSTS_CFG_GET_ALL", "get all hosts with reservations", diff --git a/src/lib/dhcpsrv/hosts_messages.h b/src/lib/dhcpsrv/hosts_messages.h index d6ed062d92..f40666e6c5 100644 --- a/src/lib/dhcpsrv/hosts_messages.h +++ b/src/lib/dhcpsrv/hosts_messages.h @@ -16,6 +16,7 @@ extern const isc::log::MessageID HOSTS_CFG_CACHE_HOST_DATA_SOURCE; extern const isc::log::MessageID HOSTS_CFG_CLOSE_HOST_DATA_SOURCE; extern const isc::log::MessageID HOSTS_CFG_DEL; extern const isc::log::MessageID HOSTS_CFG_DEL4; +extern const isc::log::MessageID HOSTS_CFG_DEL6; extern const isc::log::MessageID HOSTS_CFG_DEL_ALL_SUBNET4; extern const isc::log::MessageID HOSTS_CFG_DEL_ALL_SUBNET6; extern const isc::log::MessageID HOSTS_CFG_GET_ALL; diff --git a/src/lib/dhcpsrv/hosts_messages.mes b/src/lib/dhcpsrv/hosts_messages.mes index 1bf3ddba7a..3c671c8688 100644 --- a/src/lib/dhcpsrv/hosts_messages.mes +++ b/src/lib/dhcpsrv/hosts_messages.mes @@ -39,11 +39,18 @@ The third argument is the subnet identifier. The fourth argument is the IP address. % HOSTS_CFG_DEL4 deleted %1 host(s) for subnet id %2 and identifier %3 -This debug message is issued when reservations are deleted for the specified +This debug message is issued when IPv4 reservations are deleted for the specified subnet and identifier. The first argument specifies how many hosts have been deleted. The second argument is the subnet identifier. The third argument is the identifier. +% HOSTS_CFG_DEL6 deleted %1 host(s) having %2 IPv6 reservation(s) for subnet id %3 and identifier %4 +This debug message is issued when IPv6 reservations are deleted for the +specified subnet and identifier. The first argument specifies how many hosts +have been deleted. The second argument specifies how many reservations have +been deleted. The third argument is the subnet identifier. The fourth argument +is the identifier. + % HOSTS_CFG_DEL_ALL_SUBNET4 deleted all %1 host(s) for subnet id %2 This debug message is issued when all IPv4 reservations are deleted for the specified subnet. The first argument specifies how many reservations diff --git a/src/lib/dhcpsrv/tests/cfg_hosts_unittest.cc b/src/lib/dhcpsrv/tests/cfg_hosts_unittest.cc index 65a2b5a65e..62e1a22e3e 100644 --- a/src/lib/dhcpsrv/tests/cfg_hosts_unittest.cc +++ b/src/lib/dhcpsrv/tests/cfg_hosts_unittest.cc @@ -503,7 +503,7 @@ TEST_F(CfgHostsTest, deleteForIPv6) { { HostPtr host = HostPtr(new Host(duids_[i]->toText(), "duid", SUBNET_ID_UNUSED, subnet_id, - IOAddress("0.0.0.0"))); + IOAddress::IPV4_ZERO_ADDRESS())); host->addReservation(IPv6Resrv(IPv6Resrv::TYPE_NA, increase(IOAddress(address), i))); cfg.add(host); @@ -600,6 +600,65 @@ TEST_F(CfgHostsTest, del4) { EXPECT_FALSE(host); } +// This test checks that the host and its reservations for the specified IPv6 +// subnet and identifier can be deleted. +TEST_F(CfgHostsTest, del6) { + CfgHosts cfg; + + // Add hosts. + size_t host_count = 20; + size_t host_id = 5; + SubnetID subnet_id(42); + IOAddress address("2001:db8:1::1"); + + // Add half of the hosts with the same subnet ID but differ with DUID and + // address. + for (size_t i = 0; i < host_count / 2; i++) { + HostPtr host = HostPtr(new Host(duids_[i]->toText(), "duid", + SUBNET_ID_UNUSED, subnet_id, + IOAddress::IPV4_ZERO_ADDRESS())); + host->addReservation(IPv6Resrv(IPv6Resrv::TYPE_NA, + increase(IOAddress(address), i))); + cfg.add(host); + } + // Add half of the hosts with the same subnet DUID and address but + // differ with address. + for (size_t i = 0; i < host_count / 2; i++) { + HostPtr host = HostPtr(new Host(duids_[host_id]->toText(), "duid", + SUBNET_ID_UNUSED, SubnetID(subnet_id + i + 1), + IOAddress::IPV4_ZERO_ADDRESS())); + host->addReservation(IPv6Resrv(IPv6Resrv::TYPE_NA, + increase(address, host_id))); + cfg.add(host); + } + + + // Get all inserted hosts. + HostCollection hosts_by_subnet = cfg.getAll6(subnet_id); + HostCollection hosts_by_address = cfg.getAll6(increase(address, host_id)); + HostPtr host = cfg.get6(subnet_id, Host::IdentifierType::IDENT_DUID, + &duids_[host_id]->getDuid()[0], + duids_[host_id]->getDuid().size()); + // Make sure the hosts and IP reservations were added. + ASSERT_EQ(host_count / 2, hosts_by_subnet.size()); + ASSERT_EQ(host_count / 2 + 1, hosts_by_address.size()); + ASSERT_TRUE(host); + + // Delete one host. + EXPECT_TRUE(cfg.del6(subnet_id, Host::IdentifierType::IDENT_DUID, + &duids_[host_id]->getDuid()[0], duids_[host_id]->getDuid().size())); + + // Check if the host is actually deleted. + hosts_by_subnet = cfg.getAll6(subnet_id); + hosts_by_address = cfg.getAll6(increase(address, host_id)); + host = cfg.get6(subnet_id, Host::IdentifierType::IDENT_DUID, + &duids_[host_id]->getDuid()[0], + duids_[host_id]->getDuid().size()); + EXPECT_EQ((host_count / 2)-1, hosts_by_subnet.size()); + EXPECT_EQ(host_count / 2, hosts_by_address.size()); + EXPECT_FALSE(host); +} + // This test checks that all reservations for the specified IPv4 subnet can // be deleted. TEST_F(CfgHostsTest, deleteAll4) { -- 2.47.2