#include <hooks/hooks_manager.h>
#include <cstring>
+#include <limits>
#include <vector>
+ #include <stdint.h>
#include <string.h>
using namespace isc::asiolink;
// Copy an existing, expired lease so as it can be returned
// to the caller.
Lease6Ptr old_lease(new Lease6(*lease));
- old_leases.push_back(old_lease);
+ ctx.old_leases_.push_back(old_lease);
/// We found a lease and it is expired, so we can reuse it
- lease = reuseExpiredLease(lease, subnet, duid, iaid,
- pool->getLength(),
- fwd_dns_update, rev_dns_update,
- hostname, callout_handle,
- fake_allocation);
+ lease = reuseExpiredLease(lease, ctx, pool->getLength());
/// @todo: We support only one lease per ia for now
- Lease6Collection collection;
- collection.push_back(lease);
- return (collection);
+ leases.push_back(lease);
+ return (leases);
}
-
}
}
+ }
- // Hint is in the pool but is not available. Search the pool until first of
- // the following occurs:
- // - we find a free address
- // - we find an address for which the lease has expired
- // - we exhaust number of tries
- //
- // @todo: Current code does not handle pool exhaustion well. It will be
- // improved. Current problems:
- // 1. with attempts set to too large value (e.g. 1000) and a small pool (e.g.
- // 10 addresses), we will iterate over it 100 times before giving up
- // 2. attempts 0 mean unlimited (this is really UINT_MAX, not infinite)
- // 3. the whole concept of infinite attempts is just asking for infinite loop
- // We may consider some form or reference counting (this pool has X addresses
- // left), but this has one major problem. We exactly control allocation
- // moment, but we currently do not control expiration time at all
-
- // Initialize the maximum number of attempts to pick and allocate an
- // address. The value of 0 means "infinite", which is maximum uint32_t
- // value.
- uint32_t max_attempts = (attempts_ == 0) ?
- std::numeric_limits<uint32_t>::max() : attempts_;
-
- for (uint32_t i = 0; i < max_attempts; ++i) {
- IOAddress candidate = allocator->pickAddress(subnet, duid, hint);
-
- /// @todo: check if the address is reserved once we have host support
- /// implemented
-
- // The first step is to find out prefix length. It is 128 for
- // non-PD leases.
- uint8_t prefix_len = 128;
- if (type == Lease::TYPE_PD) {
- Pool6Ptr pool = boost::dynamic_pointer_cast<Pool6>(
- subnet->getPool(type, candidate, false));
- prefix_len = pool->getLength();
- }
+ // The hint was useless (it was not provided at all, was used by someone else,
+ // was out of pool or reserved for someone else). Search the pool until first
+ // of the following occurs:
+ // - we find a free address
+ // - we find an address for which the lease has expired
+ // - we exhaust number of tries
+ //
+ // @todo: Current code does not handle pool exhaustion well. It will be
+ // improved. Current problems:
+ // 1. with attempts set to too large value (e.g. 1000) and a small pool (e.g.
+ // 10 addresses), we will iterate over it 100 times before giving up
+ // 2. attempts 0 mean unlimited (this is really UINT_MAX, not infinite)
+ // 3. the whole concept of infinite attempts is just asking for infinite loop
+ // We may consider some form or reference counting (this pool has X addresses
+ // left), but this has one major problem. We exactly control allocation
+ // moment, but we currently do not control expiration time at all
+
- unsigned int max_attempts = (attempts_ == 0) ? UINT_MAX : attempts_;
- for (unsigned int i = 0; i < max_attempts; ++i)
++ // Initialize the maximum number of attempts to pick and allocate an
++ // address. The value of 0 means "infinite", which is maximum uint32_t
++ // value.
++ uint32_t max_attempts = (attempts_ == 0) ?
++ std::numeric_limits<uint32_t>::max() : attempts_;
++ for (uint32_t i = 0; i < max_attempts; ++i)
+ {
+ IOAddress candidate = allocator->pickAddress(ctx.subnet_, ctx.duid_, hint);
+
+ /// In-pool reservations: Check if this address is reserved for someone
+ /// else. There is no need to check for whom it is reserved, because if
+ /// it has been reserved for us we would have already allocated a lease.
+ if (hr_mode == Subnet::HR_IN_POOL &&
+ HostMgr::instance().get6(ctx.subnet_->getID(), candidate)) {
+
+ // Don't allocate.
+ continue;
+ }
- Lease6Ptr existing = LeaseMgrFactory::instance().getLease6(type,
- candidate);
- if (!existing) {
+ // The first step is to find out prefix length. It is 128 for
+ // non-PD leases.
+ uint8_t prefix_len = 128;
+ if (ctx.type_ == Lease::TYPE_PD) {
+ Pool6Ptr pool = boost::dynamic_pointer_cast<Pool6>(
+ ctx.subnet_->getPool(ctx.type_, candidate, false));
+ prefix_len = pool->getLength();
+ }
- // there's no existing lease for selected candidate, so it is
- // free. Let's allocate it.
+ Lease6Ptr existing = LeaseMgrFactory::instance().getLease6(ctx.type_,
+ candidate);
+ if (!existing) {
- Lease6Ptr lease = createLease6(subnet, duid, iaid, candidate,
- prefix_len, type, fwd_dns_update,
- rev_dns_update, hostname, hwaddr,
- callout_handle, fake_allocation);
- if (lease) {
- // We are allocating a new lease (not renewing). So, the
- // old lease should be NULL.
- old_leases.push_back(Lease6Ptr());
+ // there's no existing lease for selected candidate, so it is
+ // free. Let's allocate it.
- Lease6Collection collection;
- collection.push_back(lease);
- return (collection);
- }
+ Lease6Ptr lease = createLease6(ctx, candidate, prefix_len);
+ if (lease) {
+ // We are allocating a new lease (not renewing). So, the
+ // old lease should be NULL.
+ ctx.old_leases_.clear();
- // Although the address was free just microseconds ago, it may have
- // been taken just now. If the lease insertion fails, we continue
- // allocation attempts.
- } else {
- if (existing->expired()) {
- // Copy an existing, expired lease so as it can be returned
- // to the caller.
- Lease6Ptr old_lease(new Lease6(*existing));
- old_leases.push_back(old_lease);
+ leases.push_back(lease);
+ return (leases);
+ }
- existing = reuseExpiredLease(existing, subnet, duid, iaid,
- prefix_len, fwd_dns_update,
- rev_dns_update, hostname,
- callout_handle, fake_allocation);
- Lease6Collection collection;
- collection.push_back(existing);
- return (collection);
- }
+ // Although the address was free just microseconds ago, it may have
+ // been taken just now. If the lease insertion fails, we continue
+ // allocation attempts.
+ } else {
+ if (existing->expired()) {
+ // Copy an existing, expired lease so as it can be returned
+ // to the caller.
+ Lease6Ptr old_lease(new Lease6(*existing));
+ ctx.old_leases_.push_back(old_lease);
+
+ existing = reuseExpiredLease(existing,
+ ctx,
+ prefix_len);
+
+ leases.push_back(existing);
+ return (leases);
}
}
+ }
- // Unable to allocate an address, return an empty lease.
- LOG_WARN(dhcpsrv_logger, DHCPSRV_ADDRESS6_ALLOC_FAIL).arg(attempts_);
+ // We failed to allocate anything. Let's return empty collection.
+ return (Lease6Collection());
+ }
- } catch (const isc::Exception& e) {
+ void
+ AllocEngine::allocateReservedLeases6(ClientContext6& ctx, Lease6Collection& existing_leases) {
- // Some other error, return an empty lease.
- LOG_ERROR(dhcpsrv_logger, DHCPSRV_ADDRESS6_ALLOC_ERROR).arg(e.what());
+ // If there are no reservations or the reservation is v4, there's nothing to do.
+ if (!ctx.host_ || !ctx.host_->hasIPv6Reservation()) {
+ return;
}
- return (Lease6Collection());
+ // Let's convert this from Lease::Type to IPv6Reserv::Type
+ IPv6Resrv::Type type = ctx.type_ == Lease::TYPE_NA ? IPv6Resrv::TYPE_NA : IPv6Resrv::TYPE_PD;
+
+ // Get the IPv6 reservations of specified type.
+ const IPv6ResrvRange& reservs = ctx.host_->getIPv6Reservations(type);
+
+ if (std::distance(reservs.first, reservs.second) == 0) {
+ // No reservations? We're done here.
+ return;
+ }
+
+ for (IPv6ResrvIterator resv = reservs.first; resv != reservs.second; ++resv) {
+ // We do have a reservation for addr.
+ IOAddress addr = resv->second.getPrefix();
+ uint8_t prefix_len = resv->second.getPrefixLen();
+
+ // If there's a lease for this address, let's not create it.
+ // It doesn't matter whether it is for this client or for someone else.
+ if (LeaseMgrFactory::instance().getLease6(ctx.type_, addr)) {
+ continue;
+ }
+
+ // Ok, let's create a new lease...
+ Lease6Ptr lease = createLease6(ctx, addr, prefix_len);
+
+ // ... and add it to the existing leases list.
+ existing_leases.push_back(lease);
+ }
+ }
+
+ void
+ AllocEngine::removeNonmatchingReservedLeases6(ClientContext6& ctx,
+ Lease6Collection& existing_leases) {
+ // If there are no leases (so nothing to remove) or
+ // host reservation is disabled (so there are no reserved leases),
+ // just return.
+ if (existing_leases.empty() || !ctx.subnet_ ||
+ (ctx.subnet_->getHostReservationMode() == Subnet::HR_DISABLED) ) {
+ return;
+ }
+
+ // We need a copy, so we won't be iterating over a container and
+ // removing from it at the same time. It's only a copy of pointers,
+ // so the operation shouldn't be that expensive.
+ Lease6Collection copy = existing_leases;
+
+ for (Lease6Collection::const_iterator candidate = copy.begin();
+ candidate != copy.end(); ++candidate) {
+
+ ConstHostPtr host = HostMgr::instance().get6(ctx.subnet_->getID(),
+ (*candidate)->addr_);
+
+ if (!host || (host == ctx.host_)) {
+ // Not reserved or reserved for us. That's ok, let's check
+ // the next lease.
+ continue;
+ }
+
+ // Ok, we have a problem. This host has a lease that is reserved
+ // for someone else. We need to recover from this.
+
+ // Remove this lease from LeaseMgr
+ LeaseMgrFactory::instance().deleteLease((*candidate)->addr_);
+
+ /// @todo: Probably trigger a hook here
+
+ // Add this to the list of removed leases.
+ ctx.old_leases_.push_back(*candidate);
+
+ // Let's remove this candidate from existing leases
+ removeLeases(existing_leases, (*candidate)->addr_);
+ }
+ }
+
+ bool
+ AllocEngine::removeLeases(Lease6Collection& container, const asiolink::IOAddress& addr) {
+
+ bool removed = false;
+ for (Lease6Collection::iterator lease = container.begin();
+ lease != container.end(); ++lease) {
+ if ((*lease)->addr_ == addr) {
+ lease->reset();
+ removed = true;
+ }
+ }
+
+ // Remove all elements that have NULL value
+ container.erase(std::remove(container.begin(), container.end(), Lease6Ptr()),
+ container.end());
+
+ return (removed);
+ }
+
+ void
+ AllocEngine::removeNonreservedLeases6(ClientContext6& ctx,
+ Lease6Collection& existing_leases) {
+ // This method removes leases that are not reserved for this host.
+ // It will keep at least one lease, though.
+ if (existing_leases.empty() || !ctx.host_ || !ctx.host_->hasIPv6Reservation()) {
+ return;
+ }
+
+ // This is the total number of leases. We should not remove the last one.
+ int total = existing_leases.size();
+
+ // This is officially not scary code anymore. iterates and marks specified
+ // leases for deletion, by setting appropriate pointers to NULL.
+ for (Lease6Collection::iterator lease = existing_leases.begin();
+ lease != existing_leases.end(); ++lease) {
+ IPv6Resrv resv(ctx.type_ == Lease::TYPE_NA ? IPv6Resrv::TYPE_NA : IPv6Resrv::TYPE_PD,
+ (*lease)->addr_, (*lease)->prefixlen_);
+ if (!ctx.host_->hasReservation(resv)) {
+ // We have reservations, but not for this lease. Release it.
+
+ // Remove this lease from LeaseMgr
+ LeaseMgrFactory::instance().deleteLease((*lease)->addr_);
+
+ /// @todo: Probably trigger a hook here
+
+ // Add this to the list of removed leases.
+ ctx.old_leases_.push_back(*lease);
+
+ // Set this pointer to NULL. The pointer is still valid. We're just
+ // setting the Lease6Ptr to NULL value. We'll remove all NULL
+ // pointers once the loop is finished.
+ lease->reset();
+
+ if (--total == 1) {
+ // If there's only one lease left, break the loop.
+ break;
+ }
+ }
+
+ }
+
+ // Remove all elements that we previously marked for deletion (those that
+ // have NULL value).
+ existing_leases.erase(std::remove(existing_leases.begin(),
+ existing_leases.end(), Lease6Ptr()), existing_leases.end());
}
Lease4Ptr