#include <dhcpsrv/callout_handle_store.h>
#include <stats/stats_mgr.h>
#include <util/stopwatch.h>
+#include <util/threads/lock_guard.h>
#include <hooks/server_hooks.h>
#include <hooks/hooks_manager.h>
#include <algorithm>
#include <cstring>
-#include <sstream>
#include <limits>
-#include <vector>
+#include <sstream>
#include <stdint.h>
#include <string.h>
#include <utility>
+#include <vector>
+
using namespace isc::asiolink;
using namespace isc::dhcp;
using namespace isc::dhcp_ddns;
using namespace isc::hooks;
using namespace isc::stats;
+using namespace isc::util::thread;
namespace {
-
/// Structure that holds registered hook indexes
struct AllocEngineHooks {
int hook_index_lease4_select_; ///< index for "lease4_receive" hook point
// module is called.
AllocEngineHooks Hooks;
-}; // anonymous namespace
+} // namespace
namespace isc {
namespace dhcp {
-
-AllocEngine::IterativeAllocator::IterativeAllocator(Lease::Type lease_type)
- :Allocator(lease_type) {
+AllocEngine::IterativeAllocator::IterativeAllocator(Lease::Type lease_type) :
+ Allocator(lease_type) {
}
isc::asiolink::IOAddress
const ClientClasses& client_classes,
const DuidPtr&,
const IOAddress&) {
+ LockGuard<std::mutex> lock(&mutex_);
// Is this prefix allocation?
bool prefix = pool_type_ == Lease::TYPE_PD;
return (last);
}
-AllocEngine::HashedAllocator::HashedAllocator(Lease::Type lease_type)
- :Allocator(lease_type) {
+AllocEngine::HashedAllocator::HashedAllocator(Lease::Type lease_type) :
+ Allocator(lease_type) {
isc_throw(NotImplemented, "Hashed allocator is not implemented");
}
-
isc::asiolink::IOAddress
AllocEngine::HashedAllocator::pickAddress(const SubnetPtr&,
const ClientClasses&,
isc_throw(NotImplemented, "Hashed allocator is not implemented");
}
-AllocEngine::RandomAllocator::RandomAllocator(Lease::Type lease_type)
- :Allocator(lease_type) {
+AllocEngine::RandomAllocator::RandomAllocator(Lease::Type lease_type) :
+ Allocator(lease_type) {
isc_throw(NotImplemented, "Random allocator is not implemented");
}
-
isc::asiolink::IOAddress
AllocEngine::RandomAllocator::pickAddress(const SubnetPtr&,
const ClientClasses&,
isc_throw(NotImplemented, "Random allocator is not implemented");
}
-
-AllocEngine::AllocEngine(AllocType engine_type, uint64_t attempts,
- bool ipv6)
- : attempts_(attempts), incomplete_v4_reclamations_(0),
- incomplete_v6_reclamations_(0) {
-
+AllocEngine::AllocEngine(AllocType engine_type, uint64_t attempts, bool ipv6) :
+ attempts_(attempts), incomplete_v4_reclamations_(0),
+ incomplete_v6_reclamations_(0) {
// Choose the basic (normal address) lease type
Lease::Type basic_type = ipv6 ? Lease::TYPE_NA : Lease::TYPE_V4;
} // end of namespace isc
namespace {
-
/// @brief Checks if the specified address belongs to one of the subnets
/// within a shared network.
///
// 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) {
+ while (current_subnet) {
if (current_subnet->clientSupported(ctx.query_->getClasses())) {
if (check_subnet) {
if (current_subnet->inPool(lease_type, address)) {
}
-
// ##########################################################################
// # DHCPv6 lease allocation code starts here.
// ##########################################################################
namespace isc {
namespace dhcp {
-
AllocEngine::ClientContext6::ClientContext6()
: query_(), fake_allocation_(false), subnet_(), host_subnet_(), duid_(),
hwaddr_(), host_identifiers_(), hosts_(), fwd_dns_update_(false),
fwd_dns_update_(fwd_dns), rev_dns_update_(rev_dns), hostname_(hostname),
callout_handle_(callout_handle), allocated_resources_(), new_leases_(),
ias_() {
-
// Initialize host identifiers.
if (duid) {
addHostIdentifier(Host::IDENT_DUID, duid->getDuid());
// We can only search for the reservation if a subnet has been selected.
while (subnet) {
-
// Only makes sense to get reservations if the client has access
// to the class and host reservations are enabled.
if (subnet->clientSupported(ctx.query_->getClasses()) &&
if (host_map.count(subnet->getID()) > 0) {
ctx.hosts_[subnet->getID()] = host_map[subnet->getID()];
}
-
} else {
// Attempt to find a host using a specified identifier.
ConstHostPtr host = HostMgr::instance().get6(subnet->getID(),
}
}
}
-
}
// We need to get to the next subnet if this is a shared network. If it
return (host);
}
-
Lease6Collection
AllocEngine::allocateLeases6(ClientContext6& ctx) {
-
try {
if (!ctx.subnet_) {
isc_throw(InvalidOperation, "Subnet is required for IPv6 lease allocation");
// Case 1: There are no leases and there's a reservation for this host.
if (leases.empty() && !ctx.hosts_.empty()) {
-
LOG_DEBUG(alloc_engine_logger, ALLOC_ENGINE_DBG_TRACE,
ALLOC_ENGINE_V6_ALLOC_NO_LEASES_HR)
.arg(ctx.query_->getLabel());
// We will return these leases for the client, but we may need to update
// FQDN information.
} else if (!leases.empty() && ctx.hosts_.empty()) {
-
LOG_DEBUG(alloc_engine_logger, ALLOC_ENGINE_DBG_TRACE,
ALLOC_ENGINE_V6_ALLOC_LEASES_NO_HR)
.arg(ctx.query_->getLabel());
// Case 3: There are leases and there are reservations.
} else if (!leases.empty() && !ctx.hosts_.empty()) {
-
LOG_DEBUG(alloc_engine_logger, ALLOC_ENGINE_DBG_TRACE,
ALLOC_ENGINE_V6_ALLOC_LEASES_HR)
.arg(ctx.query_->getLabel());
}
return (leases);
}
-
-
} catch (const isc::Exception& e) {
-
// Some other error, return an empty lease.
LOG_ERROR(alloc_engine_logger, ALLOC_ENGINE_V6_ALLOC_ERROR)
.arg(ctx.query_->getLabel())
Lease6Collection
AllocEngine::allocateUnreservedLeases6(ClientContext6& ctx) {
-
AllocatorPtr allocator = getAllocator(ctx.currentIA().type_);
if (!allocator) {
CalloutHandle::CalloutNextStep callout_status = CalloutHandle::NEXT_STEP_CONTINUE;
while (subnet) {
-
if (!subnet->clientSupported(ctx.query_->getClasses())) {
subnet = subnet->getNextSubnet(original_subnet);
continue;
// 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_, ctx.query_->getClasses(),
- hint));
+ (subnet->getPool(ctx.currentIA().type_, ctx.query_->getClasses(), hint));
// check if the pool is allowed
if (pool && !pool->clientSupported(ctx.query_->getClasses())) {
}
if (pool) {
-
// Check which host reservation mode is supported in this subnet.
Network::HRMode hr_mode = subnet->getHostReservationMode();
/// @todo: We support only one hint for now
Lease6Ptr lease =
LeaseMgrFactory::instance().getLease6(ctx.currentIA().type_, hint);
- if (!lease) {
+ if (!lease) {
// In-pool reservations: Check if this address is reserved for someone
// else. There is no need to check for whom it is reserved, because if
// it has been reserved for us we would have already allocated a lease.
// no longer usable and we need to continue the regular
// allocation path.
if (lease) {
-
/// @todo: We support only one lease per ia for now
Lease6Collection collection;
collection.push_back(lease);
.arg(ctx.query_->getLabel())
.arg(hint.toText());
}
-
} else {
-
// If the lease is expired, we may likely reuse it, but...
if (lease->expired()) {
-
ConstHostPtr host;
if (hr_mode != Network::HR_DISABLED) {
host = HostMgr::instance().get6(subnet->getID(), hint);
// Let's check if there is a reservation for this address.
if (!host) {
-
// Copy an existing, expired lease so as it can be returned
// to the caller.
Lease6Ptr old_lease(new Lease6(*lease));
/// @todo: We support only one lease per ia for now
leases.push_back(lease);
return (leases);
-
} else {
LOG_DEBUG(alloc_engine_logger, ALLOC_ENGINE_DBG_TRACE,
ALLOC_ENGINE_V6_EXPIRED_HINT_RESERVED)
ctx.subnet_ = subnet = original_subnet;
while (subnet) {
-
if (!subnet->clientSupported(ctx.query_->getClasses())) {
subnet = subnet->getNextSubnet(original_subnet);
continue;
}
for (uint64_t i = 0; i < max_attempts; ++i) {
-
++total_attempts;
IOAddress candidate = allocator->pickAddress(subnet,
/// it has been reserved for us we would have already allocated a lease.
if (hr_mode == Network::HR_ALL &&
HostMgr::instance().get6(subnet->getID(), candidate)) {
-
// Don't allocate.
continue;
}
}
Lease6Ptr existing = LeaseMgrFactory::instance().getLease6(ctx.currentIA().type_,
- candidate);
- if (!existing) {
+ candidate);
+ if (!existing) {
// there's no existing lease for selected candidate, so it is
// free. Let's allocate it.
-
ctx.subnet_ = subnet;
Lease6Ptr lease = createLease6(ctx, candidate, prefix_len, callout_status);
if (lease) {
leases.push_back(lease);
return (leases);
-
} else if (ctx.callout_handle_ &&
(callout_status != CalloutHandle::NEXT_STEP_CONTINUE)) {
// Don't retry when the callout status is not continue.
void
AllocEngine::allocateReservedLeases6(ClientContext6& ctx,
Lease6Collection& existing_leases) {
-
// If there are no reservations or the reservation is v4, there's nothing to do.
if (ctx.hosts_.empty()) {
LOG_DEBUG(alloc_engine_logger, ALLOC_ENGINE_DBG_TRACE,
Subnet6Ptr subnet = ctx.subnet_;
while (subnet) {
-
SubnetID subnet_id = subnet->getID();
// No hosts for this subnet or the subnet not supported.
// If there's a lease for this address, let's not create it.
// It doesn't matter whether it is for this client or for someone else.
- if (!LeaseMgrFactory::instance().getLease6(ctx.currentIA().type_,
- addr)) {
-
+ if (!LeaseMgrFactory::instance().getLease6(ctx.currentIA().type_, addr)) {
// Let's remember the subnet from which the reserved address has been
// allocated. We'll use this subnet for allocating other reserved
// resources.
// ... and add it to the existing leases list.
existing_leases.push_back(lease);
-
if (ctx.currentIA().type_ == Lease::TYPE_NA) {
LOG_INFO(alloc_engine_logger, ALLOC_ENGINE_V6_HR_ADDR_GRANTED)
.arg(addr.toText())
// would work for any number of reservations.
return;
}
-
}
subnet = subnet->getNextSubnet(ctx.subnet_);
// If there's a lease for this address, let's not create it.
// It doesn't matter whether it is for this client or for someone else.
if (!LeaseMgrFactory::instance().getLease6(ctx.currentIA().type_, addr)) {
-
if (!ghost->getHostname().empty()) {
// If there is a hostname reservation here we should stick
// to this reservation. By updating the hostname in the
bool
AllocEngine::removeLeases(Lease6Collection& container, const asiolink::IOAddress& addr) {
-
bool removed = false;
for (Lease6Collection::iterator lease = container.begin();
lease != container.end(); ++lease) {
// leases for deletion, by setting appropriate pointers to NULL.
for (Lease6Collection::iterator lease = existing_leases.begin();
lease != existing_leases.end(); ++lease) {
-
// If there is reservation for this keep it.
IPv6Resrv resv = makeIPv6Resrv(*(*lease));
if (ctx.hasGlobalReservation(resv) ||
// If there's only one lease left, break the loop.
break;
}
-
}
// Remove all elements that we previously marked for deletion (those that
AllocEngine::reuseExpiredLease(Lease6Ptr& expired, ClientContext6& ctx,
uint8_t prefix_len,
CalloutHandle::CalloutNextStep& callout_status) {
-
if (!expired->expired()) {
isc_throw(BadValue, "Attempt to recycle lease that is still valid");
}
// Let's execute all callouts registered for lease6_select
if (ctx.callout_handle_ &&
HooksManager::getHooksManager().calloutsPresent(hook_index_lease6_select_)) {
-
// Use the RAII wrapper to make sure that the callout handle state is
// reset when this object goes out of scope. All hook points must do
// it to prevent possible circular dependency between the callout
const IOAddress& addr,
uint8_t prefix_len,
CalloutHandle::CalloutNextStep& callout_status) {
-
if (ctx.currentIA().type_ != Lease::TYPE_PD) {
prefix_len = 128; // non-PD lease types must be always /128
}
// Let's execute all callouts registered for lease6_select
if (ctx.callout_handle_ &&
HooksManager::getHooksManager().calloutsPresent(hook_index_lease6_select_)) {
-
// Use the RAII wrapper to make sure that the callout handle state is
// reset when this object goes out of scope. All hook points must do
// it to prevent possible circular dependency between the callout
*ctx.duid_,
ctx.currentIA().iaid_,
subnet->getID());
+
leases.insert(leases.end(), leases_subnet.begin(), leases_subnet.end());
subnet = subnet->getNextSubnet(ctx.subnet_);
}
-
if (!leases.empty()) {
LOG_DEBUG(alloc_engine_logger, ALLOC_ENGINE_DBG_TRACE,
ALLOC_ENGINE_V6_RENEW_REMOVE_RESERVED)
}
if (!ctx.hosts_.empty()) {
-
LOG_DEBUG(alloc_engine_logger, ALLOC_ENGINE_DBG_TRACE,
ALLOC_ENGINE_V6_RENEW_HR)
.arg(ctx.query_->getLabel());
// new leases during renewals. This is controlled with the
// allow_new_leases_in_renewals_ field.
if (leases.empty()) {
-
LOG_DEBUG(alloc_engine_logger, ALLOC_ENGINE_DBG_TRACE,
ALLOC_ENGINE_V6_EXTEND_ALLOC_UNRESERVED)
.arg(ctx.query_->getLabel());
}
return (leases);
-
} catch (const isc::Exception& e) {
-
// Some other error, return an empty lease.
LOG_ERROR(alloc_engine_logger, ALLOC_ENGINE_V6_EXTEND_ERROR)
.arg(ctx.query_->getLabel())
void
AllocEngine::extendLease6(ClientContext6& ctx, Lease6Ptr lease) {
-
if (!lease || !ctx.subnet_) {
return;
}
// Now that the lease has been reclaimed, we can go ahead and update it
// in the lease database.
LeaseMgrFactory::instance().updateLease6(lease);
-
} else {
// Copy back the original date to the lease. For MySQL it doesn't make
// much sense, but for memfile, the Lease6Ptr points to the actual lease
ctx.currentIA().changed_leases_.push_back(old_data);
}
-
Lease6Collection
AllocEngine::updateLeaseData(ClientContext6& ctx, const Lease6Collection& leases) {
Lease6Collection updated_leases;
lease->fqdn_fwd_ = ctx.fwd_dns_update_;
lease->fqdn_rev_ = ctx.rev_dns_update_;
lease->hostname_ = ctx.hostname_;
- if (!ctx.fake_allocation_) {
+ if (!ctx.fake_allocation_) {
if (lease->state_ == Lease::STATE_EXPIRED_RECLAIMED) {
// Transition lease state to default (aka assigned)
lease->state_ = Lease::STATE_DEFAULT;
AllocEngine::reclaimExpiredLeases6(const size_t max_leases, const uint16_t timeout,
const bool remove_lease,
const uint16_t max_unwarned_cycles) {
-
LOG_DEBUG(alloc_engine_logger, ALLOC_ENGINE_DBG_TRACE,
ALLOC_ENGINE_V6_LEASES_RECLAMATION_START)
.arg(max_leases)
leases.pop_back();
incomplete_reclamation = true;
}
-
} else {
// If there is no limitation on the number of leases to reclaim,
// we will try to process all. Hence, we don't mark it as incomplete
size_t leases_processed = 0;
BOOST_FOREACH(Lease6Ptr lease, leases) {
-
try {
// Reclaim the lease.
reclaimExpiredLease(lease, remove_lease, callout_handle);
++leases_processed;
-
} catch (const std::exception& ex) {
LOG_ERROR(alloc_engine_logger, ALLOC_ENGINE_V6_LEASE_RECLAMATION_FAILED)
.arg(lease->addr_.toText())
// We issued a warning, so let's now reset the counter.
incomplete_v6_reclamations_ = 0;
}
-
} else {
// This was a complete reclamation, so let's reset the counter.
incomplete_v6_reclamations_ = 0;
// Try to delete leases from the lease database.
LeaseMgr& lease_mgr = LeaseMgrFactory::instance();
deleted_leases = lease_mgr.deleteExpiredReclaimedLeases6(secs);
-
} catch (const std::exception& ex) {
LOG_ERROR(alloc_engine_logger, ALLOC_ENGINE_V6_RECLAIMED_LEASES_DELETE_FAILED)
.arg(ex.what());
.arg(deleted_leases);
}
-
void
AllocEngine::reclaimExpiredLeases4(const size_t max_leases, const uint16_t timeout,
const bool remove_lease,
const uint16_t max_unwarned_cycles) {
-
LOG_DEBUG(alloc_engine_logger, ALLOC_ENGINE_DBG_TRACE,
ALLOC_ENGINE_V4_LEASES_RECLAMATION_START)
.arg(max_leases)
leases.pop_back();
incomplete_reclamation = true;
}
-
} else {
// If there is no limitation on the number of leases to reclaim,
// we will try to process all. Hence, we don't mark it as incomplete
lease_mgr.getExpiredLeases4(leases, max_leases);
}
-
// Do not initialize the callout handle until we know if there are any
// lease4_expire callouts installed.
CalloutHandlePtr callout_handle;
size_t leases_processed = 0;
BOOST_FOREACH(Lease4Ptr lease, leases) {
-
try {
// Reclaim the lease.
reclaimExpiredLease(lease, remove_lease, callout_handle);
++leases_processed;
-
} catch (const std::exception& ex) {
LOG_ERROR(alloc_engine_logger, ALLOC_ENGINE_V4_LEASE_RECLAMATION_FAILED)
.arg(lease->addr_.toText())
// We issued a warning, so let's now reset the counter.
incomplete_v4_reclamations_ = 0;
}
-
} else {
// This was a complete reclamation, so let's reset the counter.
incomplete_v4_reclamations_ = 0;
AllocEngine::reclaimExpiredLease(const Lease6Ptr& lease,
const DbReclaimMode& reclaim_mode,
const CalloutHandlePtr& callout_handle) {
-
LOG_DEBUG(alloc_engine_logger, ALLOC_ENGINE_DBG_TRACE,
ALLOC_ENGINE_V6_LEASE_RECLAIM)
.arg(Pkt6::makeLabel(lease->duid_, lease->hwaddr_))
// it reclaims the lease itself. In this case the reclamation routine
// will not update DNS nor update the database.
bool skipped = false;
- if (callout_handle) {
+ if (callout_handle) {
// Use the RAII wrapper to make sure that the callout handle state is
// reset when this object goes out of scope. All hook points must do
// it to prevent possible circular dependency between the callout
/// Not sure if we need to support every possible status everywhere.
if (!skipped) {
-
// Generate removal name change request for D2, if required.
// This will return immediately if the DNS wasn't updated
// when the lease was created.
lease->subnet_id_,
"assigned-nas"),
int64_t(-1));
-
} else if (lease->type_ == Lease::TYPE_PD) {
// IA_PD
StatsMgr::instance().addValue(StatsMgr::generateName("subnet",
lease->subnet_id_,
"assigned-pds"),
int64_t(-1));
-
}
// Increase total number of reclaimed leases.
AllocEngine::reclaimExpiredLease(const Lease4Ptr& lease,
const DbReclaimMode& reclaim_mode,
const CalloutHandlePtr& callout_handle) {
-
LOG_DEBUG(alloc_engine_logger, ALLOC_ENGINE_DBG_TRACE,
ALLOC_ENGINE_V4_LEASE_RECLAIM)
.arg(Pkt4::makeLabel(lease->hwaddr_, lease->client_id_))
// it reclaims the lease itself. In this case the reclamation routine
// will not update DNS nor update the database.
bool skipped = false;
- if (callout_handle) {
+ if (callout_handle) {
// Use the RAII wrapper to make sure that the callout handle state is
// reset when this object goes out of scope. All hook points must do
// it to prevent possible circular dependency between the callout
/// Not sure if we need to support every possible status everywhere.
if (!skipped) {
-
// Generate removal name change request for D2, if required.
// This will return immediately if the DNS wasn't updated
// when the lease was created.
// Try to delete leases from the lease database.
LeaseMgr& lease_mgr = LeaseMgrFactory::instance();
deleted_leases = lease_mgr.deleteExpiredReclaimedLeases4(secs);
-
} catch (const std::exception& ex) {
LOG_ERROR(alloc_engine_logger, ALLOC_ENGINE_V4_RECLAIMED_LEASES_DELETE_FAILED)
.arg(ex.what());
bool
AllocEngine::reclaimDeclined(const Lease4Ptr& lease) {
-
if (!lease || (lease->state_ != Lease::STATE_DECLINED) ) {
return (true);
}
if (HooksManager::getHooksManager().calloutsPresent(Hooks.hook_index_lease4_recover_)) {
-
// Let's use a static callout handle. It will be initialized the first
// time lease4_recover is called and will keep to that value.
static CalloutHandlePtr callout_handle;
bool
AllocEngine::reclaimDeclined(const Lease6Ptr& lease) {
-
if (!lease || (lease->state_ != Lease::STATE_DECLINED) ) {
return (true);
}
if (HooksManager::getHooksManager().calloutsPresent(Hooks.hook_index_lease6_recover_)) {
-
// Let's use a static callout handle. It will be initialized the first
// time lease6_recover is called and will keep to that value.
static CalloutHandlePtr callout_handle;
return (true);
}
-
template<typename LeasePtrType>
void AllocEngine::reclaimLeaseInDatabase(const LeasePtrType& lease,
const bool remove_lease,
const boost::function<void (const LeasePtrType&)>&
lease_update_fun) const {
-
LeaseMgr& lease_mgr = LeaseMgrFactory::instance();
// Reclaim the lease - depending on the configuration, set the
// expired-reclaimed state or simply remove it.
if (remove_lease) {
lease_mgr.deleteLease(lease->addr_);
-
} else if (!lease_update_fun.empty()) {
// Clear FQDN information as we have already sent the
// name change request to remove the DNS record.
lease->fqdn_rev_ = false;
lease->state_ = Lease::STATE_EXPIRED_RECLAIMED;
lease_update_fun(lease);
-
} else {
return;
}
.arg(lease->addr_.toText());
}
-
-} // end of isc::dhcp namespace
-} // end of isc namespace
+} // namespace dhcp
+} // namespace isc
// ##########################################################################
// # DHCPv4 lease allocation code starts here.
// ##########################################################################
namespace {
-
/// @brief Check if the specific address is reserved for another client.
///
/// This function finds a host reservation for a given address and then
// Iterate over the subnets within the shared network to see if any client's
// lease belongs to them.
while (subnet) {
-
// If client identifier has been supplied and the server wasn't
// explicitly configured to ignore client identifiers for this subnet
// check if there is a lease within this subnet.
// If no lease found using the client identifier, try the lookup using
// the HW address.
if (!client_lease && ctx.hwaddr_) {
-
// Rewind to the first subnet.
subnet = original_subnet;
// If the subnet belongs to a shared network we will be iterating
// over the subnets that belong to this shared network.
Subnet4Ptr current_subnet = ctx.subnet_;
- while (current_subnet) {
+ while (current_subnet) {
if (current_subnet->inPool(Lease::TYPE_V4, address,
ctx.query_->getClasses())) {
// We found a subnet that this address belongs to, so it
return (false);
}
-} // end of anonymous namespace
+} // namespace
namespace isc {
namespace dhcp {
-
AllocEngine::ClientContext4::ClientContext4()
: subnet_(), clientid_(), hwaddr_(),
requested_address_(IOAddress::IPV4_ZERO_ADDRESS()),
hostname_(hostname), callout_handle_(),
fake_allocation_(fake_allocation), old_lease_(), new_lease_(),
hosts_(), host_identifiers_() {
-
// Initialize host identifiers.
if (hwaddr) {
addHostIdentifier(Host::IDENT_HWADDR, hwaddr->hwaddr_);
if (ctx.fake_allocation_) {
return (discoverLease4(ctx));
-
} else {
ctx.new_lease_ = requestLease4(ctx);
}
-
} catch (const isc::Exception& e) {
// Some other error, return an empty lease.
LOG_ERROR(alloc_engine_logger, ALLOC_ENGINE_V4_ALLOC_ERROR)
// We can only search for the reservation if a subnet has been selected.
while (subnet) {
-
// Only makes sense to get reservations if the client has access
// to the class.
if (subnet->clientSupported(ctx.query_->getClasses()) &&
ctx.hosts_[subnet->getID()] = host_map[subnet->getID()];
break;
}
-
} else {
// Attempt to find a host using a specified identifier.
ConstHostPtr host = HostMgr::instance().get4(subnet->getID(),
return (host);
}
-
Lease4Ptr
AllocEngine::discoverLease4(AllocEngine::ClientContext4& ctx) {
// Find an existing lease for this client. This function will return true
// Check if there is a reservation for the client. If there is, we want to
// assign the reserved address, rather than any other one.
if (hasAddressReservation(ctx)) {
-
LOG_DEBUG(alloc_engine_logger, ALLOC_ENGINE_DBG_TRACE,
ALLOC_ENGINE_V4_DISCOVER_HR)
.arg(ctx.query_->getLabel())
.arg(ctx.conflicting_lease_ ? ctx.conflicting_lease_->toText() :
"(no lease info)");
}
-
} else {
new_lease = renewLease4(client_lease, ctx);
}
// which the reservation has just been removed.
if (!new_lease && client_lease && inAllowedPool(ctx, client_lease->addr_) &&
!addressReserved(client_lease->addr_, ctx)) {
-
LOG_DEBUG(alloc_engine_logger, ALLOC_ENGINE_DBG_TRACE,
ALLOC_ENGINE_V4_OFFER_EXISTING_LEASE)
.arg(ctx.query_->getLabel());
if (!new_lease && !ctx.requested_address_.isV4Zero() &&
inAllowedPool(ctx, ctx.requested_address_) &&
!addressReserved(ctx.requested_address_, ctx)) {
-
LOG_DEBUG(alloc_engine_logger, ALLOC_ENGINE_DBG_TRACE,
ALLOC_ENGINE_V4_OFFER_REQUESTED_LEASE)
.arg(ctx.requested_address_.toText())
// addresses. We will now use the allocator to pick the address
// from the dynamic pool.
if (!new_lease) {
-
LOG_DEBUG(alloc_engine_logger, ALLOC_ENGINE_DBG_TRACE,
ALLOC_ENGINE_V4_OFFER_NEW_LEASE)
.arg(ctx.query_->getLabel());
Lease4Ptr client_lease;
findClientLease(ctx, client_lease);
- // Obtain the sole instance of the LeaseMgr.
- LeaseMgr& lease_mgr = LeaseMgrFactory::instance();
-
// When the client sends the DHCPREQUEST, it should always specify the
// address which it is requesting or renewing. That is, the client should
// either use the requested IP address option or set the ciaddr. However,
// is not reserved for another client. If it is, stop here because
// we can't allocate this address.
if (addressReserved(ctx.requested_address_, ctx)) {
-
LOG_DEBUG(alloc_engine_logger, ALLOC_ENGINE_DBG_TRACE,
ALLOC_ENGINE_V4_REQUEST_ADDRESS_RESERVED)
.arg(ctx.query_->getLabel())
return (Lease4Ptr());
}
-
} else if (hasAddressReservation(ctx)) {
// The client hasn't specified an address to allocate, so the
// allocation engine needs to find an appropriate address.
if (existing && !existing->expired() &&
!existing->belongsToClient(ctx.hwaddr_, ctx.subnet_->getMatchClientId() ?
ctx.clientid_ : ClientIdPtr())) {
-
LOG_DEBUG(alloc_engine_logger, ALLOC_ENGINE_DBG_TRACE,
ALLOC_ENGINE_V4_REQUEST_IN_USE)
.arg(ctx.query_->getLabel())
// address, return NULL. The client should go back to the
// DHCPDISCOVER and the reserved address will be offered.
if (!existing || existing->expired()) {
-
LOG_DEBUG(alloc_engine_logger, ALLOC_ENGINE_DBG_TRACE,
ALLOC_ENGINE_V4_REQUEST_INVALID)
.arg(ctx.query_->getLabel())
if ((!hasAddressReservation(ctx) ||
(ctx.currentHost()->getIPv4Reservation() != ctx.requested_address_)) &&
!inAllowedPool(ctx, ctx.requested_address_)) {
-
LOG_DEBUG(alloc_engine_logger, ALLOC_ENGINE_DBG_TRACE,
ALLOC_ENGINE_V4_REQUEST_OUT_OF_POOL)
.arg(ctx.query_->getLabel())
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)
.arg(ctx.query_->getLabel())
// The client doesn't have the lease or it is requesting an address
// which it doesn't have. Let's try to allocate the requested address.
if (!ctx.requested_address_.isV4Zero()) {
-
LOG_DEBUG(alloc_engine_logger, ALLOC_ENGINE_DBG_TRACE,
ALLOC_ENGINE_V4_REQUEST_ALLOC_REQUESTED)
.arg(ctx.query_->getLabel())
CalloutHandle::CalloutNextStep callout_status = CalloutHandle::NEXT_STEP_CONTINUE;
new_lease = allocateOrReuseLease4(ctx.requested_address_, ctx,
callout_status);
-
} else {
-
LOG_DEBUG(alloc_engine_logger, ALLOC_ENGINE_DBG_TRACE,
ALLOC_ENGINE_V4_REQUEST_PICK_ADDRESS)
.arg(ctx.query_->getLabel());
.arg(ctx.query_->getLabel())
.arg(client_lease->addr_.toText());
- lease_mgr.deleteLease(client_lease->addr_);
+ LeaseMgrFactory::instance().deleteLease(client_lease->addr_);
// Need to decrease statistic for assigned addresses.
StatsMgr::instance().addValue(
// Let's execute all callouts registered for lease4_select
if (ctx.callout_handle_ &&
HooksManager::getHooksManager().calloutsPresent(hook_index_lease4_select_)) {
-
// Use the RAII wrapper to make sure that the callout handle state is
// reset when this object goes out of scope. All hook points must do
// it to prevent possible circular dependency between the callout
if (!ctx.fake_allocation_) {
// That is a real (REQUEST) allocation
bool status = LeaseMgrFactory::instance().addLease(lease);
- if (status) {
+ if (status) {
// The lease insertion succeeded, let's bump up the statistic.
StatsMgr::instance().addValue(
StatsMgr::generateName("subnet", ctx.subnet_->getID(), "assigned-addresses"),
// involves execution of hooks and DNS update.
if (ctx.old_lease_->expired()) {
reclaimExpiredLease(ctx.old_lease_, ctx.callout_handle_);
-
}
lease->state_ = Lease::STATE_DEFAULT;
// Execute all callouts registered for lease4_renew.
if (HooksManager::getHooksManager().
calloutsPresent(Hooks.hook_index_lease4_renew_)) {
-
// Use the RAII wrapper to make sure that the callout handle state is
// reset when this object goes out of scope. All hook points must do
// it to prevent possible circular dependency between the callout
static_cast<int64_t>(1));
}
}
+
if (skip) {
// Rollback changes (really useful only for memfile)
/// @todo: remove this?
// Let's execute all callouts registered for lease4_select
if (ctx.callout_handle_ && HooksManager::getHooksManager()
.calloutsPresent(hook_index_lease4_select_)) {
-
// Enable copying options from the packet within hook library.
ScopedEnableOptionsCopy<Pkt4> query4_options_copy(ctx.query_);
if (exist_lease->expired()) {
ctx.old_lease_ = Lease4Ptr(new Lease4(*exist_lease));
return (reuseExpiredLease4(exist_lease, ctx, callout_status));
-
} else {
// If there is a lease and it is not expired, pass this lease back
// to the caller in the context. The caller may need to know
// which lease we're conflicting with.
ctx.conflicting_lease_ = exist_lease;
}
-
} else {
return (createLease4(ctx, candidate, callout_status));
}
Subnet4Ptr original_subnet = subnet;
uint64_t total_attempts = 0;
- while (subnet) {
+ while (subnet) {
ClientIdPtr client_id;
if (subnet->getMatchClientId()) {
client_id = ctx.clientid_;
ctx.requested_address_);
// If address is not reserved for another client, try to allocate it.
if (!addressReserved(candidate, ctx)) {
-
// The call below will return the non-NULL pointer if we
// successfully allocate this lease. This means that the
// address is not in use by another client.
new_lease = allocateOrReuseLease4(candidate, ctx, callout_status);
if (new_lease) {
return (new_lease);
-
} else if (ctx.callout_handle_ &&
(callout_status != CalloutHandle::NEXT_STEP_CONTINUE)) {
// Don't retry when the callout status is not continue.
return (true);
}
-}; // end of isc::dhcp namespace
-}; // end of isc namespace
+} // namespace dhcp
+} // namespace isc