mirror of
https://gitlab.isc.org/isc-projects/kea
synced 2025-08-30 13:37:55 +00:00
[#2348] Moved allocators outside the engine
This commit is contained in:
parent
aa40c18f24
commit
c599f02fe0
@ -64,6 +64,7 @@ libkea_dhcpsrv_la_SOURCES =
|
||||
libkea_dhcpsrv_la_SOURCES += alloc_engine.cc alloc_engine.h
|
||||
libkea_dhcpsrv_la_SOURCES += alloc_engine_log.cc alloc_engine_log.h
|
||||
libkea_dhcpsrv_la_SOURCES += alloc_engine_messages.h alloc_engine_messages.cc
|
||||
libkea_dhcpsrv_la_SOURCES += allocator.h
|
||||
libkea_dhcpsrv_la_SOURCES += base_host_data_source.h
|
||||
libkea_dhcpsrv_la_SOURCES += cache_host_data_source.h
|
||||
libkea_dhcpsrv_la_SOURCES += callout_handle_store.h
|
||||
@ -114,6 +115,7 @@ libkea_dhcpsrv_la_SOURCES += hosts_log.cc hosts_log.h
|
||||
libkea_dhcpsrv_la_SOURCES += hosts_messages.h hosts_messages.cc
|
||||
libkea_dhcpsrv_la_SOURCES += ip_range.h ip_range.cc
|
||||
libkea_dhcpsrv_la_SOURCES += ip_range_permutation.h ip_range_permutation.cc
|
||||
libkea_dhcpsrv_la_SOURCES += iterative_allocator.cc iterative_allocator.h
|
||||
libkea_dhcpsrv_la_SOURCES += key_from_key.h
|
||||
libkea_dhcpsrv_la_SOURCES += lease.cc lease.h
|
||||
libkea_dhcpsrv_la_SOURCES += lease_file_loader.h
|
||||
@ -292,6 +294,7 @@ libkea_dhcpsrv_include_HEADERS = \
|
||||
alloc_engine.h \
|
||||
alloc_engine_log.h \
|
||||
alloc_engine_messages.h \
|
||||
allocator.h \
|
||||
base_host_data_source.h \
|
||||
cache_host_data_source.h \
|
||||
callout_handle_store.h \
|
||||
@ -341,6 +344,7 @@ libkea_dhcpsrv_include_HEADERS = \
|
||||
hosts_log.h \
|
||||
ip_range.h \
|
||||
ip_range_permutation.h \
|
||||
iterative_allocator.h \
|
||||
key_from_key.h \
|
||||
lease.h \
|
||||
lease_file_loader.h \
|
||||
|
@ -17,6 +17,7 @@
|
||||
#include <dhcpsrv/dhcpsrv_log.h>
|
||||
#include <dhcpsrv/host_mgr.h>
|
||||
#include <dhcpsrv/host.h>
|
||||
#include <dhcpsrv/iterative_allocator.h>
|
||||
#include <dhcpsrv/lease_mgr_factory.h>
|
||||
#include <dhcpsrv/ncr_generator.h>
|
||||
#include <dhcpsrv/network.h>
|
||||
@ -34,7 +35,6 @@
|
||||
#include <boost/make_shared.hpp>
|
||||
|
||||
#include <algorithm>
|
||||
#include <cstring>
|
||||
#include <limits>
|
||||
#include <sstream>
|
||||
#include <stdint.h>
|
||||
@ -90,230 +90,7 @@ AllocEngineHooks Hooks;
|
||||
namespace isc {
|
||||
namespace dhcp {
|
||||
|
||||
AllocEngine::IterativeAllocator::IterativeAllocator(Lease::Type lease_type)
|
||||
: Allocator(lease_type) {
|
||||
}
|
||||
|
||||
isc::asiolink::IOAddress
|
||||
AllocEngine::IterativeAllocator::increasePrefix(const isc::asiolink::IOAddress& prefix,
|
||||
const uint8_t prefix_len) {
|
||||
if (!prefix.isV6()) {
|
||||
isc_throw(BadValue, "Prefix operations are for IPv6 only (attempted to "
|
||||
"increase prefix " << prefix << ")");
|
||||
}
|
||||
|
||||
// Get a buffer holding an address.
|
||||
const std::vector<uint8_t>& vec = prefix.toBytes();
|
||||
|
||||
if (prefix_len < 1 || prefix_len > 128) {
|
||||
isc_throw(BadValue, "Cannot increase prefix: invalid prefix length: "
|
||||
<< prefix_len);
|
||||
}
|
||||
|
||||
uint8_t n_bytes = (prefix_len - 1)/8;
|
||||
uint8_t n_bits = 8 - (prefix_len - n_bytes*8);
|
||||
uint8_t mask = 1 << n_bits;
|
||||
|
||||
// Explanation: n_bytes specifies number of full bytes that are in-prefix.
|
||||
// They can also be used as an offset for the first byte that is not in
|
||||
// prefix. n_bits specifies number of bits on the last byte that is
|
||||
// (often partially) in prefix. For example for a /125 prefix, the values
|
||||
// are 15 and 3, respectively. Mask is a bitmask that has the least
|
||||
// significant bit from the prefix set.
|
||||
|
||||
uint8_t packed[V6ADDRESS_LEN];
|
||||
|
||||
// Copy the address. It must be V6, but we already checked that.
|
||||
std::memcpy(packed, &vec[0], V6ADDRESS_LEN);
|
||||
|
||||
// Can we safely increase only the last byte in prefix without overflow?
|
||||
if (packed[n_bytes] + uint16_t(mask) < 256u) {
|
||||
packed[n_bytes] += mask;
|
||||
return (IOAddress::fromBytes(AF_INET6, packed));
|
||||
}
|
||||
|
||||
// Overflow (done on uint8_t, but the sum is greater than 255)
|
||||
packed[n_bytes] += mask;
|
||||
|
||||
// Deal with the overflow. Start increasing the least significant byte
|
||||
for (int i = n_bytes - 1; i >= 0; --i) {
|
||||
++packed[i];
|
||||
// If we haven't overflowed (0xff->0x0) the next byte, then we are done
|
||||
if (packed[i] != 0) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return (IOAddress::fromBytes(AF_INET6, packed));
|
||||
}
|
||||
|
||||
isc::asiolink::IOAddress
|
||||
AllocEngine::IterativeAllocator::increaseAddress(const isc::asiolink::IOAddress& address,
|
||||
bool prefix,
|
||||
const uint8_t prefix_len) {
|
||||
if (!prefix) {
|
||||
return (IOAddress::increase(address));
|
||||
} else {
|
||||
return (increasePrefix(address, prefix_len));
|
||||
}
|
||||
}
|
||||
|
||||
isc::asiolink::IOAddress
|
||||
AllocEngine::IterativeAllocator::pickAddressInternal(const SubnetPtr& subnet,
|
||||
const ClientClasses& client_classes,
|
||||
const DuidPtr&,
|
||||
const IOAddress&) {
|
||||
// Is this prefix allocation?
|
||||
bool prefix = pool_type_ == Lease::TYPE_PD;
|
||||
uint8_t prefix_len = 0;
|
||||
|
||||
// Let's get the last allocated address. It is usually set correctly,
|
||||
// but there are times when it won't be (like after removing a pool or
|
||||
// perhaps restarting the server).
|
||||
IOAddress last = subnet->getLastAllocated(pool_type_);
|
||||
bool valid = true;
|
||||
bool retrying = false;
|
||||
|
||||
const PoolCollection& pools = subnet->getPools(pool_type_);
|
||||
|
||||
if (pools.empty()) {
|
||||
isc_throw(AllocFailed, "No pools defined in selected subnet");
|
||||
}
|
||||
|
||||
// first we need to find a pool the last address belongs to.
|
||||
PoolCollection::const_iterator it;
|
||||
PoolCollection::const_iterator first = pools.end();
|
||||
PoolPtr first_pool;
|
||||
for (it = pools.begin(); it != pools.end(); ++it) {
|
||||
if (!(*it)->clientSupported(client_classes)) {
|
||||
continue;
|
||||
}
|
||||
if (first == pools.end()) {
|
||||
first = it;
|
||||
}
|
||||
if ((*it)->inRange(last)) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Caller checked this cannot happen
|
||||
if (first == pools.end()) {
|
||||
isc_throw(AllocFailed, "No allowed pools defined in selected subnet");
|
||||
}
|
||||
|
||||
// last one was bogus for one of several reasons:
|
||||
// - we just booted up and that's the first address we're allocating
|
||||
// - a subnet was removed or other reconfiguration just completed
|
||||
// - perhaps allocation algorithm was changed
|
||||
// - last pool does not allow this client
|
||||
if (it == pools.end()) {
|
||||
it = first;
|
||||
}
|
||||
|
||||
for (;;) {
|
||||
// Trying next pool
|
||||
if (retrying) {
|
||||
for (; it != pools.end(); ++it) {
|
||||
if ((*it)->clientSupported(client_classes)) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (it == pools.end()) {
|
||||
// Really out of luck today. That was the last pool.
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
last = (*it)->getLastAllocated();
|
||||
valid = (*it)->isLastAllocatedValid();
|
||||
if (!valid && (last == (*it)->getFirstAddress())) {
|
||||
// Pool was (re)initialized
|
||||
(*it)->setLastAllocated(last);
|
||||
subnet->setLastAllocated(pool_type_, last);
|
||||
return (last);
|
||||
}
|
||||
// still can be bogus
|
||||
if (valid && !(*it)->inRange(last)) {
|
||||
valid = false;
|
||||
(*it)->resetLastAllocated();
|
||||
(*it)->setLastAllocated((*it)->getFirstAddress());
|
||||
}
|
||||
|
||||
if (valid) {
|
||||
// Ok, we have a pool that the last address belonged to, let's use it.
|
||||
if (prefix) {
|
||||
Pool6Ptr pool6 = boost::dynamic_pointer_cast<Pool6>(*it);
|
||||
|
||||
if (!pool6) {
|
||||
// Something is gravely wrong here
|
||||
isc_throw(Unexpected, "Wrong type of pool: "
|
||||
<< (*it)->toText()
|
||||
<< " is not Pool6");
|
||||
}
|
||||
// Get the prefix length
|
||||
prefix_len = pool6->getLength();
|
||||
}
|
||||
|
||||
IOAddress next = increaseAddress(last, prefix, prefix_len);
|
||||
if ((*it)->inRange(next)) {
|
||||
// the next one is in the pool as well, so we haven't hit
|
||||
// pool boundary yet
|
||||
(*it)->setLastAllocated(next);
|
||||
subnet->setLastAllocated(pool_type_, next);
|
||||
return (next);
|
||||
}
|
||||
|
||||
valid = false;
|
||||
(*it)->resetLastAllocated();
|
||||
}
|
||||
// We hit pool boundary, let's try to jump to the next pool and try again
|
||||
++it;
|
||||
retrying = true;
|
||||
}
|
||||
|
||||
// Let's rewind to the beginning.
|
||||
for (it = first; it != pools.end(); ++it) {
|
||||
if ((*it)->clientSupported(client_classes)) {
|
||||
(*it)->setLastAllocated((*it)->getFirstAddress());
|
||||
(*it)->resetLastAllocated();
|
||||
}
|
||||
}
|
||||
|
||||
// ok to access first element directly. We checked that pools is non-empty
|
||||
last = (*first)->getLastAllocated();
|
||||
(*first)->setLastAllocated(last);
|
||||
subnet->setLastAllocated(pool_type_, last);
|
||||
return (last);
|
||||
}
|
||||
|
||||
AllocEngine::HashedAllocator::HashedAllocator(Lease::Type lease_type)
|
||||
: Allocator(lease_type) {
|
||||
isc_throw(NotImplemented, "Hashed allocator is not implemented");
|
||||
}
|
||||
|
||||
isc::asiolink::IOAddress
|
||||
AllocEngine::HashedAllocator::pickAddressInternal(const SubnetPtr&,
|
||||
const ClientClasses&,
|
||||
const DuidPtr&,
|
||||
const IOAddress&) {
|
||||
isc_throw(NotImplemented, "Hashed allocator is not implemented");
|
||||
}
|
||||
|
||||
AllocEngine::RandomAllocator::RandomAllocator(Lease::Type lease_type)
|
||||
: Allocator(lease_type) {
|
||||
isc_throw(NotImplemented, "Random allocator is not implemented");
|
||||
}
|
||||
|
||||
isc::asiolink::IOAddress
|
||||
AllocEngine::RandomAllocator::pickAddressInternal(const SubnetPtr&,
|
||||
const ClientClasses&,
|
||||
const DuidPtr&,
|
||||
const IOAddress&) {
|
||||
isc_throw(NotImplemented, "Random allocator is not implemented");
|
||||
}
|
||||
|
||||
AllocEngine::AllocEngine(AllocType engine_type, uint64_t attempts,
|
||||
bool ipv6)
|
||||
AllocEngine::AllocEngine(AllocType, uint64_t attempts, bool ipv6)
|
||||
: attempts_(attempts), incomplete_v4_reclamations_(0),
|
||||
incomplete_v6_reclamations_(0) {
|
||||
|
||||
@ -321,39 +98,13 @@ AllocEngine::AllocEngine(AllocType engine_type, uint64_t attempts,
|
||||
Lease::Type basic_type = ipv6 ? Lease::TYPE_NA : Lease::TYPE_V4;
|
||||
|
||||
// Initialize normal address allocators
|
||||
switch (engine_type) {
|
||||
case ALLOC_ITERATIVE:
|
||||
allocators_[basic_type] = AllocatorPtr(new IterativeAllocator(basic_type));
|
||||
break;
|
||||
case ALLOC_HASHED:
|
||||
allocators_[basic_type] = AllocatorPtr(new HashedAllocator(basic_type));
|
||||
break;
|
||||
case ALLOC_RANDOM:
|
||||
allocators_[basic_type] = AllocatorPtr(new RandomAllocator(basic_type));
|
||||
break;
|
||||
default:
|
||||
isc_throw(BadValue, "Invalid/unsupported allocation algorithm");
|
||||
}
|
||||
allocators_[basic_type] = AllocatorPtr(new IterativeAllocator(basic_type));
|
||||
|
||||
// If this is IPv6 allocation engine, initialize also temporary addrs
|
||||
// and prefixes
|
||||
if (ipv6) {
|
||||
switch (engine_type) {
|
||||
case ALLOC_ITERATIVE:
|
||||
allocators_[Lease::TYPE_TA] = AllocatorPtr(new IterativeAllocator(Lease::TYPE_TA));
|
||||
allocators_[Lease::TYPE_PD] = AllocatorPtr(new IterativeAllocator(Lease::TYPE_PD));
|
||||
break;
|
||||
case ALLOC_HASHED:
|
||||
allocators_[Lease::TYPE_TA] = AllocatorPtr(new HashedAllocator(Lease::TYPE_TA));
|
||||
allocators_[Lease::TYPE_PD] = AllocatorPtr(new HashedAllocator(Lease::TYPE_PD));
|
||||
break;
|
||||
case ALLOC_RANDOM:
|
||||
allocators_[Lease::TYPE_TA] = AllocatorPtr(new RandomAllocator(Lease::TYPE_TA));
|
||||
allocators_[Lease::TYPE_PD] = AllocatorPtr(new RandomAllocator(Lease::TYPE_PD));
|
||||
break;
|
||||
default:
|
||||
isc_throw(BadValue, "Invalid/unsupported allocation algorithm");
|
||||
}
|
||||
allocators_[Lease::TYPE_TA] = AllocatorPtr(new IterativeAllocator(Lease::TYPE_TA));
|
||||
allocators_[Lease::TYPE_PD] = AllocatorPtr(new IterativeAllocator(Lease::TYPE_PD));
|
||||
}
|
||||
|
||||
// Register hook points
|
||||
@ -361,7 +112,8 @@ AllocEngine::AllocEngine(AllocType engine_type, uint64_t attempts,
|
||||
hook_index_lease6_select_ = Hooks.hook_index_lease6_select_;
|
||||
}
|
||||
|
||||
AllocEngine::AllocatorPtr AllocEngine::getAllocator(Lease::Type type) {
|
||||
AllocatorPtr
|
||||
AllocEngine::getAllocator(Lease::Type type) {
|
||||
std::map<Lease::Type, AllocatorPtr>::const_iterator alloc = allocators_.find(type);
|
||||
|
||||
if (alloc == allocators_.end()) {
|
||||
|
@ -16,6 +16,7 @@
|
||||
#include <dhcp/option6_ia.h>
|
||||
#include <dhcp/option6_iaaddr.h>
|
||||
#include <dhcp/option6_iaprefix.h>
|
||||
#include <dhcpsrv/allocator.h>
|
||||
#include <dhcpsrv/d2_client_cfg.h>
|
||||
#include <dhcpsrv/host.h>
|
||||
#include <dhcpsrv/subnet.h>
|
||||
@ -38,20 +39,6 @@
|
||||
namespace isc {
|
||||
namespace dhcp {
|
||||
|
||||
/// An exception that is thrown when allocation module fails (e.g. due to
|
||||
/// lack of available addresses)
|
||||
class AllocFailed : public isc::Exception {
|
||||
public:
|
||||
|
||||
/// @brief Constructor
|
||||
///
|
||||
/// @param file name of the file, where exception occurred
|
||||
/// @param line line of the file, where exception occurred
|
||||
/// @param what text description of the issue that caused exception
|
||||
AllocFailed(const char* file, size_t line, const char* what)
|
||||
: isc::Exception(file, line, what) {}
|
||||
};
|
||||
|
||||
/// @brief DHCPv4 and DHCPv6 allocation engine
|
||||
///
|
||||
/// This class represents a DHCP allocation engine. It is responsible
|
||||
@ -61,207 +48,6 @@ public:
|
||||
/// @todo: Does not handle out of leases well
|
||||
/// @todo: Does not handle out of allocation attempts well
|
||||
class AllocEngine : public boost::noncopyable {
|
||||
protected:
|
||||
|
||||
/// @brief Base class for all address/prefix allocation algorithms
|
||||
///
|
||||
/// This is an abstract class that should not be used directly, but rather
|
||||
/// specialized implementations should be used instead.
|
||||
class Allocator {
|
||||
public:
|
||||
|
||||
/// @brief Picks one address out of available pools in a given subnet
|
||||
///
|
||||
/// This method returns one address from the available pools in the
|
||||
/// specified subnet. It should not check if the address is used or
|
||||
/// reserved - AllocEngine will check that and will call pickAddress
|
||||
/// again if necessary. The number of times this method is called will
|
||||
/// increase as the number of available leases will decrease.
|
||||
///
|
||||
/// This method can also be used to pick a prefix. We should not rename
|
||||
/// it to pickLease(), because at this early stage there is no concept
|
||||
/// of a lease yet. Here it is a matter of selecting one address or
|
||||
/// prefix from the defined pool, without going into details who it is
|
||||
/// for or who uses it. I thought that pickAddress() is less confusing
|
||||
/// than pickResource(), because nobody would immediately know what the
|
||||
/// resource means in this context.
|
||||
///
|
||||
/// Pools which are not allowed for client classes are skipped.
|
||||
///
|
||||
/// @param subnet next address will be returned from pool of that subnet
|
||||
/// @param client_classes list of classes client belongs to
|
||||
/// @param duid Client's DUID
|
||||
/// @param hint Client's hint
|
||||
///
|
||||
/// @return the next address
|
||||
virtual isc::asiolink::IOAddress
|
||||
pickAddress(const SubnetPtr& subnet,
|
||||
const ClientClasses& client_classes,
|
||||
const DuidPtr& duid,
|
||||
const isc::asiolink::IOAddress& hint) {
|
||||
if (isc::util::MultiThreadingMgr::instance().getMode()) {
|
||||
std::lock_guard<std::mutex> lock(mutex_);
|
||||
return pickAddressInternal(subnet, client_classes, duid, hint);
|
||||
} else {
|
||||
return pickAddressInternal(subnet, client_classes, duid, hint);
|
||||
}
|
||||
}
|
||||
|
||||
/// @brief Default constructor
|
||||
///
|
||||
/// Specifies which type of leases this allocator will assign
|
||||
/// @param pool_type specifies pool type (addresses, temp. addr or prefixes)
|
||||
Allocator(Lease::Type pool_type) : pool_type_(pool_type) {
|
||||
}
|
||||
|
||||
/// @brief Virtual destructor
|
||||
virtual ~Allocator() {
|
||||
}
|
||||
|
||||
private:
|
||||
virtual isc::asiolink::IOAddress
|
||||
pickAddressInternal(const SubnetPtr& subnet,
|
||||
const ClientClasses& client_classes,
|
||||
const DuidPtr& duid,
|
||||
const isc::asiolink::IOAddress& hint) = 0;
|
||||
|
||||
protected:
|
||||
|
||||
/// @brief Defines pool type allocation
|
||||
Lease::Type pool_type_;
|
||||
|
||||
private:
|
||||
|
||||
/// @brief The mutex to protect the allocated lease
|
||||
std::mutex mutex_;
|
||||
};
|
||||
|
||||
/// defines a pointer to allocator
|
||||
typedef boost::shared_ptr<Allocator> AllocatorPtr;
|
||||
|
||||
/// @brief Address/prefix allocator that iterates over all addresses
|
||||
///
|
||||
/// This class implements an iterative algorithm that returns all addresses in
|
||||
/// a pool iteratively, one after another. Once the last address is reached,
|
||||
/// it starts allocating from the beginning of the first pool (i.e. it loops
|
||||
/// over).
|
||||
class IterativeAllocator : public Allocator {
|
||||
public:
|
||||
|
||||
/// @brief Default constructor
|
||||
///
|
||||
/// Does not do anything
|
||||
/// @param type - specifies allocation type
|
||||
IterativeAllocator(Lease::Type type);
|
||||
|
||||
private:
|
||||
|
||||
/// @brief Returns the next address from pools in a subnet
|
||||
///
|
||||
/// @param subnet next address will be returned from pool of that subnet
|
||||
/// @param client_classes list of classes client belongs to
|
||||
/// @param duid Client's DUID (ignored)
|
||||
/// @param hint Client's hint (ignored)
|
||||
///
|
||||
/// @return the next address
|
||||
virtual isc::asiolink::IOAddress
|
||||
pickAddressInternal(const SubnetPtr& subnet,
|
||||
const ClientClasses& client_classes,
|
||||
const DuidPtr& duid,
|
||||
const isc::asiolink::IOAddress& hint);
|
||||
|
||||
protected:
|
||||
|
||||
/// @brief Returns the next prefix
|
||||
///
|
||||
/// This method works for IPv6 addresses only. It increases the
|
||||
/// specified prefix by a given prefix_len. For example, 2001:db8::
|
||||
/// increased by prefix length /32 will become 2001:db9::. This method
|
||||
/// is used to iterate over IPv6 prefix pools
|
||||
///
|
||||
/// @param prefix prefix to be increased
|
||||
/// @param prefix_len length of the prefix to be increased
|
||||
///
|
||||
/// @return result prefix
|
||||
static isc::asiolink::IOAddress
|
||||
increasePrefix(const isc::asiolink::IOAddress& prefix,
|
||||
const uint8_t prefix_len);
|
||||
|
||||
/// @brief Returns the next address or prefix
|
||||
///
|
||||
/// This method works for IPv4 addresses, IPv6 addresses and
|
||||
/// IPv6 prefixes.
|
||||
///
|
||||
/// @param address address or prefix to be increased
|
||||
/// @param prefix true when the previous argument is a prefix
|
||||
/// @param prefix_len length of the prefix
|
||||
///
|
||||
/// @return result address or prefix
|
||||
static isc::asiolink::IOAddress
|
||||
increaseAddress(const isc::asiolink::IOAddress& address,
|
||||
bool prefix, const uint8_t prefix_len);
|
||||
};
|
||||
|
||||
/// @brief Address/prefix allocator that gets an address based on a hash
|
||||
///
|
||||
/// @todo: This is a skeleton class for now and is missing an implementation.
|
||||
class HashedAllocator : public Allocator {
|
||||
public:
|
||||
|
||||
/// @brief Default constructor (does nothing)
|
||||
///
|
||||
/// @param type - specifies allocation type
|
||||
HashedAllocator(Lease::Type type);
|
||||
|
||||
private:
|
||||
|
||||
/// @brief Returns an address based on hash calculated from client's DUID.
|
||||
///
|
||||
/// @todo: Implement this method
|
||||
///
|
||||
/// @param subnet an address will be picked from pool of that subnet
|
||||
/// @param client_classes list of classes client belongs to
|
||||
/// @param duid Client's DUID
|
||||
/// @param hint a hint (last address that was picked)
|
||||
///
|
||||
/// @return selected address
|
||||
virtual isc::asiolink::IOAddress
|
||||
pickAddressInternal(const SubnetPtr& subnet,
|
||||
const ClientClasses& client_classes,
|
||||
const DuidPtr& duid,
|
||||
const isc::asiolink::IOAddress& hint);
|
||||
};
|
||||
|
||||
/// @brief Random allocator that picks address randomly
|
||||
///
|
||||
/// @todo: This is a skeleton class for now and is missing an implementation.
|
||||
class RandomAllocator : public Allocator {
|
||||
public:
|
||||
|
||||
/// @brief Default constructor (does nothing)
|
||||
///
|
||||
/// @param type - specifies allocation type
|
||||
RandomAllocator(Lease::Type type);
|
||||
|
||||
private:
|
||||
|
||||
/// @brief Returns a random address from pool of specified subnet
|
||||
///
|
||||
/// @todo: Implement this method
|
||||
///
|
||||
/// @param subnet an address will be picked from pool of that subnet
|
||||
/// @param client_classes list of classes client belongs to
|
||||
/// @param duid Client's DUID (ignored)
|
||||
/// @param hint the last address that was picked (ignored)
|
||||
///
|
||||
/// @return a random address from the pool
|
||||
virtual isc::asiolink::IOAddress
|
||||
pickAddressInternal(const SubnetPtr& subnet,
|
||||
const ClientClasses& client_classes,
|
||||
const DuidPtr& duid,
|
||||
const isc::asiolink::IOAddress& hint);
|
||||
};
|
||||
|
||||
public:
|
||||
|
||||
/// @brief Specifies allocation type
|
||||
|
115
src/lib/dhcpsrv/allocator.h
Normal file
115
src/lib/dhcpsrv/allocator.h
Normal file
@ -0,0 +1,115 @@
|
||||
// Copyright (C) 2022 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 ALLOCATOR_H
|
||||
#define ALLOCATOR_H
|
||||
|
||||
#include <asiolink/io_address.h>
|
||||
#include <dhcp/classify.h>
|
||||
#include <dhcp/duid.h>
|
||||
#include <dhcpsrv/subnet.h>
|
||||
#include <exceptions/exceptions.h>
|
||||
#include <util/multi_threading_mgr.h>
|
||||
#include <boost/shared_ptr.hpp>
|
||||
#include <mutex>
|
||||
|
||||
namespace isc {
|
||||
namespace dhcp {
|
||||
|
||||
/// An exception that is thrown when allocation module fails (e.g. due to
|
||||
/// lack of available addresses)
|
||||
class AllocFailed : public Exception {
|
||||
public:
|
||||
|
||||
/// @brief Constructor
|
||||
///
|
||||
/// @param file name of the file, where exception occurred
|
||||
/// @param line line of the file, where exception occurred
|
||||
/// @param what text description of the issue that caused exception
|
||||
AllocFailed(const char* file, size_t line, const char* what)
|
||||
: Exception(file, line, what) {}
|
||||
};
|
||||
|
||||
/// @brief Base class for all address/prefix allocation algorithms.
|
||||
///
|
||||
/// This is an abstract class that should not be used directly, but rather
|
||||
/// specialized implementations should be used instead.
|
||||
class Allocator {
|
||||
public:
|
||||
|
||||
/// @brief Picks a address or a delegated prefix
|
||||
///
|
||||
/// This method returns one address from the available pools in the
|
||||
/// specified subnet. It should not check if the address is used or
|
||||
/// reserved - AllocEngine will check that and will call pickAddress
|
||||
/// again if necessary. The number of times this method is called will
|
||||
/// increase as the number of available leases will decrease.
|
||||
///
|
||||
/// This method can also be used to pick a prefix. We should not rename
|
||||
/// it to pickLease(), because at this early stage there is no concept
|
||||
/// of a lease yet. Here it is a matter of selecting one address or
|
||||
/// prefix from the defined pool, without going into details who it is
|
||||
/// for or who uses it. I thought that pickAddress() is less confusing
|
||||
/// than pickResource(), because nobody would immediately know what the
|
||||
/// resource means in this context.
|
||||
///
|
||||
/// Pools which are not allowed for client classes are skipped.
|
||||
///
|
||||
/// @param subnet next address will be returned from pool of that subnet
|
||||
/// @param client_classes list of classes client belongs to
|
||||
/// @param duid Client's DUID
|
||||
/// @param hint Client's hint
|
||||
///
|
||||
/// @return the next address.
|
||||
virtual isc::asiolink::IOAddress
|
||||
pickAddress(const SubnetPtr& subnet,
|
||||
const ClientClasses& client_classes,
|
||||
const DuidPtr& duid,
|
||||
const asiolink::IOAddress& hint) {
|
||||
if (util::MultiThreadingMgr::instance().getMode()) {
|
||||
std::lock_guard<std::mutex> lock(mutex_);
|
||||
return pickAddressInternal(subnet, client_classes, duid, hint);
|
||||
} else {
|
||||
return pickAddressInternal(subnet, client_classes, duid, hint);
|
||||
}
|
||||
}
|
||||
|
||||
/// @brief Default constructor
|
||||
///
|
||||
/// Specifies which type of leases this allocator will assign
|
||||
/// @param pool_type specifies pool type (addresses, temp. addr or prefixes)
|
||||
Allocator(Lease::Type pool_type) : pool_type_(pool_type) {
|
||||
}
|
||||
|
||||
/// @brief Virtual destructor
|
||||
virtual ~Allocator() {
|
||||
}
|
||||
|
||||
private:
|
||||
virtual isc::asiolink::IOAddress
|
||||
pickAddressInternal(const SubnetPtr& subnet,
|
||||
const ClientClasses& client_classes,
|
||||
const DuidPtr& duid,
|
||||
const isc::asiolink::IOAddress& hint) = 0;
|
||||
|
||||
protected:
|
||||
|
||||
/// @brief Defines pool type allocation
|
||||
Lease::Type pool_type_;
|
||||
|
||||
private:
|
||||
|
||||
/// @brief The mutex to protect the allocated lease
|
||||
std::mutex mutex_;
|
||||
};
|
||||
|
||||
/// defines a pointer to allocator
|
||||
typedef boost::shared_ptr<Allocator> AllocatorPtr;
|
||||
|
||||
} // end of namespace isc::dhcp
|
||||
} // end of namespace isc
|
||||
|
||||
#endif // ALLOCATOR_H
|
216
src/lib/dhcpsrv/iterative_allocator.cc
Normal file
216
src/lib/dhcpsrv/iterative_allocator.cc
Normal file
@ -0,0 +1,216 @@
|
||||
// Copyright (C) 2022 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 <dhcpsrv/iterative_allocator.h>
|
||||
#include <exceptions/exceptions.h>
|
||||
#include <cstring>
|
||||
|
||||
using namespace isc::asiolink;
|
||||
using namespace std;
|
||||
|
||||
namespace isc {
|
||||
namespace dhcp {
|
||||
|
||||
IterativeAllocator::IterativeAllocator(Lease::Type lease_type)
|
||||
: Allocator(lease_type) {
|
||||
}
|
||||
|
||||
isc::asiolink::IOAddress
|
||||
IterativeAllocator::increasePrefix(const IOAddress& prefix,
|
||||
const uint8_t prefix_len) {
|
||||
if (!prefix.isV6()) {
|
||||
isc_throw(BadValue, "Prefix operations are for IPv6 only (attempted to "
|
||||
"increase prefix " << prefix << ")");
|
||||
}
|
||||
|
||||
// Get a buffer holding an address.
|
||||
const std::vector<uint8_t>& vec = prefix.toBytes();
|
||||
|
||||
if (prefix_len < 1 || prefix_len > 128) {
|
||||
isc_throw(BadValue, "Cannot increase prefix: invalid prefix length: "
|
||||
<< prefix_len);
|
||||
}
|
||||
|
||||
uint8_t n_bytes = (prefix_len - 1)/8;
|
||||
uint8_t n_bits = 8 - (prefix_len - n_bytes*8);
|
||||
uint8_t mask = 1 << n_bits;
|
||||
|
||||
// Explanation: n_bytes specifies number of full bytes that are in-prefix.
|
||||
// They can also be used as an offset for the first byte that is not in
|
||||
// prefix. n_bits specifies number of bits on the last byte that is
|
||||
// (often partially) in prefix. For example for a /125 prefix, the values
|
||||
// are 15 and 3, respectively. Mask is a bitmask that has the least
|
||||
// significant bit from the prefix set.
|
||||
|
||||
uint8_t packed[V6ADDRESS_LEN];
|
||||
|
||||
// Copy the address. It must be V6, but we already checked that.
|
||||
memcpy(packed, &vec[0], V6ADDRESS_LEN);
|
||||
|
||||
// Can we safely increase only the last byte in prefix without overflow?
|
||||
if (packed[n_bytes] + uint16_t(mask) < 256u) {
|
||||
packed[n_bytes] += mask;
|
||||
return (IOAddress::fromBytes(AF_INET6, packed));
|
||||
}
|
||||
|
||||
// Overflow (done on uint8_t, but the sum is greater than 255)
|
||||
packed[n_bytes] += mask;
|
||||
|
||||
// Deal with the overflow. Start increasing the least significant byte
|
||||
for (int i = n_bytes - 1; i >= 0; --i) {
|
||||
++packed[i];
|
||||
// If we haven't overflowed (0xff->0x0) the next byte, then we are done
|
||||
if (packed[i] != 0) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return (IOAddress::fromBytes(AF_INET6, packed));
|
||||
}
|
||||
|
||||
IOAddress
|
||||
IterativeAllocator::increaseAddress(const IOAddress& address,
|
||||
bool prefix,
|
||||
const uint8_t prefix_len) {
|
||||
if (!prefix) {
|
||||
return (IOAddress::increase(address));
|
||||
} else {
|
||||
return (increasePrefix(address, prefix_len));
|
||||
}
|
||||
}
|
||||
|
||||
IOAddress
|
||||
IterativeAllocator::pickAddressInternal(const SubnetPtr& subnet,
|
||||
const ClientClasses& client_classes,
|
||||
const DuidPtr&,
|
||||
const IOAddress&) {
|
||||
// Is this prefix allocation?
|
||||
bool prefix = pool_type_ == Lease::TYPE_PD;
|
||||
uint8_t prefix_len = 0;
|
||||
|
||||
// Let's get the last allocated address. It is usually set correctly,
|
||||
// but there are times when it won't be (like after removing a pool or
|
||||
// perhaps restarting the server).
|
||||
IOAddress last = subnet->getLastAllocated(pool_type_);
|
||||
bool valid = true;
|
||||
bool retrying = false;
|
||||
|
||||
const PoolCollection& pools = subnet->getPools(pool_type_);
|
||||
|
||||
if (pools.empty()) {
|
||||
isc_throw(AllocFailed, "No pools defined in selected subnet");
|
||||
}
|
||||
|
||||
// first we need to find a pool the last address belongs to.
|
||||
PoolCollection::const_iterator it;
|
||||
PoolCollection::const_iterator first = pools.end();
|
||||
PoolPtr first_pool;
|
||||
for (it = pools.begin(); it != pools.end(); ++it) {
|
||||
if (!(*it)->clientSupported(client_classes)) {
|
||||
continue;
|
||||
}
|
||||
if (first == pools.end()) {
|
||||
first = it;
|
||||
}
|
||||
if ((*it)->inRange(last)) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Caller checked this cannot happen
|
||||
if (first == pools.end()) {
|
||||
isc_throw(AllocFailed, "No allowed pools defined in selected subnet");
|
||||
}
|
||||
|
||||
// last one was bogus for one of several reasons:
|
||||
// - we just booted up and that's the first address we're allocating
|
||||
// - a subnet was removed or other reconfiguration just completed
|
||||
// - perhaps allocation algorithm was changed
|
||||
// - last pool does not allow this client
|
||||
if (it == pools.end()) {
|
||||
it = first;
|
||||
}
|
||||
|
||||
for (;;) {
|
||||
// Trying next pool
|
||||
if (retrying) {
|
||||
for (; it != pools.end(); ++it) {
|
||||
if ((*it)->clientSupported(client_classes)) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (it == pools.end()) {
|
||||
// Really out of luck today. That was the last pool.
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
last = (*it)->getLastAllocated();
|
||||
valid = (*it)->isLastAllocatedValid();
|
||||
if (!valid && (last == (*it)->getFirstAddress())) {
|
||||
// Pool was (re)initialized
|
||||
(*it)->setLastAllocated(last);
|
||||
subnet->setLastAllocated(pool_type_, last);
|
||||
return (last);
|
||||
}
|
||||
// still can be bogus
|
||||
if (valid && !(*it)->inRange(last)) {
|
||||
valid = false;
|
||||
(*it)->resetLastAllocated();
|
||||
(*it)->setLastAllocated((*it)->getFirstAddress());
|
||||
}
|
||||
|
||||
if (valid) {
|
||||
// Ok, we have a pool that the last address belonged to, let's use it.
|
||||
if (prefix) {
|
||||
Pool6Ptr pool6 = boost::dynamic_pointer_cast<Pool6>(*it);
|
||||
|
||||
if (!pool6) {
|
||||
// Something is gravely wrong here
|
||||
isc_throw(Unexpected, "Wrong type of pool: "
|
||||
<< (*it)->toText()
|
||||
<< " is not Pool6");
|
||||
}
|
||||
// Get the prefix length
|
||||
prefix_len = pool6->getLength();
|
||||
}
|
||||
|
||||
IOAddress next = increaseAddress(last, prefix, prefix_len);
|
||||
if ((*it)->inRange(next)) {
|
||||
// the next one is in the pool as well, so we haven't hit
|
||||
// pool boundary yet
|
||||
(*it)->setLastAllocated(next);
|
||||
subnet->setLastAllocated(pool_type_, next);
|
||||
return (next);
|
||||
}
|
||||
|
||||
valid = false;
|
||||
(*it)->resetLastAllocated();
|
||||
}
|
||||
// We hit pool boundary, let's try to jump to the next pool and try again
|
||||
++it;
|
||||
retrying = true;
|
||||
}
|
||||
|
||||
// Let's rewind to the beginning.
|
||||
for (it = first; it != pools.end(); ++it) {
|
||||
if ((*it)->clientSupported(client_classes)) {
|
||||
(*it)->setLastAllocated((*it)->getFirstAddress());
|
||||
(*it)->resetLastAllocated();
|
||||
}
|
||||
}
|
||||
|
||||
// ok to access first element directly. We checked that pools is non-empty
|
||||
last = (*first)->getLastAllocated();
|
||||
(*first)->setLastAllocated(last);
|
||||
subnet->setLastAllocated(pool_type_, last);
|
||||
return (last);
|
||||
}
|
||||
|
||||
} // end of namespace isc::dhcp
|
||||
} // end of namespace isc
|
79
src/lib/dhcpsrv/iterative_allocator.h
Normal file
79
src/lib/dhcpsrv/iterative_allocator.h
Normal file
@ -0,0 +1,79 @@
|
||||
// Copyright (C) 2022 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 ITERATIVE_ALLOCATOR_H
|
||||
#define ITERATIVE_ALLOCATOR_H
|
||||
|
||||
#include <dhcpsrv/allocator.h>
|
||||
#include <dhcpsrv/lease.h>
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
namespace isc {
|
||||
namespace dhcp {
|
||||
|
||||
/// @brief Address/prefix allocator that iterates over all addresses
|
||||
///
|
||||
/// This class implements an iterative algorithm that returns all addresses in
|
||||
/// a pool iteratively, one after another. Once the last address is reached,
|
||||
/// it starts allocating from the beginning of the first pool (i.e. it loops
|
||||
/// over).
|
||||
class IterativeAllocator : public Allocator {
|
||||
public:
|
||||
/// @brief Default constructor
|
||||
///
|
||||
/// Does not do anything
|
||||
/// @param type - specifies allocation type
|
||||
IterativeAllocator(Lease::Type type);
|
||||
|
||||
private:
|
||||
/// @brief Returns the next address from pools in a subnet
|
||||
///
|
||||
/// @param subnet next address will be returned from pool of that subnet
|
||||
/// @param client_classes list of classes client belongs to
|
||||
/// @param duid Client's DUID (ignored)
|
||||
/// @param hint Client's hint (ignored)
|
||||
///
|
||||
/// @return the next address
|
||||
virtual asiolink::IOAddress pickAddressInternal(const SubnetPtr& subnet,
|
||||
const ClientClasses& client_classes,
|
||||
const DuidPtr& duid,
|
||||
const asiolink::IOAddress& hint);
|
||||
|
||||
protected:
|
||||
/// @brief Returns the next prefix
|
||||
///
|
||||
/// This method works for IPv6 addresses only. It increases the
|
||||
/// specified prefix by a given prefix_len. For example, 2001:db8::
|
||||
/// increased by prefix length /32 will become 2001:db9::. This method
|
||||
/// is used to iterate over IPv6 prefix pools
|
||||
///
|
||||
/// @param prefix prefix to be increased
|
||||
/// @param prefix_len length of the prefix to be increased
|
||||
///
|
||||
/// @return result prefix
|
||||
static asiolink::IOAddress increasePrefix(const asiolink::IOAddress& prefix,
|
||||
const uint8_t prefix_len);
|
||||
|
||||
/// @brief Returns the next address or prefix
|
||||
///
|
||||
/// This method works for IPv4 addresses, IPv6 addresses and
|
||||
/// IPv6 prefixes.
|
||||
///
|
||||
/// @param address address or prefix to be increased
|
||||
/// @param prefix true when the previous argument is a prefix
|
||||
/// @param prefix_len length of the prefix
|
||||
///
|
||||
/// @return result address or prefix
|
||||
static asiolink::IOAddress increaseAddress(const asiolink::IOAddress& address,
|
||||
bool prefix,
|
||||
const uint8_t prefix_len);
|
||||
};
|
||||
|
||||
} // namespace dhcp
|
||||
} // end of namespace isc
|
||||
|
||||
#endif // ITERATIVE_ALLOCATOR_H
|
@ -99,6 +99,7 @@ libdhcpsrv_unittests_SOURCES += host_reservations_list_parser_unittest.cc
|
||||
libdhcpsrv_unittests_SOURCES += ifaces_config_parser_unittest.cc
|
||||
libdhcpsrv_unittests_SOURCES += ip_range_unittest.cc
|
||||
libdhcpsrv_unittests_SOURCES += ip_range_permutation_unittest.cc
|
||||
libdhcpsrv_unittests_SOURCES += iterative_allocator_unittest.cc
|
||||
libdhcpsrv_unittests_SOURCES += lease_file_loader_unittest.cc
|
||||
libdhcpsrv_unittests_SOURCES += lease_unittest.cc
|
||||
libdhcpsrv_unittests_SOURCES += lease_mgr_factory_unittest.cc
|
||||
|
@ -44,12 +44,6 @@ namespace test {
|
||||
TEST_F(AllocEngine4Test, constructor) {
|
||||
boost::scoped_ptr<AllocEngine> x;
|
||||
|
||||
// Hashed and random allocators are not supported yet
|
||||
ASSERT_THROW(x.reset(new AllocEngine(AllocEngine::ALLOC_HASHED, 5, false)),
|
||||
NotImplemented);
|
||||
ASSERT_THROW(x.reset(new AllocEngine(AllocEngine::ALLOC_RANDOM, 5, false)),
|
||||
NotImplemented);
|
||||
|
||||
// Create V4 (ipv6=false) Allocation Engine that will try at most
|
||||
// 100 attempts to pick up a lease
|
||||
ASSERT_NO_THROW(x.reset(new AllocEngine(AllocEngine::ALLOC_ITERATIVE, 100,
|
||||
@ -851,98 +845,6 @@ TEST_F(AllocEngine4Test, bootpRenew4) {
|
||||
EXPECT_EQ(infinity_lft, lease2->valid_lft_);
|
||||
}
|
||||
|
||||
// This test verifies that the allocator picks addresses that belong to the
|
||||
// pool
|
||||
TEST_F(AllocEngine4Test, IterativeAllocator) {
|
||||
boost::scoped_ptr<NakedAllocEngine::Allocator>
|
||||
alloc(new NakedAllocEngine::IterativeAllocator(Lease::TYPE_V4));
|
||||
|
||||
for (int i = 0; i < 1000; ++i) {
|
||||
IOAddress candidate = alloc->pickAddress(subnet_, cc_, clientid_,
|
||||
IOAddress("0.0.0.0"));
|
||||
EXPECT_TRUE(subnet_->inPool(Lease::TYPE_V4, candidate));
|
||||
}
|
||||
}
|
||||
|
||||
// This test verifies that the allocator picks addresses that belong to the
|
||||
// pool using classification
|
||||
TEST_F(AllocEngine4Test, IterativeAllocator_class) {
|
||||
boost::scoped_ptr<NakedAllocEngine::Allocator>
|
||||
alloc(new NakedAllocEngine::IterativeAllocator(Lease::TYPE_V4));
|
||||
|
||||
// Restrict pool_ to the foo class. Add a second pool with bar class.
|
||||
pool_->allowClientClass("foo");
|
||||
Pool4Ptr pool(new Pool4(IOAddress("192.0.2.200"),
|
||||
IOAddress("192.0.2.209")));
|
||||
pool->allowClientClass("bar");
|
||||
subnet_->addPool(pool);
|
||||
|
||||
// Clients are in bar
|
||||
cc_.insert("bar");
|
||||
|
||||
for (int i = 0; i < 1000; ++i) {
|
||||
IOAddress candidate = alloc->pickAddress(subnet_, cc_, clientid_,
|
||||
IOAddress("0.0.0.0"));
|
||||
EXPECT_TRUE(subnet_->inPool(Lease::TYPE_V4, candidate));
|
||||
EXPECT_TRUE(subnet_->inPool(Lease::TYPE_V4, candidate, cc_));
|
||||
}
|
||||
}
|
||||
|
||||
// This test verifies that the iterative allocator really walks over all addresses
|
||||
// in all pools in specified subnet. It also must not pick the same address twice
|
||||
// unless it runs out of pool space and must start over.
|
||||
TEST_F(AllocEngine4Test, IterativeAllocator_manyPools4) {
|
||||
NakedAllocEngine::IterativeAllocator alloc(Lease::TYPE_V4);
|
||||
|
||||
// Let's start from 2, as there is 2001:db8:1::10 - 2001:db8:1::20 pool already.
|
||||
for (int i = 2; i < 10; ++i) {
|
||||
stringstream min, max;
|
||||
|
||||
min << "192.0.2." << i * 10 + 1;
|
||||
max << "192.0.2." << i * 10 + 9;
|
||||
|
||||
Pool4Ptr pool(new Pool4(IOAddress(min.str()),
|
||||
IOAddress(max.str())));
|
||||
// cout << "Adding pool: " << min.str() << "-" << max.str() << endl;
|
||||
subnet_->addPool(pool);
|
||||
}
|
||||
|
||||
int total = 10 + 8 * 9; // first pool (.100 - .109) has 10 addresses in it,
|
||||
// there are 8 extra pools with 9 addresses in each.
|
||||
|
||||
// Let's keep picked addresses here and check their uniqueness.
|
||||
std::set<IOAddress> generated_addrs;
|
||||
int cnt = 0;
|
||||
while (++cnt) {
|
||||
IOAddress candidate = alloc.pickAddress(subnet_, cc_, clientid_, IOAddress("0.0.0.0"));
|
||||
EXPECT_TRUE(subnet_->inPool(Lease::TYPE_V4, candidate));
|
||||
|
||||
// One way to easily verify that the iterative allocator really works is
|
||||
// to uncomment the following line and observe its output that it
|
||||
// covers all defined subnets.
|
||||
// cout << candidate.toText() << endl;
|
||||
|
||||
if (generated_addrs.find(candidate) == generated_addrs.end()) {
|
||||
// We haven't had this
|
||||
generated_addrs.insert(candidate);
|
||||
} else {
|
||||
// We have seen this address before. That should mean that we
|
||||
// iterated over all addresses.
|
||||
if (generated_addrs.size() == total) {
|
||||
// We have exactly the number of address in all pools
|
||||
break;
|
||||
}
|
||||
ADD_FAILURE() << "Too many or not enough unique addresses generated.";
|
||||
break;
|
||||
}
|
||||
|
||||
if ( cnt>total ) {
|
||||
ADD_FAILURE() << "Too many unique addresses generated.";
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// This test checks if really small pools are working
|
||||
TEST_F(AllocEngine4Test, smallPool4) {
|
||||
boost::scoped_ptr<AllocEngine> engine;
|
||||
|
@ -10,6 +10,7 @@
|
||||
#include <dhcpsrv/host_mgr.h>
|
||||
#include <dhcpsrv/parsers/client_class_def_parser.h>
|
||||
#include <dhcpsrv/tests/alloc_engine_utils.h>
|
||||
#include <dhcpsrv/allocator.h>
|
||||
#include <dhcpsrv/testutils/test_utils.h>
|
||||
#include <eval/eval_context.h>
|
||||
#include <stats/stats_mgr.h>
|
||||
@ -58,10 +59,6 @@ TEST(ClientContext6Test, addAllocatedResource) {
|
||||
TEST_F(AllocEngine6Test, constructor) {
|
||||
boost::scoped_ptr<AllocEngine> x;
|
||||
|
||||
// Hashed and random allocators are not supported yet
|
||||
ASSERT_THROW(x.reset(new AllocEngine(AllocEngine::ALLOC_HASHED, 5)), NotImplemented);
|
||||
ASSERT_THROW(x.reset(new AllocEngine(AllocEngine::ALLOC_RANDOM, 5)), NotImplemented);
|
||||
|
||||
ASSERT_NO_THROW(x.reset(new AllocEngine(AllocEngine::ALLOC_ITERATIVE, 100, true)));
|
||||
|
||||
// Check that allocator for normal addresses is created
|
||||
@ -301,8 +298,7 @@ TEST_F(AllocEngine6Test, allocateAddress6Nulls) {
|
||||
// This test verifies that the allocator picks addresses that belong to the
|
||||
// pool
|
||||
TEST_F(AllocEngine6Test, IterativeAllocator) {
|
||||
boost::scoped_ptr<NakedAllocEngine::Allocator>
|
||||
alloc(new NakedAllocEngine::IterativeAllocator(Lease::TYPE_NA));
|
||||
boost::scoped_ptr<Allocator> alloc(new NakedIterativeAllocator(Lease::TYPE_NA));
|
||||
|
||||
for (int i = 0; i < 1000; ++i) {
|
||||
IOAddress candidate = alloc->pickAddress(subnet_, cc_,
|
||||
@ -314,8 +310,7 @@ TEST_F(AllocEngine6Test, IterativeAllocator) {
|
||||
// This test verifies that the allocator picks addresses that belong to the
|
||||
// pool using classification
|
||||
TEST_F(AllocEngine6Test, IterativeAllocator_class) {
|
||||
boost::scoped_ptr<NakedAllocEngine::Allocator>
|
||||
alloc(new NakedAllocEngine::IterativeAllocator(Lease::TYPE_NA));
|
||||
boost::scoped_ptr<Allocator> alloc(new NakedIterativeAllocator(Lease::TYPE_NA));
|
||||
|
||||
// Restrict pool_ to the foo class. Add a second pool with bar class.
|
||||
pool_->allowClientClass("foo");
|
||||
@ -336,7 +331,7 @@ TEST_F(AllocEngine6Test, IterativeAllocator_class) {
|
||||
}
|
||||
|
||||
TEST_F(AllocEngine6Test, IterativeAllocatorAddrStep) {
|
||||
NakedAllocEngine::NakedIterativeAllocator alloc(Lease::TYPE_NA);
|
||||
NakedIterativeAllocator alloc(Lease::TYPE_NA);
|
||||
|
||||
subnet_->delPools(Lease::TYPE_NA); // Get rid of default pool
|
||||
|
||||
@ -381,7 +376,7 @@ TEST_F(AllocEngine6Test, IterativeAllocatorAddrStep) {
|
||||
}
|
||||
|
||||
TEST_F(AllocEngine6Test, IterativeAllocatorAddrStepInClass) {
|
||||
NakedAllocEngine::NakedIterativeAllocator alloc(Lease::TYPE_NA);
|
||||
NakedIterativeAllocator alloc(Lease::TYPE_NA);
|
||||
|
||||
subnet_->delPools(Lease::TYPE_NA); // Get rid of default pool
|
||||
|
||||
@ -432,7 +427,7 @@ TEST_F(AllocEngine6Test, IterativeAllocatorAddrStepInClass) {
|
||||
}
|
||||
|
||||
TEST_F(AllocEngine6Test, IterativeAllocatorAddrStepOutClass) {
|
||||
NakedAllocEngine::NakedIterativeAllocator alloc(Lease::TYPE_NA);
|
||||
NakedIterativeAllocator alloc(Lease::TYPE_NA);
|
||||
|
||||
subnet_->delPools(Lease::TYPE_NA); // Get rid of default pool
|
||||
|
||||
@ -477,7 +472,7 @@ TEST_F(AllocEngine6Test, IterativeAllocatorAddrStepOutClass) {
|
||||
}
|
||||
|
||||
TEST_F(AllocEngine6Test, IterativeAllocatorPrefixStep) {
|
||||
NakedAllocEngine::NakedIterativeAllocator alloc(Lease::TYPE_PD);
|
||||
NakedIterativeAllocator alloc(Lease::TYPE_PD);
|
||||
|
||||
subnet_.reset(new Subnet6(IOAddress("2001:db8::"), 32, 1, 2, 3, 4));
|
||||
|
||||
@ -554,7 +549,7 @@ TEST_F(AllocEngine6Test, IterativeAllocatorPrefixStep) {
|
||||
}
|
||||
|
||||
TEST_F(AllocEngine6Test, IterativeAllocatorPrefixStepInClass) {
|
||||
NakedAllocEngine::NakedIterativeAllocator alloc(Lease::TYPE_PD);
|
||||
NakedIterativeAllocator alloc(Lease::TYPE_PD);
|
||||
|
||||
subnet_.reset(new Subnet6(IOAddress("2001:db8::"), 32, 1, 2, 3, 4));
|
||||
|
||||
@ -637,7 +632,7 @@ TEST_F(AllocEngine6Test, IterativeAllocatorPrefixStepInClass) {
|
||||
}
|
||||
|
||||
TEST_F(AllocEngine6Test, IterativeAllocatorPrefixStepOutClass) {
|
||||
NakedAllocEngine::NakedIterativeAllocator alloc(Lease::TYPE_PD);
|
||||
NakedIterativeAllocator alloc(Lease::TYPE_PD);
|
||||
|
||||
subnet_.reset(new Subnet6(IOAddress("2001:db8::"), 32, 1, 2, 3, 4));
|
||||
|
||||
@ -715,7 +710,7 @@ TEST_F(AllocEngine6Test, IterativeAllocatorPrefixStepOutClass) {
|
||||
|
||||
// This test verifies that the iterative allocator can step over addresses
|
||||
TEST_F(AllocEngine6Test, IterativeAllocatorAddressIncrease) {
|
||||
NakedAllocEngine::NakedIterativeAllocator alloc(Lease::TYPE_NA);
|
||||
NakedIterativeAllocator alloc(Lease::TYPE_NA);
|
||||
|
||||
// Let's pick the first address
|
||||
IOAddress addr1 = alloc.pickAddress(subnet_, cc_, duid_, IOAddress("2001:db8:1::10"));
|
||||
@ -735,7 +730,7 @@ TEST_F(AllocEngine6Test, IterativeAllocatorAddressIncrease) {
|
||||
|
||||
// This test verifies that the allocator can step over prefixes
|
||||
TEST_F(AllocEngine6Test, IterativeAllocatorPrefixIncrease) {
|
||||
NakedAllocEngine::NakedIterativeAllocator alloc(Lease::TYPE_PD);
|
||||
NakedIterativeAllocator alloc(Lease::TYPE_PD);
|
||||
|
||||
// For /128 prefix, increasePrefix should work the same as addressIncrease
|
||||
checkPrefixIncrease(alloc, "2001:db8::9", 128, "2001:db8::a");
|
||||
@ -787,7 +782,7 @@ TEST_F(AllocEngine6Test, IterativeAllocatorPrefixIncrease) {
|
||||
// in all pools in specified subnet. It also must not pick the same address twice
|
||||
// unless it runs out of pool space and must start over.
|
||||
TEST_F(AllocEngine6Test, IterativeAllocator_manyPools6) {
|
||||
NakedAllocEngine::IterativeAllocator alloc(Lease::TYPE_NA);
|
||||
NakedIterativeAllocator alloc(Lease::TYPE_NA);
|
||||
|
||||
// let's start from 2, as there is 2001:db8:1::10 - 2001:db8:1::20 pool already.
|
||||
for (int i = 2; i < 10; ++i) {
|
||||
|
@ -1,4 +1,4 @@
|
||||
// Copyright (C) 2015-2021 Internet Systems Consortium, Inc. ("ISC")
|
||||
// Copyright (C) 2015-2022 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
|
||||
@ -7,11 +7,13 @@
|
||||
#ifndef LIBDHCPSRV_ALLOC_ENGINE_UTILS_H
|
||||
#define LIBDHCPSRV_ALLOC_ENGINE_UTILS_H
|
||||
|
||||
#include <dhcpsrv/lease_mgr.h>
|
||||
#include <dhcpsrv/lease_mgr_factory.h>
|
||||
#include <asiolink/io_address.h>
|
||||
#include <dhcpsrv/alloc_engine.h>
|
||||
#include <dhcpsrv/cfgmgr.h>
|
||||
#include <asiolink/io_address.h>
|
||||
#include <dhcpsrv/iterative_allocator.h>
|
||||
#include <dhcpsrv/lease_mgr.h>
|
||||
#include <dhcpsrv/lease_mgr_factory.h>
|
||||
|
||||
#include <gtest/gtest.h>
|
||||
#include <vector>
|
||||
|
||||
@ -31,7 +33,6 @@ namespace test {
|
||||
/// alloc_engine6_unittest.cc - all unit-tests dedicated to IPv6
|
||||
/// alloc_engine_hooks_unittest.cc - all unit-tests dedicated to hooks
|
||||
|
||||
|
||||
/// @brief Test that statistic manager holds a given value.
|
||||
///
|
||||
/// This function may be used in many allocation tests and there's no
|
||||
@ -43,7 +44,8 @@ namespace test {
|
||||
///
|
||||
/// @return true if the statistic manager holds a particular value,
|
||||
/// false otherwise.
|
||||
bool testStatistics(const std::string& stat_name, const int64_t exp_value,
|
||||
bool testStatistics(const std::string& stat_name,
|
||||
const int64_t exp_value,
|
||||
const SubnetID subnet_id = SUBNET_ID_UNUSED);
|
||||
|
||||
/// @brief Get a value held by statistic manager.
|
||||
@ -54,42 +56,35 @@ bool testStatistics(const std::string& stat_name, const int64_t exp_value,
|
||||
/// @param stat_name Statistic name.
|
||||
/// @param subnet_id subnet_id of the desired subnet, if not zero.
|
||||
/// @return the value held by the statistic manager or zero.
|
||||
int64_t getStatistics(const std::string& stat_name,
|
||||
const SubnetID subnet_id = SUBNET_ID_UNUSED);
|
||||
int64_t getStatistics(const std::string& stat_name, const SubnetID subnet_id = SUBNET_ID_UNUSED);
|
||||
|
||||
/// @brief IterativeAllocator with internal methods exposed
|
||||
class NakedIterativeAllocator : public IterativeAllocator {
|
||||
public:
|
||||
/// @brief constructor
|
||||
/// @param type pool types that will be iterated through
|
||||
NakedIterativeAllocator(Lease::Type type) : IterativeAllocator(type) {
|
||||
}
|
||||
|
||||
using IterativeAllocator::increaseAddress;
|
||||
using IterativeAllocator::increasePrefix;
|
||||
};
|
||||
|
||||
/// @brief Allocation engine with some internal methods exposed
|
||||
class NakedAllocEngine : public AllocEngine {
|
||||
public:
|
||||
|
||||
/// @brief the sole constructor
|
||||
/// @param engine_type specifies engine type (e.g. iterative)
|
||||
/// @param attempts number of lease selection attempts before giving up
|
||||
/// @param ipv6 specifies if the engine is IPv6 or IPv4
|
||||
NakedAllocEngine(AllocEngine::AllocType engine_type,
|
||||
unsigned int attempts, bool ipv6 = true)
|
||||
:AllocEngine(engine_type, attempts, ipv6) {
|
||||
NakedAllocEngine(AllocEngine::AllocType engine_type, unsigned int attempts, bool ipv6 = true)
|
||||
: AllocEngine(engine_type, attempts, ipv6) {
|
||||
}
|
||||
|
||||
// Expose internal classes for testing purposes
|
||||
using AllocEngine::Allocator;
|
||||
using AllocEngine::IterativeAllocator;
|
||||
using AllocEngine::getAllocator;
|
||||
using AllocEngine::updateLease4ExtendedInfo;
|
||||
|
||||
/// @brief IterativeAllocator with internal methods exposed
|
||||
class NakedIterativeAllocator: public AllocEngine::IterativeAllocator {
|
||||
public:
|
||||
|
||||
/// @brief constructor
|
||||
/// @param type pool types that will be iterated through
|
||||
NakedIterativeAllocator(Lease::Type type)
|
||||
:IterativeAllocator(type) {
|
||||
}
|
||||
|
||||
using AllocEngine::IterativeAllocator::increaseAddress;
|
||||
using AllocEngine::IterativeAllocator::increasePrefix;
|
||||
};
|
||||
|
||||
/// @brief Wrapper method for invoking AllocEngine4::updateLease4ExtendedInfo().
|
||||
/// @param lease lease to update
|
||||
/// @param ctx current packet processing context
|
||||
@ -141,11 +136,10 @@ public:
|
||||
const asiolink::IOAddress& pool_start,
|
||||
const asiolink::IOAddress& pool_end,
|
||||
const asiolink::IOAddress& pd_pool_prefix =
|
||||
asiolink::IOAddress::IPV6_ZERO_ADDRESS(),
|
||||
asiolink::IOAddress::IPV6_ZERO_ADDRESS(),
|
||||
const uint8_t pd_pool_length = 0,
|
||||
const uint8_t pd_delegated_length = 0);
|
||||
|
||||
|
||||
/// @brief Initializes FQDN data for a test.
|
||||
///
|
||||
/// The initialized values are used by the test fixture class members to
|
||||
@ -198,8 +192,10 @@ public:
|
||||
/// @param exp_pd_len expected prefix length
|
||||
/// @param expected_in_subnet whether the lease is expected to be in subnet
|
||||
/// @param expected_in_pool whether the lease is expected to be in dynamic
|
||||
void checkLease6(const DuidPtr& duid, const Lease6Ptr& lease,
|
||||
Lease::Type exp_type, uint8_t exp_pd_len = 128,
|
||||
void checkLease6(const DuidPtr& duid,
|
||||
const Lease6Ptr& lease,
|
||||
Lease::Type exp_type,
|
||||
uint8_t exp_pd_len = 128,
|
||||
bool expected_in_subnet = true,
|
||||
bool expected_in_pool = true) {
|
||||
|
||||
@ -263,9 +259,9 @@ public:
|
||||
/// @param alloc IterativeAllocator that is tested
|
||||
/// @param input address to be increased
|
||||
/// @param exp_output expected address after increase
|
||||
void
|
||||
checkAddrIncrease(NakedAllocEngine::NakedIterativeAllocator& alloc,
|
||||
std::string input, std::string exp_output) {
|
||||
void checkAddrIncrease(NakedIterativeAllocator& alloc,
|
||||
std::string input,
|
||||
std::string exp_output) {
|
||||
EXPECT_EQ(exp_output, alloc.increaseAddress(asiolink::IOAddress(input),
|
||||
false, 0).toText());
|
||||
}
|
||||
@ -278,12 +274,13 @@ public:
|
||||
/// @param input IPv6 prefix (as a string)
|
||||
/// @param prefix_len prefix len
|
||||
/// @param exp_output expected output (string)
|
||||
void
|
||||
checkPrefixIncrease(NakedAllocEngine::NakedIterativeAllocator& alloc,
|
||||
std::string input, uint8_t prefix_len,
|
||||
std::string exp_output) {
|
||||
EXPECT_EQ(exp_output, alloc.increasePrefix(asiolink::IOAddress(input),
|
||||
prefix_len).toText());
|
||||
void checkPrefixIncrease(NakedIterativeAllocator& alloc,
|
||||
std::string input,
|
||||
uint8_t prefix_len,
|
||||
std::string exp_output) {
|
||||
EXPECT_EQ(exp_output,
|
||||
alloc.increasePrefix(asiolink::IOAddress(input),
|
||||
prefix_len).toText());
|
||||
}
|
||||
|
||||
/// @brief Checks if the simple allocation can succeed
|
||||
@ -297,7 +294,8 @@ public:
|
||||
/// @return allocated lease (or NULL)
|
||||
Lease6Ptr simpleAlloc6Test(const Pool6Ptr& pool,
|
||||
const asiolink::IOAddress& hint,
|
||||
bool fake, bool in_pool = true);
|
||||
bool fake,
|
||||
bool in_pool = true);
|
||||
|
||||
/// @brief Checks if the simple allocation can succeed with lifetimes.
|
||||
///
|
||||
@ -312,8 +310,10 @@ public:
|
||||
/// @return allocated lease (or NULL)
|
||||
Lease6Ptr simpleAlloc6Test(const Pool6Ptr& pool,
|
||||
const asiolink::IOAddress& hint,
|
||||
uint32_t preferred, uint32_t valid,
|
||||
uint32_t exp_preferred, uint32_t exp_valid);
|
||||
uint32_t preferred,
|
||||
uint32_t valid,
|
||||
uint32_t exp_preferred,
|
||||
uint32_t exp_valid);
|
||||
|
||||
/// @brief Checks if the simple allocation can succeed for custom DUID.
|
||||
///
|
||||
@ -325,10 +325,11 @@ public:
|
||||
/// @param fake true - this is fake allocation (SOLICIT)
|
||||
/// @param in_pool specifies whether the lease is expected to be in pool
|
||||
/// @return allocated lease (or NULL)
|
||||
Lease6Ptr simpleAlloc6Test(const Pool6Ptr& pool, const DuidPtr& duid,
|
||||
Lease6Ptr simpleAlloc6Test(const Pool6Ptr& pool,
|
||||
const DuidPtr& duid,
|
||||
const asiolink::IOAddress& hint,
|
||||
bool fake, bool in_pool = true);
|
||||
|
||||
bool fake,
|
||||
bool in_pool = true);
|
||||
|
||||
/// @brief Checks if the allocation can succeed.
|
||||
///
|
||||
@ -341,8 +342,10 @@ public:
|
||||
/// @param fake true - this is fake allocation (SOLICIT)
|
||||
/// @param in_pool specifies whether the lease is expected to be in pool
|
||||
/// @return allocated lease(s) (may be empty)
|
||||
Lease6Collection allocateTest(AllocEngine& engine, const Pool6Ptr& pool,
|
||||
const asiolink::IOAddress& hint, bool fake,
|
||||
Lease6Collection allocateTest(AllocEngine& engine,
|
||||
const Pool6Ptr& pool,
|
||||
const asiolink::IOAddress& hint,
|
||||
bool fake,
|
||||
bool in_pool = true);
|
||||
|
||||
/// @brief Checks if the allocation can be renewed.
|
||||
@ -355,7 +358,8 @@ public:
|
||||
/// @param hints address to be used as a hint
|
||||
/// @param in_pool specifies whether the lease is expected to be in pool
|
||||
/// @return allocated lease(s) (may be empty)
|
||||
Lease6Collection renewTest(AllocEngine& engine, const Pool6Ptr& pool,
|
||||
Lease6Collection renewTest(AllocEngine& engine,
|
||||
const Pool6Ptr& pool,
|
||||
AllocEngine::HintContainer& hints,
|
||||
bool in_pool = true);
|
||||
|
||||
@ -369,7 +373,8 @@ public:
|
||||
/// allocation by some other user)
|
||||
/// @param requested address requested by the client
|
||||
/// @param expected_pd_len expected PD len (128 for addresses)
|
||||
void allocWithUsedHintTest(Lease::Type type, asiolink::IOAddress used_addr,
|
||||
void allocWithUsedHintTest(Lease::Type type,
|
||||
asiolink::IOAddress used_addr,
|
||||
asiolink::IOAddress requested,
|
||||
uint8_t expected_pd_len);
|
||||
|
||||
@ -426,12 +431,12 @@ public:
|
||||
/// @param addr specifies reserved address or prefix
|
||||
/// @param prefix_len prefix length (should be 128 for addresses)
|
||||
/// @return created Host object.
|
||||
HostPtr
|
||||
createHost6(bool add_to_host_mgr, IPv6Resrv::Type type,
|
||||
const asiolink::IOAddress& addr, uint8_t prefix_len) {
|
||||
HostPtr host(new Host(&duid_->getDuid()[0], duid_->getDuid().size(),
|
||||
Host::IDENT_DUID, SUBNET_ID_UNUSED, subnet_->getID(),
|
||||
asiolink::IOAddress("0.0.0.0")));
|
||||
HostPtr createHost6(bool add_to_host_mgr,
|
||||
IPv6Resrv::Type type,
|
||||
const asiolink::IOAddress& addr,
|
||||
uint8_t prefix_len) {
|
||||
HostPtr host(new Host(&duid_->getDuid()[0], duid_->getDuid().size(), Host::IDENT_DUID,
|
||||
SUBNET_ID_UNUSED, subnet_->getID(), asiolink::IOAddress("0.0.0.0")));
|
||||
IPv6Resrv resv(type, addr, prefix_len);
|
||||
host->addReservation(resv);
|
||||
|
||||
@ -450,8 +455,7 @@ public:
|
||||
/// such as subnets.
|
||||
///
|
||||
/// @param host host reservation to add
|
||||
void
|
||||
addHost(HostPtr& host) {
|
||||
void addHost(HostPtr& host) {
|
||||
SrvConfigPtr cfg = boost::const_pointer_cast<SrvConfig>(CfgMgr::instance().getCurrentCfg());
|
||||
cfg->getCfgHosts()->add(host);
|
||||
}
|
||||
@ -464,10 +468,11 @@ public:
|
||||
/// @param addr specifies reserved address or prefix
|
||||
/// @param prefix_len prefix length (should be 128 for addresses)
|
||||
/// @return created Host object.
|
||||
HostPtr
|
||||
createHost6HWAddr(bool add_to_host_mgr, IPv6Resrv::Type type,
|
||||
HWAddrPtr& hwaddr, const asiolink::IOAddress& addr,
|
||||
uint8_t prefix_len);
|
||||
HostPtr createHost6HWAddr(bool add_to_host_mgr,
|
||||
IPv6Resrv::Type type,
|
||||
HWAddrPtr& hwaddr,
|
||||
const asiolink::IOAddress& addr,
|
||||
uint8_t prefix_len);
|
||||
|
||||
/// @brief Utility function that decrements cltt of a persisted lease
|
||||
///
|
||||
@ -478,13 +483,11 @@ public:
|
||||
///
|
||||
/// @param[in][out] lease pointer reference to the lease to modify. Upon
|
||||
/// return it will point to the newly updated lease.
|
||||
void
|
||||
rollbackPersistedCltt(Lease6Ptr& lease) {
|
||||
void rollbackPersistedCltt(Lease6Ptr& lease) {
|
||||
ASSERT_TRUE(lease) << "rollbackPersistedCltt lease is empty";
|
||||
|
||||
// Fetch it, so we can update it.
|
||||
Lease6Ptr from_mgr = LeaseMgrFactory::instance().getLease6(lease->type_,
|
||||
lease->addr_);
|
||||
Lease6Ptr from_mgr = LeaseMgrFactory::instance().getLease6(lease->type_, lease->addr_);
|
||||
ASSERT_TRUE(from_mgr) << "rollbackPersistedCltt: lease not found?";
|
||||
|
||||
// Decrement cltt then update it in the manager.
|
||||
@ -552,11 +555,9 @@ public:
|
||||
}
|
||||
if (lease->client_id_ && !clientid_) {
|
||||
ADD_FAILURE() << "Lease4 has a client-id, while it should have none.";
|
||||
} else
|
||||
if (!lease->client_id_ && clientid_) {
|
||||
} else if (!lease->client_id_ && clientid_) {
|
||||
ADD_FAILURE() << "Lease4 has no client-id, but it was expected to have one.";
|
||||
} else
|
||||
if (lease->client_id_ && clientid_) {
|
||||
} else if (lease->client_id_ && clientid_) {
|
||||
EXPECT_TRUE(*lease->client_id_ == *clientid_);
|
||||
}
|
||||
EXPECT_TRUE(*lease->hwaddr_ == *hwaddr_);
|
||||
@ -610,16 +611,16 @@ public:
|
||||
factory_.destroy();
|
||||
}
|
||||
|
||||
ClientIdPtr clientid_; ///< Client-identifier (value used in tests)
|
||||
ClientIdPtr clientid2_; ///< Alternative client-identifier.
|
||||
HWAddrPtr hwaddr_; ///< Hardware address (value used in tests)
|
||||
HWAddrPtr hwaddr2_; ///< Alternative hardware address.
|
||||
Subnet4Ptr subnet_; ///< Subnet4 (used in tests)
|
||||
Pool4Ptr pool_; ///< Pool belonging to subnet_
|
||||
LeaseMgrFactory factory_; ///< Pointer to LeaseMgr factory
|
||||
ClientIdPtr clientid_; ///< Client-identifier (value used in tests)
|
||||
ClientIdPtr clientid2_; ///< Alternative client-identifier.
|
||||
HWAddrPtr hwaddr_; ///< Hardware address (value used in tests)
|
||||
HWAddrPtr hwaddr2_; ///< Alternative hardware address.
|
||||
Subnet4Ptr subnet_; ///< Subnet4 (used in tests)
|
||||
Pool4Ptr pool_; ///< Pool belonging to subnet_
|
||||
LeaseMgrFactory factory_; ///< Pointer to LeaseMgr factory
|
||||
AllocEngine::ClientContext4 ctx_; ///< Context information passed to various
|
||||
ClientClasses cc_; ///< Client classes
|
||||
///< allocation engine functions.
|
||||
ClientClasses cc_; ///< Client classes
|
||||
///< allocation engine functions.
|
||||
};
|
||||
|
||||
} // namespace test
|
||||
|
666
src/lib/dhcpsrv/tests/iterative_allocator_unittest.cc
Normal file
666
src/lib/dhcpsrv/tests/iterative_allocator_unittest.cc
Normal file
@ -0,0 +1,666 @@
|
||||
// Copyright (C) 2022 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 <dhcpsrv/tests/alloc_engine_utils.h>
|
||||
#include <gtest/gtest.h>
|
||||
#include <sstream>
|
||||
|
||||
using namespace isc::asiolink;
|
||||
using namespace isc::dhcp;
|
||||
using namespace std;
|
||||
|
||||
namespace isc {
|
||||
namespace dhcp {
|
||||
namespace test {
|
||||
|
||||
using IterativeAllocatorTest4 = AllocEngine4Test;
|
||||
|
||||
// This test verifies that the allocator picks addresses that belong to the
|
||||
// pool
|
||||
TEST_F(IterativeAllocatorTest4, basic) {
|
||||
boost::scoped_ptr<Allocator> alloc(new IterativeAllocator(Lease::TYPE_V4));
|
||||
|
||||
for (int i = 0; i < 1000; ++i) {
|
||||
IOAddress candidate = alloc->pickAddress(subnet_, cc_, clientid_,
|
||||
IOAddress("0.0.0.0"));
|
||||
EXPECT_TRUE(subnet_->inPool(Lease::TYPE_V4, candidate));
|
||||
}
|
||||
}
|
||||
|
||||
// This test verifies that the allocator picks addresses that belong to the
|
||||
// pool using classification
|
||||
TEST_F(IterativeAllocatorTest4, clientClass) {
|
||||
boost::scoped_ptr<Allocator> alloc(new IterativeAllocator(Lease::TYPE_V4));
|
||||
|
||||
// Restrict pool_ to the foo class. Add a second pool with bar class.
|
||||
pool_->allowClientClass("foo");
|
||||
Pool4Ptr pool(new Pool4(IOAddress("192.0.2.200"),
|
||||
IOAddress("192.0.2.209")));
|
||||
pool->allowClientClass("bar");
|
||||
subnet_->addPool(pool);
|
||||
|
||||
// Clients are in bar
|
||||
cc_.insert("bar");
|
||||
|
||||
for (int i = 0; i < 1000; ++i) {
|
||||
IOAddress candidate = alloc->pickAddress(subnet_, cc_, clientid_,
|
||||
IOAddress("0.0.0.0"));
|
||||
EXPECT_TRUE(subnet_->inPool(Lease::TYPE_V4, candidate));
|
||||
EXPECT_TRUE(subnet_->inPool(Lease::TYPE_V4, candidate, cc_));
|
||||
}
|
||||
}
|
||||
|
||||
// This test verifies that the iterative allocator really walks over all addresses
|
||||
// in all pools in specified subnet. It also must not pick the same address twice
|
||||
// unless it runs out of pool space and must start over.
|
||||
TEST_F(IterativeAllocatorTest4, manyPools) {
|
||||
IterativeAllocator alloc(Lease::TYPE_V4);
|
||||
|
||||
// Let's start from 2, as there is 2001:db8:1::10 - 2001:db8:1::20 pool already.
|
||||
for (int i = 2; i < 10; ++i) {
|
||||
stringstream min, max;
|
||||
|
||||
min << "192.0.2." << i * 10 + 1;
|
||||
max << "192.0.2." << i * 10 + 9;
|
||||
|
||||
Pool4Ptr pool(new Pool4(IOAddress(min.str()),
|
||||
IOAddress(max.str())));
|
||||
// cout << "Adding pool: " << min.str() << "-" << max.str() << endl;
|
||||
subnet_->addPool(pool);
|
||||
}
|
||||
|
||||
int total = 10 + 8 * 9; // first pool (.100 - .109) has 10 addresses in it,
|
||||
// there are 8 extra pools with 9 addresses in each.
|
||||
|
||||
// Let's keep picked addresses here and check their uniqueness.
|
||||
std::set<IOAddress> generated_addrs;
|
||||
int cnt = 0;
|
||||
while (++cnt) {
|
||||
IOAddress candidate = alloc.pickAddress(subnet_, cc_, clientid_, IOAddress("0.0.0.0"));
|
||||
EXPECT_TRUE(subnet_->inPool(Lease::TYPE_V4, candidate));
|
||||
|
||||
// One way to easily verify that the iterative allocator really works is
|
||||
// to uncomment the following line and observe its output that it
|
||||
// covers all defined subnets.
|
||||
// cout << candidate.toText() << endl;
|
||||
|
||||
if (generated_addrs.find(candidate) == generated_addrs.end()) {
|
||||
// We haven't had this
|
||||
generated_addrs.insert(candidate);
|
||||
} else {
|
||||
// We have seen this address before. That should mean that we
|
||||
// iterated over all addresses.
|
||||
if (generated_addrs.size() == total) {
|
||||
// We have exactly the number of address in all pools
|
||||
break;
|
||||
}
|
||||
ADD_FAILURE() << "Too many or not enough unique addresses generated.";
|
||||
break;
|
||||
}
|
||||
|
||||
if ( cnt>total ) {
|
||||
ADD_FAILURE() << "Too many unique addresses generated.";
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
using IterativeAllocatorTest6 = AllocEngine6Test;
|
||||
|
||||
// This test verifies that the allocator picks addresses that belong to the
|
||||
// pool
|
||||
TEST_F(IterativeAllocatorTest6, basic) {
|
||||
boost::scoped_ptr<Allocator> alloc(new NakedIterativeAllocator(Lease::TYPE_NA));
|
||||
|
||||
for (int i = 0; i < 1000; ++i) {
|
||||
IOAddress candidate = alloc->pickAddress(subnet_, cc_,
|
||||
duid_, IOAddress("::"));
|
||||
EXPECT_TRUE(subnet_->inPool(Lease::TYPE_NA, candidate));
|
||||
}
|
||||
}
|
||||
|
||||
// This test verifies that the allocator picks addresses that belong to the
|
||||
// pool using classification
|
||||
TEST_F(IterativeAllocatorTest6, clientClass) {
|
||||
boost::scoped_ptr<Allocator> alloc(new NakedIterativeAllocator(Lease::TYPE_NA));
|
||||
|
||||
// Restrict pool_ to the foo class. Add a second pool with bar class.
|
||||
pool_->allowClientClass("foo");
|
||||
Pool6Ptr pool(new Pool6(Lease::TYPE_NA, IOAddress("2001:db8:1::100"),
|
||||
IOAddress("2001:db8:1::109")));
|
||||
pool->allowClientClass("bar");
|
||||
subnet_->addPool(pool);
|
||||
|
||||
// Clients are in bar
|
||||
cc_.insert("bar");
|
||||
|
||||
for (int i = 0; i < 1000; ++i) {
|
||||
IOAddress candidate = alloc->pickAddress(subnet_, cc_,
|
||||
duid_, IOAddress("::"));
|
||||
EXPECT_TRUE(subnet_->inPool(Lease::TYPE_NA, candidate));
|
||||
EXPECT_TRUE(subnet_->inPool(Lease::TYPE_NA, candidate, cc_));
|
||||
}
|
||||
}
|
||||
|
||||
// This test verifies that the allocator walks over the addresses in the
|
||||
// non-contiguous pools.
|
||||
TEST_F(IterativeAllocatorTest6, addrStep) {
|
||||
NakedIterativeAllocator alloc(Lease::TYPE_NA);
|
||||
|
||||
subnet_->delPools(Lease::TYPE_NA); // Get rid of default pool
|
||||
|
||||
Pool6Ptr pool1(new Pool6(Lease::TYPE_NA, IOAddress("2001:db8:1::1"),
|
||||
IOAddress("2001:db8:1::5")));
|
||||
Pool6Ptr pool2(new Pool6(Lease::TYPE_NA, IOAddress("2001:db8:1::100"),
|
||||
IOAddress("2001:db8:1::100")));
|
||||
Pool6Ptr pool3(new Pool6(Lease::TYPE_NA, IOAddress("2001:db8:1::105"),
|
||||
IOAddress("2001:db8:1::106")));
|
||||
subnet_->addPool(pool1);
|
||||
subnet_->addPool(pool2);
|
||||
subnet_->addPool(pool3);
|
||||
|
||||
// Let's check the first pool (5 addresses here)
|
||||
EXPECT_EQ("2001:db8:1::1",
|
||||
alloc.pickAddress(subnet_, cc_, duid_, IOAddress("::")).toText());
|
||||
EXPECT_EQ("2001:db8:1::2",
|
||||
alloc.pickAddress(subnet_, cc_, duid_, IOAddress("::")).toText());
|
||||
EXPECT_EQ("2001:db8:1::3",
|
||||
alloc.pickAddress(subnet_, cc_, duid_, IOAddress("::")).toText());
|
||||
EXPECT_EQ("2001:db8:1::4",
|
||||
alloc.pickAddress(subnet_, cc_, duid_, IOAddress("::")).toText());
|
||||
EXPECT_EQ("2001:db8:1::5",
|
||||
alloc.pickAddress(subnet_, cc_, duid_, IOAddress("::")).toText());
|
||||
|
||||
// The second pool is easy - only one address here
|
||||
EXPECT_EQ("2001:db8:1::100",
|
||||
alloc.pickAddress(subnet_, cc_, duid_, IOAddress("::")).toText());
|
||||
|
||||
// This is the third and last pool, with 2 addresses in it
|
||||
EXPECT_EQ("2001:db8:1::105",
|
||||
alloc.pickAddress(subnet_, cc_, duid_, IOAddress("::")).toText());
|
||||
EXPECT_EQ("2001:db8:1::106",
|
||||
alloc.pickAddress(subnet_, cc_, duid_, IOAddress("::")).toText());
|
||||
|
||||
// We iterated over all addresses and reached to the end of the last pool.
|
||||
// Let's wrap around and start from the beginning
|
||||
EXPECT_EQ("2001:db8:1::1",
|
||||
alloc.pickAddress(subnet_, cc_, duid_, IOAddress("::")).toText());
|
||||
EXPECT_EQ("2001:db8:1::2",
|
||||
alloc.pickAddress(subnet_, cc_, duid_, IOAddress("::")).toText());
|
||||
}
|
||||
|
||||
// This test verifies that the allocator walks over the addresses in the
|
||||
// non-contiguous pools when pools contain class guards.
|
||||
TEST_F(IterativeAllocatorTest6, addrStepInClass) {
|
||||
NakedIterativeAllocator alloc(Lease::TYPE_NA);
|
||||
|
||||
subnet_->delPools(Lease::TYPE_NA); // Get rid of default pool
|
||||
|
||||
Pool6Ptr pool1(new Pool6(Lease::TYPE_NA, IOAddress("2001:db8:1::1"),
|
||||
IOAddress("2001:db8:1::5")));
|
||||
Pool6Ptr pool2(new Pool6(Lease::TYPE_NA, IOAddress("2001:db8:1::100"),
|
||||
IOAddress("2001:db8:1::100")));
|
||||
Pool6Ptr pool3(new Pool6(Lease::TYPE_NA, IOAddress("2001:db8:1::105"),
|
||||
IOAddress("2001:db8:1::106")));
|
||||
// Set pool1 and pool3 but not pool2 in foo class
|
||||
pool1->allowClientClass("foo");
|
||||
pool3->allowClientClass("foo");
|
||||
subnet_->addPool(pool1);
|
||||
subnet_->addPool(pool2);
|
||||
subnet_->addPool(pool3);
|
||||
|
||||
// Clients are in foo
|
||||
cc_.insert("foo");
|
||||
|
||||
// Let's check the first pool (5 addresses here)
|
||||
EXPECT_EQ("2001:db8:1::1",
|
||||
alloc.pickAddress(subnet_, cc_, duid_, IOAddress("::")).toText());
|
||||
EXPECT_EQ("2001:db8:1::2",
|
||||
alloc.pickAddress(subnet_, cc_, duid_, IOAddress("::")).toText());
|
||||
EXPECT_EQ("2001:db8:1::3",
|
||||
alloc.pickAddress(subnet_, cc_, duid_, IOAddress("::")).toText());
|
||||
EXPECT_EQ("2001:db8:1::4",
|
||||
alloc.pickAddress(subnet_, cc_, duid_, IOAddress("::")).toText());
|
||||
EXPECT_EQ("2001:db8:1::5",
|
||||
alloc.pickAddress(subnet_, cc_, duid_, IOAddress("::")).toText());
|
||||
|
||||
// The second pool is easy - only one address here
|
||||
EXPECT_EQ("2001:db8:1::100",
|
||||
alloc.pickAddress(subnet_, cc_, duid_, IOAddress("::")).toText());
|
||||
|
||||
// This is the third and last pool, with 2 addresses in it
|
||||
EXPECT_EQ("2001:db8:1::105",
|
||||
alloc.pickAddress(subnet_, cc_, duid_, IOAddress("::")).toText());
|
||||
EXPECT_EQ("2001:db8:1::106",
|
||||
alloc.pickAddress(subnet_, cc_, duid_, IOAddress("::")).toText());
|
||||
|
||||
// We iterated over all addresses and reached to the end of the last pool.
|
||||
// Let's wrap around and start from the beginning
|
||||
EXPECT_EQ("2001:db8:1::1",
|
||||
alloc.pickAddress(subnet_, cc_, duid_, IOAddress("::")).toText());
|
||||
EXPECT_EQ("2001:db8:1::2",
|
||||
alloc.pickAddress(subnet_, cc_, duid_, IOAddress("::")).toText());
|
||||
}
|
||||
|
||||
// This test verifies that the allocator omits pools with non-matching class guards.
|
||||
TEST_F(IterativeAllocatorTest6, addrStepOutClass) {
|
||||
NakedIterativeAllocator alloc(Lease::TYPE_NA);
|
||||
|
||||
subnet_->delPools(Lease::TYPE_NA); // Get rid of default pool
|
||||
|
||||
Pool6Ptr pool1(new Pool6(Lease::TYPE_NA, IOAddress("2001:db8:1::1"),
|
||||
IOAddress("2001:db8:1::5")));
|
||||
Pool6Ptr pool2(new Pool6(Lease::TYPE_NA, IOAddress("2001:db8:1::100"),
|
||||
IOAddress("2001:db8:1::100")));
|
||||
Pool6Ptr pool3(new Pool6(Lease::TYPE_NA, IOAddress("2001:db8:1::105"),
|
||||
IOAddress("2001:db8:1::106")));
|
||||
// Set pool2 in foo
|
||||
pool2->allowClientClass("foo");
|
||||
subnet_->addPool(pool1);
|
||||
subnet_->addPool(pool2);
|
||||
subnet_->addPool(pool3);
|
||||
|
||||
// Let's check the first pool (5 addresses here)
|
||||
EXPECT_EQ("2001:db8:1::1",
|
||||
alloc.pickAddress(subnet_, cc_, duid_, IOAddress("::")).toText());
|
||||
EXPECT_EQ("2001:db8:1::2",
|
||||
alloc.pickAddress(subnet_, cc_, duid_, IOAddress("::")).toText());
|
||||
EXPECT_EQ("2001:db8:1::3",
|
||||
alloc.pickAddress(subnet_, cc_, duid_, IOAddress("::")).toText());
|
||||
EXPECT_EQ("2001:db8:1::4",
|
||||
alloc.pickAddress(subnet_, cc_, duid_, IOAddress("::")).toText());
|
||||
EXPECT_EQ("2001:db8:1::5",
|
||||
alloc.pickAddress(subnet_, cc_, duid_, IOAddress("::")).toText());
|
||||
|
||||
// The second pool is skipped
|
||||
|
||||
// This is the third and last pool, with 2 addresses in it
|
||||
EXPECT_EQ("2001:db8:1::105",
|
||||
alloc.pickAddress(subnet_, cc_, duid_, IOAddress("::")).toText());
|
||||
EXPECT_EQ("2001:db8:1::106",
|
||||
alloc.pickAddress(subnet_, cc_, duid_, IOAddress("::")).toText());
|
||||
|
||||
// We iterated over all addresses and reached to the end of the last pool.
|
||||
// Let's wrap around and start from the beginning
|
||||
EXPECT_EQ("2001:db8:1::1",
|
||||
alloc.pickAddress(subnet_, cc_, duid_, IOAddress("::")).toText());
|
||||
EXPECT_EQ("2001:db8:1::2",
|
||||
alloc.pickAddress(subnet_, cc_, duid_, IOAddress("::")).toText());
|
||||
}
|
||||
|
||||
// This test verifies that the allocator picks delegated prefixes from several
|
||||
// pools.
|
||||
TEST_F(IterativeAllocatorTest6, prefixStep) {
|
||||
NakedIterativeAllocator alloc(Lease::TYPE_PD);
|
||||
|
||||
subnet_.reset(new Subnet6(IOAddress("2001:db8::"), 32, 1, 2, 3, 4));
|
||||
|
||||
Pool6Ptr pool1(new Pool6(Lease::TYPE_PD, IOAddress("2001:db8::"), 56, 60));
|
||||
Pool6Ptr pool2(new Pool6(Lease::TYPE_PD, IOAddress("2001:db8:1::"), 48, 48));
|
||||
Pool6Ptr pool3(new Pool6(Lease::TYPE_PD, IOAddress("2001:db8:2::"), 56, 64));
|
||||
subnet_->addPool(pool1);
|
||||
subnet_->addPool(pool2);
|
||||
subnet_->addPool(pool3);
|
||||
|
||||
// We have a 2001:db8::/48 subnet that has 3 pools defined in it:
|
||||
// 2001:db8::/56 split into /60 prefixes (16 leases) (or 2001:db8:0:X0::)
|
||||
// 2001:db8:1::/48 split into a single /48 prefix (just 1 lease)
|
||||
// 2001:db8:2::/56 split into /64 prefixes (256 leases) (or 2001:db8:2:XX::)
|
||||
|
||||
// First pool check (Let's check over all 16 leases)
|
||||
EXPECT_EQ("2001:db8::",
|
||||
alloc.pickAddress(subnet_, cc_, duid_, IOAddress("::")).toText());
|
||||
EXPECT_EQ("2001:db8:0:10::",
|
||||
alloc.pickAddress(subnet_, cc_, duid_, IOAddress("::")).toText());
|
||||
EXPECT_EQ("2001:db8:0:20::",
|
||||
alloc.pickAddress(subnet_, cc_, duid_, IOAddress("::")).toText());
|
||||
EXPECT_EQ("2001:db8:0:30::",
|
||||
alloc.pickAddress(subnet_, cc_, duid_, IOAddress("::")).toText());
|
||||
EXPECT_EQ("2001:db8:0:40::",
|
||||
alloc.pickAddress(subnet_, cc_, duid_, IOAddress("::")).toText());
|
||||
EXPECT_EQ("2001:db8:0:50::",
|
||||
alloc.pickAddress(subnet_, cc_, duid_, IOAddress("::")).toText());
|
||||
EXPECT_EQ("2001:db8:0:60::",
|
||||
alloc.pickAddress(subnet_, cc_, duid_, IOAddress("::")).toText());
|
||||
EXPECT_EQ("2001:db8:0:70::",
|
||||
alloc.pickAddress(subnet_, cc_, duid_, IOAddress("::")).toText());
|
||||
EXPECT_EQ("2001:db8:0:80::",
|
||||
alloc.pickAddress(subnet_, cc_, duid_, IOAddress("::")).toText());
|
||||
EXPECT_EQ("2001:db8:0:90::",
|
||||
alloc.pickAddress(subnet_, cc_, duid_, IOAddress("::")).toText());
|
||||
EXPECT_EQ("2001:db8:0:a0::",
|
||||
alloc.pickAddress(subnet_, cc_, duid_, IOAddress("::")).toText());
|
||||
EXPECT_EQ("2001:db8:0:b0::",
|
||||
alloc.pickAddress(subnet_, cc_, duid_, IOAddress("::")).toText());
|
||||
EXPECT_EQ("2001:db8:0:c0::",
|
||||
alloc.pickAddress(subnet_, cc_, duid_, IOAddress("::")).toText());
|
||||
EXPECT_EQ("2001:db8:0:d0::",
|
||||
alloc.pickAddress(subnet_, cc_, duid_, IOAddress("::")).toText());
|
||||
EXPECT_EQ("2001:db8:0:e0::",
|
||||
alloc.pickAddress(subnet_, cc_, duid_, IOAddress("::")).toText());
|
||||
EXPECT_EQ("2001:db8:0:f0::",
|
||||
alloc.pickAddress(subnet_, cc_, duid_, IOAddress("::")).toText());
|
||||
|
||||
// Second pool (just one lease here)
|
||||
EXPECT_EQ("2001:db8:1::",
|
||||
alloc.pickAddress(subnet_, cc_, duid_, IOAddress("::")).toText());
|
||||
|
||||
// Third pool (256 leases, let's check first and last explicitly and the
|
||||
// rest over in a pool
|
||||
EXPECT_EQ("2001:db8:2::",
|
||||
alloc.pickAddress(subnet_, cc_, duid_, IOAddress("::")).toText());
|
||||
for (int i = 1; i < 255; i++) {
|
||||
stringstream exp;
|
||||
exp << "2001:db8:2:" << hex << i << dec << "::";
|
||||
EXPECT_EQ(exp.str(),
|
||||
alloc.pickAddress(subnet_, cc_, duid_, IOAddress("::")).toText());
|
||||
|
||||
}
|
||||
EXPECT_EQ("2001:db8:2:ff::",
|
||||
alloc.pickAddress(subnet_, cc_, duid_, IOAddress("::")).toText());
|
||||
|
||||
// Ok, we've iterated over all prefixes in all pools. We now wrap around.
|
||||
// We're looping over now (iterating over first pool again)
|
||||
EXPECT_EQ("2001:db8::",
|
||||
alloc.pickAddress(subnet_, cc_, duid_, IOAddress("::")).toText());
|
||||
EXPECT_EQ("2001:db8:0:10::",
|
||||
alloc.pickAddress(subnet_, cc_, duid_, IOAddress("::")).toText());
|
||||
}
|
||||
|
||||
// This test verifies that the allocator picks delegated prefixes from the pools
|
||||
// with class guards.
|
||||
TEST_F(IterativeAllocatorTest6, prefixStepInClass) {
|
||||
NakedIterativeAllocator alloc(Lease::TYPE_PD);
|
||||
|
||||
subnet_.reset(new Subnet6(IOAddress("2001:db8::"), 32, 1, 2, 3, 4));
|
||||
|
||||
Pool6Ptr pool1(new Pool6(Lease::TYPE_PD, IOAddress("2001:db8::"), 56, 60));
|
||||
Pool6Ptr pool2(new Pool6(Lease::TYPE_PD, IOAddress("2001:db8:1::"), 48, 48));
|
||||
Pool6Ptr pool3(new Pool6(Lease::TYPE_PD, IOAddress("2001:db8:2::"), 56, 64));
|
||||
// Set pool1 and pool3 but not pool2 in foo class
|
||||
pool1->allowClientClass("foo");
|
||||
pool3->allowClientClass("foo");
|
||||
subnet_->addPool(pool1);
|
||||
subnet_->addPool(pool2);
|
||||
subnet_->addPool(pool3);
|
||||
|
||||
// Clients are in foo
|
||||
cc_.insert("foo");
|
||||
|
||||
// We have a 2001:db8::/48 subnet that has 3 pools defined in it:
|
||||
// 2001:db8::/56 split into /60 prefixes (16 leases) (or 2001:db8:0:X0::)
|
||||
// 2001:db8:1::/48 split into a single /48 prefix (just 1 lease)
|
||||
// 2001:db8:2::/56 split into /64 prefixes (256 leases) (or 2001:db8:2:XX::)
|
||||
|
||||
// First pool check (Let's check over all 16 leases)
|
||||
EXPECT_EQ("2001:db8::",
|
||||
alloc.pickAddress(subnet_, cc_, duid_, IOAddress("::")).toText());
|
||||
EXPECT_EQ("2001:db8:0:10::",
|
||||
alloc.pickAddress(subnet_, cc_, duid_, IOAddress("::")).toText());
|
||||
EXPECT_EQ("2001:db8:0:20::",
|
||||
alloc.pickAddress(subnet_, cc_, duid_, IOAddress("::")).toText());
|
||||
EXPECT_EQ("2001:db8:0:30::",
|
||||
alloc.pickAddress(subnet_, cc_, duid_, IOAddress("::")).toText());
|
||||
EXPECT_EQ("2001:db8:0:40::",
|
||||
alloc.pickAddress(subnet_, cc_, duid_, IOAddress("::")).toText());
|
||||
EXPECT_EQ("2001:db8:0:50::",
|
||||
alloc.pickAddress(subnet_, cc_, duid_, IOAddress("::")).toText());
|
||||
EXPECT_EQ("2001:db8:0:60::",
|
||||
alloc.pickAddress(subnet_, cc_, duid_, IOAddress("::")).toText());
|
||||
EXPECT_EQ("2001:db8:0:70::",
|
||||
alloc.pickAddress(subnet_, cc_, duid_, IOAddress("::")).toText());
|
||||
EXPECT_EQ("2001:db8:0:80::",
|
||||
alloc.pickAddress(subnet_, cc_, duid_, IOAddress("::")).toText());
|
||||
EXPECT_EQ("2001:db8:0:90::",
|
||||
alloc.pickAddress(subnet_, cc_, duid_, IOAddress("::")).toText());
|
||||
EXPECT_EQ("2001:db8:0:a0::",
|
||||
alloc.pickAddress(subnet_, cc_, duid_, IOAddress("::")).toText());
|
||||
EXPECT_EQ("2001:db8:0:b0::",
|
||||
alloc.pickAddress(subnet_, cc_, duid_, IOAddress("::")).toText());
|
||||
EXPECT_EQ("2001:db8:0:c0::",
|
||||
alloc.pickAddress(subnet_, cc_, duid_, IOAddress("::")).toText());
|
||||
EXPECT_EQ("2001:db8:0:d0::",
|
||||
alloc.pickAddress(subnet_, cc_, duid_, IOAddress("::")).toText());
|
||||
EXPECT_EQ("2001:db8:0:e0::",
|
||||
alloc.pickAddress(subnet_, cc_, duid_, IOAddress("::")).toText());
|
||||
EXPECT_EQ("2001:db8:0:f0::",
|
||||
alloc.pickAddress(subnet_, cc_, duid_, IOAddress("::")).toText());
|
||||
|
||||
// Second pool (just one lease here)
|
||||
EXPECT_EQ("2001:db8:1::",
|
||||
alloc.pickAddress(subnet_, cc_, duid_, IOAddress("::")).toText());
|
||||
|
||||
// Third pool (256 leases, let's check first and last explicitly and the
|
||||
// rest over in a pool
|
||||
EXPECT_EQ("2001:db8:2::",
|
||||
alloc.pickAddress(subnet_, cc_, duid_, IOAddress("::")).toText());
|
||||
for (int i = 1; i < 255; i++) {
|
||||
stringstream exp;
|
||||
exp << "2001:db8:2:" << hex << i << dec << "::";
|
||||
EXPECT_EQ(exp.str(),
|
||||
alloc.pickAddress(subnet_, cc_, duid_, IOAddress("::")).toText());
|
||||
|
||||
}
|
||||
EXPECT_EQ("2001:db8:2:ff::",
|
||||
alloc.pickAddress(subnet_, cc_, duid_, IOAddress("::")).toText());
|
||||
|
||||
// Ok, we've iterated over all prefixes in all pools. We now wrap around.
|
||||
// We're looping over now (iterating over first pool again)
|
||||
EXPECT_EQ("2001:db8::",
|
||||
alloc.pickAddress(subnet_, cc_, duid_, IOAddress("::")).toText());
|
||||
EXPECT_EQ("2001:db8:0:10::",
|
||||
alloc.pickAddress(subnet_, cc_, duid_, IOAddress("::")).toText());
|
||||
}
|
||||
|
||||
// This test verifies that the allocator omits pools with non-matching client classes.
|
||||
TEST_F(IterativeAllocatorTest6, prefixStepOutClass) {
|
||||
NakedIterativeAllocator alloc(Lease::TYPE_PD);
|
||||
|
||||
subnet_.reset(new Subnet6(IOAddress("2001:db8::"), 32, 1, 2, 3, 4));
|
||||
|
||||
Pool6Ptr pool1(new Pool6(Lease::TYPE_PD, IOAddress("2001:db8::"), 56, 60));
|
||||
Pool6Ptr pool2(new Pool6(Lease::TYPE_PD, IOAddress("2001:db8:1::"), 48, 48));
|
||||
Pool6Ptr pool3(new Pool6(Lease::TYPE_PD, IOAddress("2001:db8:2::"), 56, 64));
|
||||
// Set pool2 in foo
|
||||
pool2->allowClientClass("foo");
|
||||
subnet_->addPool(pool1);
|
||||
subnet_->addPool(pool2);
|
||||
subnet_->addPool(pool3);
|
||||
|
||||
// We have a 2001:db8::/48 subnet that has 3 pools defined in it:
|
||||
// 2001:db8::/56 split into /60 prefixes (16 leases) (or 2001:db8:0:X0::)
|
||||
// 2001:db8:1::/48 split into a single /48 prefix (just 1 lease)
|
||||
// 2001:db8:2::/56 split into /64 prefixes (256 leases) (or 2001:db8:2:XX::)
|
||||
|
||||
// First pool check (Let's check over all 16 leases)
|
||||
EXPECT_EQ("2001:db8::",
|
||||
alloc.pickAddress(subnet_, cc_, duid_, IOAddress("::")).toText());
|
||||
EXPECT_EQ("2001:db8:0:10::",
|
||||
alloc.pickAddress(subnet_, cc_, duid_, IOAddress("::")).toText());
|
||||
EXPECT_EQ("2001:db8:0:20::",
|
||||
alloc.pickAddress(subnet_, cc_, duid_, IOAddress("::")).toText());
|
||||
EXPECT_EQ("2001:db8:0:30::",
|
||||
alloc.pickAddress(subnet_, cc_, duid_, IOAddress("::")).toText());
|
||||
EXPECT_EQ("2001:db8:0:40::",
|
||||
alloc.pickAddress(subnet_, cc_, duid_, IOAddress("::")).toText());
|
||||
EXPECT_EQ("2001:db8:0:50::",
|
||||
alloc.pickAddress(subnet_, cc_, duid_, IOAddress("::")).toText());
|
||||
EXPECT_EQ("2001:db8:0:60::",
|
||||
alloc.pickAddress(subnet_, cc_, duid_, IOAddress("::")).toText());
|
||||
EXPECT_EQ("2001:db8:0:70::",
|
||||
alloc.pickAddress(subnet_, cc_, duid_, IOAddress("::")).toText());
|
||||
EXPECT_EQ("2001:db8:0:80::",
|
||||
alloc.pickAddress(subnet_, cc_, duid_, IOAddress("::")).toText());
|
||||
EXPECT_EQ("2001:db8:0:90::",
|
||||
alloc.pickAddress(subnet_, cc_, duid_, IOAddress("::")).toText());
|
||||
EXPECT_EQ("2001:db8:0:a0::",
|
||||
alloc.pickAddress(subnet_, cc_, duid_, IOAddress("::")).toText());
|
||||
EXPECT_EQ("2001:db8:0:b0::",
|
||||
alloc.pickAddress(subnet_, cc_, duid_, IOAddress("::")).toText());
|
||||
EXPECT_EQ("2001:db8:0:c0::",
|
||||
alloc.pickAddress(subnet_, cc_, duid_, IOAddress("::")).toText());
|
||||
EXPECT_EQ("2001:db8:0:d0::",
|
||||
alloc.pickAddress(subnet_, cc_, duid_, IOAddress("::")).toText());
|
||||
EXPECT_EQ("2001:db8:0:e0::",
|
||||
alloc.pickAddress(subnet_, cc_, duid_, IOAddress("::")).toText());
|
||||
EXPECT_EQ("2001:db8:0:f0::",
|
||||
alloc.pickAddress(subnet_, cc_, duid_, IOAddress("::")).toText());
|
||||
|
||||
// The second pool is skipped
|
||||
|
||||
// Third pool (256 leases, let's check first and last explicitly and the
|
||||
// rest over in a pool
|
||||
EXPECT_EQ("2001:db8:2::",
|
||||
alloc.pickAddress(subnet_, cc_, duid_, IOAddress("::")).toText());
|
||||
for (int i = 1; i < 255; i++) {
|
||||
stringstream exp;
|
||||
exp << "2001:db8:2:" << hex << i << dec << "::";
|
||||
EXPECT_EQ(exp.str(),
|
||||
alloc.pickAddress(subnet_, cc_, duid_, IOAddress("::")).toText());
|
||||
|
||||
}
|
||||
EXPECT_EQ("2001:db8:2:ff::",
|
||||
alloc.pickAddress(subnet_, cc_, duid_, IOAddress("::")).toText());
|
||||
|
||||
// Ok, we've iterated over all prefixes in all pools. We now wrap around.
|
||||
// We're looping over now (iterating over first pool again)
|
||||
EXPECT_EQ("2001:db8::",
|
||||
alloc.pickAddress(subnet_, cc_, duid_, IOAddress("::")).toText());
|
||||
EXPECT_EQ("2001:db8:0:10::",
|
||||
alloc.pickAddress(subnet_, cc_, duid_, IOAddress("::")).toText());
|
||||
}
|
||||
|
||||
// This test verifies that the iterative allocator can step over addresses.
|
||||
TEST_F(IterativeAllocatorTest6, addressIncrease) {
|
||||
NakedIterativeAllocator alloc(Lease::TYPE_NA);
|
||||
|
||||
// Let's pick the first address
|
||||
IOAddress addr1 = alloc.pickAddress(subnet_, cc_, duid_, IOAddress("2001:db8:1::10"));
|
||||
|
||||
// Check that we can indeed pick the first address from the pool
|
||||
EXPECT_EQ("2001:db8:1::10", addr1.toText());
|
||||
|
||||
// Check that addresses can be increased properly
|
||||
checkAddrIncrease(alloc, "2001:db8::9", "2001:db8::a");
|
||||
checkAddrIncrease(alloc, "2001:db8::f", "2001:db8::10");
|
||||
checkAddrIncrease(alloc, "2001:db8::10", "2001:db8::11");
|
||||
checkAddrIncrease(alloc, "2001:db8::ff", "2001:db8::100");
|
||||
checkAddrIncrease(alloc, "2001:db8::ffff", "2001:db8::1:0");
|
||||
checkAddrIncrease(alloc, "::", "::1");
|
||||
checkAddrIncrease(alloc, "ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff", "::");
|
||||
}
|
||||
|
||||
// This test verifies that the allocator can step over prefixes.
|
||||
TEST_F(IterativeAllocatorTest6, prefixIncrease) {
|
||||
NakedIterativeAllocator alloc(Lease::TYPE_PD);
|
||||
|
||||
// For /128 prefix, increasePrefix should work the same as addressIncrease
|
||||
checkPrefixIncrease(alloc, "2001:db8::9", 128, "2001:db8::a");
|
||||
checkPrefixIncrease(alloc, "2001:db8::f", 128, "2001:db8::10");
|
||||
checkPrefixIncrease(alloc, "2001:db8::10", 128, "2001:db8::11");
|
||||
checkPrefixIncrease(alloc, "2001:db8::ff", 128, "2001:db8::100");
|
||||
checkPrefixIncrease(alloc, "2001:db8::ffff", 128, "2001:db8::1:0");
|
||||
checkPrefixIncrease(alloc, "::", 128, "::1");
|
||||
checkPrefixIncrease(alloc, "ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff", 128, "::");
|
||||
|
||||
// Check that /64 prefixes can be generated
|
||||
checkPrefixIncrease(alloc, "2001:db8::", 64, "2001:db8:0:1::");
|
||||
|
||||
// Check that prefix length not divisible by 8 are working
|
||||
checkPrefixIncrease(alloc, "2001:db8::", 128, "2001:db8::1");
|
||||
checkPrefixIncrease(alloc, "2001:db8::", 127, "2001:db8::2");
|
||||
checkPrefixIncrease(alloc, "2001:db8::", 126, "2001:db8::4");
|
||||
checkPrefixIncrease(alloc, "2001:db8::", 125, "2001:db8::8");
|
||||
checkPrefixIncrease(alloc, "2001:db8::", 124, "2001:db8::10");
|
||||
checkPrefixIncrease(alloc, "2001:db8::", 123, "2001:db8::20");
|
||||
checkPrefixIncrease(alloc, "2001:db8::", 122, "2001:db8::40");
|
||||
checkPrefixIncrease(alloc, "2001:db8::", 121, "2001:db8::80");
|
||||
checkPrefixIncrease(alloc, "2001:db8::", 120, "2001:db8::100");
|
||||
|
||||
// These are not really useful cases, because there are bits set
|
||||
// int the last (128 - prefix_len) bits. Nevertheless, it shows
|
||||
// that the algorithm is working even in such cases
|
||||
checkPrefixIncrease(alloc, "2001:db8::1", 128, "2001:db8::2");
|
||||
checkPrefixIncrease(alloc, "2001:db8::1", 127, "2001:db8::3");
|
||||
checkPrefixIncrease(alloc, "2001:db8::1", 126, "2001:db8::5");
|
||||
checkPrefixIncrease(alloc, "2001:db8::1", 125, "2001:db8::9");
|
||||
checkPrefixIncrease(alloc, "2001:db8::1", 124, "2001:db8::11");
|
||||
checkPrefixIncrease(alloc, "2001:db8::1", 123, "2001:db8::21");
|
||||
checkPrefixIncrease(alloc, "2001:db8::1", 122, "2001:db8::41");
|
||||
checkPrefixIncrease(alloc, "2001:db8::1", 121, "2001:db8::81");
|
||||
checkPrefixIncrease(alloc, "2001:db8::1", 120, "2001:db8::101");
|
||||
|
||||
// Let's try out couple real life scenarios
|
||||
checkPrefixIncrease(alloc, "2001:db8:1:abcd::", 64, "2001:db8:1:abce::");
|
||||
checkPrefixIncrease(alloc, "2001:db8:1:abcd::", 60, "2001:db8:1:abdd::");
|
||||
checkPrefixIncrease(alloc, "2001:db8:1:abcd::", 56, "2001:db8:1:accd::");
|
||||
checkPrefixIncrease(alloc, "2001:db8:1:abcd::", 52, "2001:db8:1:bbcd::");
|
||||
|
||||
// And now let's try something over the top
|
||||
checkPrefixIncrease(alloc, "::", 1, "8000::");
|
||||
}
|
||||
|
||||
// This test verifies that the iterative allocator really walks over all addresses
|
||||
// in all pools in specified subnet. It also must not pick the same address twice
|
||||
// unless it runs out of pool space and must start over.
|
||||
TEST_F(IterativeAllocatorTest6, manyPools) {
|
||||
NakedIterativeAllocator alloc(Lease::TYPE_NA);
|
||||
|
||||
// let's start from 2, as there is 2001:db8:1::10 - 2001:db8:1::20 pool already.
|
||||
for (int i = 2; i < 10; ++i) {
|
||||
stringstream min, max;
|
||||
|
||||
min << "2001:db8:1::" << hex << i*16 + 1;
|
||||
max << "2001:db8:1::" << hex << i*16 + 9;
|
||||
|
||||
Pool6Ptr pool(new Pool6(Lease::TYPE_NA, IOAddress(min.str()),
|
||||
IOAddress(max.str())));
|
||||
subnet_->addPool(pool);
|
||||
}
|
||||
|
||||
int total = 17 + 8 * 9; // First pool (::10 - ::20) has 17 addresses in it,
|
||||
// there are 8 extra pools with 9 addresses in each.
|
||||
|
||||
// Let's keep picked addresses here and check their uniqueness.
|
||||
std::set<IOAddress> generated_addrs;
|
||||
int cnt = 0;
|
||||
while (++cnt) {
|
||||
IOAddress candidate = alloc.pickAddress(subnet_, cc_,
|
||||
duid_, IOAddress("::"));
|
||||
EXPECT_TRUE(subnet_->inPool(Lease::TYPE_NA, candidate));
|
||||
|
||||
// One way to easily verify that the iterative allocator really works is
|
||||
// to uncomment the following line and observe its output that it
|
||||
// covers all defined pools.
|
||||
// cout << candidate.toText() << endl;
|
||||
|
||||
if (generated_addrs.find(candidate) == generated_addrs.end()) {
|
||||
// We haven't had this.
|
||||
generated_addrs.insert(candidate);
|
||||
} else {
|
||||
// We have seen this address before. That should mean that we
|
||||
// iterated over all addresses.
|
||||
if (generated_addrs.size() == total) {
|
||||
// We have exactly the number of address in all pools.
|
||||
break;
|
||||
}
|
||||
ADD_FAILURE() << "Too many or not enough unique addresses generated.";
|
||||
break;
|
||||
}
|
||||
|
||||
if ( cnt>total ) {
|
||||
ADD_FAILURE() << "Too many unique addresses generated.";
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} // end of namespace isc::dhcp::test
|
||||
} // end of namespace isc::dhcp
|
||||
} // end of namespace isc
|
Loading…
x
Reference in New Issue
Block a user