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::pickAddress(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_);
// 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()) {
- // ok to access first element directly. We checked that pools is non-empty
- IOAddress next = pools[0]->getFirstAddress();
- subnet->setLastAllocated(pool_type_, next);
- return (next);
+ it = first;
}
- // Ok, we have a pool that the last address belonged to, let's use it.
+ 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;
+ }
+ }
- IOAddress next("::");
- if (!prefix) {
- next = IOAddress::increase(last); // basically addr++
- } else {
- 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");
+ 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);
}
- // Get the next prefix
- next = increasePrefix(last, pool6->getLength());
- }
- if ((*it)->inRange(next)) {
- // the next one is in the pool as well, so we haven't hit pool boundary yet
- subnet->setLastAllocated(pool_type_, next);
- return (next);
+ // 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;
}
- // We hit pool boundary, let's try to jump to the next pool and try again
- ++it;
- if (it == pools.end()) {
- // Really out of luck today. That was the last pool. Let's rewind
- // to the beginning.
- next = pools[0]->getFirstAddress();
- subnet->setLastAllocated(pool_type_, next);
- return (next);
+ // Let's rewind to the beginning.
+ for (it = first; it != pools.end(); ++it) {
+ if ((*it)->clientSupported(client_classes)) {
+ (*it)->setLastAllocated((*it)->getFirstAddress());
+ (*it)->resetLastAllocated();
+ }
}
- // there is a next pool, let's try first address from it
- next = (*it)->getFirstAddress();
- subnet->setLastAllocated(pool_type_, next);
- return (next);
+ // 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)
isc::asiolink::IOAddress
AllocEngine::HashedAllocator::pickAddress(const SubnetPtr&,
+ const ClientClasses&,
const DuidPtr&,
const IOAddress&) {
isc_throw(NotImplemented, "Hashed allocator is not implemented");
isc::asiolink::IOAddress
AllocEngine::RandomAllocator::pickAddress(const SubnetPtr&,
+ const ClientClasses&,
const DuidPtr&,
const IOAddress&) {
isc_throw(NotImplemented, "Random allocator is not implemented");
/// function when it belongs to a shared network.
/// @param lease_type Type of the lease.
/// @param address IPv6 address or prefix to be checked.
+/// @param check_subnet if true only subnets are checked else both subnets
+/// and pools are checked
///
/// @return true if address belongs to a pool in a selected subnet or in
/// a pool within any of the subnets belonging to the current shared network.
bool
inAllowedPool(AllocEngine::ClientContext6& ctx, const Lease::Type& lease_type,
- const IOAddress& address) {
+ const IOAddress& address, bool check_subnet) {
// If the subnet belongs to a shared network we will be iterating
// over the subnets that belong to this shared network.
Subnet6Ptr current_subnet = ctx.subnet_;
while (current_subnet) {
if (current_subnet->clientSupported(ctx.query_->getClasses())) {
- if (current_subnet->inPool(lease_type, address)) {
- return (true);
+ if (check_subnet) {
+ if (current_subnet->inPool(lease_type, address)) {
+ return (true);
+ }
+ } else {
+ if (current_subnet->inPool(lease_type, address,
+ ctx.query_->getClasses())) {
+ return (true);
+ }
}
}
// check if the hint is in pool and is available
// This is equivalent of subnet->inPool(hint), but returns the pool
pool = boost::dynamic_pointer_cast<Pool6>
- (subnet->getPool(ctx.currentIA().type_, hint, false));
+ (subnet->getPool(ctx.currentIA().type_, ctx.query_->getClasses(), hint));
+
+ // check if the pool is allowed
+ if (pool && !pool->clientSupported(ctx.query_->getClasses())) {
+ pool.reset();
+ }
if (pool) {
// - we find a free address
// - we find an address for which the lease has expired
// - we exhaust number of tries
- uint64_t max_attempts = (attempts_ > 0 ? attempts_ :
- subnet->getPoolCapacity(ctx.currentIA().type_));
+ uint64_t possible_attempts =
+ subnet->getPoolCapacity(ctx.currentIA().type_,
+ ctx.query_->getClasses());
+ // Try next subnet if there is no chance to get something
+ if (possible_attempts == 0) {
+ subnet = subnet->getNextSubnet(original_subnet);
+ continue;
+ }
+ uint64_t max_attempts = (attempts_ > 0 ? attempts_ : possible_attempts);
for (uint64_t i = 0; i < max_attempts; ++i) {
++total_attempts;
- IOAddress candidate = allocator->pickAddress(subnet, ctx.duid_,
+ IOAddress candidate = allocator->pickAddress(subnet,
+ ctx.query_->getClasses(),
+ ctx.duid_,
hint);
/// In-pool reservations: Check if this address is reserved for someone
uint8_t prefix_len = 128;
if (ctx.currentIA().type_ == Lease::TYPE_PD) {
pool = boost::dynamic_pointer_cast<Pool6>(
- subnet->getPool(ctx.currentIA().type_, candidate, false));
+ subnet->getPool(ctx.currentIA().type_, ctx.query_->getClasses(), candidate));
if (pool) {
prefix_len = pool->getLength();
}
void
AllocEngine::removeNonmatchingReservedLeases6(ClientContext6& ctx,
Lease6Collection& existing_leases) {
- // If there are no leases (so nothing to remove) or
- // host reservation is disabled (so there are no reserved leases),
- // just return.
- if (existing_leases.empty() || !ctx.subnet_ ||
- (ctx.subnet_->getHostReservationMode() == Network::HR_DISABLED) ) {
+ // If there are no leases (so nothing to remove) just return.
+ if (existing_leases.empty() || !ctx.subnet_) {
+ return;
+ }
+ // If host reservation is disabled (so there are no reserved leases)
+ // use the simplified version.
+ if (ctx.subnet_->getHostReservationMode() == Network::HR_DISABLED) {
+ removeNonmatchingReservedNoHostLeases6(ctx, existing_leases);
return;
}
// be allocated to us from a dynamic pool, but we must check if
// this lease belongs to any pool. If it does, we can proceed to
// checking the next lease.
- if (!host && inAllowedPool(ctx, candidate->type_, candidate->addr_)) {
+ if (!host && inAllowedPool(ctx, candidate->type_,
+ candidate->addr_, false)) {
continue;
}
}
}
+void
+AllocEngine::removeNonmatchingReservedNoHostLeases6(ClientContext6& ctx,
+ Lease6Collection& existing_leases) {
+ // We need a copy, so we won't be iterating over a container and
+ // removing from it at the same time. It's only a copy of pointers,
+ // so the operation shouldn't be that expensive.
+ Lease6Collection copy = existing_leases;
+
+ BOOST_FOREACH(const Lease6Ptr& candidate, copy) {
+ // Lease can be allocated to us from a dynamic pool, but we must
+ // check if this lease belongs to any allowed pool. If it does,
+ // we can proceed to checking the next lease.
+ if (inAllowedPool(ctx, candidate->type_,
+ candidate->addr_, false)) {
+ continue;
+ }
+
+ // Remove this lease from LeaseMgr as it doesn't belong to a pool.
+ LeaseMgrFactory::instance().deleteLease(candidate->addr_);
+
+ // Update DNS if needed.
+ queueNCR(CHG_REMOVE, candidate);
+
+ // Need to decrease statistic for assigned addresses.
+ StatsMgr::instance().addValue(
+ StatsMgr::generateName("subnet", candidate->subnet_id_,
+ ctx.currentIA().type_ == Lease::TYPE_NA ?
+ "assigned-nas" : "assigned-pds"),
+ static_cast<int64_t>(-1));
+
+ // Add this to the list of removed leases.
+ ctx.currentIA().old_leases_.push_back(candidate);
+
+ // Let's remove this candidate from existing leases
+ removeLeases(existing_leases, candidate->addr_);
+ }
+}
+
bool
AllocEngine::removeLeases(Lease6Collection& container, const asiolink::IOAddress& addr) {
// If the lease is in the current subnet we need to account
// for the re-assignment of The lease.
- if (inAllowedPool(ctx, ctx.currentIA().type_, lease->addr_)) {
+ if (inAllowedPool(ctx, ctx.currentIA().type_,
+ lease->addr_, true)) {
StatsMgr::instance().addValue(
StatsMgr::generateName("subnet", lease->subnet_id_,
ctx.currentIA().type_ == Lease::TYPE_NA ?
/// within a shared network.
///
/// @todo Update this function to take client classification into account.
+/// @note client classification in pools (vs subnets) is checked
///
/// @param ctx Client context. Current subnet may be modified by this
/// function when it belongs to a shared network.
Subnet4Ptr current_subnet = ctx.subnet_;
while (current_subnet) {
- if (current_subnet->inPool(Lease::TYPE_V4, address)) {
+ if (current_subnet->inPool(Lease::TYPE_V4, address,
+ ctx.query_->getClasses())) {
// We found a subnet that this address belongs to, so it
// seems that this subnet is the good candidate for allocation.
// Let's update the selected subnet.
// If the client is requesting an address which is assigned to the client
// let's just renew this address. Also, renew this address if the client
// doesn't request any specific address.
+ // Added extra checks: the address is reserved or belongs to the dynamic
+ // pool for the case the pool class has changed before the request.
if (client_lease) {
- if ((client_lease->addr_ == ctx.requested_address_) ||
- ctx.requested_address_.isV4Zero()) {
+ if (((client_lease->addr_ == ctx.requested_address_) ||
+ ctx.requested_address_.isV4Zero()) &&
+ (hasAddressReservation(ctx) ||
+ inAllowedPool(ctx, client_lease->addr_))) {
LOG_DEBUG(alloc_engine_logger, ALLOC_ENGINE_DBG_TRACE,
ALLOC_ENGINE_V4_REQUEST_EXTEND_LEASE)
client_id = ctx.clientid_;
}
- const uint64_t max_attempts = (attempts_ > 0 ? attempts_ :
- subnet->getPoolCapacity(Lease::TYPE_V4));
+ uint64_t possible_attempts =
+ subnet->getPoolCapacity(Lease::TYPE_V4,
+ ctx.query_->getClasses());
+ uint64_t max_attempts = (attempts_ > 0 ? attempts_ : possible_attempts);
+ // Skip trying if there is no chance to get something
+ if (possible_attempts == 0) {
+ max_attempts = 0;
+ }
+
for (uint64_t i = 0; i < max_attempts; ++i) {
- IOAddress candidate = allocator->pickAddress(subnet, client_id,
+ IOAddress candidate = allocator->pickAddress(subnet,
+ ctx.query_->getClasses(),
+ client_id,
ctx.requested_address_);
// If address is not reserved for another client, try to allocate it.
if (!addressReserved(candidate, ctx)) {
#define ALLOC_ENGINE_H
#include <asiolink/io_address.h>
+#include <dhcp/classify.h>
#include <dhcp/duid.h>
#include <dhcp/hwaddr.h>
#include <dhcp/pkt4.h>
/// 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 DuidPtr& duid,
+ pickAddress(const SubnetPtr& subnet,
+ const ClientClasses& client_classes,
+ const DuidPtr& duid,
const isc::asiolink::IOAddress& hint) = 0;
/// @brief Default constructor.
/// @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
pickAddress(const SubnetPtr& subnet,
+ const ClientClasses& client_classes,
const DuidPtr& duid,
const isc::asiolink::IOAddress& hint);
protected:
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: 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 pickAddress(const SubnetPtr& subnet,
- const DuidPtr& duid,
- const isc::asiolink::IOAddress& hint);
+ virtual isc::asiolink::IOAddress
+ pickAddress(const SubnetPtr& subnet,
+ const ClientClasses& client_classes,
+ const DuidPtr& duid,
+ const isc::asiolink::IOAddress& hint);
};
/// @brief Random allocator that picks address randomly
/// @param hint the last address that was picked (ignored)
/// @return a random address from the pool
virtual isc::asiolink::IOAddress
- pickAddress(const SubnetPtr& subnet, const DuidPtr& duid,
+ pickAddress(const SubnetPtr& subnet,
+ const ClientClasses& client_classes,
+ const DuidPtr& duid,
const isc::asiolink::IOAddress& hint);
};
/// @brief Removes leases that are reserved for someone else.
///
/// Goes through the list specified in existing_leases and removes those that
- /// are reserved by someone else. The removed leases are added to the
- /// ctx.removed_leases_ collection.
+ /// are reserved by someone else or do not belong to an allowed pool.
+ /// The removed leases are added to the ctx.removed_leases_ collection.
///
/// @param ctx client context that contains all details (subnet, client-id, etc.)
/// @param existing_leases [in/out] leases that should be checked
removeNonmatchingReservedLeases6(ClientContext6& ctx,
Lease6Collection& existing_leases);
+ /// @brief Removes leases that are reserved for someone else.
+ ///
+ /// Simplified version of removeNonmatchingReservedLeases6 to be
+ /// used when host reservations are disabled.
+ ///
+ /// @param ctx client context that contains all details (subnet, client-id, etc.)
+ /// @param existing_leases [in/out] leases that should be checked
+ void
+ removeNonmatchingReservedNoHostLeases6(ClientContext6& ctx,
+ Lease6Collection& existing_leases);
+
/// @brief Removed leases that are not reserved for this client
///
/// This method iterates over existing_leases and will remove leases that are
<< " (" << option_data->getPosition() << ")");
}
}
+
+ // Client-class.
+ ConstElementPtr client_class = pool_structure->get("client-class");
+ if (client_class) {
+ string cclass = client_class->stringValue();
+ if (!cclass.empty()) {
+ pool->allowClientClass(cclass);
+ }
+ }
}
//****************************** Pool4Parser *************************
user_context_ = user_context;
}
+ ConstElementPtr client_class = pd_pool_->get("client-class");
+ if (client_class) {
+ client_class_ = client_class;
+ }
+
// Check the pool parameters. It will throw an exception if any
// of the required parameters are invalid.
try {
pool_->setContext(user_context_);
}
+
+ if (client_class_) {
+ string cclass = client_class_->stringValue();
+ if (!cclass.empty()) {
+ pool_->allowClientClass(cclass);
+ }
+ }
+
// Add the local pool to the external storage ptr.
pools->push_back(pool_);
}
CfgOptionPtr options_;
isc::data::ConstElementPtr user_context_;
+
+ isc::data::ConstElementPtr client_class_;
+
};
/// @brief Parser for a list of prefix delegation pools.
Pool::Pool(Lease::Type type, const isc::asiolink::IOAddress& first,
const isc::asiolink::IOAddress& last)
:id_(getNextID()), first_(first), last_(last), type_(type),
- capacity_(0), cfg_option_(new CfgOption()) {
+ capacity_(0), cfg_option_(new CfgOption()), white_list_(),
+ last_allocated_(first), last_allocated_valid_(false) {
}
bool Pool::inRange(const isc::asiolink::IOAddress& addr) const {
return (first_.smallerEqual(addr) && addr.smallerEqual(last_));
}
+bool Pool::clientSupported(const ClientClasses& classes) const {
+ if (white_list_.empty()) {
+ // There is no class defined for this pool, so we do
+ // support everyone.
+ return (true);
+ }
+
+ for (ClientClasses::const_iterator it = white_list_.begin();
+ it != white_list_.end(); ++it) {
+ if (classes.contains(*it)) {
+ return (true);
+ }
+ }
+
+ return (false);
+}
+
+void Pool::allowClientClass(const ClientClass& class_name) {
+ white_list_.insert(class_name);
+}
+
std::string
Pool::toText() const {
std::stringstream tmp;
// Set pool options
ConstCfgOptionPtr opts = getCfgOption();
map->set("option-data", opts->toElement());
+
+ // Set client-class
+ const ClientClasses& cclasses = getClientClasses();
+ if (cclasses.size() > 1) {
+ isc_throw(ToElementError, "client-class has too many items: "
+ << cclasses.size());
+ } else if (!cclasses.empty()) {
+ map->set("client-class", Element::create(*cclasses.cbegin()));
+ }
+
return (map);
}
-// Copyright (C) 2012-2017 Internet Systems Consortium, Inc. ("ISC")
+// Copyright (C) 2012-2018 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
#define POOL_H
#include <asiolink/io_address.h>
+#include <dhcp/classify.h>
#include <dhcp/option6_pdexclude.h>
#include <boost/shared_ptr.hpp>
#include <cc/data.h>
return (cfg_option_);
}
+ /// @Checks whether this pool supports client that belongs to
+ /// specified classes.
+ ///
+ /// @todo: currently doing the same than network which
+ /// is known to be improved.
+ ///
+ /// @param client_classes list of all classes the client belongs to
+ /// @return true if client can be supported, false otherwise
+ bool clientSupported(const ClientClasses& client_classes) const;
+
+ /// @brief Adds class class_name to the list of supported classes
+ ///
+ /// @param class_name client class to be supported by this pool
+ void allowClientClass(const ClientClass& class_name);
+
+ /// @brief returns the client class white list
+ ///
+ /// @note Currently white list is empty or has one element
+ /// @note The returned reference is only valid as long as the object
+ /// returned is valid.
+ ///
+ /// @return client classes @ref white_list_
+ const ClientClasses& getClientClasses() const {
+ return (white_list_);
+ }
+
+ /// @brief returns the last address that was tried from this pool
+ ///
+ /// @return address/prefix that was last tried from this pool
+ isc::asiolink::IOAddress getLastAllocated() const {
+ return last_allocated_;
+ }
+
+ /// @brief checks if the last address is valid
+ /// @return true if the last address is valid
+ bool isLastAllocatedValid() const {
+ return last_allocated_valid_;
+ }
+
+ /// @brief sets the last address that was tried from this pool
+ ///
+ /// @param addr address/prefix to that was tried last
+ void setLastAllocated(const isc::asiolink::IOAddress& addr) {
+ last_allocated_ = addr;
+ last_allocated_valid_ = true;
+ }
+
+ /// @brief resets the last address to invalid
+ void resetLastAllocated() {
+ last_allocated_valid_ = false;
+ }
+
/// @brief Unparse a pool object.
///
/// @return A pointer to unparsed pool configuration.
/// @brief Pointer to the option data configuration for this pool.
CfgOptionPtr cfg_option_;
+
+ /// @brief Optional definition of a client class
+ ///
+ /// @ref Network::white_list_
+ ClientClasses white_list_;
+
+ /// @brief Last allocated address
+ /// See @ref isc::dhcp::subnet::last_allocated_ia_
+ /// Initialized and reset to first
+ isc::asiolink::IOAddress last_allocated_;
+
+ /// @brief Status of last allocated address
+ bool last_allocated_valid_;
};
/// @brief Pool information for IPv4 addresses
}
}
+uint64_t
+Subnet::getPoolCapacity(Lease::Type type,
+ const ClientClasses& client_classes) const {
+ switch (type) {
+ case Lease::TYPE_V4:
+ case Lease::TYPE_NA:
+ return sumPoolCapacity(pools_, client_classes);
+ case Lease::TYPE_TA:
+ return sumPoolCapacity(pools_ta_, client_classes);
+ case Lease::TYPE_PD:
+ return sumPoolCapacity(pools_pd_, client_classes);
+ default:
+ isc_throw(BadValue, "Unsupported pool type: "
+ << static_cast<int>(type));
+ }
+}
+
uint64_t
Subnet::sumPoolCapacity(const PoolCollection& pools) const {
uint64_t sum = 0;
return (sum);
}
+uint64_t
+Subnet::sumPoolCapacity(const PoolCollection& pools,
+ const ClientClasses& client_classes) const {
+ uint64_t sum = 0;
+ for (PoolCollection::const_iterator p = pools.begin(); p != pools.end(); ++p) {
+ if (!(*p)->clientSupported(client_classes)) {
+ continue;
+ }
+ uint64_t x = (*p)->getCapacity();
+
+ // Check if we can add it. If sum + x > uint64::max, then we would have
+ // overflown if we tried to add it.
+ if (x > std::numeric_limits<uint64_t>::max() - sum) {
+ return (std::numeric_limits<uint64_t>::max());
+ }
+
+ sum += x;
+ }
+
+ return (sum);
+}
+
void Subnet4::checkType(Lease::Type type) const {
if (type != Lease::TYPE_V4) {
isc_throw(BadValue, "Only TYPE_V4 is allowed for Subnet4");
return (candidate);
}
+const PoolPtr Subnet::getPool(Lease::Type type,
+ const ClientClasses& client_classes,
+ const isc::asiolink::IOAddress& hint) const {
+ // check if the type is valid (and throw if it isn't)
+ checkType(type);
+
+ const PoolCollection& pools = getPools(type);
+
+ PoolPtr candidate;
+
+ if (!pools.empty()) {
+ PoolCollection::const_iterator ub =
+ std::upper_bound(pools.begin(), pools.end(), hint,
+ prefixLessThanFirstAddress);
+
+ if (ub != pools.begin()) {
+ --ub;
+ if ((*ub)->inRange(hint) && (*ub)->clientSupported(client_classes)) {
+ candidate = *ub;
+ }
+ }
+ }
+
+ // Return a pool or NULL if no match found.
+ return (candidate);
+}
+
void
Subnet::addPool(const PoolPtr& pool) {
// check if the type is valid (and throw if it isn't)
return (false);
}
+bool
+Subnet::inPool(Lease::Type type,
+ const isc::asiolink::IOAddress& addr,
+ const ClientClasses& client_classes) const {
+
+ // Let's start with checking if it even belongs to that subnet.
+ if ((type != Lease::TYPE_PD) && !inRange(addr)) {
+ return (false);
+ }
+
+ const PoolCollection& pools = getPools(type);
+
+ for (PoolCollection::const_iterator pool = pools.begin();
+ pool != pools.end(); ++pool) {
+ if (!(*pool)->clientSupported(client_classes)) {
+ continue;
+ }
+ if ((*pool)->inRange(addr)) {
+ return (true);
+ }
+ }
+ // There's no pool that address belongs to
+ return (false);
+}
+
bool
Subnet::poolOverlaps(const Lease::Type& pool_type, const PoolPtr& pool) const {
const PoolCollection& pools = getPools(pool_type);
// Set pool options
ConstCfgOptionPtr opts = (*pool)->getCfgOption();
pool_map->set("option-data", opts->toElement());
+ // Set client-class
+ const ClientClasses& cclasses = (*pool)->getClientClasses();
+ if (cclasses.size() > 1) {
+ isc_throw(ToElementError, "client-class has too many items: "
+ << cclasses.size());
+ } else if (!cclasses.empty()) {
+ pool_map->set("client-class", Element::create(*cclasses.cbegin()));
+ }
// Push on the pool list
pool_list->add(pool_map);
}
// Set pool options
ConstCfgOptionPtr opts = pdpool->getCfgOption();
pool_map->set("option-data", opts->toElement());
+ // Set client-class
+ const ClientClasses& cclasses = pdpool->getClientClasses();
+ if (cclasses.size() > 1) {
+ isc_throw(ToElementError, "client-class has too many items: "
+ << cclasses.size());
+ } else if (!cclasses.empty()) {
+ pool_map->set("client-class", Element::create(*cclasses.cbegin()));
+ }
// Push on the pool list
pdpool_list->add(pool_map);
}
/// @return true if the address is in any of the pools
bool inPool(Lease::Type type, const isc::asiolink::IOAddress& addr) const;
- /// @brief returns the last address that was tried from this pool
+ /// @brief checks if the specified address is in allowed pools
+ ///
+ /// This takes also into account client classes
+ ///
+ /// @param type type of pools to iterate over
+ /// @param addr this address will be checked if it belongs to any pools in
+ /// that subnet
+ /// @param client_classes client class list which must be allowed
+ /// @return true if the address is in any of the allowed pools
+ bool inPool(Lease::Type type,
+ const isc::asiolink::IOAddress& addr,
+ const ClientClasses& client_classes) const;
+
+ /// @brief returns the last address that was tried from this subnet
///
/// This method returns the last address that was attempted to be allocated
/// from this subnet. This is used as helper information for the next
/// iteration of the allocation algorithm.
///
- /// @todo: Define map<SubnetID, IOAddress> somewhere in the
+ /// @todo: Define map<SubnetID, ClientClass, IOAddress> somewhere in the
/// AllocEngine::IterativeAllocator and keep the data there
///
/// @param type lease type to be returned
- /// @return address/prefix that was last tried from this pool
+ /// @return address/prefix that was last tried from this subnet
isc::asiolink::IOAddress getLastAllocated(Lease::Type type) const;
- /// @brief sets the last address that was tried from this pool
+ /// @brief sets the last address that was tried from this subnet
///
/// This method sets the last address that was attempted to be allocated
/// from this subnet. This is used as helper information for the next
/// iteration of the allocation algorithm.
///
- /// @todo: Define map<SubnetID, IOAddress> somewhere in the
+ /// @todo: Define map<SubnetID, ClientClass, IOAddress> somewhere in the
/// AllocEngine::IterativeAllocator and keep the data there
/// @param addr address/prefix to that was tried last
/// @param type lease type to be set
const PoolPtr getPool(Lease::Type type, const isc::asiolink::IOAddress& addr,
bool anypool = true) const;
+ /// @brief Returns a pool that specified address belongs to with classes
+ ///
+ /// Variant using only pools allowing given classes
+ ///
+ /// @param type pool type that the pool is looked for
+ /// @param client_classes client class list which must be allowed
+ /// @param addr address that the returned pool should cover (optional)
+ const PoolPtr getPool(Lease::Type type,
+ const ClientClasses& client_classes,
+ const isc::asiolink::IOAddress& addr) const;
+
/// @brief Returns a pool without any address specified
///
/// @param type pool type that the pool is looked for
/// @param type type of the lease
uint64_t getPoolCapacity(Lease::Type type) const;
+ /// @brief Returns the number of possible leases for specified lease type
+ /// allowed for a client which belongs to classes.
+ ///
+ /// @param type type of the lease
+ /// @param client_classes List of classes the client belongs to.
+ uint64_t getPoolCapacity(Lease::Type type,
+ const ClientClasses& client_classes) const;
+
/// @brief Returns textual representation of the subnet (e.g.
/// "2001:db8::/64")
///
/// @return sum of possible leases
uint64_t sumPoolCapacity(const PoolCollection& pools) const;
+ /// @brief returns a sum of possible leases in all pools allowing classes
+ /// @param pools list of pools
+ /// @param client_classes list of classes
+ /// @return sum of possible/allowed leases
+ uint64_t sumPoolCapacity(const PoolCollection& pools,
+ const ClientClasses& client_classes) const;
+
/// @brief Checks if the specified pool overlaps with an existing pool.
///
/// @param pool_type Pool type.
alloc(new NakedAllocEngine::IterativeAllocator(Lease::TYPE_V4));
for (int i = 0; i < 1000; ++i) {
- IOAddress candidate = alloc->pickAddress(subnet_, clientid_,
+ 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
std::set<IOAddress> generated_addrs;
int cnt = 0;
while (++cnt) {
- IOAddress candidate = alloc.pickAddress(subnet_, clientid_, IOAddress("0.0.0.0"));
+ 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
EXPECT_EQ("192.0.2.17", lease->addr_.toText());
}
+// This test verifies that the server can offer an address from a
+// different subnet than orginally selected, when the address pool in
+// the first subnet requires another class.
+TEST_F(SharedNetworkAlloc4Test, discoverSharedNetworkPoolClassification) {
+
+ // Try to offer address from subnet1. There is one address available
+ // so it should be offerred.
+ AllocEngine::ClientContext4
+ ctx(subnet1_, ClientIdPtr(), hwaddr_, IOAddress::IPV4_ZERO_ADDRESS(),
+ false, false, "host.example.com.", true);
+ ctx.query_.reset(new Pkt4(DHCPDISCOVER, 1234));
+ Lease4Ptr lease = engine_.allocateLease4(ctx);
+ ASSERT_TRUE(lease);
+ EXPECT_TRUE(subnet1_->inPool(Lease::TYPE_V4, lease->addr_));
+
+ // Apply restrictions on the pool1. This should be only assigned
+ // to clients belonging to cable-modem class.
+ pool1_->allowClientClass("cable-modem");
+
+ // The allocation engine should determine that the pool1 is not
+ // available for the client not belonging to the cable-modem class.
+ // Instead, it should offer an address from subnet2 that belongs
+ // to the same shared network.
+ ctx.subnet_ = subnet1_;
+ lease = engine_.allocateLease4(ctx);
+ ASSERT_TRUE(lease);
+ EXPECT_TRUE(subnet2_->inPool(Lease::TYPE_V4, lease->addr_));
+
+ // Assign cable-modem class and try again. This time, we should
+ // offer an address from the pool1.
+ ctx.query_->addClass(ClientClass("cable-modem"));
+
+ ctx.subnet_ = subnet1_;
+ lease = engine_.allocateLease4(ctx);
+ ASSERT_TRUE(lease);
+ EXPECT_EQ("192.0.2.17", lease->addr_.toText());
+}
+
// Test that reservations within shared network take precedence over the
// existing leases regardless in which subnet belonging to a shared network
// reservations belong.
EXPECT_TRUE(subnet2_->inPool(Lease::TYPE_V4, lease->addr_));
}
+// This test verifies that the server can assign an address from a
+// different subnet than orginally selected, when the address pool in
+// the first subnet requires another class.
+TEST_F(SharedNetworkAlloc4Test, requestSharedNetworkPoolClassification) {
+ // Try to offer address from subnet1. There is one address available
+ // so it should be offerred.
+ AllocEngine::ClientContext4
+ ctx(subnet1_, ClientIdPtr(), hwaddr_, IOAddress::IPV4_ZERO_ADDRESS(),
+ false, false, "host.example.com.", false);
+ ctx.query_.reset(new Pkt4(DHCPREQUEST, 1234));
+ Lease4Ptr lease = engine_.allocateLease4(ctx);
+ ASSERT_TRUE(lease);
+ EXPECT_TRUE(subnet1_->inPool(Lease::TYPE_V4, lease->addr_));
+
+ // Remove the lease so as we can start over.
+ LeaseMgrFactory::instance().deleteLease(lease->addr_);
+
+ // Apply restrictions on the pool1. This should be only assigned
+ // to clients belonging to cable-modem class.
+ pool1_->allowClientClass("cable-modem");
+
+ // The allocation engine should determine that the pool1 is not
+ // available for the client not belonging to the cable-modem class.
+ // Instead, it should assign an address from subnet2 that belongs
+ // to the same shared network.
+ ctx.subnet_ = subnet1_;
+ lease = engine_.allocateLease4(ctx);
+ ASSERT_TRUE(lease);
+ EXPECT_TRUE(subnet2_->inPool(Lease::TYPE_V4, lease->addr_));
+
+ // Remove the lease so as we can start over.
+ LeaseMgrFactory::instance().deleteLease(lease->addr_);
+
+ // Assign cable-modem class and try again. This time, we should
+ // offer an address from the pool1.
+ ctx.query_->addClass(ClientClass("cable-modem"));
+
+ ctx.subnet_ = subnet1_;
+ lease = engine_.allocateLease4(ctx);
+ ASSERT_TRUE(lease);
+ EXPECT_TRUE(subnet1_->inPool(Lease::TYPE_V4, lease->addr_));
+
+ // Let's now remove the client from the cable-modem class and try
+ // to renew the address. The engine should determine that the
+ // client doesn't have access to the pool1 anymore and
+ // assign an address from unrestricted pool.
+ ctx.query_.reset(new Pkt4(DHCPREQUEST, 1234));
+ ctx.subnet_ = subnet1_;
+ lease = engine_.allocateLease4(ctx);
+ ASSERT_TRUE(lease);
+ EXPECT_TRUE(subnet2_->inPool(Lease::TYPE_V4, lease->addr_));
+}
+
// Test that reservations within shared network take precedence over the
// existing leases regardless in which subnet belonging to a shared network
// reservations belong (DHCPREQUEST case).
alloc(new NakedAllocEngine::IterativeAllocator(Lease::TYPE_NA));
for (int i = 0; i < 1000; ++i) {
- IOAddress candidate = alloc->pickAddress(subnet_, duid_, IOAddress("::"));
+ 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(AllocEngine6Test, IterativeAllocator_class) {
+ boost::scoped_ptr<NakedAllocEngine::Allocator>
+ alloc(new NakedAllocEngine::IterativeAllocator(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_));
+ }
+}
+
TEST_F(AllocEngine6Test, IterativeAllocatorAddrStep) {
NakedAllocEngine::NakedIterativeAllocator alloc(Lease::TYPE_NA);
subnet_->addPool(pool3);
// Let's check the first pool (5 addresses here)
- EXPECT_EQ("2001:db8:1::1", alloc.pickAddress(subnet_, duid_, IOAddress("::")).toText());
- EXPECT_EQ("2001:db8:1::2", alloc.pickAddress(subnet_, duid_, IOAddress("::")).toText());
- EXPECT_EQ("2001:db8:1::3", alloc.pickAddress(subnet_, duid_, IOAddress("::")).toText());
- EXPECT_EQ("2001:db8:1::4", alloc.pickAddress(subnet_, duid_, IOAddress("::")).toText());
- EXPECT_EQ("2001:db8:1::5", alloc.pickAddress(subnet_, duid_, IOAddress("::")).toText());
+ 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_, duid_, IOAddress("::")).toText());
+ 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_, duid_, IOAddress("::")).toText());
- EXPECT_EQ("2001:db8:1::106", alloc.pickAddress(subnet_, duid_, IOAddress("::")).toText());
+ 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_, duid_, IOAddress("::")).toText());
- EXPECT_EQ("2001:db8:1::2", alloc.pickAddress(subnet_, duid_, IOAddress("::")).toText());
+ 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());
+}
+
+TEST_F(AllocEngine6Test, IterativeAllocatorAddrStepInClass) {
+ NakedAllocEngine::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());
+}
+
+TEST_F(AllocEngine6Test, IterativeAllocatorAddrStepOutClass) {
+ NakedAllocEngine::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());
}
TEST_F(AllocEngine6Test, IterativeAllocatorPrefixStep) {
// 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_, duid_, IOAddress("::")).toText());
- EXPECT_EQ("2001:db8:0:10::", alloc.pickAddress(subnet_, duid_, IOAddress("::")).toText());
- EXPECT_EQ("2001:db8:0:20::", alloc.pickAddress(subnet_, duid_, IOAddress("::")).toText());
- EXPECT_EQ("2001:db8:0:30::", alloc.pickAddress(subnet_, duid_, IOAddress("::")).toText());
- EXPECT_EQ("2001:db8:0:40::", alloc.pickAddress(subnet_, duid_, IOAddress("::")).toText());
- EXPECT_EQ("2001:db8:0:50::", alloc.pickAddress(subnet_, duid_, IOAddress("::")).toText());
- EXPECT_EQ("2001:db8:0:60::", alloc.pickAddress(subnet_, duid_, IOAddress("::")).toText());
- EXPECT_EQ("2001:db8:0:70::", alloc.pickAddress(subnet_, duid_, IOAddress("::")).toText());
- EXPECT_EQ("2001:db8:0:80::", alloc.pickAddress(subnet_, duid_, IOAddress("::")).toText());
- EXPECT_EQ("2001:db8:0:90::", alloc.pickAddress(subnet_, duid_, IOAddress("::")).toText());
- EXPECT_EQ("2001:db8:0:a0::", alloc.pickAddress(subnet_, duid_, IOAddress("::")).toText());
- EXPECT_EQ("2001:db8:0:b0::", alloc.pickAddress(subnet_, duid_, IOAddress("::")).toText());
- EXPECT_EQ("2001:db8:0:c0::", alloc.pickAddress(subnet_, duid_, IOAddress("::")).toText());
- EXPECT_EQ("2001:db8:0:d0::", alloc.pickAddress(subnet_, duid_, IOAddress("::")).toText());
- EXPECT_EQ("2001:db8:0:e0::", alloc.pickAddress(subnet_, duid_, IOAddress("::")).toText());
- EXPECT_EQ("2001:db8:0:f0::", alloc.pickAddress(subnet_, duid_, IOAddress("::")).toText());
+ 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_, duid_, IOAddress("::")).toText());
+ 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_, duid_, IOAddress("::")).toText());
+ 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_, duid_, IOAddress("::")).toText());
+ EXPECT_EQ(exp.str(), alloc.pickAddress(subnet_, cc_, duid_, IOAddress("::")).toText());
}
- EXPECT_EQ("2001:db8:2:ff::", alloc.pickAddress(subnet_, 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_, duid_, IOAddress("::")).toText());
- EXPECT_EQ("2001:db8:0:10::", alloc.pickAddress(subnet_, duid_, IOAddress("::")).toText());
+ EXPECT_EQ("2001:db8::", alloc.pickAddress(subnet_, cc_, duid_, IOAddress("::")).toText());
+ EXPECT_EQ("2001:db8:0:10::", alloc.pickAddress(subnet_, cc_, duid_, IOAddress("::")).toText());
+}
+
+TEST_F(AllocEngine6Test, IterativeAllocatorPrefixStepInClass) {
+ NakedAllocEngine::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());
+}
+
+TEST_F(AllocEngine6Test, IterativeAllocatorPrefixStepOutClass) {
+ NakedAllocEngine::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
NakedAllocEngine::NakedIterativeAllocator alloc(Lease::TYPE_NA);
// Let's pick the first address
- IOAddress addr1 = alloc.pickAddress(subnet_, duid_, IOAddress("2001:db8:1::10"));
+ 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());
std::set<IOAddress> generated_addrs;
int cnt = 0;
while (++cnt) {
- IOAddress candidate = alloc.pickAddress(subnet_, duid_, IOAddress("::"));
+ 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
EXPECT_EQ("2001:db8:1::1", lease->addr_.toText());
}
+// This test verifies that the server can offer an address from a
+// different subnet than orginally selected, when the address pool in
+// the first subnet requires another class.
+TEST_F(SharedNetworkAlloc6Test, solicitSharedNetworkPoolClassification) {
+ // Try to offer address from subnet1. There is an address available so
+ // it should be offerred.
+ Pkt6Ptr query(new Pkt6(DHCPV6_SOLICIT, 1234));
+ AllocEngine::ClientContext6 ctx(subnet1_, duid_, false, false, "", true,
+ query);
+ ctx.currentIA().iaid_ = iaid_;
+
+ Lease6Ptr lease;
+ ASSERT_NO_THROW(lease = expectOneLease(engine_.allocateLeases6(ctx)));
+ ASSERT_TRUE(lease);
+ ASSERT_TRUE(subnet1_->inRange(lease->addr_));
+
+ // Apply restrictions on the pool1. This should be only assigned
+ // to clients belonging to cable-modem class.
+ pool1_->allowClientClass("cable-modem");
+
+ // The allocation engine should determine that the pool1 is not
+ // available for the client not belonging to the cable-modem class.
+ // Instead, it should offer an address from subnet2 that belongs
+ // to the same shared network.
+ AllocEngine::ClientContext6 ctx2(subnet1_, duid_, false, false, "", true,
+ query);
+ ctx2.currentIA().iaid_ = iaid_;
+ ctx2.query_ = query;
+ ASSERT_NO_THROW(lease = expectOneLease(engine_.allocateLeases6(ctx2)));
+ ASSERT_TRUE(lease);
+ ASSERT_TRUE(subnet2_->inRange(lease->addr_));
+
+ AllocEngine::ClientContext6 ctx3(subnet1_, duid_, false, false, "", true,
+ query);
+ ctx3.currentIA().iaid_ = iaid_;
+ ctx3.query_ = query;
+
+ AllocEngine::ClientContext6 ctx4(subnet1_, duid_, false, false, "", true,
+ query);
+ ctx4.currentIA().iaid_ = iaid_;
+ ctx4.query_ = query;
+
+ // Assign cable-modem class and try again. This time, we should
+ // offer an address from the pool1_.
+ ctx4.query_->addClass(ClientClass("cable-modem"));
+
+ AllocEngine::findReservation(ctx4);
+ ASSERT_NO_THROW(lease = expectOneLease(engine_.allocateLeases6(ctx4)));
+ ASSERT_TRUE(lease);
+ EXPECT_EQ("2001:db8:1::1", lease->addr_.toText());
+}
+
// This test verifies that the client is offerred a reserved address
// even if this address belongs to another subnet within the same
// shared network.
:IterativeAllocator(type) {
}
+ using AllocEngine::IterativeAllocator::increaseAddress;
using AllocEngine::IterativeAllocator::increasePrefix;
};
};
/// @param input address to be increased
/// @param exp_output expected address after increase
void
- checkAddrIncrease(NakedAllocEngine::NakedIterativeAllocator&,
+ checkAddrIncrease(NakedAllocEngine::NakedIterativeAllocator& alloc,
std::string input, std::string exp_output) {
- EXPECT_EQ(exp_output, asiolink::IOAddress::increase(
- asiolink::IOAddress(input)).toText());
+ EXPECT_EQ(exp_output, alloc.increaseAddress(asiolink::IOAddress(input),
+ false, 0).toText());
}
/// @brief Checks if increasePrefix() works as expected
bool fqdn_fwd_; ///< Perform forward update for a lease.
bool fqdn_rev_; ///< Perform reverse update for a lease.
LeaseMgrFactory factory_; ///< pointer to LeaseMgr factory
+ ClientClasses cc_; ///< client classes
};
/// @brief Used in Allocation Engine tests for IPv4
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.
};
-// Copyright (C) 2014-2017 Internet Systems Consortium, Inc. ("ISC")
+// Copyright (C) 2014-2018 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
Subnet4Ptr subnet(new Subnet4(IOAddress("192.0.2.0"), 24, 1, 2, 3, 123));
Pool4Ptr pool1(new Pool4(IOAddress("192.0.2.1"), IOAddress("192.0.2.10")));
Pool4Ptr pool2(new Pool4(IOAddress("192.0.2.64"), 26));
+ pool2->allowClientClass("bar");
std::string json1 = "{ \"comment\": \"foo\", \"version\": 1 }";
data::ElementPtr ctx1 = data::Element::fromJSON(json1);
" },{\n"
" \"option-data\": [ ],\n"
" \"pool\": \"192.0.2.64/26\"\n,"
+ " \"client-class\": \"bar\",\n"
" \"user-context\": { \"foo\": \"bar\" }\n"
" }\n"
" ]\n"
-// Copyright (C) 2014-2017 Internet Systems Consortium, Inc. ("ISC")
+// Copyright (C) 2014-2018 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
IOAddress("2001:db8:1::100"),
IOAddress("2001:db8:1::199")));
Pool6Ptr pool2(new Pool6(Lease::TYPE_NA, IOAddress("2001:db8:1:1::"), 64));
+ pool2->allowClientClass("bar");
std::string json1 = "{ \"comment\": \"foo\", \"version\": 1 }";
data::ElementPtr ctx1 = data::Element::fromJSON(json1);
" \"option-data\": [ ]\n"
" },{\n"
" \"pool\": \"2001:db8:1:1::/64\",\n"
+ " \"client-class\": \"bar\",\n"
" \"user-context\": { \"foo\": \"bar\" },\n"
" \"option-data\": [ ]\n"
" }\n"
IOAddress("2001:db8:2::"), 48, 64));
Pool6Ptr pdpool2(new Pool6(IOAddress("2001:db8:3::"), 48, 56,
IOAddress("2001:db8:3::"), 64));
+ pdpool2->allowClientClass("bar");
data::ElementPtr ctx1 = data::Element::fromJSON("{ \"foo\": [ \"bar\" ] }");
pdpool1->setContext(ctx1);
" \"delegated-len\": 56,\n"
" \"excluded-prefix\": \"2001:db8:3::\",\n"
" \"excluded-prefix-len\": 64,\n"
- " \"option-data\": [ ]\n"
+ " \"option-data\": [ ],\n"
+ " \"client-class\": \"bar\"\n"
" }\n"
" ],\n"
" \"option-data\": [ ]\n"
EXPECT_EQ(ctx->str(), pool->getContext()->str());
}
+// This test checks that handling for client-class is valid.
+TEST(Pool4Test, clientClass) {
+ // Create a pool.
+ Pool4Ptr pool(new Pool4(IOAddress("192.0.2.0"),
+ IOAddress("192.0.2.255")));
+
+ // This client does not belong to any class.
+ isc::dhcp::ClientClasses no_class;
+
+ // This client belongs to foo only.
+ isc::dhcp::ClientClasses foo_class;
+ foo_class.insert("foo");
+
+ // This client belongs to bar only. I like that client.
+ isc::dhcp::ClientClasses bar_class;
+ bar_class.insert("bar");
+
+ // This client belongs to foo, bar and baz classes.
+ isc::dhcp::ClientClasses three_classes;
+ three_classes.insert("foo");
+ three_classes.insert("bar");
+ three_classes.insert("baz");
+
+ // No class restrictions defined, any client should be supported
+ EXPECT_EQ(0, pool->getClientClasses().size());
+ EXPECT_TRUE(pool->clientSupported(no_class));
+ EXPECT_TRUE(pool->clientSupported(foo_class));
+ EXPECT_TRUE(pool->clientSupported(bar_class));
+ EXPECT_TRUE(pool->clientSupported(three_classes));
+
+ // Let's allow only clients belonging to "bar" class.
+ pool->allowClientClass("bar");
+ EXPECT_EQ(1, pool->getClientClasses().size());
+
+ EXPECT_FALSE(pool->clientSupported(no_class));
+ EXPECT_FALSE(pool->clientSupported(foo_class));
+ EXPECT_TRUE(pool->clientSupported(bar_class));
+ EXPECT_TRUE(pool->clientSupported(three_classes));
+}
+
+// This test checks that handling for multiple client-classes is valid.
+TEST(Pool4Test, clientClasses) {
+ // Create a pool.
+ Pool4Ptr pool(new Pool4(IOAddress("192.0.2.0"),
+ IOAddress("192.0.2.255")));
+
+ // This client does not belong to any class.
+ isc::dhcp::ClientClasses no_class;
+
+ // This client belongs to foo only.
+ isc::dhcp::ClientClasses foo_class;
+ foo_class.insert("foo");
+
+ // This client belongs to bar only. I like that client.
+ isc::dhcp::ClientClasses bar_class;
+ bar_class.insert("bar");
+
+ // No class restrictions defined, any client should be supported
+ EXPECT_EQ(0, pool->getClientClasses().size());
+ EXPECT_TRUE(pool->clientSupported(no_class));
+ EXPECT_TRUE(pool->clientSupported(foo_class));
+ EXPECT_TRUE(pool->clientSupported(bar_class));
+
+ // Let's allow clients belonging to "bar" or "foo" class.
+ pool->allowClientClass("bar");
+ pool->allowClientClass("foo");
+ EXPECT_EQ(2, pool->getClientClasses().size());
+
+ // Class-less clients are to be rejected.
+ EXPECT_FALSE(pool->clientSupported(no_class));
+
+ // Clients in foo class should be accepted.
+ EXPECT_TRUE(pool->clientSupported(foo_class));
+
+ // Clients in bar class should be accepted as well.
+ EXPECT_TRUE(pool->clientSupported(bar_class));
+}
+
+// This test checks that handling for last allocated address/prefix is valid.
+TEST(Pool4Test, lastAllocated) {
+ // Create a pool.
+ IOAddress first("192.0.2.0");
+ Pool4Ptr pool(new Pool4(first, IOAddress("192.0.2.255")));
+
+ // Initial values are first invalid.
+ EXPECT_EQ(first.toText(), pool->getLastAllocated().toText());
+ EXPECT_FALSE(pool->isLastAllocatedValid());
+
+ // Now set last allocated
+ IOAddress addr("192.0.2.100");
+ EXPECT_NO_THROW(pool->setLastAllocated(addr));
+ EXPECT_EQ(addr.toText(), pool->getLastAllocated().toText());
+ EXPECT_TRUE(pool->isLastAllocatedValid());
+
+ // Reset makes it invalid and does not touch address
+ pool->resetLastAllocated();
+ EXPECT_EQ(addr.toText(), pool->getLastAllocated().toText());
+ EXPECT_FALSE(pool->isLastAllocatedValid());
+}
+
TEST(Pool6Test, constructor_first_last) {
// let's construct 2001:db8:1:: - 2001:db8:1::ffff:ffff:ffff:ffff pool
EXPECT_EQ(ctx->str(), pool.getContext()->str());
}
+// This test checks that handling for client-class is valid.
+TEST(Pool6Test, clientClass) {
+ // Create a pool.
+ Pool6 pool(Lease::TYPE_NA, IOAddress("2001:db8::1"),
+ IOAddress("2001:db8::2"));
+
+ // This client does not belong to any class.
+ isc::dhcp::ClientClasses no_class;
+
+ // This client belongs to foo only.
+ isc::dhcp::ClientClasses foo_class;
+ foo_class.insert("foo");
+
+ // This client belongs to bar only. I like that client.
+ isc::dhcp::ClientClasses bar_class;
+ bar_class.insert("bar");
+
+ // This client belongs to foo, bar and baz classes.
+ isc::dhcp::ClientClasses three_classes;
+ three_classes.insert("foo");
+ three_classes.insert("bar");
+ three_classes.insert("baz");
+
+ // No class restrictions defined, any client should be supported
+ EXPECT_EQ(0, pool.getClientClasses().size());
+ EXPECT_TRUE(pool.clientSupported(no_class));
+ EXPECT_TRUE(pool.clientSupported(foo_class));
+ EXPECT_TRUE(pool.clientSupported(bar_class));
+ EXPECT_TRUE(pool.clientSupported(three_classes));
+
+ // Let's allow only clients belonging to "bar" class.
+ pool.allowClientClass("bar");
+ EXPECT_EQ(1, pool.getClientClasses().size());
+
+ EXPECT_FALSE(pool.clientSupported(no_class));
+ EXPECT_FALSE(pool.clientSupported(foo_class));
+ EXPECT_TRUE(pool.clientSupported(bar_class));
+ EXPECT_TRUE(pool.clientSupported(three_classes));
+}
+
+// This test checks that handling for multiple client-classes is valid.
+TEST(Pool6Test, clientClasses) {
+ // Create a pool.
+ Pool6 pool(Lease::TYPE_NA, IOAddress("2001:db8::1"),
+ IOAddress("2001:db8::2"));
+
+ // This client does not belong to any class.
+ isc::dhcp::ClientClasses no_class;
+
+ // This client belongs to foo only.
+ isc::dhcp::ClientClasses foo_class;
+ foo_class.insert("foo");
+
+ // This client belongs to bar only. I like that client.
+ isc::dhcp::ClientClasses bar_class;
+ bar_class.insert("bar");
+
+ // No class restrictions defined, any client should be supported
+ EXPECT_EQ(0, pool.getClientClasses().size());
+ EXPECT_TRUE(pool.clientSupported(no_class));
+ EXPECT_TRUE(pool.clientSupported(foo_class));
+ EXPECT_TRUE(pool.clientSupported(bar_class));
+
+ // Let's allow clients belonging to "bar" or "foo" class.
+ pool.allowClientClass("bar");
+ pool.allowClientClass("foo");
+ EXPECT_EQ(2, pool.getClientClasses().size());
+
+ // Class-less clients are to be rejected.
+ EXPECT_FALSE(pool.clientSupported(no_class));
+
+ // Clients in foo class should be accepted.
+ EXPECT_TRUE(pool.clientSupported(foo_class));
+
+ // Clients in bar class should be accepted as well.
+ EXPECT_TRUE(pool.clientSupported(bar_class));
+}
+
+// This test checks that handling for last allocated address/prefix is valid.
+TEST(Pool6Test, lastAllocated) {
+ // Create a pool.
+ IOAddress first("2001:db8::1");
+ Pool6 pool(Lease::TYPE_NA, first, IOAddress("2001:db8::200"));
+
+ // Initial values are first invalid.
+ EXPECT_EQ(first.toText(), pool.getLastAllocated().toText());
+ EXPECT_FALSE(pool.isLastAllocatedValid());
+
+ // Now set last allocated
+ IOAddress addr("2001:db8::100");
+ EXPECT_NO_THROW(pool.setLastAllocated(addr));
+ EXPECT_EQ(addr.toText(), pool.getLastAllocated().toText());
+ EXPECT_TRUE(pool.isLastAllocatedValid());
+
+ // Reset makes it invalid and does not touch address
+ pool.resetLastAllocated();
+ EXPECT_EQ(addr.toText(), pool.getLastAllocated().toText());
+ EXPECT_FALSE(pool.isLastAllocatedValid());
+}
+
}; // end of anonymous namespace
PoolPtr pool1(new Pool4(IOAddress("192.1.2.0"), 25));
PoolPtr pool2(new Pool4(IOAddress("192.1.2.128"), 26));
PoolPtr pool3(new Pool4(IOAddress("192.1.2.192"), 30));
+ pool3->allowClientClass("bar");
+ PoolPtr pool4(new Pool4(IOAddress("192.1.2.200"), 30));
// Add pools in reverse order to make sure that they get ordered by
// first address.
- EXPECT_NO_THROW(subnet->addPool(pool3));
+ EXPECT_NO_THROW(subnet->addPool(pool4));
// If there's only one pool, get that pool
PoolPtr mypool = subnet->getAnyPool(Lease::TYPE_V4);
- EXPECT_EQ(mypool, pool3);
+ EXPECT_EQ(mypool, pool4);
+ EXPECT_NO_THROW(subnet->addPool(pool3));
EXPECT_NO_THROW(subnet->addPool(pool2));
EXPECT_NO_THROW(subnet->addPool(pool1));
// If we provide a hint, we should get a pool that this hint belongs to
ASSERT_NO_THROW(mypool = subnet->getPool(Lease::TYPE_V4,
- IOAddress("192.1.2.195")));
- EXPECT_EQ(mypool, pool3);
+ IOAddress("192.1.2.201")));
+ EXPECT_EQ(mypool, pool4);
ASSERT_NO_THROW(mypool = subnet->getPool(Lease::TYPE_V4,
IOAddress("192.1.2.129")));
// third parameter prevents it from returning "any" available
// pool if a good match is not found.
ASSERT_NO_THROW(mypool = subnet->getPool(Lease::TYPE_V4,
- IOAddress("192.1.2.200"),
+ IOAddress("192.1.2.210"),
false));
EXPECT_FALSE(mypool);
IOAddress("192.1.1.254"),
false));
EXPECT_FALSE(mypool);
+
+ // Now play with classes
+
+ // This client does not belong to any class.
+ isc::dhcp::ClientClasses no_class;
+
+ // This client belongs to foo only.
+ isc::dhcp::ClientClasses foo_class;
+ foo_class.insert("foo");
+
+ // This client belongs to bar only. I like that client.
+ isc::dhcp::ClientClasses bar_class;
+ bar_class.insert("bar");
+
+ // This client belongs to foo, bar and baz classes.
+ isc::dhcp::ClientClasses three_classes;
+ three_classes.insert("foo");
+ three_classes.insert("bar");
+ three_classes.insert("baz");
+
+ // If we provide a hint, we should get a pool that this hint belongs to
+ ASSERT_NO_THROW(mypool = subnet->getPool(Lease::TYPE_V4, no_class,
+ IOAddress("192.1.2.201")));
+ EXPECT_EQ(mypool, pool4);
+
+ ASSERT_NO_THROW(mypool = subnet->getPool(Lease::TYPE_V4, no_class,
+ IOAddress("192.1.2.129")));
+ EXPECT_EQ(mypool, pool2);
+
+ ASSERT_NO_THROW(mypool = subnet->getPool(Lease::TYPE_V4, no_class,
+ IOAddress("192.1.2.64")));
+ EXPECT_EQ(mypool, pool1);
+
+ // Specify addresses which don't belong to any existing pools.
+ ASSERT_NO_THROW(mypool = subnet->getPool(Lease::TYPE_V4, three_classes,
+ IOAddress("192.1.2.210")));
+ EXPECT_FALSE(mypool);
+
+ // Pool3 requires a member of bar
+ ASSERT_NO_THROW(mypool = subnet->getPool(Lease::TYPE_V4, no_class,
+ IOAddress("192.1.2.195")));
+ EXPECT_FALSE(mypool);
+ ASSERT_NO_THROW(mypool = subnet->getPool(Lease::TYPE_V4, foo_class,
+ IOAddress("192.1.2.195")));
+ EXPECT_FALSE(mypool);
+ ASSERT_NO_THROW(mypool = subnet->getPool(Lease::TYPE_V4, bar_class,
+ IOAddress("192.1.2.195")));
+ EXPECT_EQ(mypool, pool3);
+ ASSERT_NO_THROW(mypool = subnet->getPool(Lease::TYPE_V4, three_classes,
+ IOAddress("192.1.2.195")));
+ EXPECT_EQ(mypool, pool3);
}
// Check if it's possible to get specified number of possible leases for
PoolPtr pool3(new Pool4(IOAddress("192.1.2.192"), 30));
subnet->addPool(pool3);
EXPECT_EQ(196, subnet->getPoolCapacity(Lease::TYPE_V4));
+
+ // Let's add a forth pool /30. This one has 4 addresses.
+ PoolPtr pool4(new Pool4(IOAddress("192.1.2.200"), 30));
+ subnet->addPool(pool4);
+ EXPECT_EQ(200, subnet->getPoolCapacity(Lease::TYPE_V4));
+
+ // Now play with classes
+
+ // This client does not belong to any class.
+ isc::dhcp::ClientClasses no_class;
+
+ // This client belongs to foo only.
+ isc::dhcp::ClientClasses foo_class;
+ foo_class.insert("foo");
+
+ // This client belongs to bar only. I like that client.
+ isc::dhcp::ClientClasses bar_class;
+ bar_class.insert("bar");
+
+ // This client belongs to foo, bar and baz classes.
+ isc::dhcp::ClientClasses three_classes;
+ three_classes.insert("foo");
+ three_classes.insert("bar");
+ three_classes.insert("baz");
+
+ pool3->allowClientClass("bar");
+
+ // Pool3 requires a member of bar
+ EXPECT_EQ(196, subnet->getPoolCapacity(Lease::TYPE_V4, no_class));
+ EXPECT_EQ(196, subnet->getPoolCapacity(Lease::TYPE_V4, foo_class));
+ EXPECT_EQ(200, subnet->getPoolCapacity(Lease::TYPE_V4, bar_class));
+ EXPECT_EQ(200, subnet->getPoolCapacity(Lease::TYPE_V4, three_classes));
}
// Checks that it is not allowed to add invalid pools.
// the first address that is in range, in pool
EXPECT_TRUE(subnet->inRange(IOAddress("192.2.0.0")));
- EXPECT_TRUE (subnet->inPool(Lease::TYPE_V4, IOAddress("192.2.0.0")));
+ EXPECT_TRUE(subnet->inPool(Lease::TYPE_V4, IOAddress("192.2.0.0")));
// let's try something in the middle as well
EXPECT_TRUE(subnet->inRange(IOAddress("192.2.3.4")));
- EXPECT_TRUE (subnet->inPool(Lease::TYPE_V4, IOAddress("192.2.3.4")));
+ EXPECT_TRUE(subnet->inPool(Lease::TYPE_V4, IOAddress("192.2.3.4")));
// the last address that is in range, in pool
EXPECT_TRUE(subnet->inRange(IOAddress("192.2.255.255")));
- EXPECT_TRUE (subnet->inPool(Lease::TYPE_V4, IOAddress("192.2.255.255")));
+ EXPECT_TRUE(subnet->inPool(Lease::TYPE_V4, IOAddress("192.2.255.255")));
// the first address that is in range, but out of pool
EXPECT_TRUE(subnet->inRange(IOAddress("192.3.0.0")));
EXPECT_FALSE(subnet->inPool(Lease::TYPE_V4, IOAddress("192.3.0.0")));
+
+ // Add with classes
+ pool1->allowClientClass("bar");
+ EXPECT_TRUE(subnet->inPool(Lease::TYPE_V4, IOAddress("192.2.3.4")));
+
+ // This client does not belong to any class.
+ isc::dhcp::ClientClasses no_class;
+ EXPECT_FALSE(subnet->inPool(Lease::TYPE_V4, IOAddress("192.2.3.4"), no_class));
+
+ // This client belongs to foo only
+ isc::dhcp::ClientClasses foo_class;
+ foo_class.insert("foo");
+ EXPECT_FALSE(subnet->inPool(Lease::TYPE_V4, IOAddress("192.2.3.4"), foo_class));
+
+ // This client belongs to bar only. I like that client.
+ isc::dhcp::ClientClasses bar_class;
+ bar_class.insert("bar");
+ EXPECT_TRUE(subnet->inPool(Lease::TYPE_V4, IOAddress("192.2.3.4"), bar_class));
+
+ // This client belongs to foo, bar and baz classes.
+ isc::dhcp::ClientClasses three_classes;
+ three_classes.insert("foo");
+ three_classes.insert("bar");
+ three_classes.insert("baz");
+ EXPECT_TRUE(subnet->inPool(Lease::TYPE_V4, IOAddress("192.2.3.4"), three_classes));
}
// This test checks if the toText() method returns text representation
EXPECT_EQ(uint64_t(4294967296ull + 4294967296ull + 65536),
subnet->getPoolCapacity(Lease::TYPE_NA));
+ // Now play with classes
+
+ // This client does not belong to any class.
+ isc::dhcp::ClientClasses no_class;
+
+ // This client belongs to foo only.
+ isc::dhcp::ClientClasses foo_class;
+ foo_class.insert("foo");
+
+ // This client belongs to bar only. I like that client.
+ isc::dhcp::ClientClasses bar_class;
+ bar_class.insert("bar");
+
+ // This client belongs to foo, bar and baz classes.
+ isc::dhcp::ClientClasses three_classes;
+ three_classes.insert("foo");
+ three_classes.insert("bar");
+ three_classes.insert("baz");
+
+ pool3->allowClientClass("bar");
+
+ // Pool3 requires a member of bar
+ EXPECT_EQ(uint64_t(4294967296ull + 65536),
+ subnet->getPoolCapacity(Lease::TYPE_NA, no_class));
+ EXPECT_EQ(uint64_t(4294967296ull + 65536),
+ subnet->getPoolCapacity(Lease::TYPE_NA, foo_class));
+ EXPECT_EQ(uint64_t(4294967296ull + 4294967296ull + 65536),
+ subnet->getPoolCapacity(Lease::TYPE_NA, bar_class));
+ EXPECT_EQ(uint64_t(4294967296ull + 4294967296ull + 65536),
+ subnet->getPoolCapacity(Lease::TYPE_NA, three_classes));
+
// This is 2^64 prefixes. We're overflown uint64_t.
PoolPtr pool4(new Pool6(Lease::TYPE_NA, IOAddress("2001:db8:1:4::"), 64));
subnet->addPool(pool4);
subnet->addPool(pool5);
EXPECT_EQ(std::numeric_limits<uint64_t>::max(),
subnet->getPoolCapacity(Lease::TYPE_NA));
+
}
// Test checks whether the number of prefixes available in the pools are
mypool = subnet->getPool(Lease::TYPE_NA, IOAddress("2001:db8:1:3::dead:beef"));
EXPECT_EQ(mypool, pool3);
+
+ // Now play with classes
+
+ // This client does not belong to any class.
+ isc::dhcp::ClientClasses no_class;
+
+ // This client belongs to foo only.
+ isc::dhcp::ClientClasses foo_class;
+ foo_class.insert("foo");
+
+ // This client belongs to bar only. I like that client.
+ isc::dhcp::ClientClasses bar_class;
+ bar_class.insert("bar");
+
+ // This client belongs to foo, bar and baz classes.
+ isc::dhcp::ClientClasses three_classes;
+ three_classes.insert("foo");
+ three_classes.insert("bar");
+ three_classes.insert("baz");
+
+ pool3->allowClientClass("bar");
+
+ // Pool3 requires a member of bar
+ mypool = subnet->getPool(Lease::TYPE_NA, no_class,
+ IOAddress("2001:db8:1:3::dead:beef"));
+ EXPECT_FALSE(mypool);
+ mypool = subnet->getPool(Lease::TYPE_NA, foo_class,
+ IOAddress("2001:db8:1:3::dead:beef"));
+ EXPECT_FALSE(mypool);
+ mypool = subnet->getPool(Lease::TYPE_NA, bar_class,
+ IOAddress("2001:db8:1:3::dead:beef"));
+ EXPECT_EQ(mypool, pool3);
+ mypool = subnet->getPool(Lease::TYPE_NA, three_classes,
+ IOAddress("2001:db8:1:3::dead:beef"));
+ EXPECT_EQ(mypool, pool3);
}
// Check if Subnet6 supports different types of pools properly.
// the first address that is in range, in pool
EXPECT_TRUE(subnet->inRange(IOAddress("2001:db8::10")));
- EXPECT_TRUE (subnet->inPool(Lease::TYPE_NA, IOAddress("2001:db8::10")));
+ EXPECT_TRUE(subnet->inPool(Lease::TYPE_NA, IOAddress("2001:db8::10")));
// let's try something in the middle as well
EXPECT_TRUE(subnet->inRange(IOAddress("2001:db8::18")));
- EXPECT_TRUE (subnet->inPool(Lease::TYPE_NA, IOAddress("2001:db8::18")));
+ EXPECT_TRUE(subnet->inPool(Lease::TYPE_NA, IOAddress("2001:db8::18")));
// the last address that is in range, in pool
EXPECT_TRUE(subnet->inRange(IOAddress("2001:db8::20")));
// the first address that is in range, but out of pool
EXPECT_TRUE(subnet->inRange(IOAddress("2001:db8::21")));
EXPECT_FALSE(subnet->inPool(Lease::TYPE_NA, IOAddress("2001:db8::21")));
+
+ // Add with classes
+ pool1->allowClientClass("bar");
+ EXPECT_TRUE(subnet->inPool(Lease::TYPE_NA, IOAddress("2001:db8::18")));
+
+ // This client does not belong to any class.
+ isc::dhcp::ClientClasses no_class;
+ EXPECT_FALSE(subnet->inPool(Lease::TYPE_NA, IOAddress("2001:db8::18"), no_class));
+
+ // This client belongs to foo only
+ isc::dhcp::ClientClasses foo_class;
+ foo_class.insert("foo");
+ EXPECT_FALSE(subnet->inPool(Lease::TYPE_NA, IOAddress("2001:db8::18"), foo_class));
+
+ // This client belongs to bar only. I like that client.
+ isc::dhcp::ClientClasses bar_class;
+ bar_class.insert("bar");
+ EXPECT_TRUE(subnet->inPool(Lease::TYPE_NA, IOAddress("2001:db8::18"), bar_class));
+
+ // This client belongs to foo, bar and baz classes.
+ isc::dhcp::ClientClasses three_classes;
+ three_classes.insert("foo");
+ three_classes.insert("bar");
+ three_classes.insert("baz");
+ EXPECT_TRUE(subnet->inPool(Lease::TYPE_NA, IOAddress("2001:db8::18"), three_classes));
}
// This test verifies that inRange() and inPool() methods work properly