// module is called.
AllocEngineHooks Hooks;
-bool
-addressReserved(const IOAddress& address, const AllocEngine::ClientContext4& ctx) {
- ConstHostPtr host = HostMgr::instance().get4(ctx.subnet_->getID(), address);
- HWAddrPtr host_hwaddr;
- if (host) {
- host_hwaddr = host->getHWAddress();
- if (ctx.hwaddr_ && host_hwaddr) {
- /// @todo Use the equality operators for HWAddr class.
- /// Currently, this is impossible because the HostMgr uses the
- /// HTYPE_ETHER type, whereas the unit tests may use other types
- /// which HostMgr doesn't support yet.
- return (host_hwaddr->hwaddr_ != ctx.hwaddr_->hwaddr_);
-
- } else {
- return (true);
-
- }
- }
- return (false);
-}
-
-
}; // anonymous namespace
namespace isc {
requested_address_(IOAddress::IPV4_ZERO_ADDRESS()),
fwd_dns_update_(false), rev_dns_update_(false),
hostname_(""), callout_handle_(), fake_allocation_(false),
- old_lease_(), host_(), lease_for_host_(),
- interrupt_processing_(false) {
+ old_lease_(), host_(), interrupt_processing_(false) {
}
bool
hook_index_lease6_select_ = Hooks.hook_index_lease6_select_;
}
+AllocEngine::AllocatorPtr AllocEngine::getAllocator(Lease::Type type) {
+ std::map<Lease::Type, AllocatorPtr>::const_iterator alloc = allocators_.find(type);
+
+ if (alloc == allocators_.end()) {
+ isc_throw(BadValue, "No allocator initialized for pool type "
+ << Lease::typeToText(type));
+ }
+ return (alloc->second);
+}
+
+// ##########################################################################
+// # DHCPv6 lease allocation code starts here.
+// ##########################################################################
+
Lease6Collection
AllocEngine::allocateLeases6(ClientContext6& ctx) {
existing_leases.end(), Lease6Ptr()), existing_leases.end());
}
-Lease4Ptr
-AllocEngine::allocateLease4(const SubnetPtr& subnet, const ClientIdPtr& clientid,
- const HWAddrPtr& hwaddr, const IOAddress& hint,
- const bool fwd_dns_update, const bool rev_dns_update,
- const std::string& hostname, bool fake_allocation,
- const isc::hooks::CalloutHandlePtr& callout_handle,
- Lease4Ptr& old_lease) {
-
- // The NULL pointer indicates that the old lease didn't exist. It may
- // be later set to non NULL value if existing lease is found in the
- // database.
- old_lease.reset();
-
- Lease4Ptr new_lease;
-
- /// @todo The context for lease allocation should really be created
- /// by the DHCPv4 server and passed to this function. The reason for
- /// this is that the server should retrieve the Host object for the
- /// client because the Host object contains the data not only useful
- /// for the address allocation but also hostname and DHCP options
- /// for the client. The Host object should be passed in the context.
- /// Making this change would require a change to the allocateLease4
- /// API which would in turn require lots of changes in unit tests.
- /// The ticket introducing a context and host reservation in the
- /// allocation engine is complex enough by itself to warrant that
- /// the API change is done with a separate ticket (#3709).
- ClientContext4 ctx;
- ctx.subnet_ = subnet;
- ctx.clientid_ = clientid;
- ctx.hwaddr_ = hwaddr;
- ctx.requested_address_ = hint;
- ctx.fwd_dns_update_ = fwd_dns_update;
- ctx.rev_dns_update_ = rev_dns_update;
- ctx.hostname_ = hostname;
- ctx.fake_allocation_ = fake_allocation;
- ctx.callout_handle_ = callout_handle;
- ctx.old_lease_ = old_lease;
-
- try {
- if (!subnet) {
- isc_throw(BadValue, "Can't allocate IPv4 address without subnet");
- }
-
- if (!hwaddr) {
- isc_throw(BadValue, "HWAddr must be defined");
- }
-
- ctx.host_ = HostMgr::instance().get4(subnet->getID(), hwaddr);
-
- new_lease = ctx.fake_allocation_ ? discoverLease4(ctx) : requestLease4(ctx);
- if (!new_lease) {
- // Unable to allocate an address, return an empty lease.
- LOG_WARN(dhcpsrv_logger, DHCPSRV_ADDRESS4_ALLOC_FAIL).arg(attempts_);
- }
-
- } catch (const isc::Exception& e) {
- // Some other error, return an empty lease.
- LOG_ERROR(dhcpsrv_logger, DHCPSRV_ADDRESS4_ALLOC_ERROR).arg(e.what());
- }
-
- if (ctx.old_lease_) {
- old_lease.reset(new Lease4(*ctx.old_lease_));
- }
-
- return (new_lease);
-}
-
-Lease4Ptr
-AllocEngine::renewLease4(const Lease4Ptr& lease,
- AllocEngine::ClientContext4& ctx) {
- if (!lease) {
- isc_throw(BadValue, "null lease specified for renewLease4");
- }
-
- // The ctx.host_ possibly contains a reservation for the client for which
- // we are renewing a lease. If this reservation exists, we assume that
- // there is no conflict in assigning the address to this client. Note
- // that the reallocateClientLease checks if the address reserved for
- // the client matches the address in the lease we're renewing here.
- if (!ctx.host_) {
- // Do not renew the lease if:
- // - If address is reserved for someone else or ...
- // - renewed address doesn't belong to a pool.
- if (addressReserved(lease->addr_, ctx) ||
- (!ctx.subnet_->inPool(Lease::TYPE_V4, lease->addr_))) {
- ctx.interrupt_processing_ = !ctx.fake_allocation_;
- return (Lease4Ptr());
- }
-
- }
-
- // Let's keep the old data. This is essential if we are using memfile
- // (the lease returned points directly to the lease4 object in the database)
- // We'll need it if we want to skip update (i.e. roll back renewal)
- /// @todo: remove this once #3083 is implemented
- Lease4 old_values = *lease;
- ctx.old_lease_.reset(new Lease4(old_values));
-
- // Update the lease with the information from the context.
- updateLease4Information(lease, ctx);
-
- bool skip = false;
- // Execute all callouts registered for lease4_renew.
- if (HooksManager::getHooksManager().
- calloutsPresent(Hooks.hook_index_lease4_renew_)) {
-
- // Delete all previous arguments
- ctx.callout_handle_->deleteAllArguments();
-
- // Subnet from which we do the allocation. Convert the general subnet
- // pointer to a pointer to a Subnet4. Note that because we are using
- // boost smart pointers here, we need to do the cast using the boost
- // version of dynamic_pointer_cast.
- Subnet4Ptr subnet4 = boost::dynamic_pointer_cast<Subnet4>(ctx.subnet_);
-
- // Pass the parameters
- ctx.callout_handle_->setArgument("subnet4", subnet4);
- ctx.callout_handle_->setArgument("clientid", ctx.clientid_);
- ctx.callout_handle_->setArgument("hwaddr", ctx.hwaddr_);
-
- // Pass the lease to be updated
- ctx.callout_handle_->setArgument("lease4", lease);
-
- // Call all installed callouts
- HooksManager::callCallouts(Hooks.hook_index_lease4_renew_,
- *ctx.callout_handle_);
-
- // Callouts decided to skip the next processing step. The next
- // processing step would to actually renew the lease, so skip at this
- // stage means "keep the old lease as it is".
- if (ctx.callout_handle_->getSkip()) {
- skip = true;
- LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_HOOKS,
- DHCPSRV_HOOK_LEASE4_RENEW_SKIP);
- }
- }
-
- if (!ctx.fake_allocation_ && !skip) {
- // for REQUEST we do update the lease
- LeaseMgrFactory::instance().updateLease4(lease);
- }
- if (skip) {
- // Rollback changes (really useful only for memfile)
- /// @todo: remove this once #3083 is implemented
- *lease = old_values;
- }
-
- return (lease);
-}
-
-Lease6Ptr AllocEngine::reuseExpiredLease(Lease6Ptr& expired,
- ClientContext6& ctx,
- uint8_t prefix_len) {
+Lease6Ptr
+AllocEngine::reuseExpiredLease(Lease6Ptr& expired, ClientContext6& ctx,
+ uint8_t prefix_len) {
if (!expired->expired()) {
isc_throw(BadValue, "Attempt to recycle lease that is still valid");
return (expired);
}
-Lease4Ptr
-AllocEngine::allocateOrReuseLease(const IOAddress& candidate, ClientContext4& ctx) {
- Lease4Ptr exist_lease = LeaseMgrFactory::instance().getLease4(candidate);
- if (exist_lease) {
- if (exist_lease->expired()) {
- ctx.old_lease_.reset(new Lease4(*exist_lease));
- return (reuseExpiredLease(exist_lease, ctx));
- }
-
- } else {
- return (createLease4(ctx.subnet_, ctx.clientid_,
- ctx.hwaddr_, candidate, ctx.fwd_dns_update_,
- ctx.rev_dns_update_, ctx.hostname_, ctx.callout_handle_,
- ctx.fake_allocation_));
- }
- return (Lease4Ptr());
-}
-
-
-Lease4Ptr
-AllocEngine::reuseExpiredLease(Lease4Ptr& expired,
- AllocEngine::ClientContext4& ctx) {
- if (!expired) {
- isc_throw(BadValue, "null lease specified for reuseExpiredLease");
- }
+Lease6Ptr AllocEngine::createLease6(ClientContext6& ctx,
+ const IOAddress& addr,
+ uint8_t prefix_len) {
- if (!ctx.subnet_) {
- isc_throw(BadValue, "null subnet specified for the reuseExpiredLease");
+ if (ctx.type_ != Lease::TYPE_PD) {
+ prefix_len = 128; // non-PD lease types must be always /128
}
- updateLease4Information(expired, ctx);
- expired->fixed_ = false;
+ Lease6Ptr lease(new Lease6(ctx.type_, addr, ctx.duid_, ctx.iaid_,
+ ctx.subnet_->getPreferred(), ctx.subnet_->getValid(),
+ ctx.subnet_->getT1(), ctx.subnet_->getT2(),
+ ctx.subnet_->getID(), ctx.hwaddr_, prefix_len));
- /// @todo: log here that the lease was reused (there's ticket #2524 for
- /// logging in libdhcpsrv)
+ lease->fqdn_fwd_ = ctx.fwd_dns_update_;
+ lease->fqdn_rev_ = ctx.rev_dns_update_;
+ lease->hostname_ = ctx.hostname_;
- // Let's execute all callouts registered for lease4_select
- if (ctx.callout_handle_ && HooksManager::getHooksManager()
- .calloutsPresent(hook_index_lease4_select_)) {
+ // Let's execute all callouts registered for lease6_select
+ if (ctx.callout_handle_ &&
+ HooksManager::getHooksManager().calloutsPresent(hook_index_lease6_select_)) {
// Delete all previous arguments
ctx.callout_handle_->deleteAllArguments();
// Pass necessary arguments
- // Subnet from which we do the allocation. Convert the general subnet
- // pointer to a pointer to a Subnet4. Note that because we are using
- // boost smart pointers here, we need to do the cast using the boost
- // version of dynamic_pointer_cast.
- Subnet4Ptr subnet4 = boost::dynamic_pointer_cast<Subnet4>(ctx.subnet_);
- ctx.callout_handle_->setArgument("subnet4", subnet4);
+ // Subnet from which we do the allocation
+ ctx.callout_handle_->setArgument("subnet6", ctx.subnet_);
// Is this solicit (fake = true) or request (fake = false)
- ctx.callout_handle_->setArgument("fake_allocation",
- ctx.fake_allocation_);
-
- // The lease that will be assigned to a client
- ctx.callout_handle_->setArgument("lease4", expired);
+ ctx.callout_handle_->setArgument("fake_allocation", ctx.fake_allocation_);
+ ctx.callout_handle_->setArgument("lease6", lease);
- // Call the callouts
- HooksManager::callCallouts(hook_index_lease4_select_, *ctx.callout_handle_);
+ // This is the first callout, so no need to clear any arguments
+ HooksManager::callCallouts(hook_index_lease6_select_, *ctx.callout_handle_);
// Callouts decided to skip the action. This means that the lease is not
// assigned, so the client will get NoAddrAvail as a result. The lease
// won't be inserted into the database.
if (ctx.callout_handle_->getSkip()) {
- LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_HOOKS,
- DHCPSRV_HOOK_LEASE4_SELECT_SKIP);
- return (Lease4Ptr());
+ LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_HOOKS, DHCPSRV_HOOK_LEASE6_SELECT_SKIP);
+ return (Lease6Ptr());
}
// Let's use whatever callout returned. Hopefully it is the same lease
// we handled to it.
- ctx.callout_handle_->getArgument("lease4", expired);
+ ctx.callout_handle_->getArgument("lease6", lease);
}
if (!ctx.fake_allocation_) {
- // for REQUEST we do update the lease
- LeaseMgrFactory::instance().updateLease4(expired);
- }
-
- // We do nothing for SOLICIT. We'll just update database when
- // the client gets back to us with REQUEST message.
-
- // it's not really expired at this stage anymore - let's return it as
- // an updated lease
- return (expired);
-}
-
-Lease4Ptr
-AllocEngine::discoverLease4(AllocEngine::ClientContext4& ctx) {
- LeaseMgr& lease_mgr = LeaseMgrFactory::instance();
+ // That is a real (REQUEST) allocation
+ bool status = LeaseMgrFactory::instance().addLease(lease);
- Lease4Ptr client_lease = lease_mgr.getLease4(*ctx.hwaddr_, ctx.subnet_->getID());
- if (!client_lease && ctx.clientid_) {
- client_lease = lease_mgr.getLease4(*ctx.clientid_, ctx.subnet_->getID());
- }
+ if (status) {
- Lease4Ptr new_lease;
- if (ctx.host_) {
- new_lease = allocateOrReuseLease(ctx.host_->getIPv4Reservation(), ctx);
- if (new_lease) {
- if (client_lease) {
- ctx.old_lease_.reset(new Lease4(*client_lease));
- }
- return (new_lease);
+ return (lease);
+ } else {
+ // One of many failures with LeaseMgr (e.g. lost connection to the
+ // database, database failed etc.). One notable case for that
+ // is that we are working in multi-process mode and we lost a race
+ // (some other process got that address first)
+ return (Lease6Ptr());
}
- }
+ } else {
+ // That is only fake (SOLICIT without rapid-commit) allocation
- if (ctx.requested_address_.isV4Zero() && !addressReserved(ctx.requested_address_, ctx)) {
- if (ctx.subnet_->inPool(Lease::TYPE_V4, ctx.requested_address_)) {
- new_lease = allocateOrReuseLease(ctx.requested_address_, ctx);
- if (new_lease) {
- if (client_lease) {
- ctx.old_lease_.reset(new Lease4(*client_lease));
- }
- return (new_lease);
- }
+ // It is for advertise only. We should not insert the lease into LeaseMgr,
+ // but rather check that we could have inserted it.
+ Lease6Ptr existing = LeaseMgrFactory::instance().getLease6(
+ Lease::TYPE_NA, addr);
+ if (!existing) {
+ return (lease);
+ } else {
+ return (Lease6Ptr());
}
+ }
+}
- if (client_lease) {
- if ((client_lease->addr_ == ctx.requested_address_) ||
- ctx.requested_address_.isV4Zero()) {
- return (renewLease4(client_lease, ctx));
- }
+Lease6Collection
+AllocEngine::renewLeases6(ClientContext6& ctx) {
+ try {
+ if (!ctx.subnet_) {
+ isc_throw(InvalidOperation, "Subnet is required for allocation");
}
- }
- AllocatorPtr allocator = getAllocator(Lease::TYPE_V4);
- const uint64_t attempts = attempts_ == 0 ? std::numeric_limits<uint64_t>::max() : attempts_;
- for (uint64_t i = ctx.subnet_->getPoolCapacity(Lease::TYPE_V4); i < attempts; ++i) {
- IOAddress candidate = allocator->pickAddress(ctx.subnet_, ctx.clientid_,
- ctx.requested_address_);
- if (!addressReserved(candidate, ctx)) {
- new_lease = allocateOrReuseLease(candidate, ctx);
- if (new_lease) {
- return (new_lease);
- }
+ if (!ctx.duid_) {
+ isc_throw(InvalidOperation, "DUID is mandatory for allocation");
}
- }
- return (Lease4Ptr());
-}
+ // Check which host reservation mode is supported in this subnet.
+ Subnet::HRMode hr_mode = ctx.subnet_->getHostReservationMode();
-Lease4Ptr
-AllocEngine::requestLease4(AllocEngine::ClientContext4& ctx) {
- LeaseMgr& lease_mgr = LeaseMgrFactory::instance();
- Lease4Ptr client_lease = lease_mgr.getLease4(*ctx.hwaddr_, ctx.subnet_->getID());
- if (!client_lease && ctx.clientid_) {
- client_lease = lease_mgr.getLease4(*ctx.clientid_, ctx.subnet_->getID());
- }
+ // Check if there's a host reservation for this client. Attempt to get
+ // host info only if reservations are not disabled.
+ if (hr_mode != Subnet::HR_DISABLED) {
- if (!ctx.requested_address_.isV4Zero()) {
- if (addressReserved(ctx.requested_address_, ctx)) {
- return (Lease4Ptr());
+ ctx.host_ = HostMgr::instance().get6(ctx.subnet_->getID(), ctx.duid_,
+ ctx.hwaddr_);
+ } else {
+ // Host reservations disabled? Then explicitly set host to NULL
+ ctx.host_.reset();
}
- } else if (ctx.host_) {
- ctx.requested_address_ = ctx.host_->getIPv4Reservation();
- }
-
- if (!ctx.requested_address_.isV4Zero()) {
- Lease4Ptr existing = LeaseMgrFactory::instance().getLease4(ctx.requested_address_);
- if (existing && !existing->expired()) {
- if (!ctx.myLease(*existing)) {
- return (Lease4Ptr());
- }
+ // Check if there are any leases for this client.
+ Lease6Collection leases = LeaseMgrFactory::instance()
+ .getLeases6(ctx.type_, *ctx.duid_, ctx.iaid_, ctx.subnet_->getID());
- } else if (ctx.host_ && (ctx.host_->getIPv4Reservation() != ctx.requested_address_)) {
- return (Lease4Ptr());
+ if (!leases.empty()) {
+ // Check if the existing leases are reserved for someone else.
+ // If they're not, we're ok to keep using them.
+ removeNonmatchingReservedLeases6(ctx, leases);
}
- }
- if (client_lease) {
- if ((client_lease->addr_ == ctx.requested_address_) ||
- ctx.requested_address_.isV4Zero()) {
- return (renewLease4(client_lease, ctx));
- }
- }
+ if (ctx.host_) {
+ // If we have host reservation, allocate those leases.
+ allocateReservedLeases6(ctx, leases);
- if (!ctx.subnet_->inPool(Lease4::TYPE_V4, ctx.requested_address_)) {
- if ((ctx.host_ && (ctx.host_->getIPv4Reservation() != ctx.requested_address_)) ||
- (!ctx.host_ && !ctx.requested_address_.isV4Zero())) {
- return (Lease4Ptr());
+ // There's one more check to do. Let's remove leases that are not
+ // matching reservations, i.e. if client X has address A, but there's
+ // a reservation for address B, we should release A and reassign B.
+ // Caveat: do this only if we have at least one reserved address.
+ removeNonreservedLeases6(ctx, leases);
}
- }
- Lease4Ptr new_lease;
- if (!ctx.requested_address_.isV4Zero()) {
- new_lease = allocateOrReuseLease(ctx.requested_address_, ctx);
- if (new_lease) {
- if (client_lease && (client_lease->addr_ != new_lease->addr_)) {
- ctx.old_lease_ = client_lease;
- lease_mgr.deleteLease(client_lease->addr_);
- }
- return (new_lease);
+ // If we happen to removed all leases, get something new for this guy.
+ // Depending on the configuration, we may enable or disable granting
+ // new leases during renewals. This is controlled with the
+ // allow_new_leases_in_renewals_ field.
+ if (leases.empty() && ctx.allow_new_leases_in_renewals_) {
+ leases = allocateUnreservedLeases6(ctx);
}
- }
- AllocatorPtr allocator = getAllocator(Lease::TYPE_V4);
- const uint64_t attempts = attempts_ == 0 ? std::numeric_limits<uint64_t>::max() : attempts_;
- for (uint64_t i = ctx.subnet_->getPoolCapacity(Lease::TYPE_V4); i < attempts; ++i) {
- IOAddress candidate = allocator->pickAddress(ctx.subnet_, ctx.clientid_,
- ctx.requested_address_);
- if (!addressReserved(candidate, ctx)) {
- new_lease = allocateOrReuseLease(candidate, ctx);
- if (new_lease) {
- return (new_lease);
- }
+ // Extend all existing leases that passed all checks.
+ for (Lease6Collection::iterator l = leases.begin(); l != leases.end(); ++l) {
+ extendLease6(ctx, *l);
}
+
+ return (leases);
+
+ } catch (const isc::Exception& e) {
+
+ // Some other error, return an empty lease.
+ LOG_ERROR(dhcpsrv_logger, DHCPSRV_RENEW6_ERROR).arg(e.what());
}
- return (Lease4Ptr());
+ return (Lease6Collection());
}
-Lease6Ptr AllocEngine::createLease6(ClientContext6& ctx,
- const IOAddress& addr,
- uint8_t prefix_len) {
+void
+AllocEngine::extendLease6(ClientContext6& ctx, Lease6Ptr lease) {
- if (ctx.type_ != Lease::TYPE_PD) {
- prefix_len = 128; // non-PD lease types must be always /128
+ if (!lease || !ctx.subnet_) {
+ return;
}
- Lease6Ptr lease(new Lease6(ctx.type_, addr, ctx.duid_, ctx.iaid_,
- ctx.subnet_->getPreferred(), ctx.subnet_->getValid(),
- ctx.subnet_->getT1(), ctx.subnet_->getT2(),
- ctx.subnet_->getID(), ctx.hwaddr_, prefix_len));
+ // Check if the lease still belongs to the subnet. If it doesn't,
+ // we'll need to remove it.
+ if ((lease->type_ != Lease::TYPE_PD) && !ctx.subnet_->inRange(lease->addr_)) {
+ // Oh dear, the lease is no longer valid. We need to get rid of it.
+
+ // Remove this lease from LeaseMgr
+ LeaseMgrFactory::instance().deleteLease(lease->addr_);
+
+ // Add it to the removed leases list.
+ ctx.old_leases_.push_back(lease);
+
+ return;
+ }
+
+ // Keep the old data in case the callout tells us to skip update.
+ Lease6 old_data = *lease;
+ lease->preferred_lft_ = ctx.subnet_->getPreferred();
+ lease->valid_lft_ = ctx.subnet_->getValid();
+ lease->t1_ = ctx.subnet_->getT1();
+ lease->t2_ = ctx.subnet_->getT2();
+ lease->cltt_ = time(NULL);
+ lease->hostname_ = ctx.hostname_;
lease->fqdn_fwd_ = ctx.fwd_dns_update_;
lease->fqdn_rev_ = ctx.rev_dns_update_;
- lease->hostname_ = ctx.hostname_;
+ lease->hwaddr_ = ctx.hwaddr_;
- // Let's execute all callouts registered for lease6_select
- if (ctx.callout_handle_ &&
- HooksManager::getHooksManager().calloutsPresent(hook_index_lease6_select_)) {
+ bool skip = false;
+ // Get the callouts specific for the processed message and execute them.
+ int hook_point = ctx.query_->getType() == DHCPV6_RENEW ?
+ Hooks.hook_index_lease6_renew_ : Hooks.hook_index_lease6_rebind_;
+ if (HooksManager::calloutsPresent(hook_point)) {
+ CalloutHandlePtr callout_handle = ctx.callout_handle_;
// Delete all previous arguments
- ctx.callout_handle_->deleteAllArguments();
-
- // Pass necessary arguments
-
- // Subnet from which we do the allocation
- ctx.callout_handle_->setArgument("subnet6", ctx.subnet_);
+ callout_handle->deleteAllArguments();
- // Is this solicit (fake = true) or request (fake = false)
- ctx.callout_handle_->setArgument("fake_allocation", ctx.fake_allocation_);
- ctx.callout_handle_->setArgument("lease6", lease);
+ // Pass the original packet
+ callout_handle->setArgument("query6", ctx.query_);
- // This is the first callout, so no need to clear any arguments
- HooksManager::callCallouts(hook_index_lease6_select_, *ctx.callout_handle_);
+ // Pass the lease to be updated
+ callout_handle->setArgument("lease6", lease);
- // Callouts decided to skip the action. This means that the lease is not
- // assigned, so the client will get NoAddrAvail as a result. The lease
- // won't be inserted into the database.
- if (ctx.callout_handle_->getSkip()) {
- LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_HOOKS, DHCPSRV_HOOK_LEASE6_SELECT_SKIP);
- return (Lease6Ptr());
+ // Pass the IA option to be sent in response
+ if (lease->type_ == Lease::TYPE_NA) {
+ callout_handle->setArgument("ia_na", ctx.ia_rsp_);
+ } else {
+ callout_handle->setArgument("ia_pd", ctx.ia_rsp_);
}
- // Let's use whatever callout returned. Hopefully it is the same lease
- // we handled to it.
- ctx.callout_handle_->getArgument("lease6", lease);
- }
+ // Call all installed callouts
+ HooksManager::callCallouts(hook_point, *callout_handle);
- if (!ctx.fake_allocation_) {
- // That is a real (REQUEST) allocation
- bool status = LeaseMgrFactory::instance().addLease(lease);
+ // Callouts decided to skip the next processing step. The next
+ // processing step would to actually renew the lease, so skip at this
+ // stage means "keep the old lease as it is".
+ if (callout_handle->getSkip()) {
+ skip = true;
+ LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_HOOKS,
+ DHCPSRV_HOOK_LEASE6_EXTEND_SKIP)
+ .arg(ctx.query_->getName());
+ }
+ }
- if (status) {
+ if (!skip) {
+ 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
+ // in memfile, so the actual update is performed when we manipulate
+ // fields of returned Lease6Ptr, the actual updateLease6() is no-op.
+ *lease = old_data;
+ }
+}
- return (lease);
- } else {
- // One of many failures with LeaseMgr (e.g. lost connection to the
- // database, database failed etc.). One notable case for that
- // is that we are working in multi-process mode and we lost a race
- // (some other process got that address first)
- return (Lease6Ptr());
+Lease6Collection
+AllocEngine::updateFqdnData(ClientContext6& ctx, const Lease6Collection& leases) {
+ Lease6Collection updated_leases;
+ for (Lease6Collection::const_iterator lease_it = leases.begin();
+ lease_it != leases.end(); ++lease_it) {
+ Lease6Ptr lease(new Lease6(**lease_it));
+ lease->fqdn_fwd_ = ctx.fwd_dns_update_;
+ lease->fqdn_rev_ = ctx.rev_dns_update_;
+ lease->hostname_ = ctx.hostname_;
+ if (!ctx.fake_allocation_ &&
+ ((lease->fqdn_fwd_ != (*lease_it)->fqdn_fwd_) ||
+ (lease->fqdn_rev_ != (*lease_it)->fqdn_rev_) ||
+ (lease->hostname_ != (*lease_it)->hostname_))) {
+ ctx.changed_leases_.push_back(*lease_it);
+ LeaseMgrFactory::instance().updateLease6(lease);
}
- } else {
- // That is only fake (SOLICIT without rapid-commit) allocation
+ updated_leases.push_back(lease);
+ }
+ return (updated_leases);
+}
+
+} // end of isc::dhcp namespace
+} // end of isc namespace
+
+// ##########################################################################
+// # DHCPv4 lease allocation code starts here.
+// ##########################################################################
+
+namespace {
+
+bool
+addressReserved(const IOAddress& address, const AllocEngine::ClientContext4& ctx) {
+ ConstHostPtr host = HostMgr::instance().get4(ctx.subnet_->getID(), address);
+ HWAddrPtr host_hwaddr;
+ if (host) {
+ host_hwaddr = host->getHWAddress();
+ if (ctx.hwaddr_ && host_hwaddr) {
+ /// @todo Use the equality operators for HWAddr class.
+ /// Currently, this is impossible because the HostMgr uses the
+ /// HTYPE_ETHER type, whereas the unit tests may use other types
+ /// which HostMgr doesn't support yet.
+ return (host_hwaddr->hwaddr_ != ctx.hwaddr_->hwaddr_);
- // It is for advertise only. We should not insert the lease into LeaseMgr,
- // but rather check that we could have inserted it.
- Lease6Ptr existing = LeaseMgrFactory::instance().getLease6(
- Lease::TYPE_NA, addr);
- if (!existing) {
- return (lease);
} else {
- return (Lease6Ptr());
+ return (true);
+
+ }
+ }
+ return (false);
+}
+
+} // end of anonymous namespace
+
+namespace isc {
+namespace dhcp {
+
+Lease4Ptr
+AllocEngine::allocateLease4(const SubnetPtr& subnet, const ClientIdPtr& clientid,
+ const HWAddrPtr& hwaddr, const IOAddress& hint,
+ const bool fwd_dns_update, const bool rev_dns_update,
+ const std::string& hostname, bool fake_allocation,
+ const isc::hooks::CalloutHandlePtr& callout_handle,
+ Lease4Ptr& old_lease) {
+
+ // The NULL pointer indicates that the old lease didn't exist. It may
+ // be later set to non NULL value if existing lease is found in the
+ // database.
+ old_lease.reset();
+
+ Lease4Ptr new_lease;
+
+ /// @todo The context for lease allocation should really be created
+ /// by the DHCPv4 server and passed to this function. The reason for
+ /// this is that the server should retrieve the Host object for the
+ /// client because the Host object contains the data not only useful
+ /// for the address allocation but also hostname and DHCP options
+ /// for the client. The Host object should be passed in the context.
+ /// Making this change would require a change to the allocateLease4
+ /// API which would in turn require lots of changes in unit tests.
+ /// The ticket introducing a context and host reservation in the
+ /// allocation engine is complex enough by itself to warrant that
+ /// the API change is done with a separate ticket (#3709).
+ ClientContext4 ctx;
+ ctx.subnet_ = subnet;
+ ctx.clientid_ = clientid;
+ ctx.hwaddr_ = hwaddr;
+ ctx.requested_address_ = hint;
+ ctx.fwd_dns_update_ = fwd_dns_update;
+ ctx.rev_dns_update_ = rev_dns_update;
+ ctx.hostname_ = hostname;
+ ctx.fake_allocation_ = fake_allocation;
+ ctx.callout_handle_ = callout_handle;
+ ctx.old_lease_ = old_lease;
+
+ try {
+ if (!subnet) {
+ isc_throw(BadValue, "Can't allocate IPv4 address without subnet");
+ }
+
+ if (!hwaddr) {
+ isc_throw(BadValue, "HWAddr must be defined");
+ }
+
+ ctx.host_ = HostMgr::instance().get4(subnet->getID(), hwaddr);
+
+ new_lease = ctx.fake_allocation_ ? discoverLease4(ctx) : requestLease4(ctx);
+ if (!new_lease) {
+ // Unable to allocate an address, return an empty lease.
+ LOG_WARN(dhcpsrv_logger, DHCPSRV_ADDRESS4_ALLOC_FAIL).arg(attempts_);
+ }
+
+ } catch (const isc::Exception& e) {
+ // Some other error, return an empty lease.
+ LOG_ERROR(dhcpsrv_logger, DHCPSRV_ADDRESS4_ALLOC_ERROR).arg(e.what());
+ }
+
+ if (ctx.old_lease_) {
+ old_lease.reset(new Lease4(*ctx.old_lease_));
+ }
+
+ return (new_lease);
+}
+
+Lease4Ptr
+AllocEngine::discoverLease4(AllocEngine::ClientContext4& ctx) {
+ LeaseMgr& lease_mgr = LeaseMgrFactory::instance();
+
+ Lease4Ptr client_lease = lease_mgr.getLease4(*ctx.hwaddr_, ctx.subnet_->getID());
+ if (!client_lease && ctx.clientid_) {
+ client_lease = lease_mgr.getLease4(*ctx.clientid_, ctx.subnet_->getID());
+ }
+
+ Lease4Ptr new_lease;
+ if (ctx.host_) {
+ new_lease = allocateOrReuseLease(ctx.host_->getIPv4Reservation(), ctx);
+ if (new_lease) {
+ if (client_lease) {
+ ctx.old_lease_.reset(new Lease4(*client_lease));
+ }
+ return (new_lease);
+ }
+ }
+
+ if (ctx.requested_address_.isV4Zero() && !addressReserved(ctx.requested_address_, ctx)) {
+ if (ctx.subnet_->inPool(Lease::TYPE_V4, ctx.requested_address_)) {
+ new_lease = allocateOrReuseLease(ctx.requested_address_, ctx);
+ if (new_lease) {
+ if (client_lease) {
+ ctx.old_lease_.reset(new Lease4(*client_lease));
+ }
+ return (new_lease);
+ }
+ }
+
+ if (client_lease) {
+ if ((client_lease->addr_ == ctx.requested_address_) ||
+ ctx.requested_address_.isV4Zero()) {
+ return (renewLease4(client_lease, ctx));
+ }
+ }
+ }
+
+ AllocatorPtr allocator = getAllocator(Lease::TYPE_V4);
+ const uint64_t max_attempts = ctx.subnet_->getPoolCapacity(Lease::TYPE_V4);
+ for (uint64_t i = 0; i < max_attempts; ++i) {
+ IOAddress candidate = allocator->pickAddress(ctx.subnet_, ctx.clientid_,
+ ctx.requested_address_);
+ if (!addressReserved(candidate, ctx)) {
+ new_lease = allocateOrReuseLease(candidate, ctx);
+ if (new_lease) {
+ return (new_lease);
+ }
}
}
+
+ return (Lease4Ptr());
+}
+
+Lease4Ptr
+AllocEngine::requestLease4(AllocEngine::ClientContext4& ctx) {
+ LeaseMgr& lease_mgr = LeaseMgrFactory::instance();
+ Lease4Ptr client_lease = lease_mgr.getLease4(*ctx.hwaddr_, ctx.subnet_->getID());
+ if (!client_lease && ctx.clientid_) {
+ client_lease = lease_mgr.getLease4(*ctx.clientid_, ctx.subnet_->getID());
+ }
+
+ if (!ctx.requested_address_.isV4Zero()) {
+ if (addressReserved(ctx.requested_address_, ctx)) {
+ return (Lease4Ptr());
+ }
+
+ } else if (ctx.host_) {
+ ctx.requested_address_ = ctx.host_->getIPv4Reservation();
+ }
+
+ if (!ctx.requested_address_.isV4Zero()) {
+ Lease4Ptr existing = LeaseMgrFactory::instance().getLease4(ctx.requested_address_);
+ if (existing && !existing->expired()) {
+ if (!ctx.myLease(*existing)) {
+ return (Lease4Ptr());
+ }
+
+ } else if (ctx.host_ && (ctx.host_->getIPv4Reservation() != ctx.requested_address_)) {
+ return (Lease4Ptr());
+ }
+ }
+
+ if (client_lease) {
+ if ((client_lease->addr_ == ctx.requested_address_) ||
+ ctx.requested_address_.isV4Zero()) {
+ return (renewLease4(client_lease, ctx));
+ }
+ }
+
+ if (!ctx.subnet_->inPool(Lease4::TYPE_V4, ctx.requested_address_)) {
+ if ((ctx.host_ && (ctx.host_->getIPv4Reservation() != ctx.requested_address_)) ||
+ (!ctx.host_ && !ctx.requested_address_.isV4Zero())) {
+ return (Lease4Ptr());
+ }
+ }
+
+ Lease4Ptr new_lease;
+ if (!ctx.requested_address_.isV4Zero()) {
+ new_lease = allocateOrReuseLease(ctx.requested_address_, ctx);
+ if (new_lease) {
+ if (client_lease && (client_lease->addr_ != new_lease->addr_)) {
+ ctx.old_lease_ = client_lease;
+ lease_mgr.deleteLease(client_lease->addr_);
+ }
+ return (new_lease);
+ }
+ }
+
+ AllocatorPtr allocator = getAllocator(Lease::TYPE_V4);
+ const uint64_t max_attempts = ctx.subnet_->getPoolCapacity(Lease::TYPE_V4);
+ for (uint64_t i = 0; i < max_attempts; ++i) {
+ IOAddress candidate = allocator->pickAddress(ctx.subnet_, ctx.clientid_,
+ ctx.requested_address_);
+ if (!addressReserved(candidate, ctx)) {
+ new_lease = allocateOrReuseLease(candidate, ctx);
+ if (new_lease) {
+ return (new_lease);
+ }
+ }
+ }
+
+ return (Lease4Ptr());
}
Lease4Ptr AllocEngine::createLease4(const SubnetPtr& subnet,
callout_handle->setArgument("subnet4", subnet4);
// Is this solicit (fake = true) or request (fake = false)
- callout_handle->setArgument("fake_allocation", fake_allocation);
+ callout_handle->setArgument("fake_allocation", fake_allocation);
+
+ // Pass the intended lease as well
+ callout_handle->setArgument("lease4", lease);
+
+ // This is the first callout, so no need to clear any arguments
+ HooksManager::callCallouts(hook_index_lease4_select_, *callout_handle);
+
+ // Callouts decided to skip the action. This means that the lease is not
+ // assigned, so the client will get NoAddrAvail as a result. The lease
+ // won't be inserted into the database.
+ if (callout_handle->getSkip()) {
+ LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_HOOKS, DHCPSRV_HOOK_LEASE4_SELECT_SKIP);
+ return (Lease4Ptr());
+ }
+
+ // Let's use whatever callout returned. Hopefully it is the same lease
+ // we handled to it.
+ callout_handle->getArgument("lease4", lease);
+ }
+
+ if (!fake_allocation) {
+ // That is a real (REQUEST) allocation
+ bool status = LeaseMgrFactory::instance().addLease(lease);
+ if (status) {
+ return (lease);
+ } else {
+ // One of many failures with LeaseMgr (e.g. lost connection to the
+ // database, database failed etc.). One notable case for that
+ // is that we are working in multi-process mode and we lost a race
+ // (some other process got that address first)
+ return (Lease4Ptr());
+ }
+ } else {
+ // That is only fake (DISCOVER) allocation
+
+ // It is for OFFER only. We should not insert the lease into LeaseMgr,
+ // but rather check that we could have inserted it.
+ Lease4Ptr existing = LeaseMgrFactory::instance().getLease4(addr);
+ if (!existing) {
+ return (lease);
+ } else {
+ return (Lease4Ptr());
+ }
+ }
+}
+
+Lease4Ptr
+AllocEngine::renewLease4(const Lease4Ptr& lease,
+ AllocEngine::ClientContext4& ctx) {
+ if (!lease) {
+ isc_throw(BadValue, "null lease specified for renewLease4");
+ }
+
+ // The ctx.host_ possibly contains a reservation for the client for which
+ // we are renewing a lease. If this reservation exists, we assume that
+ // there is no conflict in assigning the address to this client. Note
+ // that the reallocateClientLease checks if the address reserved for
+ // the client matches the address in the lease we're renewing here.
+ if (!ctx.host_) {
+ // Do not renew the lease if:
+ // - If address is reserved for someone else or ...
+ // - renewed address doesn't belong to a pool.
+ if (addressReserved(lease->addr_, ctx) ||
+ (!ctx.subnet_->inPool(Lease::TYPE_V4, lease->addr_))) {
+ ctx.interrupt_processing_ = !ctx.fake_allocation_;
+ return (Lease4Ptr());
+ }
+
+ }
+
+ // Let's keep the old data. This is essential if we are using memfile
+ // (the lease returned points directly to the lease4 object in the database)
+ // We'll need it if we want to skip update (i.e. roll back renewal)
+ /// @todo: remove this once #3083 is implemented
+ Lease4 old_values = *lease;
+ ctx.old_lease_.reset(new Lease4(old_values));
+
+ // Update the lease with the information from the context.
+ updateLease4Information(lease, ctx);
+
+ bool skip = false;
+ // Execute all callouts registered for lease4_renew.
+ if (HooksManager::getHooksManager().
+ calloutsPresent(Hooks.hook_index_lease4_renew_)) {
+
+ // Delete all previous arguments
+ ctx.callout_handle_->deleteAllArguments();
+
+ // Subnet from which we do the allocation. Convert the general subnet
+ // pointer to a pointer to a Subnet4. Note that because we are using
+ // boost smart pointers here, we need to do the cast using the boost
+ // version of dynamic_pointer_cast.
+ Subnet4Ptr subnet4 = boost::dynamic_pointer_cast<Subnet4>(ctx.subnet_);
+
+ // Pass the parameters
+ ctx.callout_handle_->setArgument("subnet4", subnet4);
+ ctx.callout_handle_->setArgument("clientid", ctx.clientid_);
+ ctx.callout_handle_->setArgument("hwaddr", ctx.hwaddr_);
+
+ // Pass the lease to be updated
+ ctx.callout_handle_->setArgument("lease4", lease);
+
+ // Call all installed callouts
+ HooksManager::callCallouts(Hooks.hook_index_lease4_renew_,
+ *ctx.callout_handle_);
+
+ // Callouts decided to skip the next processing step. The next
+ // processing step would to actually renew the lease, so skip at this
+ // stage means "keep the old lease as it is".
+ if (ctx.callout_handle_->getSkip()) {
+ skip = true;
+ LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_HOOKS,
+ DHCPSRV_HOOK_LEASE4_RENEW_SKIP);
+ }
+ }
+
+ if (!ctx.fake_allocation_ && !skip) {
+ // for REQUEST we do update the lease
+ LeaseMgrFactory::instance().updateLease4(lease);
+ }
+ if (skip) {
+ // Rollback changes (really useful only for memfile)
+ /// @todo: remove this once #3083 is implemented
+ *lease = old_values;
+ }
+
+ return (lease);
+}
+
+Lease4Ptr
+AllocEngine::reuseExpiredLease(Lease4Ptr& expired,
+ AllocEngine::ClientContext4& ctx) {
+ if (!expired) {
+ isc_throw(BadValue, "null lease specified for reuseExpiredLease");
+ }
+
+ if (!ctx.subnet_) {
+ isc_throw(BadValue, "null subnet specified for the reuseExpiredLease");
+ }
+
+ updateLease4Information(expired, ctx);
+ expired->fixed_ = false;
+
+ /// @todo: log here that the lease was reused (there's ticket #2524 for
+ /// logging in libdhcpsrv)
+
+ // Let's execute all callouts registered for lease4_select
+ if (ctx.callout_handle_ && HooksManager::getHooksManager()
+ .calloutsPresent(hook_index_lease4_select_)) {
+
+ // Delete all previous arguments
+ ctx.callout_handle_->deleteAllArguments();
+
+ // Pass necessary arguments
+
+ // Subnet from which we do the allocation. Convert the general subnet
+ // pointer to a pointer to a Subnet4. Note that because we are using
+ // boost smart pointers here, we need to do the cast using the boost
+ // version of dynamic_pointer_cast.
+ Subnet4Ptr subnet4 = boost::dynamic_pointer_cast<Subnet4>(ctx.subnet_);
+ ctx.callout_handle_->setArgument("subnet4", subnet4);
+
+ // Is this solicit (fake = true) or request (fake = false)
+ ctx.callout_handle_->setArgument("fake_allocation",
+ ctx.fake_allocation_);
- // Pass the intended lease as well
- callout_handle->setArgument("lease4", lease);
+ // The lease that will be assigned to a client
+ ctx.callout_handle_->setArgument("lease4", expired);
- // This is the first callout, so no need to clear any arguments
- HooksManager::callCallouts(hook_index_lease4_select_, *callout_handle);
+ // Call the callouts
+ HooksManager::callCallouts(hook_index_lease4_select_, *ctx.callout_handle_);
// Callouts decided to skip the action. This means that the lease is not
// assigned, so the client will get NoAddrAvail as a result. The lease
// won't be inserted into the database.
- if (callout_handle->getSkip()) {
- LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_HOOKS, DHCPSRV_HOOK_LEASE4_SELECT_SKIP);
+ if (ctx.callout_handle_->getSkip()) {
+ LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_HOOKS,
+ DHCPSRV_HOOK_LEASE4_SELECT_SKIP);
return (Lease4Ptr());
}
// Let's use whatever callout returned. Hopefully it is the same lease
// we handled to it.
- callout_handle->getArgument("lease4", lease);
+ ctx.callout_handle_->getArgument("lease4", expired);
}
- if (!fake_allocation) {
- // That is a real (REQUEST) allocation
- bool status = LeaseMgrFactory::instance().addLease(lease);
- if (status) {
- return (lease);
- } else {
- // One of many failures with LeaseMgr (e.g. lost connection to the
- // database, database failed etc.). One notable case for that
- // is that we are working in multi-process mode and we lost a race
- // (some other process got that address first)
- return (Lease4Ptr());
- }
- } else {
- // That is only fake (DISCOVER) allocation
+ if (!ctx.fake_allocation_) {
+ // for REQUEST we do update the lease
+ LeaseMgrFactory::instance().updateLease4(expired);
+ }
- // It is for OFFER only. We should not insert the lease into LeaseMgr,
- // but rather check that we could have inserted it.
- Lease4Ptr existing = LeaseMgrFactory::instance().getLease4(addr);
- if (!existing) {
- return (lease);
- } else {
- return (Lease4Ptr());
+ // We do nothing for SOLICIT. We'll just update database when
+ // the client gets back to us with REQUEST message.
+
+ // it's not really expired at this stage anymore - let's return it as
+ // an updated lease
+ return (expired);
+}
+
+Lease4Ptr
+AllocEngine::allocateOrReuseLease(const IOAddress& candidate, ClientContext4& ctx) {
+ Lease4Ptr exist_lease = LeaseMgrFactory::instance().getLease4(candidate);
+ if (exist_lease) {
+ if (exist_lease->expired()) {
+ ctx.old_lease_.reset(new Lease4(*exist_lease));
+ return (reuseExpiredLease(exist_lease, ctx));
}
+
+ } else {
+ return (createLease4(ctx.subnet_, ctx.clientid_,
+ ctx.hwaddr_, candidate, ctx.fwd_dns_update_,
+ ctx.rev_dns_update_, ctx.hostname_, ctx.callout_handle_,
+ ctx.fake_allocation_));
}
+ return (Lease4Ptr());
}
void
lease->hostname_ = ctx.hostname_;
}
-Lease6Collection
-AllocEngine::updateFqdnData(ClientContext6& ctx, const Lease6Collection& leases) {
- Lease6Collection updated_leases;
- for (Lease6Collection::const_iterator lease_it = leases.begin();
- lease_it != leases.end(); ++lease_it) {
- Lease6Ptr lease(new Lease6(**lease_it));
- lease->fqdn_fwd_ = ctx.fwd_dns_update_;
- lease->fqdn_rev_ = ctx.rev_dns_update_;
- lease->hostname_ = ctx.hostname_;
- if (!ctx.fake_allocation_ &&
- ((lease->fqdn_fwd_ != (*lease_it)->fqdn_fwd_) ||
- (lease->fqdn_rev_ != (*lease_it)->fqdn_rev_) ||
- (lease->hostname_ != (*lease_it)->hostname_))) {
- ctx.changed_leases_.push_back(*lease_it);
- LeaseMgrFactory::instance().updateLease6(lease);
- }
- updated_leases.push_back(lease);
- }
- return (updated_leases);
-}
-
-AllocEngine::AllocatorPtr AllocEngine::getAllocator(Lease::Type type) {
- std::map<Lease::Type, AllocatorPtr>::const_iterator alloc = allocators_.find(type);
-
- if (alloc == allocators_.end()) {
- isc_throw(BadValue, "No allocator initialized for pool type "
- << Lease::typeToText(type));
- }
- return (alloc->second);
-}
-
-Lease6Collection
-AllocEngine::renewLeases6(ClientContext6& ctx) {
- try {
- if (!ctx.subnet_) {
- isc_throw(InvalidOperation, "Subnet is required for allocation");
- }
-
- if (!ctx.duid_) {
- isc_throw(InvalidOperation, "DUID is mandatory for allocation");
- }
-
- // Check which host reservation mode is supported in this subnet.
- Subnet::HRMode hr_mode = ctx.subnet_->getHostReservationMode();
-
- // Check if there's a host reservation for this client. Attempt to get
- // host info only if reservations are not disabled.
- if (hr_mode != Subnet::HR_DISABLED) {
-
- ctx.host_ = HostMgr::instance().get6(ctx.subnet_->getID(), ctx.duid_,
- ctx.hwaddr_);
- } else {
- // Host reservations disabled? Then explicitly set host to NULL
- ctx.host_.reset();
- }
-
- // Check if there are any leases for this client.
- Lease6Collection leases = LeaseMgrFactory::instance()
- .getLeases6(ctx.type_, *ctx.duid_, ctx.iaid_, ctx.subnet_->getID());
-
- if (!leases.empty()) {
- // Check if the existing leases are reserved for someone else.
- // If they're not, we're ok to keep using them.
- removeNonmatchingReservedLeases6(ctx, leases);
- }
-
- if (ctx.host_) {
- // If we have host reservation, allocate those leases.
- allocateReservedLeases6(ctx, leases);
-
- // There's one more check to do. Let's remove leases that are not
- // matching reservations, i.e. if client X has address A, but there's
- // a reservation for address B, we should release A and reassign B.
- // Caveat: do this only if we have at least one reserved address.
- removeNonreservedLeases6(ctx, leases);
- }
-
- // If we happen to removed all leases, get something new for this guy.
- // Depending on the configuration, we may enable or disable granting
- // new leases during renewals. This is controlled with the
- // allow_new_leases_in_renewals_ field.
- if (leases.empty() && ctx.allow_new_leases_in_renewals_) {
- leases = allocateUnreservedLeases6(ctx);
- }
-
- // Extend all existing leases that passed all checks.
- for (Lease6Collection::iterator l = leases.begin(); l != leases.end(); ++l) {
- extendLease6(ctx, *l);
- }
-
- return (leases);
-
- } catch (const isc::Exception& e) {
-
- // Some other error, return an empty lease.
- LOG_ERROR(dhcpsrv_logger, DHCPSRV_RENEW6_ERROR).arg(e.what());
- }
-
- return (Lease6Collection());
-}
-
-void
-AllocEngine::extendLease6(ClientContext6& ctx, Lease6Ptr lease) {
-
- if (!lease || !ctx.subnet_) {
- return;
- }
-
- // Check if the lease still belongs to the subnet. If it doesn't,
- // we'll need to remove it.
- if ((lease->type_ != Lease::TYPE_PD) && !ctx.subnet_->inRange(lease->addr_)) {
- // Oh dear, the lease is no longer valid. We need to get rid of it.
-
- // Remove this lease from LeaseMgr
- LeaseMgrFactory::instance().deleteLease(lease->addr_);
-
- // Add it to the removed leases list.
- ctx.old_leases_.push_back(lease);
-
- return;
- }
-
- // Keep the old data in case the callout tells us to skip update.
- Lease6 old_data = *lease;
-
- lease->preferred_lft_ = ctx.subnet_->getPreferred();
- lease->valid_lft_ = ctx.subnet_->getValid();
- lease->t1_ = ctx.subnet_->getT1();
- lease->t2_ = ctx.subnet_->getT2();
- lease->cltt_ = time(NULL);
- lease->hostname_ = ctx.hostname_;
- lease->fqdn_fwd_ = ctx.fwd_dns_update_;
- lease->fqdn_rev_ = ctx.rev_dns_update_;
- lease->hwaddr_ = ctx.hwaddr_;
-
- bool skip = false;
- // Get the callouts specific for the processed message and execute them.
- int hook_point = ctx.query_->getType() == DHCPV6_RENEW ?
- Hooks.hook_index_lease6_renew_ : Hooks.hook_index_lease6_rebind_;
- if (HooksManager::calloutsPresent(hook_point)) {
- CalloutHandlePtr callout_handle = ctx.callout_handle_;
-
- // Delete all previous arguments
- callout_handle->deleteAllArguments();
-
- // Pass the original packet
- callout_handle->setArgument("query6", ctx.query_);
-
- // Pass the lease to be updated
- callout_handle->setArgument("lease6", lease);
-
- // Pass the IA option to be sent in response
- if (lease->type_ == Lease::TYPE_NA) {
- callout_handle->setArgument("ia_na", ctx.ia_rsp_);
- } else {
- callout_handle->setArgument("ia_pd", ctx.ia_rsp_);
- }
-
- // Call all installed callouts
- HooksManager::callCallouts(hook_point, *callout_handle);
-
- // Callouts decided to skip the next processing step. The next
- // processing step would to actually renew the lease, so skip at this
- // stage means "keep the old lease as it is".
- if (callout_handle->getSkip()) {
- skip = true;
- LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_HOOKS,
- DHCPSRV_HOOK_LEASE6_EXTEND_SKIP)
- .arg(ctx.query_->getName());
- }
- }
-
- if (!skip) {
- 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
- // in memfile, so the actual update is performed when we manipulate
- // fields of returned Lease6Ptr, the actual updateLease6() is no-op.
- *lease = old_data;
- }
-}
-
-AllocEngine::~AllocEngine() {
- // no need to delete allocator. smart_ptr will do the trick for us
-}
-
}; // end of isc::dhcp namespace
}; // end of isc namespace
class AllocEngine : public boost::noncopyable {
protected:
+ /// @name Declaration of the allocator classes.
+ ///
+ //@{
+
/// @brief base class for all address/prefix allocation algorithms
///
/// This is an abstract class that should not be used directly, but rather
ALLOC_RANDOM // random - an address is randomly selected
} AllocType;
- /// @brief Context information for the DHCPv4 lease allocation.
+ //@}
+
+ /// @name Construction, destruction and allocator.
///
- /// This structure holds a set of information provided by the DHCPv4
- /// server to the allocation engine. In particular, it holds the
- /// client identifying information, such as HW address or client
- /// identifier. It also holds the information about the subnet that
- /// the client is connected to.
+ //@{
+
+ /// @brief Constructor.
///
- /// This structure is also used to pass some information from
- /// the allocation engine back to the server, i.e. the old lease
- /// which the client had before the allocation.
+ /// Instantiates necessary services, required to run DHCP server.
+ /// In particular, creates IfaceMgr that will be responsible for
+ /// network interaction. Will instantiate lease manager, and load
+ /// old or create new DUID.
///
- /// This structure is meant to be extended in the future, if more
- /// information should be passed to the allocation engine. Note
- /// that the big advantage of using the context structure to pass
- /// information to the allocation engine methods is that adding
- /// new information doesn't modify the API of the allocation engine.
- struct ClientContext4 {
- /// @brief Subnet selected for the client by the server.
- SubnetPtr subnet_;
-
- /// @brief Client identifier from the DHCP message.
- ClientIdPtr clientid_;
-
- /// @brief HW address from the DHCP message.
- HWAddrPtr hwaddr_;
-
- /// @brief An address that the client desires.
- ///
- /// If this address is set to 0 it indicates that this address
- /// is unspecified.
- asiolink::IOAddress requested_address_;
-
- /// @brief Perform forward DNS update.
- bool fwd_dns_update_;
+ /// @param engine_type selects allocation algorithm
+ /// @param attempts number of attempts for each lease allocation before
+ /// we give up (0 means unlimited)
+ /// @param ipv6 specifies if the engine should work for IPv4 or IPv6
+ AllocEngine(AllocType engine_type, unsigned int attempts, bool ipv6 = true);
- /// @brief Perform reverse DNS update.
- bool rev_dns_update_;
+ /// @brief Destructor.
+ virtual ~AllocEngine() { }
- /// @brief Hostname.
- ///
- /// The server retrieves the hostname from the Client FQDN option,
- /// Hostname option or the host reservation record for the client.
- std::string hostname_;
+ /// @brief Returns allocator for a given pool type
+ ///
+ /// @param type type of pool (V4, IA, TA or PD)
+ /// @throw BadValue if allocator for a given type is missing
+ /// @return pointer to allocator handing a given resource types
+ AllocatorPtr getAllocator(Lease::Type type);
- /// @brief Callout handle associated with the client's message.
- hooks::CalloutHandlePtr callout_handle_;
+ //@}
- /// @brief Indicates if this is a real or fake allocation.
- ///
- /// The real allocation is when the allocation engine is supposed
- /// to make an update in a lease database: create new lease, or
- /// update existing lease.
- bool fake_allocation_;
+private:
- /// @brief A pointer to an old lease that the client had before update.
- Lease4Ptr old_lease_;
+ /// @name Private members, common for DHCPv4 and DHCPv6 service.
+ ///
+ //@{
- /// @brief A pointer to the object identifying host reservations.
- ConstHostPtr host_;
+ /// @brief a pointer to currently used allocator
+ ///
+ /// For IPv4, there will be only one allocator: TYPE_V4
+ /// For IPv6, there will be 3 allocators: TYPE_NA, TYPE_TA, TYPE_PD
+ std::map<Lease::Type, AllocatorPtr> allocators_;
- /// @brief A pointer to the existing lease for the host reservation.
- Lease4Ptr lease_for_host_;
+ /// @brief number of attempts before we give up lease allocation (0=unlimited)
+ unsigned int attempts_;
- /// @brief Signals that the allocation should be interrupted.
- ///
- /// This flag is set by the downstream methods called by the
- /// @c AllocEngine::allocateLease4. This flag is set to true to
- /// indicate that an attempt to allocate a lease should be
- /// interrupted.
- ///
- /// One possible use case is when the allocation engine tries
- /// to renew the client's lease and the leased address appears
- /// to be reserved for someone else. In such case, the allocation
- /// engine should signal to the server that the address that the
- /// client should stop using this address. The
- /// @c AllocEngine::renewLease4 sets this flag so as the
- /// upstream methods return the NULL lease pointer to the server.
- bool interrupt_processing_;
+ // hook name indexes (used in hooks callouts)
+ int hook_index_lease4_select_; ///< index for lease4_select hook
+ int hook_index_lease6_select_; ///< index for lease6_select hook
- /// @brief Default constructor.
- ClientContext4();
+ //@}
- bool myLease(const Lease4& lease) const;
+public:
- };
+ /// @name Public method, structure and types for DHCPv6 leass allocation.
+ ///
+ //@{
/// @brief Defines a single hint (an address + prefix-length).
///
}
};
- /// @brief Default constructor.
- ///
- /// Instantiates necessary services, required to run DHCPv6 server.
- /// In particular, creates IfaceMgr that will be responsible for
- /// network interaction. Will instantiate lease manager, and load
- /// old or create new DUID.
- ///
- /// @param engine_type selects allocation algorithm
- /// @param attempts number of attempts for each lease allocation before
- /// we give up (0 means unlimited)
- /// @param ipv6 specifies if the engine should work for IPv4 or IPv6
- AllocEngine(AllocType engine_type, unsigned int attempts, bool ipv6 = true);
-
- /// @brief Returns IPv4 lease.
- ///
- /// This method finds a lease for a client using the following algorithm:
- /// - If a lease exists for the combination of the HW address or client id
- /// and a subnet, try to use this lease for the client. If the client
- /// has a reservation for an address for which the lease was created or
- /// the client desires to renew the lease for this address (ciaddr or
- /// requested IP address option), the server renews the lease for the
- /// client. If the client desires a different address or the server has
- /// a (potentially new) reservation for a different address for this
- /// client, the existing lease is replaced with a new lease.
- /// - If the client has no lease in the lease database the server will try
- /// to allocate a new lease. If the client has a reservation for the
- /// particular address or if it has specified a desired address the
- /// server will check if the particular address is not allocated to
- /// other client. If the address is available, the server will allocate
- /// this address for the client.
- /// - If the desired address is unavailable the server checks if the
- /// lease for this address has expired. If the lease is expired, the
- /// server will allocate this lease to the client. The relevant
- /// information will be updated, e.g. new client HW address, host name
- /// etc.
- /// - If the desired address is in use by other client, the server will try
- /// to allocate a different address. The server picks addresses from
- /// a dynamic pool and checks if the address is available and that
- /// it is not reserved for another client. If it is in use by another
- /// client or if it is reserved for another client, this address is not
- /// allocated. The server picks next address and repeats this check.
- /// Note that the server ceases allocation after configured number
- /// of unsuccessful attempts.
- ///
- /// The lease allocation process is slightly different for the
- /// DHCPDISCOVER and DHCPREQUEST messages. In the former case, the client
- /// may specify the requested IP address option with a desired address and
- /// the server treats this address as hint. This means that the server may
- /// allocate a different address on its discretion and send it to the
- /// client in the DHCPOFFER. If the client accepts this offer it specifies
- /// this address in the requested IP address option in the DHCPREQUEST.
- /// At this point, the allocation engine will use the request IP address
- /// as a hard requirement and if this address can't be allocated for
- /// any reason, the allocation engine returns NULL lease. As a result,
- /// the DHCP server sends a DHCPNAK to the client and the client
- /// falls back to the DHCP server discovery.
- ///
- /// The only exception from this rule is when the client doesn't specify
- /// a requested IP address option (invalid behavior) in which case the
- /// allocation engine will try to allocate any address.
- ///
- /// If there is an address reservation specified for the particular client
- /// the reserved address always takes precedence over addresses from the
- /// dynamic pool or even an address currently allocated for this client.
- ///
- /// It is possible that the address reserved for the particular client
- /// is in use by other client, e.g. as a result of pools reconfigruation.
- /// In this case, when the client requests allocation of the reserved
- /// address and the server determines that it is leased to someone else,
- /// the allocation engine doesn't allocate a lease for the client having
- /// a reservation. When the client having a lease returns to renew, the
- /// allocation engine doesn't extend the lease for it and returns a NULL
- /// pointer. The client falls back to the 4-way exchange and a different
- /// lease is allocated. At this point, the reserved address is freed and
- /// can be allocated to the client which holds this reservation.
- ///
- /// When a server should do DNS updates, it is required that allocation
- /// returns the information how the lease was obtained by the allocation
- /// engine. In particular, the DHCP server should be able to check whether
- /// existing lease was returned, or new lease was allocated. When existing
- /// lease was returned, server should check whether the FQDN has changed
- /// between the allocation of the old and new lease. If so, server should
- /// perform appropriate DNS update. If not, server may choose to not
- /// perform the update. The information about the old lease is returned via
- /// @c old_lease parameter. If NULL value is returned, it is an indication
- /// that new lease was allocated for the client. If non-NULL value is
- /// returned, it is an indication that allocation engine reused/renewed an
- /// existing lease.
- ///
- /// @todo Replace parameters with a single parameter of a
- /// @c ClientContext4 type.
- ///
- /// @param subnet subnet the allocation should come from
- /// @param clientid Client identifier
- /// @param hwaddr Client's hardware address info
- /// @param hint A hint that the client provided
- /// @param fwd_dns_update Indicates whether forward DNS update will be
- /// performed for the client (true) or not (false).
- /// @param rev_dns_update Indicates whether reverse DNS update will be
- /// performed for the client (true) or not (false).
- /// @param hostname A string carrying hostname to be used for DNS updates.
- /// @param fake_allocation Is this real i.e. REQUEST (false) or just picking
- /// an address for DISCOVER that is not really allocated (true)
- /// @param callout_handle A callout handle (used in hooks). A lease callouts
- /// will be executed if this parameter is passed.
- /// @param [out] old_lease Holds the pointer to a previous instance of a
- /// lease. The NULL pointer indicates that lease didn't exist prior
- /// to calling this function (e.g. new lease has been allocated).
- ///
- /// @return Allocated IPv4 lease (or NULL if allocation failed).
- Lease4Ptr
- allocateLease4(const SubnetPtr& subnet, const ClientIdPtr& clientid,
- const HWAddrPtr& hwaddr,
- const isc::asiolink::IOAddress& hint,
- const bool fwd_dns_update, const bool rev_dns_update,
- const std::string& hostname, bool fake_allocation,
- const isc::hooks::CalloutHandlePtr& callout_handle,
- Lease4Ptr& old_lease);
-
- /// @brief Renews an DHCPv4 lease.
- ///
- /// This method updates the lease with the information from the provided
- /// context and invokes the lease4_renew callout.
- ///
- /// The address of the lease being renewed is NOT updated.
- ///
- /// @param lease A lease to be renewed.
- /// @param ctx Message processing context. It holds various information
- /// extracted from the client's message and required to allocate a lease.
- ///
- /// @return Returns renewed lease. Note that the lease is only updated when
- /// it is an actual allocation (not processing DHCPDISCOVER message).
- Lease4Ptr
- renewLease4(const Lease4Ptr& lease, ClientContext4& ctx);
-
/// @brief Allocates IPv6 leases for a given IA container
///
/// This method uses currently selected allocator to pick allocatable
Lease6Collection
renewLeases6(ClientContext6& ctx);
+ //@}
-
- /// @brief returns allocator for a given pool type
- /// @param type type of pool (V4, IA, TA or PD)
- /// @throw BadValue if allocator for a given type is missing
- /// @return pointer to allocator handing a given resource types
- AllocatorPtr getAllocator(Lease::Type type);
-
- /// @brief Destructor. Used during DHCPv6 service shutdown.
- virtual ~AllocEngine();
private:
- /// @brief Creates a lease and inserts it in LeaseMgr if necessary
+ /// @name Private methods for DHCPv6 leases allocation
+ ///
+ //@{
+
+ /// @brief creates a lease and inserts it in LeaseMgr if necessary
///
/// Creates a lease based on specified parameters and tries to insert it
- /// into the database. That may fail in some cases, e.g. when there is another
- /// allocation process and we lost a race to a specific lease.
- ///
- /// @param subnet Subnet the lease is allocated from
- /// @param clientid Client identifier
- /// @param hwaddr Client's hardware address
- /// @param addr An address that was selected and is confirmed to be available
- /// @param fwd_dns_update Indicates whether forward DNS update will be
- /// performed for the client (true) or not (false).
- /// @param rev_dns_update Indicates whether reverse DNS update will be
- /// performed for the client (true) or not (false).
- /// @param hostname A string carrying hostname to be used for DNS updates.
- /// @param callout_handle a callout handle (used in hooks). A lease callouts
- /// will be executed if this parameter is passed (and there are callouts
- /// registered)
- /// @param fake_allocation Is this real i.e. REQUEST (false) or just picking
- /// an address for DISCOVER that is not really allocated (true)
- /// @return allocated lease (or NULL in the unlikely case of the lease just
- /// becomed unavailable)
- Lease4Ptr createLease4(const SubnetPtr& subnet, const DuidPtr& clientid,
- const HWAddrPtr& hwaddr,
- const isc::asiolink::IOAddress& addr,
- const bool fwd_dns_update,
- const bool rev_dns_update,
- const std::string& hostname,
- const isc::hooks::CalloutHandlePtr& callout_handle,
- bool fake_allocation = false);
-
- /// @brief Updates the specified lease with the information from a context.
- ///
- /// The context, specified as an argument to this method, holds various
- /// information gathered from the client's message and passed to the
- /// allocation engine. The allocation engine uses this information to make
- /// lease allocation decisions. Some public methods of the allocation engine
- /// requires updating the lease information with the data gathered from the
- /// context, e.g. @c AllocEngine::reuseExpiredLease requires updating the
- /// expired lease with a fresh information from the context to create a
- /// lease to be held for the client.
- ///
- /// Note that this doesn't update the lease address.
- ///
- /// @param [out] lease A pointer to the lease to be updated.
- /// @param ctx A context containing information from the server about the
- /// client and its message.
- void updateLease4Information(const Lease4Ptr& lease,
- ClientContext4& ctx) const;
-
- /// @brief creates a lease and inserts it in LeaseMgr if necessary
- ///
- /// Creates a lease based on specified parameters and tries to insert it
- /// into the database. That may fail in some cases, i.e. when there is another
+ /// into the database. That may fail in some cases, i.e. when there is another
/// allocation process and we lost a race to a specific lease.
///
/// @param ctx client context that passes all necessary information. See
removeNonreservedLeases6(ClientContext6& ctx,
Lease6Collection& existing_leases);
- Lease4Ptr allocateOrReuseLease(const asiolink::IOAddress& address,
- ClientContext4& ctx);
-
- /// @brief Reuses expired DHCPv4 lease.
- ///
- /// Makes new allocation using an expired lease. The lease is updated with
- /// the information from the provided context. Typically, an expired lease
- /// lease which belonged to one client may be assigned to another client
- /// which asked for the specific address.
- ///
- /// @param expired An old, expired lease.
- /// @param ctx Message processing context. It holds various information
- /// extracted from the client's message and required to allocate a lease.
- ///
- /// @return Updated lease instance.
- /// @throw BadValue if trying to reuse a lease which is still valid or
- /// when the provided parameters are invalid.
- Lease4Ptr reuseExpiredLease(Lease4Ptr& expired, ClientContext4& ctx);
-
- /// @brief Updates the existing, non expired lease with a information from
- /// the context.
- ///
- /// This method is invoked when the client requests allocation of the
- /// (reserved) lease but there is a lease for this client with a different
- /// address in the database already. In this case the existing lease must
- /// be updated in the database with a new information. In particular,
- /// with a new address.
- ///
- /// This method invokes the lease4_release and lease4_select callouts.
- ///
- /// @param lease A pointer to the lease to be updated.
- /// @param ctx A context to be used to update the lease.
- ///
- /// @return Pointer to the updated lease.
- /// @throw BadValue if the provided parameters are invalid.
- Lease4Ptr replaceClientLease(Lease4Ptr& lease, ClientContext4& ctx);
-
- /// @brief Replace or renew client's lease.
- ///
- /// This method is ivoked by the @c AllocEngine::allocateLease4 when it
- /// finds that the lease for the particular client already exists in the
- /// database. If the existing lease has the same IP address as the one
- /// that the client should be allocated the existing lease is renewed.
- /// If the client should be allocated a different address, e.g. there
- /// is a static reservation for the client, the existing lease is replaced
- /// with a new one. This method handles both cases.
- ///
- /// @param lease Existing lease.
- /// @param ctx Context holding parameters to be used for the lease
- /// allocation.
- ///
- /// @return Updated lease, or NULL if allocation was unsucessful.
- /// @throw BadValue if specified parameters are invalid.
- Lease4Ptr reallocateClientLease(Lease4Ptr& lease, ClientContext4& ctx);
-
- Lease4Ptr discoverLease4(ClientContext4& ctx);
-
- Lease4Ptr requestLease4(ClientContext4& ctx);
-
/// @brief Reuses expired IPv6 lease
///
/// Updates existing expired lease with new information. Lease database
/// @param lease IPv6 lease to be extended.
void extendLease6(ClientContext6& ctx, Lease6Ptr lease);
- /// @brief a pointer to currently used allocator
+ //@}
+
+public:
+
+ /// @name Public method and structure for DHCPv4 lease allocation.
///
- /// For IPv4, there will be only one allocator: TYPE_V4
- /// For IPv6, there will be 3 allocators: TYPE_NA, TYPE_TA, TYPE_PD
- std::map<Lease::Type, AllocatorPtr> allocators_;
+ //@{
- /// @brief number of attempts before we give up lease allocation (0=unlimited)
- unsigned int attempts_;
+ /// @brief Context information for the DHCPv4 lease allocation.
+ ///
+ /// This structure holds a set of information provided by the DHCPv4
+ /// server to the allocation engine. In particular, it holds the
+ /// client identifying information, such as HW address or client
+ /// identifier. It also holds the information about the subnet that
+ /// the client is connected to.
+ ///
+ /// This structure is also used to pass some information from
+ /// the allocation engine back to the server, i.e. the old lease
+ /// which the client had before the allocation.
+ ///
+ /// This structure is meant to be extended in the future, if more
+ /// information should be passed to the allocation engine. Note
+ /// that the big advantage of using the context structure to pass
+ /// information to the allocation engine methods is that adding
+ /// new information doesn't modify the API of the allocation engine.
+ struct ClientContext4 {
+ /// @brief Subnet selected for the client by the server.
+ SubnetPtr subnet_;
- // hook name indexes (used in hooks callouts)
- int hook_index_lease4_select_; ///< index for lease4_select hook
- int hook_index_lease6_select_; ///< index for lease6_select hook
+ /// @brief Client identifier from the DHCP message.
+ ClientIdPtr clientid_;
+
+ /// @brief HW address from the DHCP message.
+ HWAddrPtr hwaddr_;
+
+ /// @brief An address that the client desires.
+ ///
+ /// If this address is set to 0 it indicates that this address
+ /// is unspecified.
+ asiolink::IOAddress requested_address_;
+
+ /// @brief Perform forward DNS update.
+ bool fwd_dns_update_;
+
+ /// @brief Perform reverse DNS update.
+ bool rev_dns_update_;
+
+ /// @brief Hostname.
+ ///
+ /// The server retrieves the hostname from the Client FQDN option,
+ /// Hostname option or the host reservation record for the client.
+ std::string hostname_;
+
+ /// @brief Callout handle associated with the client's message.
+ hooks::CalloutHandlePtr callout_handle_;
+
+ /// @brief Indicates if this is a real or fake allocation.
+ ///
+ /// The real allocation is when the allocation engine is supposed
+ /// to make an update in a lease database: create new lease, or
+ /// update existing lease.
+ bool fake_allocation_;
+
+ /// @brief A pointer to an old lease that the client had before update.
+ Lease4Ptr old_lease_;
+
+ /// @brief A pointer to the object identifying host reservations.
+ ConstHostPtr host_;
+
+ /// @brief Signals that the allocation should be interrupted.
+ ///
+ /// This flag is set by the downstream methods called by the
+ /// @c AllocEngine::allocateLease4. This flag is set to true to
+ /// indicate that an attempt to allocate a lease should be
+ /// interrupted.
+ ///
+ /// One possible use case is when the allocation engine tries
+ /// to renew the client's lease and the leased address appears
+ /// to be reserved for someone else. In such case, the allocation
+ /// engine should signal to the server that the address that the
+ /// client should stop using this address. The
+ /// @c AllocEngine::renewLease4 sets this flag so as the
+ /// upstream methods return the NULL lease pointer to the server.
+ bool interrupt_processing_;
+
+ /// @brief Default constructor.
+ ClientContext4();
+
+ bool myLease(const Lease4& lease) const;
+
+ };
+
+ /// @brief Returns IPv4 lease.
+ ///
+ /// This method finds a lease for a client using the following algorithm:
+ /// - If a lease exists for the combination of the HW address or client id
+ /// and a subnet, try to use this lease for the client. If the client
+ /// has a reservation for an address for which the lease was created or
+ /// the client desires to renew the lease for this address (ciaddr or
+ /// requested IP address option), the server renews the lease for the
+ /// client. If the client desires a different address or the server has
+ /// a (potentially new) reservation for a different address for this
+ /// client, the existing lease is replaced with a new lease.
+ /// - If the client has no lease in the lease database the server will try
+ /// to allocate a new lease. If the client has a reservation for the
+ /// particular address or if it has specified a desired address the
+ /// server will check if the particular address is not allocated to
+ /// other client. If the address is available, the server will allocate
+ /// this address for the client.
+ /// - If the desired address is unavailable the server checks if the
+ /// lease for this address has expired. If the lease is expired, the
+ /// server will allocate this lease to the client. The relevant
+ /// information will be updated, e.g. new client HW address, host name
+ /// etc.
+ /// - If the desired address is in use by other client, the server will try
+ /// to allocate a different address. The server picks addresses from
+ /// a dynamic pool and checks if the address is available and that
+ /// it is not reserved for another client. If it is in use by another
+ /// client or if it is reserved for another client, this address is not
+ /// allocated. The server picks next address and repeats this check.
+ /// Note that the server ceases allocation after configured number
+ /// of unsuccessful attempts.
+ ///
+ /// The lease allocation process is slightly different for the
+ /// DHCPDISCOVER and DHCPREQUEST messages. In the former case, the client
+ /// may specify the requested IP address option with a desired address and
+ /// the server treats this address as hint. This means that the server may
+ /// allocate a different address on its discretion and send it to the
+ /// client in the DHCPOFFER. If the client accepts this offer it specifies
+ /// this address in the requested IP address option in the DHCPREQUEST.
+ /// At this point, the allocation engine will use the request IP address
+ /// as a hard requirement and if this address can't be allocated for
+ /// any reason, the allocation engine returns NULL lease. As a result,
+ /// the DHCP server sends a DHCPNAK to the client and the client
+ /// falls back to the DHCP server discovery.
+ ///
+ /// The only exception from this rule is when the client doesn't specify
+ /// a requested IP address option (invalid behavior) in which case the
+ /// allocation engine will try to allocate any address.
+ ///
+ /// If there is an address reservation specified for the particular client
+ /// the reserved address always takes precedence over addresses from the
+ /// dynamic pool or even an address currently allocated for this client.
+ ///
+ /// It is possible that the address reserved for the particular client
+ /// is in use by other client, e.g. as a result of pools reconfigruation.
+ /// In this case, when the client requests allocation of the reserved
+ /// address and the server determines that it is leased to someone else,
+ /// the allocation engine doesn't allocate a lease for the client having
+ /// a reservation. When the client having a lease returns to renew, the
+ /// allocation engine doesn't extend the lease for it and returns a NULL
+ /// pointer. The client falls back to the 4-way exchange and a different
+ /// lease is allocated. At this point, the reserved address is freed and
+ /// can be allocated to the client which holds this reservation.
+ ///
+ /// When a server should do DNS updates, it is required that allocation
+ /// returns the information how the lease was obtained by the allocation
+ /// engine. In particular, the DHCP server should be able to check whether
+ /// existing lease was returned, or new lease was allocated. When existing
+ /// lease was returned, server should check whether the FQDN has changed
+ /// between the allocation of the old and new lease. If so, server should
+ /// perform appropriate DNS update. If not, server may choose to not
+ /// perform the update. The information about the old lease is returned via
+ /// @c old_lease parameter. If NULL value is returned, it is an indication
+ /// that new lease was allocated for the client. If non-NULL value is
+ /// returned, it is an indication that allocation engine reused/renewed an
+ /// existing lease.
+ ///
+ /// @todo Replace parameters with a single parameter of a
+ /// @c ClientContext4 type.
+ ///
+ /// @param subnet subnet the allocation should come from
+ /// @param clientid Client identifier
+ /// @param hwaddr Client's hardware address info
+ /// @param hint A hint that the client provided
+ /// @param fwd_dns_update Indicates whether forward DNS update will be
+ /// performed for the client (true) or not (false).
+ /// @param rev_dns_update Indicates whether reverse DNS update will be
+ /// performed for the client (true) or not (false).
+ /// @param hostname A string carrying hostname to be used for DNS updates.
+ /// @param fake_allocation Is this real i.e. REQUEST (false) or just picking
+ /// an address for DISCOVER that is not really allocated (true)
+ /// @param callout_handle A callout handle (used in hooks). A lease callouts
+ /// will be executed if this parameter is passed.
+ /// @param [out] old_lease Holds the pointer to a previous instance of a
+ /// lease. The NULL pointer indicates that lease didn't exist prior
+ /// to calling this function (e.g. new lease has been allocated).
+ ///
+ /// @return Allocated IPv4 lease (or NULL if allocation failed).
+ Lease4Ptr allocateLease4(const SubnetPtr& subnet, const ClientIdPtr& clientid,
+ const HWAddrPtr& hwaddr,
+ const isc::asiolink::IOAddress& hint,
+ const bool fwd_dns_update, const bool rev_dns_update,
+ const std::string& hostname, bool fake_allocation,
+ const isc::hooks::CalloutHandlePtr& callout_handle,
+ Lease4Ptr& old_lease);
+
+ //@}
+
+private:
+
+ /// @name Private methods for DHCPv4 lease allocation
+ ///
+ //@{
+
+ Lease4Ptr discoverLease4(ClientContext4& ctx);
+
+ Lease4Ptr requestLease4(ClientContext4& ctx);
+
+ /// @brief Creates a lease and inserts it in LeaseMgr if necessary
+ ///
+ /// Creates a lease based on specified parameters and tries to insert it
+ /// into the database. That may fail in some cases, e.g. when there is another
+ /// allocation process and we lost a race to a specific lease.
+ ///
+ /// @param subnet Subnet the lease is allocated from
+ /// @param clientid Client identifier
+ /// @param hwaddr Client's hardware address
+ /// @param addr An address that was selected and is confirmed to be available
+ /// @param fwd_dns_update Indicates whether forward DNS update will be
+ /// performed for the client (true) or not (false).
+ /// @param rev_dns_update Indicates whether reverse DNS update will be
+ /// performed for the client (true) or not (false).
+ /// @param hostname A string carrying hostname to be used for DNS updates.
+ /// @param callout_handle a callout handle (used in hooks). A lease callouts
+ /// will be executed if this parameter is passed (and there are callouts
+ /// registered)
+ /// @param fake_allocation Is this real i.e. REQUEST (false) or just picking
+ /// an address for DISCOVER that is not really allocated (true)
+ /// @return allocated lease (or NULL in the unlikely case of the lease just
+ /// becomed unavailable)
+ Lease4Ptr createLease4(const SubnetPtr& subnet, const DuidPtr& clientid,
+ const HWAddrPtr& hwaddr,
+ const isc::asiolink::IOAddress& addr,
+ const bool fwd_dns_update,
+ const bool rev_dns_update,
+ const std::string& hostname,
+ const isc::hooks::CalloutHandlePtr& callout_handle,
+ bool fake_allocation = false);
+
+ /// @brief Renews an DHCPv4 lease.
+ ///
+ /// This method updates the lease with the information from the provided
+ /// context and invokes the lease4_renew callout.
+ ///
+ /// The address of the lease being renewed is NOT updated.
+ ///
+ /// @param lease A lease to be renewed.
+ /// @param ctx Message processing context. It holds various information
+ /// extracted from the client's message and required to allocate a lease.
+ ///
+ /// @return Returns renewed lease. Note that the lease is only updated when
+ /// it is an actual allocation (not processing DHCPDISCOVER message).
+ Lease4Ptr renewLease4(const Lease4Ptr& lease, ClientContext4& ctx);
+
+ /// @brief Reuses expired DHCPv4 lease.
+ ///
+ /// Makes new allocation using an expired lease. The lease is updated with
+ /// the information from the provided context. Typically, an expired lease
+ /// lease which belonged to one client may be assigned to another client
+ /// which asked for the specific address.
+ ///
+ /// @param expired An old, expired lease.
+ /// @param ctx Message processing context. It holds various information
+ /// extracted from the client's message and required to allocate a lease.
+ ///
+ /// @return Updated lease instance.
+ /// @throw BadValue if trying to reuse a lease which is still valid or
+ /// when the provided parameters are invalid.
+ Lease4Ptr reuseExpiredLease(Lease4Ptr& expired, ClientContext4& ctx);
+
+ Lease4Ptr allocateOrReuseLease(const asiolink::IOAddress& address,
+ ClientContext4& ctx);
+
+
+ /// @brief Updates the specified lease with the information from a context.
+ ///
+ /// The context, specified as an argument to this method, holds various
+ /// information gathered from the client's message and passed to the
+ /// allocation engine. The allocation engine uses this information to make
+ /// lease allocation decisions. Some public methods of the allocation engine
+ /// requires updating the lease information with the data gathered from the
+ /// context, e.g. @c AllocEngine::reuseExpiredLease requires updating the
+ /// expired lease with a fresh information from the context to create a
+ /// lease to be held for the client.
+ ///
+ /// Note that this doesn't update the lease address.
+ ///
+ /// @param [out] lease A pointer to the lease to be updated.
+ /// @param ctx A context containing information from the server about the
+ /// client and its message.
+ void updateLease4Information(const Lease4Ptr& lease,
+ ClientContext4& ctx) const;
+
+ //@}
};
}; // namespace isc::dhcp