#include <dhcpsrv/cfg_subnets4.h>
#include <dhcpsrv/dhcpsrv_log.h>
#include <dhcpsrv/lease_mgr_factory.h>
+#include <dhcpsrv/sflq_allocator.h>
#include <dhcpsrv/shared_network.h>
#include <dhcpsrv/subnet_id.h>
#include <asiolink/io_address.h>
void
CfgSubnets4::initAllocatorsAfterConfigure() {
+ SharedFlqAllocator::setInUse(false);
for (auto const& subnet : subnets_) {
subnet->initAllocatorsAfterConfigure();
}
#include <asiolink/addr_utilities.h>
#include <dhcpsrv/cfg_subnets6.h>
#include <dhcpsrv/dhcpsrv_log.h>
+#include <dhcpsrv/sflq_allocator.h>
#include <dhcpsrv/lease_mgr_factory.h>
#include <dhcpsrv/subnet_id.h>
#include <stats/stats_mgr.h>
void
CfgSubnets6::initAllocatorsAfterConfigure() {
+ SharedFlqAllocator::setInUse(false);
for (auto const& subnet : subnets_) {
subnet->initAllocatorsAfterConfigure();
}
/// @return allocation state instance for the pool.
PoolIterativeAllocationStatePtr getPoolState(const PoolPtr& pool) const;
-protected:
+public:
/// @brief Returns the next prefix.
///
'random_allocator.cc',
'resource_handler.cc',
'sanity_checker.cc',
+ 'sflq_allocator.cc',
'shared_network.cc',
'srv_config.cc',
'subnet.cc',
'random_allocator.h',
'resource_handler.h',
'sanity_checker.h',
+ 'sflq_allocator.h',
'shared_network.h',
'srv_config.h',
'subnet.h',
--- /dev/null
+// Copyright (C) 2026 Internet Systems Consortium, Inc. ("ISC")
+//
+// This Source Code Form is subject to the terms of the Mozilla Public
+// License, v. 2.0. If a copy of the MPL was not distributed with this
+// file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+#include <config.h>
+
+#include <asiolink/addr_utilities.h>
+#include <dhcpsrv/dhcpsrv_log.h>
+#include <dhcpsrv/sflq_allocator.h>
+#include <dhcpsrv/lease_mgr_factory.h>
+#include <dhcpsrv/subnet.h>
+#include <util/stopwatch.h>
+#include <limits>
+
+using namespace isc::asiolink;
+using namespace isc::util;
+using namespace std;
+
+namespace isc {
+ bool sflq_in_use_ = false;
+}
+
+namespace isc {
+namespace dhcp {
+
+void
+SharedFlqAllocator::setInUse(bool in_use) {
+ sflq_in_use_ = in_use;
+}
+
+bool
+SharedFlqAllocator::inUse() {
+ return (sflq_in_use_);
+}
+
+SharedFlqAllocator::SharedFlqAllocator(Lease::Type type, const WeakSubnetPtr& subnet)
+ : Allocator(type, subnet), generator_() {
+ random_device rd;
+ generator_.seed(rd());
+}
+
+void
+SharedFlqAllocator::initAfterConfigureInternal() {
+ auto subnet = subnet_.lock();
+ auto const& pools = subnet->getPools(pool_type_);
+ if (pools.empty()) {
+ // If there are no pools there is nothing to do.
+ return;
+ }
+
+ // Set static class flag marking at least one pool is using SFlq.
+ setInUse(true);
+
+ for (const auto& pool : pools) {
+ switch (pool_type_) {
+ case Lease::TYPE_V4:
+ LeaseMgrFactory::instance().sflqCreateFlqPool4(pool->getFirstAddress(),
+ pool->getLastAddress(),
+ subnet->getID(), false);
+ break;
+ case Lease::TYPE_NA:
+ /// @todo guard against large ranges?
+ LeaseMgrFactory::instance().sflqCreateFlqPool6(pool->getFirstAddress(),
+ pool->getLastAddress(),
+ Lease::TYPE_NA, 128,
+ subnet->getID(), false);
+ break;
+ case Lease::TYPE_PD: {
+ auto pdpool = boost::dynamic_pointer_cast<Pool6>(pool);
+ LeaseMgrFactory::instance().sflqCreateFlqPool6(pool->getFirstAddress(),
+ pool->getLastAddress(),
+ Lease::TYPE_PD, pdpool->getLength(),
+ subnet->getID(), false);
+ break;
+ }
+ default:
+ ;
+ }
+ }
+}
+
+IOAddress
+SharedFlqAllocator::pickAddressInternal(const ClientClasses& client_classes,
+ const IdentifierBaseTypePtr&,
+ const IOAddress&) {
+ // Let's iterate over the subnet's pools and identify the ones that
+ // meet client class criteria.
+ auto subnet = subnet_.lock();
+ auto const& pools = subnet->getPools(pool_type_);
+ std::vector<PoolPtr> available;
+ for (auto const& pool : pools) {
+ // Check if the pool is allowed for the client's classes.
+ if (pool->clientSupported(client_classes)) {
+ available.push_back(pool);
+ }
+ }
+
+ // Try each pool in random order.
+ while (available.size()) {
+ // Get a random pool from the available ones.
+ auto offset = getRandomNumber(available.size() - 1);
+ auto const& pool = available[offset];
+ switch (pool_type_) {
+ case Lease::TYPE_V4: {
+ // Ask the lease manager for a lease from the pool.
+ auto free_lease = LeaseMgrFactory::instance()
+ .sflqPickFreeLease4(pool->getFirstAddress(),
+ pool->getLastAddress());
+ if (!free_lease.isV4Zero()) {
+ return (free_lease);
+ }
+
+ break;
+ }
+ case Lease::TYPE_NA:
+ case Lease::TYPE_TA:{
+ auto free_lease = LeaseMgrFactory::instance()
+ .sflqPickFreeLease6(pool->getFirstAddress(),
+ pool->getLastAddress());
+ if (!free_lease.isV6Zero()) {
+ return (free_lease);
+ }
+
+ break;
+ }
+ case Lease::TYPE_PD:
+ isc_throw(Unexpected, "pickAddressInternal called for Lease::TYPE_PD");
+ break;
+ }
+
+ // Remove the exhausted pool from the list then try another one.
+ available.erase(available.begin() + offset);
+ }
+
+ // No address available.
+ return (pool_type_ == Lease::TYPE_V4 ? IOAddress::IPV4_ZERO_ADDRESS()
+ : IOAddress::IPV6_ZERO_ADDRESS());
+}
+
+IOAddress
+SharedFlqAllocator::pickPrefixInternal(const ClientClasses& client_classes,
+ Pool6Ptr& /* pool6 */,
+ const IdentifierBaseTypePtr&,
+ PrefixLenMatchType prefix_length_match,
+ const IOAddress&,
+ uint8_t hint_prefix_length) {
+ // Let's iterate over the subnet's pools and identify the ones that
+ // meet client class criteria.
+ auto subnet = subnet_.lock();
+ auto const& pools = subnet->getPools(pool_type_);
+ std::vector<PoolPtr> available;
+ for (auto const& pool : pools) {
+ // Check if the pool is allowed for the client's classes.
+ if (pool->clientSupported(client_classes)) {
+ if (!Allocator::isValidPrefixPool(prefix_length_match, pool,
+ hint_prefix_length)) {
+ continue;
+ }
+
+ available.push_back(pool);
+ }
+ }
+
+ // Try each pool in random order.
+ while (available.size()) {
+ // Get a random pool from the available ones.
+ auto offset = getRandomNumber(available.size() - 1);
+ auto const& pool = available[offset];
+ switch(pool_type_) {
+ case Lease::TYPE_V4:
+ isc_throw(Unexpected, "pickAddressInternal called for Lease::TYPE_V4");
+ break;
+ case Lease::TYPE_NA:
+ case Lease::TYPE_TA:
+ isc_throw(Unexpected, "pickAddressInternal called for Lease::TYPE_NA");
+ break;
+ case Lease::TYPE_PD:
+ // Ask the lease manager for a lease from the pool.
+ auto free_lease = LeaseMgrFactory::instance()
+ .sflqPickFreeLease6(pool->getFirstAddress(),
+ pool->getLastAddress());
+ if (!free_lease.isV6Zero()) {
+ return (free_lease);
+ }
+
+ break;
+ }
+
+ // Remove the exhausted pool from the list then try another one.
+ available.erase(available.begin() + offset);
+ }
+
+ // No address available.
+ return (IOAddress::IPV6_ZERO_ADDRESS());
+}
+
+uint64_t
+SharedFlqAllocator::getRandomNumber(uint64_t limit) {
+ // Take the short path if there is only one number to randomize from.
+ if (limit == 0) {
+ return (0);
+ }
+ std::uniform_int_distribution<uint64_t> dist(0, limit);
+ return (dist(generator_));
+}
+
+} // end of namespace isc::dhcp
+} // end of namespace isc
--- /dev/null
+// Copyright (C) 2026 Internet Systems Consortium, Inc. ("ISC")
+//
+// This Source Code Form is subject to the terms of the Mozilla Public
+// License, v. 2.0. If a copy of the MPL was not distributed with this
+// file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+#ifndef SFLQ_ALLOCATOR_H
+#define SFLQ_ALLOCATOR_H
+
+#include <dhcpsrv/allocator.h>
+#include <dhcpsrv/lease.h>
+#include <cstdint>
+
+namespace isc {
+namespace dhcp {
+
+/// @brief An allocator maintaining a shared queue of free leases.
+///
+/// This allocator is part of the Shared Free Lease Queue (SFLQ) Allocation
+/// scheme. The concept is similar to FLQ Alloction but rather than the
+/// creating and maintaining free lease data locally, it is created and
+/// maintained in the lease back end (MySql and PosgreSQL only) where it
+/// can be shared by other servers.
+///
+/// Th allocator relies on stored procedures in the lease back end to
+/// return free leases for a given IP address range (i.e. pool). This can
+/// greatly reduces the number of queries made to lease back end as only
+/// addresses that are actually free are returned. The allocation engine
+/// must still check for HR conflicts but generally, the maximum number
+/// of queries for a client request is one query per client-qualified
+/// pool rather than one query * the total number of addresses in the
+/// pools.
+///
+/// The SLFQ data tracks the last address picked for each SFLQ pool such
+/// that consecutive queries for the same pool will return a differnent
+/// free address. This should minimize conflicts with other servers until
+/// the number of free addresses approaches zero.
+///
+/// @TODO The following needs updating once we decide what do about IA_NA.
+///
+/// This allocator should only be used for reasonably small pools due to the
+/// overhead to populate the free leases. A reasonably small pool is an IPv4
+/// pool (including /8) and the prefix delegation pools with similar capacity.
+/// This allocator is not suitable for a typical IPv6 address pool (e.g., /64).
+/// An attempt to populate free leases for such a giant pool would freeze the
+/// server and likely exhaust its memory.
+///
+/// Free leases are populated in a random order.
+class SharedFlqAllocator : public Allocator {
+public:
+
+ /// @brief Constructor.
+ ///
+ /// @param type specifies the type of allocated leases.
+ /// @param subnet weak pointer to the subnet owning the allocator.
+ SharedFlqAllocator(Lease::Type type, const WeakSubnetPtr& subnet);
+
+ /// @brief Returns the allocator type string.
+ ///
+ /// @return flq string.
+ virtual std::string getType() const {
+ return ("shared-flq");
+ }
+
+ /// @brief Sets the global in-use flag.
+ ///
+ /// @param in_use new value to assign to the in-use flag.
+ static void setInUse(bool in_use);
+
+ /// @brief Returns the global in-use flag.
+ ///
+ /// @return True if at least one subnet in the current configuration
+ /// is using Shared FLQ allocation.
+ static bool inUse();
+
+private:
+
+ /// @brief Performs allocator initialization after server's reconfiguration.
+ ///
+ /// The allocator installs the callbacks in the lease manager to keep track of
+ /// the lease allocations and maintain the free leases queue.
+ virtual void initAfterConfigureInternal();
+
+ /// @brief Populates the queue of free addresses (IPv4 and IPv6).
+ ///
+ /// Instructs lease the laase back end to (re)create SFLQ datam for
+ /// each pool in a subnet.
+ ///
+ /// @param pools collection of pools in the subnet.
+ void populateFreeAddressLeases(const PoolCollection& pools);
+
+ /// @brief Returns next available address from the queue.
+ ///
+ /// Internal thread-unsafe implementation of the @c pickAddress.
+ ///
+ /// @param client_classes list of classes client belongs to.
+ /// @param duid client DUID (ignored).
+ /// @param hint client hint (ignored).
+ ///
+ /// @return next offered address.
+ virtual asiolink::IOAddress pickAddressInternal(const ClientClasses& client_classes,
+ const IdentifierBaseTypePtr& duid,
+ const asiolink::IOAddress& hint);
+
+ /// @brief Returns next available delegated prefix from the queue.
+ ///
+ /// Internal thread-unsafe implementation of the @c pickPrefix.
+ ///
+ /// @param client_classes list of classes client belongs to.
+ /// @param pool the selected pool satisfying all required conditions.
+ /// @param duid Client's DUID.
+ /// @param prefix_length_match type which indicates the selection criteria
+ /// for the pools relative to the provided hint prefix length
+ /// @param hint Client's hint.
+ /// @param hint_prefix_length the hint prefix length that the client
+ /// provided. The 0 value means that there is no hint and that any
+ /// pool will suffice.
+ ///
+ /// @return the next prefix.
+ virtual isc::asiolink::IOAddress
+ pickPrefixInternal(const ClientClasses& client_classes,
+ Pool6Ptr& pool,
+ const IdentifierBaseTypePtr& duid,
+ PrefixLenMatchType prefix_length_match,
+ const isc::asiolink::IOAddress& hint,
+ uint8_t hint_prefix_length);
+
+ /// @brief Convenience function returning a random number.
+ ///
+ /// It is used internally by the @c pickAddressInternal and @c pickPrefixInternal
+ /// functions to select a random pool.
+ ///
+ /// @param limit upper bound of the range.
+ /// @returns random number between 0 and limit.
+ uint64_t getRandomNumber(uint64_t limit);
+
+ /// @brief Random generator used by this class.
+ std::mt19937 generator_;
+};
+
+} // end of namespace isc::dhcp
+} // end of namespace isc
+
+#endif // SFLQ_ALLOCATOR_H
'resource_handler_unittest.cc',
'run_unittests.cc',
'sanity_checks_unittest.cc',
+ 'sflq_allocator_unittest.cc',
'shared_network_parser_unittest.cc',
'shared_network_unittest.cc',
'shared_networks_list_parser_unittest.cc',
--- /dev/null
+// Copyright (C) 2026 Internet Systems Consortium, Inc. ("ISC")
+//
+// This Source Code Form is subject to the terms of the Mozilla Public
+// License, v. 2.0. If a copy of the MPL was not distributed with this
+// file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+#include <config.h>
+//#include <database/database_connection.h>
+//#include <dhcpsrv/cfgmgr.h>
+#include <dhcpsrv/lease_mgr_factory.h>
+#include <asiolink/io_address.h>
+#include <dhcpsrv/sflq_allocator.h>
+#include <dhcpsrv/testutils/sflqtest_lease_mgr.h>
+#include <gtest/gtest.h>
+#include <testutils/gtest_utils.h>
+
+using namespace isc::asiolink;
+using namespace isc::db;
+using namespace std;
+
+namespace isc {
+namespace dhcp {
+namespace test {
+
+
+/// @brief Test fixture class for the DHCPv4 SharedFlqAllocator.
+class SharedFlqAllocatorTest4 : public testing::Test {
+public:
+
+ /// @brief Pre-test setup.
+ ///
+ /// Installs the lease manager factory, creates a manager instance
+ /// and initializes a V4 subnet.
+ virtual void SetUp() {
+ LeaseMgrFactory::registerFactory("sflqtest", SflqTestLeaseMgr::factory);
+ ASSERT_NO_THROW_LOG(LeaseMgrFactory::create("type=sflqtest universe=4"));
+ ASSERT_TRUE(LeaseMgrFactory::haveInstance());
+ ASSERT_EQ(LeaseMgrFactory::instance().getType(), "sflqtest");
+ initSubnet4();
+ SharedFlqAllocator::setInUse(false);
+ }
+
+ virtual void TearDown() {
+ SharedFlqAllocator::setInUse(false);
+ }
+
+ /// @brief Initializes the test subnet for V4 tests.
+ void initSubnet4() {
+ static SubnetID id(1);
+ subnet_ = Subnet4::create(IOAddress("192.0.0.0"), 8, 1, 2, 3, id);
+ PoolPtr pool(new Pool4(IOAddress("192.0.1.0"), IOAddress("192.0.1.1")));
+ pool->allowClientClass("ALL");
+ pool->allowClientClass("one");
+ subnet_->addPool(pool);
+
+ pool = PoolPtr(new Pool4(IOAddress("192.0.2.0"), IOAddress("192.0.2.1")));
+ pool->allowClientClass("ALL");
+ pool->allowClientClass("two");
+ subnet_->addPool(pool);
+
+ pool = PoolPtr(new Pool4(IOAddress("192.0.3.0"), IOAddress("192.0.3.1")));
+ pool->allowClientClass("ALL");
+ pool->allowClientClass("three");
+ subnet_->addPool(pool);
+ }
+
+ /// @brief Subnet used in tests.
+ SubnetPtr subnet_;
+};
+
+// Test that the allocator returns the correct type.
+TEST_F(SharedFlqAllocatorTest4, getType) {
+ SharedFlqAllocator alloc(Lease::TYPE_V4, subnet_);
+ EXPECT_EQ("shared-flq", alloc.getType());
+}
+
+// Tests initAfterConfigure() function. It should create
+// an SFLQ pool for each pool in the subnet.
+TEST_F(SharedFlqAllocatorTest4, initAfterConfigure) {
+ SharedFlqAllocator alloc(Lease::TYPE_V4, subnet_);
+
+ EXPECT_FALSE(SharedFlqAllocator::inUse());
+ EXPECT_NO_THROW(alloc.initAfterConfigure());
+ EXPECT_TRUE(SharedFlqAllocator::inUse());
+
+ SflqTestLeaseMgr& slm = dynamic_cast<SflqTestLeaseMgr&>(LeaseMgrFactory::instance());
+
+ for (auto pool : subnet_->getPools(Lease::TYPE_V4)) {
+ auto sflq_pool = slm.findPool(pool->getFirstAddress(), pool->getLastAddress());
+ ASSERT_TRUE(sflq_pool) << "no sflq pool for: " << pool->toText();
+ }
+}
+
+// Exercises ShareFlqAllocator::pickAddressInternal() for a V4 subnet.
+TEST_F(SharedFlqAllocatorTest4, pickAddress) {
+ IOAddress zero_address = IOAddress::IPV4_ZERO_ADDRESS();
+ SharedFlqAllocator alloc(Lease::TYPE_V4, subnet_);
+
+ EXPECT_NO_THROW(alloc.initAfterConfigure());
+
+ SflqTestLeaseMgr& slm = dynamic_cast<SflqTestLeaseMgr&>(LeaseMgrFactory::instance());
+ for (auto pool : subnet_->getPools(Lease::TYPE_V4)) {
+ auto sflq_pool = slm.findPool(pool->getFirstAddress(), pool->getLastAddress());
+ ASSERT_TRUE(sflq_pool) << "no sflq pool for: " << pool->toText();
+ }
+
+ // Verify that all addresses can be picked with client_class = 'ALL'.
+ // We use a set to collect the picked addresses. This way we do not
+ // rely on the order they are picked but can still verify they
+ // all get picked.
+ std::set<IOAddress> picked;
+ ClientClasses client_classes;
+ client_classes.insert("ALL");
+ for (int i = 0; i < 6; ++i) {
+ IOAddress picked_address = zero_address;
+ ASSERT_NO_THROW_LOG(picked_address =
+ alloc.pickAddress(client_classes,
+ IdentifierBaseTypePtr(),
+ zero_address));
+ ASSERT_NE(picked_address, zero_address);
+ picked.emplace(picked_address);
+ }
+
+ ASSERT_EQ(picked.size(), 6);
+ ASSERT_TRUE(picked.contains(IOAddress("192.0.1.0")));
+ ASSERT_TRUE(picked.contains(IOAddress("192.0.1.1")));
+ ASSERT_TRUE(picked.contains(IOAddress("192.0.2.0")));
+ ASSERT_TRUE(picked.contains(IOAddress("192.0.2.1")));
+ ASSERT_TRUE(picked.contains(IOAddress("192.0.3.0")));
+ ASSERT_TRUE(picked.contains(IOAddress("192.0.3.1")));
+
+ // Verify an additional pick returns zero address.
+ ASSERT_EQ(alloc.pickAddress(client_classes, IdentifierBaseTypePtr(), zero_address),
+ zero_address);
+
+ // Verify that only pool 2 addresses are picked for class "two"
+ slm.repopulateFlqPools();
+ picked.clear();
+ client_classes.clear();
+ client_classes.insert("two");
+ for (int i = 0; i < 2; ++i) {
+ IOAddress picked_address = zero_address;
+ ASSERT_NO_THROW_LOG(picked_address =
+ alloc.pickAddress(client_classes,
+ IdentifierBaseTypePtr(),
+ zero_address))
+ ASSERT_NE(picked_address, zero_address);
+ picked.emplace(picked_address);
+ }
+
+ ASSERT_EQ(picked.size(), 2);
+ ASSERT_TRUE(picked.contains(IOAddress("192.0.2.0")));
+ ASSERT_TRUE(picked.contains(IOAddress("192.0.2.1")));
+
+ // Verify an additional pick returns zero address.
+ ASSERT_EQ(alloc.pickAddress(client_classes, IdentifierBaseTypePtr(), zero_address),
+ zero_address);
+
+ // Verify that only pool 1 and 3 addresses are picked for classes "one" or "three"
+ slm.repopulateFlqPools();
+ picked.clear();
+ client_classes.clear();
+ client_classes.insert("one");
+ client_classes.insert("three");
+ for (int i = 0; i < 4; ++i) {
+ IOAddress picked_address = zero_address;
+ ASSERT_NO_THROW_LOG(picked_address =
+ alloc.pickAddress(client_classes,
+ IdentifierBaseTypePtr(),
+ zero_address));
+ ASSERT_NE(picked_address, zero_address);
+ picked.emplace(picked_address);
+ }
+
+ ASSERT_EQ(picked.size(), 4);
+ ASSERT_TRUE(picked.contains(IOAddress("192.0.1.0")));
+ ASSERT_TRUE(picked.contains(IOAddress("192.0.1.1")));
+ ASSERT_TRUE(picked.contains(IOAddress("192.0.3.0")));
+ ASSERT_TRUE(picked.contains(IOAddress("192.0.3.1")));
+
+ // Verify an additional pick returns zero address.
+ ASSERT_EQ(alloc.pickAddress(client_classes, IdentifierBaseTypePtr(), zero_address),
+ zero_address);
+}
+
+/// @brief Test fixture class for the DHCPv6 SharedFlqAllocator.
+class SharedFlqAllocatorTest6 : public testing::Test {
+public:
+
+ /// @brief Pre-test setup.
+ ///
+ /// Installs the lease manager factory, creates a manager instance
+ /// and initializes a V4 subnet.
+ virtual void SetUp() {
+ LeaseMgrFactory::registerFactory("sflqtest", SflqTestLeaseMgr::factory);
+ ASSERT_NO_THROW_LOG(LeaseMgrFactory::create("type=sflqtest universe=6"));
+ ASSERT_TRUE(LeaseMgrFactory::haveInstance());
+ ASSERT_EQ(LeaseMgrFactory::instance().getType(), "sflqtest");
+ initSubnet6();
+ }
+
+ /// @brief Initializes the test subnet for V4 tests.
+ void initSubnet6() {
+ static SubnetID id(1);
+ subnet_ = Subnet6::create(IOAddress("3001::"), 64, 0, 0, 0, 0 , id);
+ PoolPtr pool(new Pool6(Lease::TYPE_NA, IOAddress("3001::10"), IOAddress("3001::11")));
+ pool->allowClientClass("ALL");
+ pool->allowClientClass("one");
+ subnet_->addPool(pool);
+
+ pool.reset(new Pool6(Lease::TYPE_NA, IOAddress("3001::20"), IOAddress("3001::21")));
+ pool->allowClientClass("ALL");
+ pool->allowClientClass("two");
+ subnet_->addPool(pool);
+
+ pool.reset(new Pool6(Lease::TYPE_NA, IOAddress("3001::30"), IOAddress("3001::31")));
+ pool->allowClientClass("ALL");
+ pool->allowClientClass("three");
+ subnet_->addPool(pool);
+
+ // Now add PD pools to match classes, we use same length to make it
+ // verification easy .
+ pool.reset(new Pool6(Lease::TYPE_PD, IOAddress("2001::10"), 127, 128));
+ pool->allowClientClass("ALL");
+ pool->allowClientClass("one");
+ subnet_->addPool(pool);
+
+ pool.reset(new Pool6(Lease::TYPE_PD, IOAddress("2001::20"), 127, 128));
+ pool->allowClientClass("ALL");
+ pool->allowClientClass("two");
+ subnet_->addPool(pool);
+
+ pool.reset(new Pool6(Lease::TYPE_PD, IOAddress("2001::30"), 127, 128));
+ pool->allowClientClass("ALL");
+ pool->allowClientClass("three");
+ subnet_->addPool(pool);
+
+ // Now add PD pools of with low, middle, high for testing prefix
+ // hint logic.
+ pool.reset(new Pool6(Lease::TYPE_PD, IOAddress("4001:10::"), 124, 124));
+ pool->allowClientClass("LENGTH_HINT");
+ subnet_->addPool(pool);
+
+ pool.reset(new Pool6(Lease::TYPE_PD, IOAddress("4001:20::"), 124, 126));
+ pool->allowClientClass("LENGTH_HINT");
+ subnet_->addPool(pool);
+
+ pool.reset(new Pool6(Lease::TYPE_PD, IOAddress("4001:30::"), 124, 128));
+ pool->allowClientClass("LENGTH_HINT");
+ subnet_->addPool(pool);
+ }
+
+ /// @brief Subnet used in tests.
+ SubnetPtr subnet_;
+};
+
+// Test that the allocator returns the correct type.
+TEST_F(SharedFlqAllocatorTest6, getType) {
+ {
+ SharedFlqAllocator alloc(Lease::TYPE_NA, subnet_);
+ EXPECT_EQ("shared-flq", alloc.getType());
+ }
+
+ {
+ SharedFlqAllocator alloc(Lease::TYPE_PD, subnet_);
+ EXPECT_EQ("shared-flq", alloc.getType());
+ }
+}
+
+// Tests initAfterConfigure() for V6/TYPE_NA pools.
+TEST_F(SharedFlqAllocatorTest6, initAfterConfigureNA) {
+ SharedFlqAllocator alloc(Lease::TYPE_NA, subnet_);
+
+ EXPECT_FALSE(SharedFlqAllocator::inUse());
+ EXPECT_NO_THROW(alloc.initAfterConfigure());
+ EXPECT_TRUE(SharedFlqAllocator::inUse());
+
+ // Verify the NA pools.
+ SflqTestLeaseMgr& slm = dynamic_cast<SflqTestLeaseMgr&>(LeaseMgrFactory::instance());
+
+ for (auto pool : subnet_->getPools(Lease::TYPE_NA)) {
+ auto sflq_pool = slm.findPool(pool->getFirstAddress(), pool->getLastAddress());
+ ASSERT_TRUE(sflq_pool) << "no sflq pool for: " << pool->toText();
+ ASSERT_EQ(sflq_pool->lease_type_, Lease::TYPE_NA);
+ }
+}
+
+// Exercises ShareFlqAllocator::pickAddressInternal() for V6/TYPE_NA
+// using various class matches.
+TEST_F(SharedFlqAllocatorTest6, pickAddress) {
+ IOAddress zero_address = IOAddress::IPV6_ZERO_ADDRESS();
+
+ SharedFlqAllocator alloc(Lease::TYPE_NA, subnet_);
+
+ ASSERT_NO_THROW_LOG(alloc.initAfterConfigure());
+
+ SflqTestLeaseMgr& slm = dynamic_cast<SflqTestLeaseMgr&>(LeaseMgrFactory::instance());
+ for (auto pool : subnet_->getPools(Lease::TYPE_NA)) {
+ auto sflq_pool = slm.findPool(pool->getFirstAddress(), pool->getLastAddress());
+ ASSERT_TRUE(sflq_pool) << "no sflq pool for: " << pool->toText();
+ }
+
+ // Verify that all addresses can be picked with client_class = 'ALL'.
+ // We use a set to collect the picked addresses. This way we do not
+ // rely on the order they are picked but can still verify they
+ // all get picked.
+ std::set<IOAddress> picked;
+ ClientClasses client_classes;
+ client_classes.insert("ALL");
+ for (int i = 0; i < 6; ++i) {
+ IOAddress picked_address = zero_address;
+ ASSERT_NO_THROW_LOG(picked_address =
+ alloc.pickAddress(client_classes,
+ IdentifierBaseTypePtr(),
+ zero_address));
+ ASSERT_NE(picked_address, zero_address);
+ picked.emplace(picked_address);
+ }
+
+ ASSERT_EQ(picked.size(), 6);
+ ASSERT_TRUE(picked.contains(IOAddress("3001::10")));
+ ASSERT_TRUE(picked.contains(IOAddress("3001::11")));
+ ASSERT_TRUE(picked.contains(IOAddress("3001::20")));
+ ASSERT_TRUE(picked.contains(IOAddress("3001::21")));
+ ASSERT_TRUE(picked.contains(IOAddress("3001::30")));
+ ASSERT_TRUE(picked.contains(IOAddress("3001::31")));
+
+ // Verify an additional pick returns zero address.
+ ASSERT_EQ(alloc.pickAddress(client_classes, IdentifierBaseTypePtr(), zero_address),
+ zero_address);
+
+ // Verify that only pool 2 addresses are picked for class "two"
+ slm.repopulateFlqPools();
+ picked.clear();
+ client_classes.clear();
+ client_classes.insert("two");
+ for (int i = 0; i < 2; ++i) {
+ IOAddress picked_address = zero_address;
+ ASSERT_NO_THROW_LOG(picked_address =
+ alloc.pickAddress(client_classes,
+ IdentifierBaseTypePtr(),
+ zero_address))
+ ASSERT_NE(picked_address, zero_address);
+ picked.emplace(picked_address);
+ }
+
+ ASSERT_EQ(picked.size(), 2);
+ ASSERT_TRUE(picked.contains(IOAddress("3001::20")));
+ ASSERT_TRUE(picked.contains(IOAddress("3001::21")));
+
+ // Verify an additional pick returns zero address.
+ ASSERT_EQ(alloc.pickAddress(client_classes, IdentifierBaseTypePtr(), zero_address),
+ zero_address);
+
+ // Verify that only pool 1 and 3 addresses are picked for classes "one" or "three"
+ slm.repopulateFlqPools();
+ picked.clear();
+ client_classes.clear();
+ client_classes.insert("one");
+ client_classes.insert("three");
+ for (int i = 0; i < 4; ++i) {
+ IOAddress picked_address = zero_address;
+ ASSERT_NO_THROW_LOG(picked_address =
+ alloc.pickAddress(client_classes,
+ IdentifierBaseTypePtr(),
+ zero_address));
+ ASSERT_NE(picked_address, zero_address);
+ picked.emplace(picked_address);
+ }
+
+ ASSERT_EQ(picked.size(), 4);
+ ASSERT_TRUE(picked.contains(IOAddress("3001::10")));
+ ASSERT_TRUE(picked.contains(IOAddress("3001::11")));
+ ASSERT_TRUE(picked.contains(IOAddress("3001::30")));
+ ASSERT_TRUE(picked.contains(IOAddress("3001::31")));
+
+ // Verify an additional pick returns zero address.
+ ASSERT_EQ(alloc.pickAddress(client_classes, IdentifierBaseTypePtr(), zero_address),
+ zero_address);
+}
+
+// Tests initAfterConfigure() for V6/TYPE_PD pools.
+TEST_F(SharedFlqAllocatorTest6, initAfterConfigurePD) {
+ SharedFlqAllocator alloc(Lease::TYPE_PD, subnet_);
+
+ ASSERT_NO_THROW_LOG(alloc.initAfterConfigure());
+
+ // Verify the PD pools.
+ SflqTestLeaseMgr& slm = dynamic_cast<SflqTestLeaseMgr&>(LeaseMgrFactory::instance());
+
+ for (auto pool : subnet_->getPools(Lease::TYPE_PD)) {
+ auto sflq_pool = slm.findPool(pool->getFirstAddress(), pool->getLastAddress());
+ ASSERT_TRUE(sflq_pool) << "no sflq pool for: " << pool->toText();
+ ASSERT_EQ(sflq_pool->lease_type_, Lease::TYPE_PD);
+ }
+}
+
+// Exercises ShareFlqAllocator::pickPrefixInternal() V6/TYPE_PD using
+// various class matches.
+TEST_F(SharedFlqAllocatorTest6, pickPrefix) {
+ IOAddress zero_address = IOAddress::IPV6_ZERO_ADDRESS();
+
+ SharedFlqAllocator alloc(Lease::TYPE_PD, subnet_);
+
+ EXPECT_NO_THROW(alloc.initAfterConfigure());
+
+ SflqTestLeaseMgr& slm = dynamic_cast<SflqTestLeaseMgr&>(LeaseMgrFactory::instance());
+
+ // Verify we have the expected SFLQ PD pools.
+ for (auto pool : subnet_->getPools(Lease::TYPE_PD)) {
+ auto sflq_pool = slm.findPool(pool->getFirstAddress(), pool->getLastAddress());
+ ASSERT_TRUE(sflq_pool) << "no sflq pool for: " << pool->toText();
+ }
+
+ // Verify that all addresses can be picked with client_class = 'ALL'.
+ // We use a set to collect the picked addresses. This way we do not
+ // rely on the order they are picked but can still verify they
+ // all get picked.
+ std::set<IOAddress> picked;
+ ClientClasses client_classes;
+ client_classes.insert("ALL");
+ auto dummy = Pool6Ptr();
+ for (int i = 0; i < 6; ++i) {
+ IOAddress picked_address = zero_address;
+ ASSERT_NO_THROW_LOG(picked_address =
+ alloc.pickPrefix(client_classes,
+ dummy,
+ IdentifierBaseTypePtr(),
+ Allocator::PREFIX_LEN_EQUAL,
+ zero_address,
+ 0));
+ ASSERT_NE(picked_address, zero_address);
+ picked.emplace(picked_address);
+ }
+
+ ASSERT_EQ(picked.size(), 6);
+ ASSERT_TRUE(picked.contains(IOAddress("2001::10")));
+ ASSERT_TRUE(picked.contains(IOAddress("2001::11")));
+ ASSERT_TRUE(picked.contains(IOAddress("2001::20")));
+ ASSERT_TRUE(picked.contains(IOAddress("2001::21")));
+ ASSERT_TRUE(picked.contains(IOAddress("2001::30")));
+ ASSERT_TRUE(picked.contains(IOAddress("2001::31")));
+
+ // Verify an additional pick returns zero address.
+ ASSERT_EQ(alloc.pickPrefix(client_classes, dummy, IdentifierBaseTypePtr(),
+ Allocator::PREFIX_LEN_EQUAL, zero_address, 0),
+ zero_address);
+
+ // Verify that only pool 2 addresses are picked for class "two"
+ slm.repopulateFlqPools();
+ picked.clear();
+ client_classes.clear();
+ client_classes.insert("two");
+ for (int i = 0; i < 2; ++i) {
+ IOAddress picked_address = zero_address;
+ ASSERT_NO_THROW_LOG(picked_address =
+ alloc.pickPrefix(client_classes,
+ dummy,
+ IdentifierBaseTypePtr(),
+ Allocator::PREFIX_LEN_EQUAL,
+ zero_address,
+ 0));
+ ASSERT_NE(picked_address, zero_address);
+ picked.emplace(picked_address);
+ }
+
+ ASSERT_EQ(picked.size(), 2);
+ ASSERT_TRUE(picked.contains(IOAddress("2001::20")));
+ ASSERT_TRUE(picked.contains(IOAddress("2001::21")));
+
+ // Verify an additional pick returns zero address.
+ ASSERT_EQ(alloc.pickPrefix(client_classes, dummy, IdentifierBaseTypePtr(),
+ Allocator::PREFIX_LEN_EQUAL, zero_address, 0),
+ zero_address);
+
+ // Verify that only pool 1 and 3 addresses are picked for classes "one" or "three"
+ slm.repopulateFlqPools();
+ picked.clear();
+ client_classes.clear();
+ client_classes.insert("one");
+ client_classes.insert("three");
+ for (int i = 0; i < 4; ++i) {
+ IOAddress picked_address = zero_address;
+ ASSERT_NO_THROW_LOG(picked_address = alloc.pickPrefix(client_classes,
+ dummy,
+ IdentifierBaseTypePtr(),
+ Allocator::PREFIX_LEN_EQUAL,
+ zero_address,
+ 0));
+ ASSERT_NE(picked_address, zero_address);
+ picked.emplace(picked_address);
+ }
+
+ ASSERT_EQ(picked.size(), 4);
+ ASSERT_TRUE(picked.contains(IOAddress("2001::10")));
+ ASSERT_TRUE(picked.contains(IOAddress("2001::11")));
+ ASSERT_TRUE(picked.contains(IOAddress("2001::30")));
+ ASSERT_TRUE(picked.contains(IOAddress("2001::31")));
+
+ // Verify an additional pick returns zero address.
+ ASSERT_EQ(alloc.pickPrefix(client_classes, dummy, IdentifierBaseTypePtr(),
+ Allocator::PREFIX_LEN_EQUAL, zero_address, 0),
+ zero_address);
+}
+
+// Exercises ShareFlqAllocator::pickPrefixInternal() V6/TYPE_PD
+// when specifying a prefix length mode and hint.
+TEST_F(SharedFlqAllocatorTest6, pickPrefixLenHint) {
+ IOAddress zero_address = IOAddress::IPV6_ZERO_ADDRESS();
+
+ SharedFlqAllocator alloc(Lease::TYPE_PD, subnet_);
+
+ EXPECT_NO_THROW(alloc.initAfterConfigure());
+
+ SflqTestLeaseMgr& slm = dynamic_cast<SflqTestLeaseMgr&>(LeaseMgrFactory::instance());
+
+ // Verify we have the expected SFLQ PD pools.
+ for (auto pool : subnet_->getPools(Lease::TYPE_PD)) {
+ auto sflq_pool = slm.findPool(pool->getFirstAddress(), pool->getLastAddress());
+ ASSERT_TRUE(sflq_pool) << "no sflq pool for: " << pool->toText();
+ }
+
+ auto dummy = Pool6Ptr();
+ ClientClasses client_classes;
+ client_classes.insert("LENGTH_HINT");
+
+ IOAddress picked_address = zero_address;
+ ASSERT_NO_THROW_LOG(picked_address = alloc.pickPrefix(client_classes,
+ dummy,
+ IdentifierBaseTypePtr(),
+ Allocator::PREFIX_LEN_EQUAL,
+ zero_address,
+ 126));
+ EXPECT_EQ(picked_address, IOAddress("4001:20::"));
+
+ ASSERT_NO_THROW_LOG(picked_address = alloc.pickPrefix(client_classes,
+ dummy,
+ IdentifierBaseTypePtr(),
+ Allocator::PREFIX_LEN_LOWER,
+ zero_address,
+ 126));
+ EXPECT_EQ(picked_address, IOAddress("4001:10::"));
+
+ ASSERT_NO_THROW_LOG(picked_address = alloc.pickPrefix(client_classes,
+ dummy,
+ IdentifierBaseTypePtr(),
+ Allocator::PREFIX_LEN_HIGHER,
+ zero_address,
+ 126));
+ EXPECT_EQ(picked_address, IOAddress("4001:30::"));
+}
+
+
+} // end of isc::dhcp::test namespace
+} // end of isc::dhcp namespace
+} // end of isc namespace
'host_data_source_utils.cc',
'lease_file_io.cc',
'memory_host_data_source.cc',
+ 'sflqtest_lease_mgr.cc',
'test_config_backend_dhcp4.cc',
'test_config_backend_dhcp6.cc',
'test_utils.cc',
--- /dev/null
+// Copyright (C) 2026 Internet Systems Consortium, Inc. ("ISC")
+//
+// This Source Code Form is subject to the terms of the Mozilla Public
+// License, v. 2.0. If a copy of the MPL was not distributed with this
+// file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+#include <config.h>
+
+#include <asiolink/io_address.h>
+#include <dhcpsrv/iterative_allocator.h>
+#include <sflqtest_lease_mgr.h>
+
+using namespace isc::asiolink;
+using namespace isc::db;
+using namespace isc::dhcp;
+using namespace std;
+
+namespace isc {
+namespace dhcp {
+namespace test {
+
+SflqPool::SflqPool(asiolink::IOAddress start_address,
+ asiolink::IOAddress end_address,
+ SubnetID subnet_id,
+ Lease::Type lease_type /* = Lease::TYPE_V4 */,
+ uint8_t delegated_len /* = 1 */)
+ : start_address_(start_address), end_address_(end_address),
+ subnet_id_(subnet_id), lease_type_(lease_type),
+ delegated_len_(delegated_len) {
+ repopulateFreeLeases();
+}
+
+IOAddress
+SflqPool::zeroAddress() {
+ if (lease_type_ == Lease::TYPE_V4) {
+ return (IOAddress::IPV4_ZERO_ADDRESS());
+ }
+
+ return (IOAddress::IPV6_ZERO_ADDRESS());
+}
+
+void
+SflqPool::repopulateFreeLeases() {
+ // Populate list of free leases with all addresses in
+ // the pool. For purposes of testing the SharedFlqAllocator
+ // class we don't care about actual leases.
+ IOAddress next_address = start_address_;
+ free_addresses_.clear();
+ while (next_address <= end_address_) {
+ free_addresses_.push_back(next_address);
+ next_address = IterativeAllocator::increaseAddress(next_address,
+ (lease_type_ == Lease::TYPE_PD),
+ delegated_len_);
+ }
+}
+
+IOAddress
+SflqPool::popFreeAddress() {
+ if (free_addresses_.empty()) {
+ return (zeroAddress());
+ }
+
+ IOAddress free_address = free_addresses_.front();
+ free_addresses_.pop_front();
+ return (free_address);
+}
+
+TrackingLeaseMgrPtr
+SflqTestLeaseMgr::factory(const DatabaseConnection::ParameterMap& params) {
+ return (TrackingLeaseMgrPtr(new SflqTestLeaseMgr(params)));
+}
+
+SflqTestLeaseMgr::SflqTestLeaseMgr(const DatabaseConnection::ParameterMap& params)
+ : ConcreteLeaseMgr(params) {
+}
+
+SflqTestLeaseMgr::~SflqTestLeaseMgr() {
+}
+
+bool
+SflqTestLeaseMgr::sflqCreateFlqPool4(IOAddress start_address, IOAddress end_address,
+ SubnetID subnet_id, bool recreate) {
+ auto sflq_pool = findPool(start_address, end_address);
+ if (sflq_pool && recreate) {
+ sflq_pool->repopulateFreeLeases();
+ }
+
+ // Create the pool and add it to the list of pools.
+ sflq_pool.reset(new SflqPool(start_address, end_address, subnet_id));
+ sflq_pools_.push_back(sflq_pool);
+ return(true);
+}
+
+IOAddress
+SflqTestLeaseMgr::sflqPickFreeLease4(IOAddress start_address, IOAddress end_address) {
+ auto sflq_pool = findPool(start_address, end_address);
+ if (!sflq_pool) {
+ return (IOAddress::IPV4_ZERO_ADDRESS());
+ }
+
+ return (sflq_pool->popFreeAddress());
+}
+
+bool
+SflqTestLeaseMgr::sflqCreateFlqPool6(IOAddress start_address, IOAddress end_address,
+ Lease::Type lease_type, uint8_t delegated_len,
+ SubnetID subnet_id, bool recreate) {
+ auto sflq_pool = findPool(start_address, end_address);
+ if (sflq_pool && recreate) {
+ sflq_pool->repopulateFreeLeases();
+ }
+
+ // Create the pool and add it to the list of pools.
+ sflq_pool.reset(new SflqPool(start_address, end_address, subnet_id,
+ lease_type, delegated_len));
+ sflq_pools_.push_back(sflq_pool);
+ return(true);
+}
+
+IOAddress
+SflqTestLeaseMgr::sflqPickFreeLease6(IOAddress start_address, IOAddress end_address) {
+ auto sflq_pool = findPool(start_address, end_address);
+ if (!sflq_pool) {
+ return (IOAddress::IPV6_ZERO_ADDRESS());
+ }
+
+ return (sflq_pool->popFreeAddress());
+}
+
+SflqPoolPtr
+SflqTestLeaseMgr::findPool(IOAddress start_address, IOAddress end_address) {
+ for (auto pool : sflq_pools_) {
+ if (pool->start_address_ == start_address &&
+ pool->end_address_ == end_address) {
+ return (pool);
+ }
+ }
+
+ return (SflqPoolPtr());
+}
+
+void
+SflqTestLeaseMgr::repopulateFlqPools() {
+ for (auto pool : sflq_pools_) {
+ pool->repopulateFreeLeases();
+ }
+}
+
+std::string
+SflqTestLeaseMgr::getType() const {
+ return (std::string("sflqtest"));
+}
+
+} // end of namespace isc::dhcp::test
+} // end of namespace isc::dhcp
+} // end of namespace isc
--- /dev/null
+// Copyright (C) 2023-2025 Internet Systems Consortium, Inc. ("ISC")
+//
+// This Source Code Form is subject to the terms of the Mozilla Public
+// License, v. 2.0. If a copy of the MPL was not distributed with this
+// file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+#ifndef SFLQ_TEST_LEASE_MGR_H
+#define SFLQ_TEST_LEASE_MGR_H
+
+#include <config.h>
+
+#include <database/database_connection.h>
+#include <dhcpsrv/testutils/concrete_lease_mgr.h>
+#include <list>
+#include <utility>
+
+namespace isc {
+namespace dhcp {
+namespace test {
+
+/// @brief Mock Shared FLQ pool and lease data.
+///
+/// Takes the place of flq_poolX and free_leaseX tables.
+struct SflqPool {
+
+ /// @brief Constructor.
+ ///
+ /// @param start_address first address in the pool.
+ /// @param last_addresss last address in the pool.
+ /// @param subnet_id id of the subnet to which the pool belongs.
+ /// @param recreate when true, the pool is recreated if it already exits.
+ /// @param lease_type Lease::TYPE_V4, TYPE_NA, or TYPE_PD
+ /// @param delegated_len bit length of the address/prefix to be leases. For
+ /// TYPE_NA this parameter should be 128.
+ SflqPool(asiolink::IOAddress start_address_,
+ asiolink::IOAddress end_address_,
+ SubnetID subnet_id_,
+ Lease::Type lease_type = Lease::TYPE_V4,
+ uint8_t delegated_len = 1);
+
+ /// @brief Desructor.
+ ~SflqPool(){};
+
+ /// @brief Refills the free lease list with all leases in the pool.
+ void repopulateFreeLeases();
+
+ /// @brief Removes and returns an address from the front of the free
+ /// address list.
+ ///
+ /// @return An address or '0.0.0.0'/'::' is there are none.
+ asiolink::IOAddress popFreeAddress();
+
+ /// @brief Convenience function that returns a pool-appropriate
+ /// empty IOAddress.
+ ///
+ /// @return IPV4_ZERO_ADDRESS() if pool leas type is TYPE_V4,
+ /// IPV6_ZERO_ADDRESS() otherwise.
+ asiolink::IOAddress zeroAddress();
+
+ /// @brief First address in the pool.
+ asiolink::IOAddress start_address_;
+
+ /// @brief Last address in the pool.
+ asiolink::IOAddress end_address_;
+
+ /// @brief Id of the subnet to which the pool belongs.
+ SubnetID subnet_id_;
+
+ /// @brief Lease type of pool.
+ Lease::Type lease_type_;
+
+ /// @brief Length of the address/prefix to be leases.
+ uint8_t delegated_len_;
+
+ /// @brief List of free addresses in the pool.
+ std::list<asiolink::IOAddress> free_addresses_;
+
+};
+
+/// @brief A pointer to a SflqPool.
+typedef boost::shared_ptr<SflqPool> SflqPoolPtr;
+
+/// @brief A list of SflqPoolPtrs.
+typedef std::list<SflqPoolPtr> SflqPoolCollection;
+
+// This is a concrete implementation of a Lease database. It does not do
+// anything useful and is used for abstract LeaseMgr class testing.
+class SflqTestLeaseMgr : public ConcreteLeaseMgr {
+public:
+
+ /// @brief The sole lease manager constructor
+ ///
+ /// dbconfig is a generic way of passing parameters. Parameters
+ /// are passed in the "name=value" format, separated by spaces.
+ /// Values may be enclosed in double quotes, if needed.
+ SflqTestLeaseMgr(const db::DatabaseConnection::ParameterMap&);
+
+ /// @brief Destructor
+ virtual ~SflqTestLeaseMgr();
+
+ /// @brief Factory for creating a SlfqTestLeaseMgr.
+ ///
+ /// The only required parameters are "type=sflqtest" adn "universe=4"
+ /// or "universe=6".
+ ///
+ /// @param params Connection parameters for creating the manager.
+ static TrackingLeaseMgrPtr factory(const db::DatabaseConnection
+ ::ParameterMap& params);
+
+ /// @brief Creates a v4 SFLQ Pool
+ ///
+ /// @param start_address first address in the pool.
+ /// @param last_addresss last address in the pool.
+ /// @param subnet_id id of the subnet to which the pool belongs.
+ /// @param recreate when true, the pool is recreated if it already exits.
+ ///
+ /// @return True if the pool is (re)created, false it if already exists.
+ virtual bool sflqCreateFlqPool4(asiolink::IOAddress start_address,
+ asiolink::IOAddress end_address,
+ SubnetID subnet_id, bool recreate = false);
+
+ /// @brief Finds a free V4 address within the given pool range.
+ ///
+ /// @param start_address first address in the pool.
+ /// @param last_addresss last address in the pool.
+ ///
+ /// @return A free V4 address or IOAddress::IPV4_ZERO_ADDRESS().
+ virtual asiolink::IOAddress sflqPickFreeLease4(asiolink::IOAddress start_address,
+ asiolink::IOAddress end_address);
+
+ /// @brief Calls stored procedure to create an SFLQ pool for v6.
+ ///
+ /// @param start_address first address/prefix in the pool.
+ /// @param last_addresss last address/prefix in the pool.
+ /// @param lease_type TYPE_NA or TYPE_PD.
+ /// @param delegated_len bit length of the address/prefix to be leases. For
+ /// TYPE_NA this parameter should be 128.
+ /// @param subnet_id id of the subnet to which the pool belongs.
+ /// @param recreate when true, the pool is recreated if it already exits.
+ ///
+ /// @return True if the pool is (re)created, false it if already exists.
+ virtual bool sflqCreateFlqPool6(asiolink::IOAddress start_address,
+ asiolink::IOAddress end_address,
+ Lease::Type lease_type, uint8_t delegated_len,
+ SubnetID subnet_id, bool recreate = false);
+
+ /// @brief Finds a free V6 address/prefix within the given pool range.
+ ///
+ /// @param start_address first address in the pool.
+ /// @param last_addresss last address in the pool.
+ ///
+ /// @return A free V6 address/prefix or IOAddress::IPV6_ZERO_ADDRESS().
+ virtual asiolink::IOAddress sflqPickFreeLease6(asiolink::IOAddress start_address,
+ asiolink::IOAddress end_address);
+
+ /// @brief Finds an SflqPool in the list of SflqPools
+ ///
+ /// @param start_address first address in the pool.
+ /// @param last_addresss last address in the pool.
+ ///
+ /// @return Pointer to the desired pool are an empty pointer.
+ SflqPoolPtr findPool(asiolink::IOAddress start_address, asiolink::IOAddress end_address);
+
+ /// @brief Refills the free lease lists for all SFLQ pools.
+ void repopulateFlqPools();
+
+ /// @brief Returns backend type.
+ ///
+ /// Returns the type of the backend (e.g. "mysql", "memfile" etc.)
+ ///
+ /// @return Type of the backend.
+ virtual std::string getType() const override;
+
+ /// @brief Collection of SFLQ pools that have been created.
+ SflqPoolCollection sflq_pools_;
+};
+
+} // end of namespace isc::dhcp::test
+} // end of namespace isc::dhcp
+} // end of namespace isc
+
+#endif // SFLQ_TEST_LEASE_MGR_H