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