]> git.ipfire.org Git - thirdparty/kea.git/commitdiff
[master] Merge branch 'trac3563' (Reservations in DHCPv6)
authorTomek Mrugalski <tomasz@isc.org>
Thu, 5 Feb 2015 15:12:03 +0000 (16:12 +0100)
committerTomek Mrugalski <tomasz@isc.org>
Thu, 5 Feb 2015 15:12:03 +0000 (16:12 +0100)
Conflicts:
src/lib/dhcpsrv/alloc_engine.cc

1  2 
src/bin/dhcp6/dhcp6_srv.cc
src/lib/dhcpsrv/alloc_engine.cc
src/lib/dhcpsrv/subnet.h
src/lib/dhcpsrv/tests/alloc_engine_unittest.cc

Simple merge
index 38eb92bb30fdd6840c716de3ed37b2b2dc322131,11193ab4cfb3fb4b4f5a7f23d9450bbd88e4d28d..9d52aca8153fad34fea437e09b5288b23f3b5e72
@@@ -21,8 -22,8 +22,9 @@@
  #include <hooks/hooks_manager.h>
  
  #include <cstring>
 +#include <limits>
  #include <vector>
+ #include <stdint.h>
  #include <string.h>
  
  using namespace isc::asiolink;
@@@ -372,113 -542,248 +543,252 @@@ AllocEngine::allocateUnreservedLeases6(
                      // 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
Simple merge