diff options
Diffstat (limited to '')
-rw-r--r-- | src/lib/dhcpsrv/subnet.cc | 952 |
1 files changed, 952 insertions, 0 deletions
diff --git a/src/lib/dhcpsrv/subnet.cc b/src/lib/dhcpsrv/subnet.cc new file mode 100644 index 0000000..2e480d4 --- /dev/null +++ b/src/lib/dhcpsrv/subnet.cc @@ -0,0 +1,952 @@ +// Copyright (C) 2012-2023 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 <asiolink/addr_utilities.h> +#include <dhcp/option_space.h> +#include <dhcpsrv/dhcpsrv_log.h> +#include <dhcpsrv/flq_allocation_state.h> +#include <dhcpsrv/flq_allocator.h> +#include <dhcpsrv/iterative_allocation_state.h> +#include <dhcpsrv/iterative_allocator.h> +#include <dhcpsrv/random_allocation_state.h> +#include <dhcpsrv/random_allocator.h> +#include <dhcpsrv/shared_network.h> +#include <dhcpsrv/subnet.h> +#include <util/multi_threading_mgr.h> + +#include <boost/lexical_cast.hpp> +#include <boost/make_shared.hpp> + +#include <algorithm> +#include <limits> +#include <sstream> + +using namespace isc::asiolink; +using namespace isc::data; +using namespace isc::dhcp; +using namespace isc::util; + +using namespace std; + +namespace { + +/// @brief Function used in calls to std::upper_bound to check +/// if the specified prefix is lower than the first address a pool. +/// +/// @return true if prefix is lower than the first address in the pool. +bool +prefixLessThanFirstAddress(const IOAddress& prefix, + const PoolPtr& pool) { + return (prefix < pool->getFirstAddress()); +} + +/// @brief Function used in calls to std::sort to compare first +/// prefixes of the two pools. +/// +/// @param pool1 First pool. +/// @param pool2 Second pool. +/// +/// @return true if first prefix of the first pool is smaller than +/// the first address of the second pool. +bool +comparePoolFirstAddress(const PoolPtr& pool1, + const PoolPtr& pool2) { + return (pool1->getFirstAddress() < pool2->getFirstAddress()); +} + +} + +namespace isc { +namespace dhcp { + +// This is an initial value of subnet-id. See comments in subnet.h for details. +SubnetID Subnet::static_id_ = 1; + +Subnet::Subnet(const isc::asiolink::IOAddress& prefix, uint8_t len, + const SubnetID id) + : id_(id == 0 ? generateNextID() : id), prefix_(prefix), + prefix_len_(len), + shared_network_name_() { + if ((id == 0) && (id_ == 1)) { + // Emit a warning on the first auto-numbered subnet. + LOG_WARN(dhcpsrv_logger, DHCPSRV_CONFIGURED_SUBNET_WITHOUT_ID) + .arg(toText()); + } + if ((prefix.isV6() && len > 128) || + (prefix.isV4() && len > 32)) { + isc_throw(BadValue, + "Invalid prefix length specified for subnet: " << len); + } +} + +bool +Subnet::inRange(const isc::asiolink::IOAddress& addr) const { + IOAddress first = firstAddrInPrefix(prefix_, prefix_len_); + IOAddress last = lastAddrInPrefix(prefix_, prefix_len_); + + return ((first <= addr) && (addr <= last)); +} + +std::string +Subnet::toText() const { + std::stringstream tmp; + tmp << prefix_ << "/" << static_cast<unsigned int>(prefix_len_); + return (tmp.str()); +} + +uint128_t +Subnet::getPoolCapacity(Lease::Type type) const { + switch (type) { + case Lease::TYPE_V4: + case Lease::TYPE_NA: + return sumPoolCapacity(pools_); + case Lease::TYPE_TA: + return sumPoolCapacity(pools_ta_); + case Lease::TYPE_PD: + return sumPoolCapacity(pools_pd_); + default: + isc_throw(BadValue, "Unsupported pool type: " + << static_cast<int>(type)); + } +} + +uint128_t +Subnet::getPoolCapacity(Lease::Type type, + const ClientClasses& client_classes) const { + switch (type) { + case Lease::TYPE_V4: + case Lease::TYPE_NA: + return sumPoolCapacity(pools_, client_classes); + case Lease::TYPE_TA: + return sumPoolCapacity(pools_ta_, client_classes); + case Lease::TYPE_PD: + return sumPoolCapacity(pools_pd_, client_classes); + default: + isc_throw(BadValue, "Unsupported pool type: " + << static_cast<int>(type)); + } +} + +uint128_t +Subnet::getPoolCapacity(Lease::Type type, + const ClientClasses& client_classes, + Allocator::PrefixLenMatchType prefix_length_match, + uint8_t hint_prefix_length) const { + switch (type) { + case Lease::TYPE_V4: + case Lease::TYPE_NA: + return sumPoolCapacity(pools_, client_classes); + case Lease::TYPE_TA: + return sumPoolCapacity(pools_ta_, client_classes); + case Lease::TYPE_PD: + return sumPoolCapacity(pools_pd_, client_classes, prefix_length_match, + hint_prefix_length); + default: + isc_throw(BadValue, "Unsupported pool type: " + << static_cast<int>(type)); + } +} + +uint128_t +Subnet::sumPoolCapacity(const PoolCollection& pools) const { + uint128_t sum(0); + for (auto const& p : pools) { + uint128_t const c(p->getCapacity()); + + // Check if we can add it. If sum + c > UINT128_MAX, then we would have + // overflown if we tried to add it. + if (c > numeric_limits<uint128_t>::max() - sum) { + return (numeric_limits<uint128_t>::max()); + } + + sum += c; + } + + return (sum); +} + +uint128_t +Subnet::sumPoolCapacity(const PoolCollection& pools, + const ClientClasses& client_classes) const { + uint128_t sum(0); + for (auto const& p : pools) { + if (!p->clientSupported(client_classes)) { + continue; + } + + uint128_t const c(p->getCapacity()); + + // Check if we can add it. If sum + c > UINT128_MAX, then we would have + // overflown if we tried to add it. + if (c > numeric_limits<uint128_t>::max() - sum) { + return (numeric_limits<uint128_t>::max()); + } + + sum += c; + } + + return (sum); +} + +uint128_t +Subnet::sumPoolCapacity(const PoolCollection& pools, + const ClientClasses& client_classes, + Allocator::PrefixLenMatchType prefix_length_match, + uint8_t hint_prefix_length) const { + uint128_t sum(0); + for (auto const& p : pools) { + if (!p->clientSupported(client_classes)) { + continue; + } + + if (!Allocator::isValidPrefixPool(prefix_length_match, p, + hint_prefix_length)) { + continue; + } + + uint128_t const c(p->getCapacity()); + + // Check if we can add it. If sum + c > UINT128_MAX, then we would have + // overflown if we tried to add it. + if (c > numeric_limits<uint128_t>::max() - sum) { + return (numeric_limits<uint128_t>::max()); + } + + sum += c; + } + + return (sum); +} + +std::pair<IOAddress, uint8_t> +Subnet::parsePrefixCommon(const std::string& prefix) { + auto pos = prefix.find('/'); + if ((pos == std::string::npos) || + (pos == prefix.size() - 1) || + (pos == 0)) { + isc_throw(BadValue, "unable to parse invalid prefix " << prefix); + } + + try { + IOAddress address(prefix.substr(0, pos)); + int length = boost::lexical_cast<int>(prefix.substr(pos + 1)); + return (std::make_pair(address, static_cast<int>(length))); + + } catch (...) { + isc_throw(BadValue, "unable to parse invalid prefix " << prefix); + } +} + + +void Subnet4::checkType(Lease::Type type) const { + if (type != Lease::TYPE_V4) { + isc_throw(BadValue, "Only TYPE_V4 is allowed for Subnet4"); + } +} + +Subnet4::Subnet4(const IOAddress& prefix, uint8_t length, + const Triplet<uint32_t>& t1, + const Triplet<uint32_t>& t2, + const Triplet<uint32_t>& valid_lifetime, + const SubnetID id) + : Subnet(prefix, length, id), Network4() { + if (!prefix.isV4()) { + isc_throw(BadValue, "Non IPv4 prefix " << prefix.toText() + << " specified in subnet4"); + } + // Timers. + setT1(t1); + setT2(t2); + setValid(valid_lifetime); +} + +Subnet4Ptr +Subnet4::create(const IOAddress& prefix, uint8_t length, + const Triplet<uint32_t>& t1, + const Triplet<uint32_t>& t2, + const Triplet<uint32_t>& valid_lifetime, + const SubnetID id) { + Subnet4Ptr subnet = boost::make_shared<Subnet4> + (prefix, length, t1, t2, valid_lifetime, id); + subnet->setAllocator(Lease::TYPE_V4, + boost::make_shared<IterativeAllocator> + (Lease::TYPE_V4, subnet)); + subnet->setAllocationState(Lease::TYPE_V4, + SubnetIterativeAllocationState::create(subnet)); + + return (subnet); +} + +Subnet4Ptr +Subnet4::getNextSubnet(const Subnet4Ptr& first_subnet) const { + SharedNetwork4Ptr network; + getSharedNetwork(network); + if (network) { + return (network->getNextSubnet(first_subnet, getID())); + } + + return (Subnet4Ptr()); +} + +Subnet4Ptr +Subnet4::getNextSubnet(const Subnet4Ptr& first_subnet, + const ClientClasses& client_classes) const { + SharedNetwork4Ptr network; + getSharedNetwork(network); + // We can only get next subnet if shared network has been defined for + // the current subnet. + if (network) { + Subnet4Ptr subnet; + do { + // Use subnet identifier of this subnet if this is the first + // time we're calling getNextSubnet. Otherwise, use the + // subnet id of the previously returned subnet. + SubnetID subnet_id = subnet ? subnet->getID() : getID(); + subnet = network->getNextSubnet(first_subnet, subnet_id); + // If client classes match the subnet, return it. Otherwise, + // try another subnet. + if (subnet && subnet->clientSupported(client_classes)) { + return (subnet); + } + } while (subnet); + } + + // No subnet found. + return (Subnet4Ptr()); +} + + +bool +Subnet4::clientSupported(const isc::dhcp::ClientClasses& client_classes) const { + NetworkPtr network; + getSharedNetwork(network); + if (network && !network->clientSupported(client_classes)) { + return (false); + } + + return (Network4::clientSupported(client_classes)); +} + +const PoolCollection& Subnet::getPools(Lease::Type type) const { + // check if the type is valid (and throw if it isn't) + checkType(type); + + switch (type) { + case Lease::TYPE_V4: + case Lease::TYPE_NA: + return (pools_); + case Lease::TYPE_TA: + return (pools_ta_); + case Lease::TYPE_PD: + return (pools_pd_); + default: + isc_throw(BadValue, "Unsupported pool type: " + << static_cast<int>(type)); + } +} + +PoolCollection& Subnet::getPoolsWritable(Lease::Type type) { + // check if the type is valid (and throw if it isn't) + checkType(type); + + switch (type) { + case Lease::TYPE_V4: + case Lease::TYPE_NA: + return (pools_); + case Lease::TYPE_TA: + return (pools_ta_); + case Lease::TYPE_PD: + return (pools_pd_); + default: + isc_throw(BadValue, "Invalid pool type specified: " + << static_cast<int>(type)); + } +} + +AllocatorPtr +Subnet::getAllocator(Lease::Type type) const { + auto alloc = allocators_.find(type); + + if (alloc == allocators_.end()) { + isc_throw(BadValue, "no allocator initialized for pool type " + << Lease::typeToText(type)); + } + return (alloc->second); +} + +void +Subnet::setAllocator(Lease::Type type, const AllocatorPtr& allocator) { + allocators_[type] = allocator; +} + +SubnetAllocationStatePtr +Subnet::getAllocationState(Lease::Type type) const { + auto state = allocation_states_.find(type); + + if (state == allocation_states_.end()) { + isc_throw(BadValue, "no allocation state initialized for pool type " + << Lease::typeToText(type)); + } + return (state->second); +} + +void +Subnet::setAllocationState(Lease::Type type, const SubnetAllocationStatePtr& allocation_state) { + allocation_states_[type] = allocation_state; +} + +const PoolPtr Subnet::getPool(Lease::Type type, const isc::asiolink::IOAddress& hint, + bool anypool /* true */) const { + // check if the type is valid (and throw if it isn't) + checkType(type); + + const auto& pools = getPools(type); + + PoolPtr candidate; + + if (!pools.empty()) { + // Pools are sorted by their first prefixes. For example: 2001::, + // 2001::db8::, 3000:: etc. If our hint is 2001:db8:5:: we want to + // find the pool with the longest matching prefix, so: 2001:db8::, + // rather than 2001::. upper_bound returns the first pool with a prefix + // that is greater than 2001:db8:5::, i.e. 3000::. To find the longest + // matching prefix we use decrement operator to go back by one item. + // If returned iterator points to begin it means that prefixes in all + // pools are greater than out prefix, and thus there is no match. + auto ub = + std::upper_bound(pools.begin(), pools.end(), hint, + prefixLessThanFirstAddress); + + if (ub != pools.begin()) { + --ub; + if ((*ub)->inRange(hint)) { + candidate = *ub; + } + } + + // If we don't find anything better, then let's just use the first pool + if (!candidate && anypool) { + candidate = *pools.begin(); + } + } + + // Return a pool or NULL if no match found. + return (candidate); +} + +void +Subnet::initAllocatorsAfterConfigure() { + for (auto allocator : allocators_) { + allocator.second->initAfterConfigure(); + } +} + +const PoolPtr Subnet::getPool(Lease::Type type, + const ClientClasses& client_classes, + const isc::asiolink::IOAddress& hint) const { + // check if the type is valid (and throw if it isn't) + checkType(type); + + const auto& pools = getPools(type); + + PoolPtr candidate; + + if (!pools.empty()) { + auto ub = + std::upper_bound(pools.begin(), pools.end(), hint, + prefixLessThanFirstAddress); + + if (ub != pools.begin()) { + --ub; + if ((*ub)->inRange(hint) && + (*ub)->clientSupported(client_classes)) { + candidate = *ub; + } + } + } + + // Return a pool or NULL if no match found. + return (candidate); +} + +void +Subnet::addPool(const PoolPtr& pool) { + // check if the type is valid (and throw if it isn't) + checkType(pool->getType()); + + // Check that the pool is in range with a subnet only if this is + // not a pool of IPv6 prefixes. The IPv6 prefixes delegated for + // the particular subnet don't need to match the prefix of the + // subnet. + if (pool->getType() != Lease::TYPE_PD) { + if (!inRange(pool->getFirstAddress()) || !inRange(pool->getLastAddress())) { + isc_throw(BadValue, "a pool of type " + << Lease::typeToText(pool->getType()) + << ", with the following address range: " + << pool->getFirstAddress() << "-" + << pool->getLastAddress() << " does not match" + << " the prefix of a subnet: " + << prefix_ << "/" << static_cast<int>(prefix_len_) + << " to which it is being added"); + + } + } + + bool overlaps = false; + if (pool->getType() == Lease::TYPE_V4) { + overlaps = poolOverlaps(Lease::TYPE_V4, pool); + + } else { + overlaps = + poolOverlaps(Lease::TYPE_NA, pool) || + poolOverlaps(Lease::TYPE_PD, pool) || + poolOverlaps(Lease::TYPE_TA, pool); + } + + if (overlaps) { + isc_throw(BadValue,"a pool of type " + << Lease::typeToText(pool->getType()) + << ", with the following address range: " + << pool->getFirstAddress() << "-" + << pool->getLastAddress() << " overlaps with " + "an existing pool in the subnet: " + << prefix_ << "/" << static_cast<int>(prefix_len_) + << " to which it is being added"); + } + + PoolCollection& pools_writable = getPoolsWritable(pool->getType()); + + // Add the pool to the appropriate pools collection + pools_writable.push_back(pool); + + // Sort pools by first address. + std::sort(pools_writable.begin(), pools_writable.end(), + comparePoolFirstAddress); +} + +void +Subnet::delPools(Lease::Type type) { + getPoolsWritable(type).clear(); +} + +bool +Subnet::inPool(Lease::Type type, const isc::asiolink::IOAddress& addr) const { + + // Let's start with checking if it even belongs to that subnet. + if ((type != Lease::TYPE_PD) && !inRange(addr)) { + return (false); + } + + const auto& pools = getPools(type); + for (const auto& pool : pools) { + if (pool->inRange(addr)) { + return (true); + } + } + // There's no pool that address belongs to + return (false); +} + +bool +Subnet::inPool(Lease::Type type, + const isc::asiolink::IOAddress& addr, + const ClientClasses& client_classes) const { + + // Let's start with checking if it even belongs to that subnet. + if ((type != Lease::TYPE_PD) && !inRange(addr)) { + return (false); + } + + const auto& pools = getPools(type); + for (const auto& pool : pools) { + if (!pool->clientSupported(client_classes)) { + continue; + } + if (pool->inRange(addr)) { + return (true); + } + } + // There's no pool that address belongs to + return (false); +} + +bool +Subnet::poolOverlaps(const Lease::Type& pool_type, const PoolPtr& pool) const { + const auto& pools = getPools(pool_type); + + // If no pools, we don't overlap. Nothing to do. + if (pools.empty()) { + return (false); + } + + // We're going to insert a new pool, likely between two existing pools. + // So we're going to end up with the following case: + // |<---- pool1 ---->| |<-------- pool2 ------>| |<-- pool3 -->| + // F1 L1 F2 L2 F3 L3 + // where pool1 and pool3 are existing pools, pool2 is a pool being + // inserted and "F"/"L" mark first and last address in the pools + // respectively. So the following conditions must be fulfilled: + // F2 > L1 and L2 < F3. Obviously, for any pool: F < L. + + // Search for pool3. We use F2 and upper_bound to find the F3 (upper_bound + // returns first pool in the sorted container which first address is + // greater than F2). prefixLessThanPoolAddress with the first argument + // set to "true" is the custom comparison function for upper_bound, which + // compares F2 with the first addresses of the existing pools. + const auto pool3_it = + std::upper_bound(pools.begin(), pools.end(), pool->getFirstAddress(), + prefixLessThanFirstAddress); + + // The upper_bound function returns a first pool which first address is + // greater than the address F2. However, it is also possible that there is a + // pool which first address is equal to F2. Such pool is also in conflict + // with a new pool. If the returned value is pools.begin() it means that all + // pools have greater first address than F2, thus none of the pools can have + // first address equal to F2. Otherwise, we'd need to check them for + // equality. However any pool has first address <= last address, so checking + // that the new pool first address is greater than the pool before pool3 + // last address is enough. We now have to find the pool1. This pool should + // be right before the pool3 if there is any pool before pool3. + if (pool3_it != pools.begin()) { + PoolPtr pool1 = *(pool3_it - 1); + // F2 must be greater than L1, otherwise pools will overlap. + if (pool->getFirstAddress() <= pool1->getLastAddress()) { + return (true); + } + } + + // If returned value is unequal pools.end() it means that there is a pool3, + // with F3 > F2. + if (pool3_it != pools.end()) { + // Let's store the pointer to this pool. + PoolPtr pool3 = *pool3_it; + // F3 must be greater than L2, otherwise pools will overlap. + if (pool3->getFirstAddress() <= pool->getLastAddress()) { + return (true); + } + } + + return (false); +} + +Subnet6::Subnet6(const IOAddress& prefix, uint8_t length, + const Triplet<uint32_t>& t1, + const Triplet<uint32_t>& t2, + const Triplet<uint32_t>& preferred_lifetime, + const Triplet<uint32_t>& valid_lifetime, + const SubnetID id) + : Subnet(prefix, length, id), Network6() { + if (!prefix.isV6()) { + isc_throw(BadValue, "Non IPv6 prefix " << prefix + << " specified in subnet6"); + } + + // Timers. + setT1(t1); + setT2(t2); + setPreferred(preferred_lifetime); + setValid(valid_lifetime); +} + +Subnet6Ptr +Subnet6::create(const IOAddress& prefix, uint8_t length, + const Triplet<uint32_t>& t1, + const Triplet<uint32_t>& t2, + const Triplet<uint32_t>& preferred_lifetime, + const Triplet<uint32_t>& valid_lifetime, + const SubnetID id) { + Subnet6Ptr subnet = boost::make_shared<Subnet6> + (prefix, length, t1, t2, preferred_lifetime, valid_lifetime, id); + // IA_NA + subnet->setAllocator(Lease::TYPE_NA, + boost::make_shared<IterativeAllocator> + (Lease::TYPE_NA, subnet)); + subnet->setAllocationState(Lease::TYPE_NA, + SubnetIterativeAllocationState::create(subnet)); + // IA_TA + subnet->setAllocator(Lease::TYPE_TA, + boost::make_shared<IterativeAllocator> + (Lease::TYPE_TA, subnet)); + subnet->setAllocationState(Lease::TYPE_TA, + SubnetIterativeAllocationState::create(subnet)); + // IA_PD + subnet->setAllocator(Lease::TYPE_PD, + boost::make_shared<IterativeAllocator> + (Lease::TYPE_PD, subnet)); + subnet->setAllocationState(Lease::TYPE_PD, + SubnetIterativeAllocationState::create(subnet)); + return (subnet); +} + +void Subnet6::checkType(Lease::Type type) const { + if ((type != Lease::TYPE_NA) && (type != Lease::TYPE_TA) && (type != Lease::TYPE_PD)) { + isc_throw(BadValue, "Invalid Pool type: " << Lease::typeToText(type) + << "(" << static_cast<int>(type) + << "), must be TYPE_NA, TYPE_TA or TYPE_PD for Subnet6"); + } +} + +Subnet6Ptr +Subnet6::getNextSubnet(const Subnet6Ptr& first_subnet) const { + SharedNetwork6Ptr network; + getSharedNetwork(network); + if (network) { + return (network->getNextSubnet(first_subnet, getID())); + } + + return (Subnet6Ptr()); +} + +Subnet6Ptr +Subnet6::getNextSubnet(const Subnet6Ptr& first_subnet, + const ClientClasses& client_classes) const { + SharedNetwork6Ptr network; + getSharedNetwork(network); + // We can only get next subnet if shared network has been defined for + // the current subnet. + if (network) { + Subnet6Ptr subnet; + do { + // Use subnet identifier of this subnet if this is the first + // time we're calling getNextSubnet. Otherwise, use the + // subnet id of the previously returned subnet. + SubnetID subnet_id = subnet ? subnet->getID() : getID(); + subnet = network->getNextSubnet(first_subnet, subnet_id); + // If client classes match the subnet, return it. Otherwise, + // try another subnet. + if (subnet && subnet->clientSupported(client_classes)) { + return (subnet); + } + } while (subnet); + } + + // No subnet found. + return (Subnet6Ptr()); +} + +bool +Subnet6::clientSupported(const isc::dhcp::ClientClasses& client_classes) const { + NetworkPtr network; + getSharedNetwork(network); + if (network && !network->clientSupported(client_classes)) { + return (false); + } + + return (Network6::clientSupported(client_classes)); +} + +data::ElementPtr +Subnet::toElement() const { + ElementPtr map = Element::createMap(); + + // Add user-context + contextToElement(map); + + // Set subnet id + SubnetID id = getID(); + map->set("id", Element::create(static_cast<long long>(id))); + + // Set subnet + map->set("subnet", Element::create(toText())); + + return (map); +} + +void +Subnet4::createAllocators() { + auto allocator_type = getAllocatorType(); + if (allocator_type.empty()) { + allocator_type = getDefaultAllocatorType(); + } + if (allocator_type == "random") { + setAllocator(Lease::TYPE_V4, + boost::make_shared<RandomAllocator> + (Lease::TYPE_V4, shared_from_this())); + setAllocationState(Lease::TYPE_V4, SubnetAllocationStatePtr()); + + for (auto pool : pools_) { + pool->setAllocationState(PoolRandomAllocationState::create(pool)); + } + + } else if (allocator_type == "flq") { + setAllocator(Lease::TYPE_V4, + boost::make_shared<FreeLeaseQueueAllocator> + (Lease::TYPE_V4, shared_from_this())); + setAllocationState(Lease::TYPE_V4, SubnetAllocationStatePtr()); + + for (auto pool : pools_) { + pool->setAllocationState(PoolFreeLeaseQueueAllocationState::create(pool)); + } + + } else { + setAllocator(Lease::TYPE_V4, + boost::make_shared<IterativeAllocator> + (Lease::TYPE_V4, shared_from_this())); + setAllocationState(Lease::TYPE_V4, + SubnetIterativeAllocationState::create(shared_from_this())); + + for (auto pool : pools_) { + pool->setAllocationState(PoolIterativeAllocationState::create(pool)); + } + } +} + +data::ElementPtr +Subnet4::toElement() const { + // Prepare the map + ElementPtr map = Subnet::toElement(); + ElementPtr network_map = Network4::toElement(); + + merge(map, network_map); + + // Set DHCP4o6 + const Cfg4o6& d4o6 = get4o6(); + isc::data::merge(map, d4o6.toElement()); + + // Set pools + const auto& pools = getPools(Lease::TYPE_V4); + ElementPtr pool_list = Element::createList(); + for (const auto& pool : pools) { + // Add the formatted pool to the list + pool_list->add(pool->toElement()); + } + map->set("pools", pool_list); + + return (map); +} + +std::pair<IOAddress, uint8_t> +Subnet4::parsePrefix(const std::string& prefix) { + std::pair<IOAddress, uint8_t> parsed = Subnet::parsePrefixCommon(prefix); + if (!parsed.first.isV4() || parsed.first.isV4Zero() || + (parsed.second > 32) || (parsed.second == 0)) { + isc_throw(BadValue, "unable to parse invalid IPv4 prefix " << prefix); + } + return (parsed); +} + +void +Subnet6::createAllocators() { + auto allocator_type = getAllocatorType(); + if (allocator_type.empty()) { + allocator_type = getDefaultAllocatorType(); + } + if (allocator_type == "random") { + setAllocator(Lease::TYPE_NA, + boost::make_shared<RandomAllocator> + (Lease::TYPE_NA, shared_from_this())); + setAllocator(Lease::TYPE_TA, + boost::make_shared<RandomAllocator> + (Lease::TYPE_TA, shared_from_this())); + setAllocationState(Lease::TYPE_NA, SubnetAllocationStatePtr()); + setAllocationState(Lease::TYPE_TA, SubnetAllocationStatePtr()); + + } else if (allocator_type == "flq") { + isc_throw(BadValue, "Free Lease Queue allocator is not supported for IPv6 address pools"); + + } else { + setAllocator(Lease::TYPE_NA, + boost::make_shared<IterativeAllocator> + (Lease::TYPE_NA, shared_from_this())); + setAllocationState(Lease::TYPE_NA, SubnetIterativeAllocationState::create(shared_from_this())); + setAllocationState(Lease::TYPE_TA, SubnetIterativeAllocationState::create(shared_from_this())); + } + + auto pd_allocator_type = getPdAllocatorType(); + if (pd_allocator_type.empty()) { + pd_allocator_type = getDefaultPdAllocatorType(); + } + // Repeat the same for the delegated prefix allocator. + if (pd_allocator_type == "random") { + setAllocator(Lease::TYPE_PD, + boost::make_shared<RandomAllocator> + (Lease::TYPE_PD, shared_from_this())); + setAllocationState(Lease::TYPE_PD, SubnetAllocationStatePtr()); + + } else if (pd_allocator_type == "flq") { + setAllocator(Lease::TYPE_PD, + boost::make_shared<FreeLeaseQueueAllocator> + (Lease::TYPE_PD, shared_from_this())); + setAllocationState(Lease::TYPE_PD, SubnetAllocationStatePtr()); + + } else { + setAllocator(Lease::TYPE_PD, + boost::make_shared<IterativeAllocator> + (Lease::TYPE_PD, shared_from_this())); + setAllocationState(Lease::TYPE_PD, SubnetIterativeAllocationState::create(shared_from_this())); + } + // Create allocation states for NA pools. + for (auto pool : pools_) { + if (allocator_type == "random") { + pool->setAllocationState(PoolRandomAllocationState::create(pool)); + } else { + pool->setAllocationState(PoolIterativeAllocationState::create(pool)); + } + } + // Create allocation states for TA pools. + for (auto pool : pools_ta_) { + if (allocator_type == "random") { + pool->setAllocationState(PoolRandomAllocationState::create(pool)); + } else { + pool->setAllocationState(PoolIterativeAllocationState::create(pool)); + } + } + // Create allocation states for PD pools. + for (auto pool : pools_pd_) { + if (pd_allocator_type == "random") { + pool->setAllocationState(PoolRandomAllocationState::create(pool)); + } else if (pd_allocator_type == "flq") { + pool->setAllocationState(PoolFreeLeaseQueueAllocationState::create(pool)); + } else { + pool->setAllocationState(PoolIterativeAllocationState::create(pool)); + } + } +} + +data::ElementPtr +Subnet6::toElement() const { + // Prepare the map + ElementPtr map = Subnet::toElement(); + ElementPtr network_map = Network6::toElement(); + + merge(map, network_map); + + // Set pools + const auto& pools = getPools(Lease::TYPE_NA); + ElementPtr pool_list = Element::createList(); + for (const auto& pool : pools) { + // Add the formatted pool to the list + pool_list->add(pool->toElement()); + } + map->set("pools", pool_list); + + // Set pd-pools + const auto& pdpools = getPools(Lease::TYPE_PD); + ElementPtr pdpool_list = Element::createList(); + for (const auto& pool : pdpools) { + // Add the formatted pool to the list + pdpool_list->add(pool->toElement()); + } + map->set("pd-pools", pdpool_list); + + return (map); +} + +std::pair<IOAddress, uint8_t> +Subnet6::parsePrefix(const std::string& prefix) { + std::pair<IOAddress, uint8_t> parsed = Subnet::parsePrefixCommon(prefix); + if (!parsed.first.isV6() || parsed.first.isV6Zero() || + (parsed.second > 128) || (parsed.second == 0)) { + isc_throw(BadValue, "unable to parse invalid IPv6 prefix " << prefix); + } + return (parsed); +} + +} // namespace dhcp +} // namespace isc |