#include <dhcpsrv/alloc_engine.h>
#include <dhcpsrv/dhcpsrv_log.h>
+#include <dhcpsrv/host_mgr.h>
#include <dhcpsrv/lease_mgr_factory.h>
#include <hooks/server_hooks.h>
isc_throw(InvalidOperation, "HWAddr must be defined");
}
- // Check if there's existing lease for that subnet/clientid/hwaddr combination.
- Lease4Ptr existing = LeaseMgrFactory::instance().getLease4(*hwaddr, subnet->getID());
- if (existing) {
- // Save the old lease, before renewal.
- old_lease.reset(new Lease4(*existing));
- // We have a lease already. This is a returning client, probably after
- // its reboot.
- existing = renewLease4(subnet, clientid, hwaddr,
- fwd_dns_update, rev_dns_update, hostname,
- existing, callout_handle, fake_allocation);
- if (existing) {
- return (existing);
+ // Build the processing context.
+ Context4 ctx;
+ ctx.subnet_ = subnet;
+ ctx.clientid_ = clientid;
+ ctx.hwaddr_ = hwaddr;
+ ctx.hint_ = 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;
+ ctx.host_ = HostMgr::instance().get4(subnet->getID(), hwaddr);
+
+ if (ctx.host_) {
+ if (ctx.hint_ == IOAddress("0.0.0.0")) {
+ ctx.hint_ = ctx.host_->getIPv4Reservation();
+
+ } else if (!ctx.fake_allocation_ &&
+ (ctx.hint_ != ctx.host_->getIPv4Reservation())) {
+ return (Lease4Ptr());
}
+ }
- // If renewal failed (e.g. the lease no longer matches current configuration)
- // let's continue the allocation process
+ LeaseMgr& lease_mgr = LeaseMgrFactory::instance();
+ Lease4Ptr existing = lease_mgr.getLease4(*hwaddr, ctx.subnet_->getID());
+ if (!existing && clientid) {
+ existing = lease_mgr.getLease4(*clientid, ctx.subnet_->getID());
}
- if (clientid) {
- existing = LeaseMgrFactory::instance().getLease4(*clientid, subnet->getID());
- if (existing) {
- // Save the old lease before renewal.
- old_lease.reset(new Lease4(*existing));
- // we have a lease already. This is a returning client, probably after
- // its reboot.
- existing = renewLease4(subnet, clientid, hwaddr,
- fwd_dns_update, rev_dns_update,
- hostname, existing, callout_handle,
- fake_allocation);
- // @todo: produce a warning. We haven't found him using MAC address, but
- // we found him using client-id
- if (existing) {
- return (existing);
- }
+ if (existing) {
+ existing = reallocateClientLease(existing, ctx);
+ if (ctx.host_ || existing) {
+ old_lease = ctx.old_lease_;
+ return (existing);
}
}
- // check if the hint is in pool and is available
- if (subnet->inPool(Lease::TYPE_V4, hint)) {
- existing = LeaseMgrFactory::instance().getLease4(hint);
- if (!existing) {
- /// @todo: Check if the hint is reserved once we have host support
- /// implemented
+ // Check if we have a specific address we would like to allocate for the
+ // client. It may either be an address which the client is currently
+ // using, or it may be a administratively reserved address.
+ if (ctx.host_ || subnet->inPool(Lease::TYPE_V4, hint)) {
+ // If a client is requesting specific IP address, but the
+ // reservation was made for a different address the server returns
+ // NAK to the client. By returning NULL lease here we indicate to
+ // the server that we're not able to fulfil client's request for the
+ // particular IP address.
+ if (!ctx.fake_allocation_ && ctx.host_ &&
+ (hint != IOAddress("0.0.0.0")) &&
+ (ctx.host_->getIPv4Reservation() != hint)) {
+ return (Lease4Ptr());
+ }
- // The hint is valid and not currently used, let's create a lease for it
- Lease4Ptr lease = createLease4(subnet, clientid, hwaddr, hint,
- fwd_dns_update, rev_dns_update,
+ // The reserved address always takes precedence over hints. But, if
+ // there is no reservation, try to respect the client's hint.
+ const IOAddress& candidate = ctx.host_ ?
+ ctx.host_->getIPv4Reservation() : hint;
+
+ existing = LeaseMgrFactory::instance().getLease4(candidate);
+ if (!existing) {
+ // The candidate address is currently unused. Let's create a
+ // lease for it.
+ Lease4Ptr lease = createLease4(subnet, clientid, hwaddr,
+ candidate, fwd_dns_update,
+ rev_dns_update,
hostname, callout_handle,
fake_allocation);
- // It can happen that the lease allocation failed (we could have lost
- // the race condition. That means that the hint is lo longer usable and
- // we need to continue the regular allocation path.
- if (lease) {
+ // If we have allocated the lease let's return it. Also,
+ // always return when tried to allocate reserved address,
+ // regardless if allocation was successful or not. If it
+ // was not successful, we will return a NULL pointer which
+ // indicates to the server that it should send NAK to the
+ // client.
+ if (lease || ctx.host_) {
return (lease);
}
+
} else {
if (existing->expired()) {
// Save the old lease, before reusing it.
old_lease.reset(new Lease4(*existing));
- return (reuseExpiredLease(existing, subnet, clientid, hwaddr,
- fwd_dns_update, rev_dns_update,
- hostname, callout_handle,
- fake_allocation));
+ return (reuseExpiredLease(existing, ctx));
+
+ } else if (ctx.host_ && (ctx.host_->getIPv4Reservation() !=
+ IOAddress("0.0.0.0"))) {
+ return (Lease4Ptr());
+
}
}
if (existing->expired()) {
// Save old lease before reusing it.
old_lease.reset(new Lease4(*existing));
- return (reuseExpiredLease(existing, subnet, clientid, hwaddr,
- fwd_dns_update, rev_dns_update,
- hostname, callout_handle,
- fake_allocation));
+ return (reuseExpiredLease(existing, ctx));
}
}
return (Lease4Ptr());
}
-Lease4Ptr AllocEngine::renewLease4(const SubnetPtr& subnet,
- const ClientIdPtr& clientid,
- const HWAddrPtr& hwaddr,
- const bool fwd_dns_update,
- const bool rev_dns_update,
- const std::string& hostname,
- const Lease4Ptr& lease,
- const isc::hooks::CalloutHandlePtr& callout_handle,
- bool fake_allocation /* = false */) {
-
+Lease4Ptr
+AllocEngine::renewLease4(const Lease4Ptr& lease,
+ const AllocEngine::Context4& ctx) {
if (!lease) {
- isc_throw(InvalidOperation, "Lease4 must be specified");
+ isc_throw(BadValue, "null lease specified for renewLease4");
}
// Let's keep the old data. This is essential if we are using memfile
/// @todo: remove this once #3083 is implemented
Lease4 old_values = *lease;
- lease->subnet_id_ = subnet->getID();
- lease->hwaddr_ = hwaddr;
- lease->client_id_ = clientid;
- lease->cltt_ = time(NULL);
- lease->t1_ = subnet->getT1();
- lease->t2_ = subnet->getT2();
- lease->valid_lft_ = subnet->getValid();
- lease->fqdn_fwd_ = fwd_dns_update;
- lease->fqdn_rev_ = rev_dns_update;
- lease->hostname_ = hostname;
+ // Update the lease with the information from the context.
+ updateLease4Information(lease, ctx);
bool skip = false;
- // Execute all callouts registered for packet6_send
- if (HooksManager::getHooksManager().calloutsPresent(Hooks.hook_index_lease4_renew_)) {
+ // Execute all callouts registered for lease4_renew.
+ if (HooksManager::getHooksManager().
+ calloutsPresent(Hooks.hook_index_lease4_renew_)) {
// Delete all previous arguments
- callout_handle->deleteAllArguments();
+ 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>(subnet);
+ Subnet4Ptr subnet4 = boost::dynamic_pointer_cast<Subnet4>(ctx.subnet_);
// Pass the parameters
- callout_handle->setArgument("subnet4", subnet4);
- callout_handle->setArgument("clientid", clientid);
- callout_handle->setArgument("hwaddr", hwaddr);
+ 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
- callout_handle->setArgument("lease4", lease);
+ ctx.callout_handle_->setArgument("lease4", lease);
// Call all installed callouts
- HooksManager::callCallouts(Hooks.hook_index_lease4_renew_, *callout_handle);
+ 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 (callout_handle->getSkip()) {
+ if (ctx.callout_handle_->getSkip()) {
skip = true;
- LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_HOOKS, DHCPSRV_HOOK_LEASE4_RENEW_SKIP);
+ LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_HOOKS,
+ DHCPSRV_HOOK_LEASE4_RENEW_SKIP);
}
}
- if (!fake_allocation && !skip) {
+ if (!ctx.fake_allocation_ && !skip) {
// for REQUEST we do update the lease
LeaseMgrFactory::instance().updateLease4(lease);
}
return (expired);
}
-Lease4Ptr AllocEngine::reuseExpiredLease(Lease4Ptr& expired,
- const SubnetPtr& subnet,
- const ClientIdPtr& clientid,
- const HWAddrPtr& hwaddr,
- const bool fwd_dns_update,
- const bool rev_dns_update,
- const std::string& hostname,
- const isc::hooks::CalloutHandlePtr& callout_handle,
- bool fake_allocation /*= false */ ) {
+Lease4Ptr
+AllocEngine::reuseExpiredLease(Lease4Ptr& expired,
+ const AllocEngine::Context4& ctx) {
+ if (!expired) {
+ isc_throw(BadValue, "null lease specified for reuseExpiredLease");
+ }
- if (!expired->expired()) {
- isc_throw(BadValue, "Attempt to recycle lease that is still valid");
+ if (!ctx.subnet_) {
+ isc_throw(BadValue, "null subnet specified for the reuseExpiredLease");
}
- // address, lease type and prefixlen (0) stay the same
- expired->client_id_ = clientid;
- expired->hwaddr_ = hwaddr;
- expired->valid_lft_ = subnet->getValid();
- expired->t1_ = subnet->getT1();
- expired->t2_ = subnet->getT2();
- expired->cltt_ = time(NULL);
- expired->subnet_id_ = subnet->getID();
+ updateLease4Information(expired, ctx);
expired->fixed_ = false;
- expired->hostname_ = hostname;
- expired->fqdn_fwd_ = fwd_dns_update;
- expired->fqdn_rev_ = rev_dns_update;
/// @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 (callout_handle &&
- HooksManager::getHooksManager().calloutsPresent(hook_index_lease4_select_)) {
+ if (ctx.callout_handle_ && HooksManager::getHooksManager()
+ .calloutsPresent(hook_index_lease4_select_)) {
// Delete all previous arguments
- callout_handle->deleteAllArguments();
+ ctx.callout_handle_->deleteAllArguments();
// Pass necessary arguments
// 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>(subnet);
- callout_handle->setArgument("subnet4", subnet4);
+ Subnet4Ptr subnet4 = boost::dynamic_pointer_cast<Subnet4>(ctx.subnet_);
+ ctx.callout_handle_->setArgument("subnet4", subnet4);
// Is this solicit (fake = true) or request (fake = false)
- callout_handle->setArgument("fake_allocation", fake_allocation);
+ ctx.callout_handle_->setArgument("fake_allocation",
+ ctx.fake_allocation_);
// The lease that will be assigned to a client
- callout_handle->setArgument("lease4", expired);
+ ctx.callout_handle_->setArgument("lease4", expired);
// Call the callouts
- HooksManager::callCallouts(hook_index_lease6_select_, *callout_handle);
+ 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", expired);
+ ctx.callout_handle_->getArgument("lease4", expired);
}
- if (!fake_allocation) {
+ if (!ctx.fake_allocation_) {
// for REQUEST we do update the lease
LeaseMgrFactory::instance().updateLease4(expired);
}
return (expired);
}
+Lease4Ptr
+AllocEngine::replaceClientLease(Lease4Ptr& lease, const Context4& ctx) {
+ if (!lease) {
+ isc_throw(BadValue, "null lease specified for replaceClientLease");
+ }
+
+ if (!ctx.subnet_) {
+ isc_throw(BadValue, "null subnet specified for replaceClientLease");
+ }
+
+ if (!ctx.host_) {
+ isc_throw(BadValue, "null host specified for replaceClientLease");
+ }
+
+ if (ctx.hint_ == IOAddress("0.0.0.0")) {
+ isc_throw(BadValue, "zero address specified for the"
+ " replaceClientLease");
+ }
+
+ IOAddress prev_address = lease->addr_;
+ updateLease4Information(lease, ctx);
+ lease->addr_ = ctx.host_->getIPv4Reservation();
+
+ // Execute 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 arguments.
+ Subnet4Ptr subnet4 = boost::dynamic_pointer_cast<Subnet4>(ctx.subnet_);
+ ctx.callout_handle_->setArgument("subnet4", subnet4);
+
+ ctx.callout_handle_->setArgument("fake_allocation",
+ ctx.fake_allocation_);
+
+ ctx.callout_handle_->setArgument("lease4", lease);
+
+ HooksManager::callCallouts(hook_index_lease4_select_,
+ *ctx.callout_handle_);
+
+ 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.
+ ctx.callout_handle_->getArgument("lease4", lease);
+ }
+
+ if (!ctx.fake_allocation_) {
+ LeaseMgrFactory::instance().deleteLease(prev_address);
+ LeaseMgrFactory::instance().addLease(lease);
+ }
+
+ return (lease);
+}
+
+Lease4Ptr
+AllocEngine::reallocateClientLease(Lease4Ptr& lease,
+ AllocEngine::Context4& ctx) {
+ // Save the old lease, before renewal.
+ ctx.old_lease_.reset(new Lease4(*lease));
+
+ if (ctx.host_ && ctx.host_->getIPv4Reservation() != lease->addr_) {
+ lease = replaceClientLease(lease, ctx);
+ return (lease);
+
+ } else {
+ lease = renewLease4(lease, ctx);
+ if (lease) {
+ return (lease);
+ }
+ }
+
+ return (Lease4Ptr());
+}
+
+
Lease6Ptr AllocEngine::createLease6(const Subnet6Ptr& subnet,
const DuidPtr& duid,
const uint32_t iaid,
}
}
+void
+AllocEngine::updateLease4Information(const Lease4Ptr& lease,
+ const AllocEngine::Context4& ctx) const {
+ // This should not happen in theory.
+ if (!lease) {
+ isc_throw(BadValue, "null lease specified for updateLease4Information");
+ }
+
+ if (!ctx.subnet_) {
+ isc_throw(BadValue, "null subnet specified for"
+ " updateLease4Information");
+ }
+
+ lease->subnet_id_ = ctx.subnet_->getID();
+ lease->hwaddr_ = ctx.hwaddr_;
+ lease->client_id_ = ctx.clientid_;
+ lease->cltt_ = time(NULL);
+ lease->t1_ = ctx.subnet_->getT1();
+ lease->t2_ = ctx.subnet_->getT2();
+ lease->valid_lft_ = ctx.subnet_->getValid();
+ lease->fqdn_fwd_ = ctx.fwd_dns_update_;
+ lease->fqdn_rev_ = ctx.rev_dns_update_;
+ lease->hostname_ = ctx.hostname_;
+}
+
Lease6Collection
AllocEngine::updateFqdnData(const Lease6Collection& leases,
const bool fwd_dns_update,
#include <asiolink/io_address.h>
#include <dhcp/duid.h>
#include <dhcp/hwaddr.h>
+#include <dhcpsrv/host.h>
#include <dhcpsrv/subnet.h>
#include <dhcpsrv/lease_mgr.h>
#include <hooks/callout_handle.h>
ALLOC_RANDOM // random - an address is randomly selected
} AllocType;
+ /// @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 Context4 {
+ /// @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 hint_;
+
+ /// @brief Perform forward DNS update.
+ bool fwd_dns_update_;
+
+ /// @brief Perform reverse DNS update.
+ bool rev_dns_update_;
+
+ /// @brief Hostname.
+ 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 Default constructor.
+ Context4()
+ : subnet_(), clientid_(), hwaddr_(), hint_("0.0.0.0"),
+ fwd_dns_update_(false), rev_dns_update_(false),
+ hostname_(""), callout_handle_(), fake_allocation_(false),
+ old_lease_(), host_() {
+ }
+ };
/// @brief Default constructor.
///
const isc::hooks::CalloutHandlePtr& callout_handle,
Lease4Ptr& old_lease);
- /// @brief Renews a IPv4 lease
+ /// @brief Renews an DHCPv4 lease.
///
- /// Since both request and renew are implemented in DHCPv4 as the sending of
- /// a REQUEST packet, it is difficult to easily distinguish between those
- /// cases. Therefore renew for DHCPv4 is done in the allocation engine.
- /// This method is also used when client crashed/rebooted and tries
- /// to get a new lease. It thinks that it gets a new lease, but in fact
- /// we are only renewing the still valid lease for that client.
+ /// This method updates the lease with the information from the provided
+ /// context and invokes the lease4_renew callout.
///
- /// @param subnet A subnet the client is attached to
- /// @param clientid Client identifier
- /// @param hwaddr Client's hardware address
- /// @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 lease A lease to be renewed
- /// @param callout_handle a callout handle (used in hooks). A lease callouts
- /// will be executed if this parameter is passed.
- /// @param fake_allocation Is this real i.e. REQUEST (false) or just picking
- /// an address for DISCOVER that is not really allocated (true)
+ /// The address of the lease being renewed is NOT updated.
+ ///
+ /// @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 SubnetPtr& subnet,
- const ClientIdPtr& clientid,
- const HWAddrPtr& hwaddr,
- const bool fwd_dns_update,
- const bool rev_dns_update,
- const std::string& hostname,
- const Lease4Ptr& lease,
- const isc::hooks::CalloutHandlePtr& callout_handle,
- bool fake_allocation /* = false */);
+ renewLease4(const Lease4Ptr& lease, const Context4& ctx);
/// @brief Allocates an IPv6 lease
///
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,
+ const Context4& 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
const isc::hooks::CalloutHandlePtr& callout_handle,
bool fake_allocation = false);
- /// @brief Reuses expired IPv4 lease
+ /// @brief Reuses expired DHCPv4 lease.
///
- /// Updates existing expired lease with new information. Lease database
- /// is updated if this is real (i.e. REQUEST, fake_allocation = false), not
- /// dummy allocation request (i.e. DISCOVER, fake_allocation = true).
+ /// 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 Old, expired lease
- /// @param subnet Subnet the lease is allocated from
- /// @param clientid Client identifier
- /// @param hwaddr Client's hardware address
- /// @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.
- /// @param fake_allocation Is this real i.e. REQUEST (false) or just picking
- /// an address for DISCOVER that is not really allocated (true)
- /// @return refreshed lease
- /// @throw BadValue if trying to recycle lease that is still valid
- Lease4Ptr reuseExpiredLease(Lease4Ptr& expired,
- const SubnetPtr& subnet,
- const ClientIdPtr& clientid,
- const HWAddrPtr& hwaddr,
- const bool fwd_dns_update,
- const bool rev_dns_update,
- const std::string& hostname,
- const isc::hooks::CalloutHandlePtr& callout_handle,
- bool fake_allocation = false);
+ /// @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, const Context4& 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, const Context4& 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, Context4& ctx);
/// @brief Reuses expired IPv6 lease
///
#include <dhcp/dhcp4.h>
#include <dhcpsrv/alloc_engine.h>
#include <dhcpsrv/cfgmgr.h>
+#include <dhcpsrv/host_mgr.h>
#include <dhcpsrv/lease_mgr.h>
#include <dhcpsrv/lease_mgr_factory.h>
#include <dhcpsrv/memfile_lease_mgr.h>
/// Sets clientid_, hwaddr_, subnet_, pool_ fields to example values
/// used in many tests, initializes cfg_mgr configuration and creates
/// lease database.
+ ///
+ /// It also re-initializes the Host Manager.
AllocEngine4Test() {
+ // Create fresh instance of the HostMgr, and drop any previous HostMgr
+ // state.
+ HostMgr::instance().create();
+
clientid_ = ClientIdPtr(new ClientId(vector<uint8_t>(8, 0x44)));
- static uint8_t mac[] = { 0, 1, 22, 33, 44, 55};
+ uint8_t mac[] = { 0, 1, 22, 33, 44, 55};
// Let's use odd hardware type to check if there is no Ethernet
// hardcoded anywhere.
hwaddr_ = HWAddrPtr(new HWAddr(mac, sizeof(mac), HTYPE_FDDI));
+ // Allocate different MAC address for the tests that require two
+ // different MAC addresses.
+ ++mac[sizeof(mac) - 1];
+ hwaddr2_ = HWAddrPtr(new HWAddr(mac, sizeof (mac), HTYPE_FDDI));
+
// instantiate cfg_mgr
CfgMgr& cfg_mgr = CfgMgr::instance();
cfg_mgr.commit();
factory_.create("type=memfile universe=4 persist=false");
+
+ // Create a default context. Note that remaining parameters must be
+ // assigned when needed.
+ ctx_.subnet_ = subnet_;
+ ctx_.clientid_ = clientid_;
+ ctx_.hwaddr_ = hwaddr_;
+ ctx_.callout_handle_ = HooksManager::createCalloutHandle();
}
/// @brief checks if Lease4 matches expected configuration
factory_.destroy();
}
- ClientIdPtr clientid_; ///< Client-identifier (value used in tests)
- HWAddrPtr hwaddr_; ///< Hardware address (value used in tests)
- Subnet4Ptr subnet_; ///< Subnet4 (used in tests)
- Pool4Ptr pool_; ///< Pool belonging to subnet_
- LeaseMgrFactory factory_; ///< Pointer to LeaseMgr factory
- Lease4Ptr old_lease_; ///< Holds previous instance of the lease.
+ ClientIdPtr clientid_; ///< Client-identifier (value used in tests)
+ HWAddrPtr hwaddr_; ///< Hardware address (value used in tests)
+ HWAddrPtr hwaddr2_; ///< Alternative hardware address.
+ Subnet4Ptr subnet_; ///< Subnet4 (used in tests)
+ Pool4Ptr pool_; ///< Pool belonging to subnet_
+ LeaseMgrFactory factory_; ///< Pointer to LeaseMgr factory
+ Lease4Ptr old_lease_; ///< Holds previous instance of the lease.
+ AllocEngine::Context4 ctx_; ///< Context information passed to various
+ ///< allocation engine functions.
};
// This test checks if the v6 Allocation Engine can be instantiated, parses
// called
TEST_F(AllocEngine4Test, renewLease4) {
boost::scoped_ptr<AllocEngine> engine;
- CalloutHandlePtr callout_handle = HooksManager::createCalloutHandle();
ASSERT_NO_THROW(engine.reset(new AllocEngine(AllocEngine::ALLOC_ITERATIVE,
100, false)));
// Lease was assigned 45 seconds ago and is valid for 100 seconds. Let's
// renew it.
ASSERT_FALSE(lease->expired());
- lease = engine->renewLease4(subnet_, clientid_, hwaddr_, true,
- true, "host.example.com.", lease,
- callout_handle, false);
+ ctx_.fwd_dns_update_ = true;
+ ctx_.rev_dns_update_ = true;
+ ctx_.hostname_ = "host.example.com.";
+ ctx_.fake_allocation_ = false;
+ lease = engine->renewLease4(lease, ctx_);
+
// Check that he got that single lease
ASSERT_TRUE(lease);
EXPECT_EQ(addr, lease->addr_);
detailCompareLease(lease, from_mgr);
}
+// This test checks the behavior of the allocation engine in the following
+// scenario:
+// - Client has no lease in the database.
+// - Client has a reservation.
+// - Client sends DHCPREQUEST without requested IP Address, nor ciaddr.
+// - Client is allocated a reserved address.
+//
+// Note that client must normally include a requested IP address or ciaddr
+// in its message. But, we still want to provision clients that don't do that.
+// The server simply picks reserved address or any other available one if there
+// is no reservation.
+TEST_F(AllocEngine4Test, reservedAddressNoHint) {
+ // Create reservation for the client.
+ HostPtr host(new Host(&hwaddr_->hwaddr_[0], hwaddr_->hwaddr_.size(),
+ Host::IDENT_HWADDR, subnet_->getID(),
+ SubnetID(0), IOAddress("192.0.2.123")));
+ CfgMgr::instance().getStagingCfg()->getCfgHosts()->add(host);
+ CfgMgr::instance().commit();
+
+ AllocEngine engine(AllocEngine::ALLOC_ITERATIVE, 100, false);
+
+ // Try to allocate a lease without specifying a hint. This is actually
+ // incorrect behavior of the client to not send an address it wants to
+ // obtain but the server should handle this gracefully.
+ Lease4Ptr lease = engine.allocateLease4(subnet_, clientid_, hwaddr_,
+ IOAddress("0.0.0.0"),
+ false, false, "",
+ false, CalloutHandlePtr(),
+ old_lease_);
+ ASSERT_TRUE(lease);
+ EXPECT_EQ("192.0.2.123", lease->addr_.toText());
+
+ // Make sure that the lease has been committed to the lease database.
+ // And that the committed lease is equal to the one returned.
+ Lease4Ptr from_mgr = LeaseMgrFactory::instance().getLease4(lease->addr_);
+ ASSERT_TRUE(from_mgr);
+ detailCompareLease(lease, from_mgr);
+
+ // Initially, there was no lease for this client, so the returned old
+ // lease should be NULL.
+ EXPECT_FALSE(old_lease_);
+}
+
+// This test checks behavior of the allocation engine in the following scenario:
+// - Client has no lease in the database.
+// - Client has a reservation.
+// - Client sends DHCPDISCOVER without requested IP Address.
+// - Server returns DHCPOFFER with the reserved address.
+TEST_F(AllocEngine4Test,reservedAddressNoHintFakeAllocation) {
+ // Create reservation for the client.
+ HostPtr host(new Host(&hwaddr_->hwaddr_[0], hwaddr_->hwaddr_.size(),
+ Host::IDENT_HWADDR, subnet_->getID(),
+ SubnetID(0), IOAddress("192.0.2.123")));
+ CfgMgr::instance().getStagingCfg()->getCfgHosts()->add(host);
+ CfgMgr::instance().commit();
+
+ AllocEngine engine(AllocEngine::ALLOC_ITERATIVE, 100, false);
+
+ // Query allocation engine for the lease to be assigned to this
+ // client without specifying the address to be assigned.
+ Lease4Ptr lease = engine.allocateLease4(subnet_, clientid_, hwaddr_,
+ IOAddress("0.0.0.0"),
+ false, false, "",
+ true, CalloutHandlePtr(),
+ old_lease_);
+ ASSERT_TRUE(lease);
+ // The allocation engine should return a reserved address.
+ EXPECT_EQ("192.0.2.123", lease->addr_.toText());
+
+ // This is a "fake" allocation so the returned lease should not be committed
+ // to the lease database.
+ EXPECT_FALSE(LeaseMgrFactory::instance().getLease4(lease->addr_));
+
+ // Client had no lease in the database, so the old lease returned should
+ // be NULL.
+ EXPECT_FALSE(old_lease_);
+}
+
+// This test checks the behavior of the allocation engine in the following
+// scenario:
+// - Client has no lease in the database.
+// - Client has a reservation.
+// - Client sends DHCPREQUEST with a requested IP address
+// - Server returns DHCPNAK when requested IP address is different than
+// the reserved address.
+// - Server allocates a reserved address to the client when the client requests
+// this address using requested IP address option.
+TEST_F(AllocEngine4Test, reservedAddressHint) {
+ HostPtr host(new Host(&hwaddr_->hwaddr_[0], hwaddr_->hwaddr_.size(),
+ Host::IDENT_HWADDR, subnet_->getID(),
+ SubnetID(0), IOAddress("192.0.2.123")));
+ CfgMgr::instance().getStagingCfg()->getCfgHosts()->add(host);
+ CfgMgr::instance().commit();
+
+ AllocEngine engine(AllocEngine::ALLOC_ITERATIVE, 100, false);
+
+ Lease4Ptr lease = engine.allocateLease4(subnet_, clientid_, hwaddr_,
+ IOAddress("192.0.2.234"),
+ false, false, "",
+ false, CalloutHandlePtr(),
+ old_lease_);
+ ASSERT_FALSE(lease);
+ ASSERT_FALSE(old_lease_);
+
+ lease = engine.allocateLease4(subnet_, clientid_, hwaddr_,
+ IOAddress("192.0.2.123"),
+ false, false, "",
+ false, CalloutHandlePtr(),
+ old_lease_);
+ ASSERT_TRUE(lease);
+ EXPECT_EQ("192.0.2.123", lease->addr_.toText());
+
+ // Make sure that the lease has been committed to the lease database.
+ // And that the committed lease is equal to the one returned.
+ Lease4Ptr from_mgr = LeaseMgrFactory::instance().getLease4(lease->addr_);
+ ASSERT_TRUE(from_mgr);
+ detailCompareLease(lease, from_mgr);
+
+ EXPECT_FALSE(old_lease_);
+}
+
+// This test checks the behavior of the allocation engine in the following
+// scenario:
+// - Client has no lease in the database.
+// - Client has a reservation.
+// - Client sends DHCPDISCOVER with a requested IP address as a hint.
+// - Server offers a reserved address, even though it is different than the
+// requested address.
+TEST_F(AllocEngine4Test, reservedAddressHintFakeAllocation) {
+ // Create a reservation for the client.
+ HostPtr host(new Host(&hwaddr_->hwaddr_[0], hwaddr_->hwaddr_.size(),
+ Host::IDENT_HWADDR, subnet_->getID(),
+ SubnetID(0), IOAddress("192.0.2.123")));
+ CfgMgr::instance().getStagingCfg()->getCfgHosts()->add(host);
+ CfgMgr::instance().commit();
+
+ AllocEngine engine(AllocEngine::ALLOC_ITERATIVE, 100, false);
+
+ // Query the allocation engine for the lease to be assigned to the client
+ // and specify a hint being a different address than the reserved one.
+ Lease4Ptr lease = engine.allocateLease4(subnet_, clientid_, hwaddr_,
+ IOAddress("192.0.2.234"),
+ false, false, "",
+ true, CalloutHandlePtr(),
+ old_lease_);
+ ASSERT_TRUE(lease);
+ // Allocation engine should return reserved address.
+ EXPECT_EQ("192.0.2.123", lease->addr_.toText());
+
+ // This is a "fake" allocation so the returned lease should not be committed
+ // to the lease database.
+ EXPECT_FALSE(LeaseMgrFactory::instance().getLease4(lease->addr_));
+
+ EXPECT_FALSE(old_lease_);
+}
+
+// This test checks that the behavior of the allocation engine in the following
+// scenario:
+// - Client has a lease for the address from the dynamic pool in the database.
+// - Client has a reservation for a different address than the one for which
+// the client has a lease.
+// - Client sends DHCPREQUEST, asking for the reserved address (as it has been
+// offered to it when it sent DHCPDISCOVER).
+// - Server allocates a reserved address and removes the lease for the address
+// previously allocated to the client.
+TEST_F(AllocEngine4Test, reservedAddressExistingLease) {
+ // Create the reservation for the client.
+ HostPtr host(new Host(&hwaddr_->hwaddr_[0], hwaddr_->hwaddr_.size(),
+ Host::IDENT_HWADDR, subnet_->getID(),
+ SubnetID(0), IOAddress("192.0.2.123")));
+ CfgMgr::instance().getStagingCfg()->getCfgHosts()->add(host);
+ CfgMgr::instance().commit();
+
+ // Create a lease for the client with a different address than the reserved
+ // one.
+ Lease4Ptr lease(new Lease4(IOAddress("192.0.2.101"), hwaddr_, 0, 0,
+ 100, 30, 60, time(NULL), subnet_->getID(),
+ false, false, ""));
+ LeaseMgrFactory::instance().addLease(lease);
+
+ AllocEngine engine(AllocEngine::ALLOC_ITERATIVE, 100, false);
+
+ // Request allocation of the reserved address.
+ Lease4Ptr allocated_lease = engine.allocateLease4(subnet_, clientid_,
+ hwaddr_,
+ IOAddress("192.0.2.123"),
+ false, false, "",
+ false, CalloutHandlePtr(),
+ old_lease_);
+ ASSERT_TRUE(allocated_lease);
+ // The engine should have allocated the reserved address.
+ EXPECT_EQ("192.0.2.123", allocated_lease->addr_.toText());
+
+ // Make sure that the lease has been committed to the lease database.
+ Lease4Ptr from_mgr =
+ LeaseMgrFactory::instance().getLease4(allocated_lease->addr_);
+ ASSERT_TRUE(from_mgr);
+ detailCompareLease(allocated_lease, from_mgr);
+
+ // The previous lease should have been replaced by a new one. The previous
+ // lease should be returned by the allocation engine to the caller.
+ ASSERT_TRUE(old_lease_);
+ EXPECT_EQ("192.0.2.101", old_lease_->addr_.toText());
+ detailCompareLease(old_lease_, lease);
+}
+
+// This test checks that the behavior of the allocation engine in the following
+// scenario:
+// - Client A has a lease in the database.
+// - Client B has a reservation for the address in use by client A.
+// - Client B sends a DHCPREQUEST requesting the allocation of the reserved
+// lease (in use by client A).
+// - Server determines that the reserved address is in use by a different client
+// and returns DHCPNAK to client B.
+TEST_F(AllocEngine4Test, reservedAddressHijacked) {
+ // Create host reservation for the client B.
+ HostPtr host(new Host(&hwaddr_->hwaddr_[0], hwaddr_->hwaddr_.size(),
+ Host::IDENT_HWADDR, subnet_->getID(),
+ SubnetID(0), IOAddress("192.0.2.123")));
+ CfgMgr::instance().getStagingCfg()->getCfgHosts()->add(host);
+ CfgMgr::instance().commit();
+
+ // Allocate a lease for the client A for the same address as reserved
+ // for the client B.
+ Lease4Ptr lease(new Lease4(IOAddress("192.0.2.123"), hwaddr2_, 0, 0,
+ 100, 30, 60, time(NULL), subnet_->getID(),
+ false, false, ""));
+ LeaseMgrFactory::instance().addLease(lease);
+
+ AllocEngine engine(AllocEngine::ALLOC_ITERATIVE, 100, false);
+
+ // Try to allocate the reserved lease to client B.
+ Lease4Ptr allocated_lease = engine.allocateLease4(subnet_, clientid_,
+ hwaddr_,
+ IOAddress("192.0.2.123"),
+ false, false, "",
+ false, CalloutHandlePtr(),
+ old_lease_);
+ // The lease is allocated to someone else, so the allocation should not
+ // succeed.
+ ASSERT_FALSE(allocated_lease);
+ EXPECT_FALSE(old_lease_);
+
+ // Make sure that the allocation engine didn't modify the lease of the
+ // client A.
+ Lease4Ptr from_mgr = LeaseMgrFactory::instance().getLease4(lease->addr_);
+ ASSERT_TRUE(from_mgr);
+ detailCompareLease(lease, from_mgr);
+
+ // Try doing the same thing, but this time do not request any specific
+ // address. It should have the same effect.
+ allocated_lease = engine.allocateLease4(subnet_, clientid_,
+ hwaddr_,
+ IOAddress("0.0.0.0"),
+ false, false, "",
+ false, CalloutHandlePtr(),
+ old_lease_);
+ ASSERT_FALSE(allocated_lease);
+ EXPECT_FALSE(old_lease_);
+
+ from_mgr = LeaseMgrFactory::instance().getLease4(lease->addr_);
+ ASSERT_TRUE(from_mgr);
+ detailCompareLease(lease, from_mgr);
+}
+
+// This test checks that the behavior of the allocation engine in the following
+// scenario:
+// - Client A has a lease in the database.
+// - Client B has a reservation for the address in use by client A.
+// - Client B sends a DHCPDISCOVER.
+// - Server determines that the reserved address is in use by a different client
+// and that it can't allocate a lease to the client B.
+//
+// In the scenario presented here, the allocation engine should return a
+// NULL lease to the server. When the server receives NULL pointer from the
+// allocation engine the proper action for the server will be to not
+// respond to the client. Instead it should report to the administrator
+// that it was unable to allocate the (reserved) lease.
+TEST_F(AllocEngine4Test, reservedAddressHijackedFakeAllocation) {
+ // Create a reservation for the client B.
+ HostPtr host(new Host(&hwaddr_->hwaddr_[0], hwaddr_->hwaddr_.size(),
+ Host::IDENT_HWADDR, subnet_->getID(),
+ SubnetID(0), IOAddress("192.0.2.123")));
+ CfgMgr::instance().getStagingCfg()->getCfgHosts()->add(host);
+ CfgMgr::instance().commit();
+
+ // Create a lease for the client A.
+ Lease4Ptr lease(new Lease4(IOAddress("192.0.2.123"), hwaddr2_, 0, 0,
+ 100, 30, 60, time(NULL), subnet_->getID(),
+ false, false, ""));
+ LeaseMgrFactory::instance().addLease(lease);
+
+ AllocEngine engine(AllocEngine::ALLOC_ITERATIVE, 100, false);
+
+ // Query allocation engine for the lease to be allocated to the client B.
+ // The allocation engine is not able to allocate the lease to the client
+ // B, because the address is in use by client A.
+ Lease4Ptr allocated_lease = engine.allocateLease4(subnet_, clientid_,
+ hwaddr_,
+ IOAddress("192.0.2.123"),
+ false, false, "",
+ true, CalloutHandlePtr(),
+ old_lease_);
+ // The allocation engine should return no lease.
+ ASSERT_FALSE(allocated_lease);
+ EXPECT_FALSE(old_lease_);
+
+ // Do the same test. But, this time do not specify any address to be
+ // allocated.
+ allocated_lease = engine.allocateLease4(subnet_, clientid_,
+ hwaddr_,
+ IOAddress("0.0.0.0"),
+ false, false, "",
+ true, CalloutHandlePtr(),
+ old_lease_);
+ EXPECT_FALSE(allocated_lease);
+ EXPECT_FALSE(old_lease_);
+}
+
+// This test checks that the behavior of the allocation engine in the following
+// scenario:
+// - Client has a reservation.
+// - Client has a lease in the database for a different address than reserved.
+// - Client sends a DHCPREQUEST and asks for a different address than reserved,
+// and different than it has in a database.
+// - Server doesn't allocate the reserved address to the client because the
+// client asked for the different address.
+//
+// Note that in this case the client should get the DHCPNAK and should fall back
+// to the DHCPDISCOVER.
+TEST_F(AllocEngine4Test, reservedAddressExistingLeaseInvalidHint) {
+ // Create a reservation for the client.
+ HostPtr host(new Host(&hwaddr_->hwaddr_[0], hwaddr_->hwaddr_.size(),
+ Host::IDENT_HWADDR, subnet_->getID(),
+ SubnetID(0), IOAddress("192.0.2.123")));
+ CfgMgr::instance().getStagingCfg()->getCfgHosts()->add(host);
+ CfgMgr::instance().commit();
+
+ // Create a lease for the client for a different address than reserved.
+ Lease4Ptr lease(new Lease4(IOAddress("192.0.2.101"), hwaddr_, 0, 0,
+ 100, 30, 60, time(NULL), subnet_->getID(),
+ false, false, ""));
+ LeaseMgrFactory::instance().addLease(lease);
+
+ AllocEngine engine(AllocEngine::ALLOC_ITERATIVE, 100, false);
+
+ // Try to allocate a lease and specify a different address than reserved
+ // and different from the one that client is currently using.
+ Lease4Ptr allocated_lease = engine.allocateLease4(subnet_, clientid_,
+ hwaddr_,
+ IOAddress("192.0.2.102"),
+ false, false, "",
+ false, CalloutHandlePtr(),
+ old_lease_);
+ ASSERT_FALSE(allocated_lease);
+ ASSERT_FALSE(old_lease_);
+
+ // Repeat the test, but this time ask for the address that the client
+ // has allocated.
+ allocated_lease = engine.allocateLease4(subnet_, clientid_,
+ hwaddr_,
+ IOAddress("192.0.2.101"),
+ false, false, "",
+ false, CalloutHandlePtr(),
+ old_lease_);
+ // The client has reservation so the server wants to allocate a
+ // reserved address and doesn't want to renew the address that the
+ // client is currently using. This is equivalent of the case when
+ // the client tries to renew the lease but there is a new reservation
+ // for this client. The server doesn't allow for the renewal and
+ // responds with DHCPNAK to force the client to return to the
+ // DHCP server discovery.
+ EXPECT_FALSE(allocated_lease);
+ EXPECT_FALSE(old_lease_);
+}
+
+// This test checks that the behavior of the allocation engine in the following
+// scenario:
+// - Client has a lease in the database.
+// - Client has a reservation for a different address than the one for which it
+// has a lease.
+// - Client sends a DHCPDISCOVER and asks for a different address than reserved
+// and different from which it has a lease for.
+// - Server ignores the client's hint and offers a reserved address.
+TEST_F(AllocEngine4Test, reservedAddressExistingLeaseFakeAllocation) {
+ // Create a reservation for the client.
+ HostPtr host(new Host(&hwaddr_->hwaddr_[0], hwaddr_->hwaddr_.size(),
+ Host::IDENT_HWADDR, subnet_->getID(),
+ SubnetID(0), IOAddress("192.0.2.123")));
+ CfgMgr::instance().getStagingCfg()->getCfgHosts()->add(host);
+ CfgMgr::instance().commit();
+
+ // Create a lease for a different address than reserved.
+ Lease4Ptr lease(new Lease4(IOAddress("192.0.2.101"), hwaddr_, 0, 0,
+ 100, 30, 60, time(NULL), subnet_->getID(),
+ false, false, ""));
+ LeaseMgrFactory::instance().addLease(lease);
+
+ AllocEngine engine(AllocEngine::ALLOC_ITERATIVE, 100, false);
+
+ // Try to allocate a lease and use a completely different address
+ // as a hint.
+ Lease4Ptr allocated_lease = engine.allocateLease4(subnet_, clientid_,
+ hwaddr_,
+ IOAddress("192.0.2.102"),
+ false, false, "",
+ true, CalloutHandlePtr(),
+ old_lease_);
+ // Server should offer a lease for a reserved address.
+ ASSERT_TRUE(allocated_lease);
+ EXPECT_EQ("192.0.2.123", allocated_lease->addr_.toText());
+
+ // The lease should not be allocated until the client sends a DHCPREQUEST.
+ EXPECT_FALSE(LeaseMgrFactory::instance().getLease4(allocated_lease->addr_));
+
+ // Old lease should contain the currently used lease.
+ ASSERT_TRUE(old_lease_);
+ EXPECT_EQ("192.0.2.101", old_lease_->addr_.toText());
+
+ // Repeat the test but this time ask for the address for which the
+ // client has a lease.
+ allocated_lease = engine.allocateLease4(subnet_, clientid_,
+ hwaddr_,
+ IOAddress("192.0.2.101"),
+ false, false, "",
+ true, CalloutHandlePtr(),
+ old_lease_);
+ // The server should offer the lease, but not for the address that
+ // the client requested. The server should offer a reserved address.
+ ASSERT_TRUE(allocated_lease);
+ EXPECT_EQ("192.0.2.123", allocated_lease->addr_.toText());
+
+ // Old lease should contain the currently used lease.
+ ASSERT_TRUE(old_lease_);
+ EXPECT_EQ("192.0.2.101", old_lease_->addr_.toText());
+}
+
+// This test checks that the behavior of the allocation engine in the following
+// scenario:
+// - Client has a reservation.
+// - Client has a lease for a different address than reserved.
+// - Client sends a DHCPREQUEST to allocate a lease.
+// - The server determines that the client has a reservation for the
+// different address than it is currently using and should assign
+// a reserved address and remove the previous lease.
+TEST_F(AllocEngine4Test, reservedAddressExistingLeaseNoHint) {
+ // Create a reservation.
+ HostPtr host(new Host(&hwaddr_->hwaddr_[0], hwaddr_->hwaddr_.size(),
+ Host::IDENT_HWADDR, subnet_->getID(),
+ SubnetID(0), IOAddress("192.0.2.123")));
+ CfgMgr::instance().getStagingCfg()->getCfgHosts()->add(host);
+ CfgMgr::instance().commit();
+
+ // Create a lease for a different address than reserved.
+ Lease4Ptr lease(new Lease4(IOAddress("192.0.2.101"), hwaddr_, 0, 0,
+ 100, 30, 60, time(NULL), subnet_->getID(),
+ false, false, ""));
+ LeaseMgrFactory::instance().addLease(lease);
+
+ AllocEngine engine(AllocEngine::ALLOC_ITERATIVE, 100, false);
+
+ // Try to allocate a lease with providing no hint.
+ Lease4Ptr allocated_lease = engine.allocateLease4(subnet_, clientid_,
+ hwaddr_,
+ IOAddress("0.0.0.0"),
+ false, false, "",
+ false, CalloutHandlePtr(),
+ old_lease_);
+ // The reserved address should be allocated.
+ ASSERT_TRUE(allocated_lease);
+ EXPECT_EQ("192.0.2.123", allocated_lease->addr_.toText());
+
+ // The previous lease should be removed.
+ EXPECT_FALSE(LeaseMgrFactory::instance().getLease4(lease->addr_));
+
+ // Make sure that the allocated lease is committed in the lease database.
+ Lease4Ptr from_mgr =
+ LeaseMgrFactory::instance().getLease4(allocated_lease->addr_);
+ ASSERT_TRUE(from_mgr);
+ detailCompareLease(allocated_lease, from_mgr);
+
+ // Old lease should be returned.
+ ASSERT_TRUE(old_lease_);
+ detailCompareLease(lease, old_lease_);
+}
+
+// This test checks that the behavior of the allocation engine in the following
+// scenario:
+// - Client has a reservation.
+// - Client has a lease for a different address than reserved.
+// - Client sends a DHCPDISCOVER with no hint.
+// - Server determines that there is a reservation for the client and that
+// the current lease should be removed and the reserved address should be
+// allocated.
+TEST_F(AllocEngine4Test, reservedAddressExistingLeaseNoHintFakeAllocation) {
+ // Create a reservation.
+ HostPtr host(new Host(&hwaddr_->hwaddr_[0], hwaddr_->hwaddr_.size(),
+ Host::IDENT_HWADDR, subnet_->getID(),
+ SubnetID(0), IOAddress("192.0.2.123")));
+ CfgMgr::instance().getStagingCfg()->getCfgHosts()->add(host);
+ CfgMgr::instance().commit();
+
+ // Create a lease for a different address than reserved.
+ Lease4Ptr lease(new Lease4(IOAddress("192.0.2.101"), hwaddr_, 0, 0,
+ 100, 30, 60, time(NULL), subnet_->getID(),
+ false, false, ""));
+ LeaseMgrFactory::instance().addLease(lease);
+
+ AllocEngine engine(AllocEngine::ALLOC_ITERATIVE, 100, false);
+
+ // Query the allocation engine for the lease to be allocated for the
+ // client.
+ Lease4Ptr allocated_lease = engine.allocateLease4(subnet_, clientid_,
+ hwaddr_,
+ IOAddress("0.0.0.0"),
+ false, false, "",
+ true, CalloutHandlePtr(),
+ old_lease_);
+ // The server should offer the reserved address.
+ ASSERT_TRUE(allocated_lease);
+ EXPECT_EQ("192.0.2.123", allocated_lease->addr_.toText());
+
+ // The lease should not be committed to the lease database until the
+ // client sends a DHCPREQUEST.
+ EXPECT_FALSE(LeaseMgrFactory::instance().getLease4(allocated_lease->addr_));
+
+ // The old lease should reflect what is in the database.
+ ASSERT_TRUE(old_lease_);
+ Lease4Ptr from_mgr = LeaseMgrFactory::instance().getLease4(lease->addr_);
+ ASSERT_TRUE(from_mgr);
+ detailCompareLease(lease, from_mgr);
+}
+
/// @brief helper class used in Hooks testing in AllocEngine6
///
/// It features a couple of callout functions and buffers to store