BIND 10 trac2324, updated. ee7d5cf20406b4ff00a52ce8218304c6623571ea [2324] AllocEngine is now part of b10-dhcpsrv lib.

BIND 10 source code commits bind10-changes at lists.isc.org
Fri Oct 19 17:32:40 UTC 2012


The branch, trac2324 has been updated
       via  ee7d5cf20406b4ff00a52ce8218304c6623571ea (commit)
       via  d1c32c6bd6bb7621968266b09ca979ecce428530 (commit)
      from  d97fa9cb68dea0cf8873f9b28ac6671ee321a1f6 (commit)

Those revisions listed above that are new to this repository have
not appeared on any other notification email; so we list those
revisions in full, below.

- Log -----------------------------------------------------------------
commit ee7d5cf20406b4ff00a52ce8218304c6623571ea
Author: Tomek Mrugalski <tomasz at isc.org>
Date:   Fri Oct 19 19:32:16 2012 +0200

    [2324] AllocEngine is now part of b10-dhcpsrv lib.

commit d1c32c6bd6bb7621968266b09ca979ecce428530
Author: Tomek Mrugalski <tomasz at isc.org>
Date:   Fri Oct 19 18:57:09 2012 +0200

    [2324] LeaseMgr tests improved.

-----------------------------------------------------------------------

Summary of changes:
 src/bin/dhcp6/Makefile.am                   |    1 -
 src/lib/dhcp/Makefile.am                    |    3 +-
 src/{bin/dhcp6 => lib/dhcp}/alloc_engine.cc |   53 ++++++++++++++++++--------
 src/{bin/dhcp6 => lib/dhcp}/alloc_engine.h  |    6 ++-
 src/lib/dhcp/lease_mgr.cc                   |    4 ++
 src/lib/dhcp/lease_mgr.h                    |   23 ++++++++---
 src/lib/dhcp/tests/Makefile.am              |    2 +
 src/lib/dhcp/tests/alloc_engine_unittest.cc |   53 ++++++++++++++++++++++++++
 src/lib/dhcp/tests/lease_mgr_unittest.cc    |   55 +++++++++++++++++++++++++++
 9 files changed, 176 insertions(+), 24 deletions(-)
 rename src/{bin/dhcp6 => lib/dhcp}/alloc_engine.cc (81%)
 rename src/{bin/dhcp6 => lib/dhcp}/alloc_engine.h (95%)
 create mode 100644 src/lib/dhcp/tests/alloc_engine_unittest.cc

-----------------------------------------------------------------------
diff --git a/src/bin/dhcp6/Makefile.am b/src/bin/dhcp6/Makefile.am
index 13e43f7..68aadea 100644
--- a/src/bin/dhcp6/Makefile.am
+++ b/src/bin/dhcp6/Makefile.am
@@ -47,7 +47,6 @@ pkglibexec_PROGRAMS = b10-dhcp6
 b10_dhcp6_SOURCES  = main.cc
 b10_dhcp6_SOURCES += ctrl_dhcp6_srv.cc ctrl_dhcp6_srv.h
 b10_dhcp6_SOURCES += config_parser.cc config_parser.h
-b10_dhcp6_SOURCES += alloc_engine.cc alloc_engine.h
 b10_dhcp6_SOURCES += dhcp6_log.cc dhcp6_log.h
 b10_dhcp6_SOURCES += dhcp6_srv.cc dhcp6_srv.h
 
diff --git a/src/bin/dhcp6/alloc_engine.cc b/src/bin/dhcp6/alloc_engine.cc
deleted file mode 100644
index 0f67c14..0000000
--- a/src/bin/dhcp6/alloc_engine.cc
+++ /dev/null
@@ -1,216 +0,0 @@
-// Copyright (C) 2012 Internet Systems Consortium, Inc. ("ISC")
-//
-// Permission to use, copy, modify, and/or distribute this software for any
-// purpose with or without fee is hereby granted, provided that the above
-// copyright notice and this permission notice appear in all copies.
-//
-// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
-// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
-// AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
-// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
-// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
-// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
-// PERFORMANCE OF THIS SOFTWARE.
-
-#include <asiolink/io_address.h>
-#include <alloc_engine.h>
-
-using namespace isc::asiolink;
-
-namespace isc {
-namespace dhcp {
-
-AllocEngine::IterativeAllocator::IterativeAllocator()
-    :Allocator() {
-}
-
-isc::asiolink::IOAddress
-AllocEngine::IterativeAllocator::increaseAddress(const isc::asiolink::IOAddress& addr) {
-    uint8_t packed[V6ADDRESS_LEN];
-    int len;
-    if (addr.getFamily()==AF_INET) {
-        // IPv4
-        memcpy(packed, addr.getAddress().to_v4().to_bytes().data(), 4);
-        len = 4;
-    } else {
-        // IPv6
-        memcpy(packed, addr.getAddress().to_v6().to_bytes().data(), 16);
-        len = 16;
-    }
-
-    // First we copy the whole address as 16 bytes.
-    bool carry = false;
-    for (int i = len; i >=0; --i) {
-        packed[i]++;
-        if (packed[i] != 0) {
-            break;
-        }
-    }
-
-    return (IOAddress::from_bytes(addr.getFamily(), packed));
-}
-
-
-isc::asiolink::IOAddress
-AllocEngine::IterativeAllocator::pickAddress(const Subnet6Ptr& subnet,
-                                             const DuidPtr& duid,
-                                             const IOAddress& hint) {
-
-    // Let's get the last allocated address. It is usually be set correctly,
-    // but there are times when it won't be (like after removing a pool or
-    // perhaps restaring the server).
-    IOAddress last = subnet->getLastAllocated();
-
-    const Pool6Collection& pools = subnet->getPools();
-
-    if (pools.size() == 0) {
-        isc_throw(AllocFailed, "No pools defined in selected subnet");
-    }
-
-    Pool6Ptr pool = Pool6Ptr(); // null
-
-    // first we need to find a pool the last address belongs to.
-    Pool6Collection::const_iterator it;
-    for (it = pools.begin(); it != pools.end(); ++it) {
-        if ((*it)->inRange(last)) {
-            break;
-        }
-    }
-
-    // last one was bogus for one of several reasons:
-    // - we just booted up and that's the first address we're allocating
-    // - a subnet was removed or other reconfiguration just completed
-    // - perhaps allocation algorithm was changed
-    if (it == pools.end()) {
-        // ok to access first element directly. We checked that pools is non-empty
-        IOAddress next = pools[0]->getFirstAddress();
-        subnet->setLastAllocated(next);
-        return (next);
-    }
-
-    // Ok, we have a pool that the last address belonged to, let's use it.
-
-    IOAddress next = increaseAddress(last); // basically addr++
-    if ((*it)->inRange(next)) {
-        // the next one it in pool as well, so we haven't hit pool boundary yet
-        subnet->setLastAllocated(next);
-        return (next);
-    }
-
-    // We hit pool boundary, let's try to jump to the next pool and try again
-    ++it;
-    if (it == pools.end()) {
-        // Really out of luck today. That was the last pool. Let's rewind
-        // to the beginning.
-        next = pools[0]->getFirstAddress();
-        subnet->setLastAllocated(next);
-        return (next);
-    }
-
-    // there is a next pool, let's try first adddress from it
-    next = (*it)->getFirstAddress();
-    subnet->setLastAllocated(next);
-    return (next);
-}
-
-
-AllocEngine::AllocEngine(AllocType engine_type, unsigned int attempts)
-    :attempts_(attempts) {
-    switch (engine_type) {
-    case ALLOC_ITERATIVE:
-        allocator_ = new IterativeAllocator();
-        break;
-#if 0
-    case ALLOC_HASHED:
-        allocator_ = new HashedAllocator();
-        break;
-    case ALLOC_RANDOM:
-        allocator_ = new RandomAllocator();
-        break;
-#endif
-
-    default:
-        isc_throw(BadValue, "Invalid/unsupported allocation algorithm");
-    }
-}
-
-Lease6Ptr
-AllocEngine::allocateAddress6(const Subnet6Ptr& subnet,
-                              const DuidPtr& duid,
-                              uint32_t iaid,
-                              const IOAddress& hint) {
-    // That check is not necessary. We create allocator in AllocEngine
-    // constructor
-    if (!allocator_) {
-        isc_throw(InvalidOperation, "No allocator selected");
-    }
-
-    // check if there's existing lease for that subnet/duid/iaid combination.
-    Lease6Ptr existing = LeaseMgr::instance().getLease6(*duid, iaid, subnet->getID());
-    if (existing) {
-        // we have a lease already. This is a returning client, probably after
-        // his reboot.
-        return (existing);
-    }
-
-    unsigned int i = attempts_;
-    do {
-        IOAddress candidate = allocator_->pickAddress(subnet, duid, hint);
-
-        /// @todo: check if the address is reserved once we have host support
-        /// implemented
-
-        Lease6Ptr existing = LeaseMgr::instance().getLease6(candidate);
-        // there's no existing lease for selected candidate, so it is
-        // free. Let's allocate it.
-        if (!existing) {
-            Lease6Ptr lease = createLease(subnet, duid, iaid, candidate);
-            if (lease) {
-                return (lease);
-            }
-
-            // Although the address was free just microseconds ago, it may have
-            // been taken just now. If the lease insertion fails, we continue
-            // allocation attempts.
-        }
-
-        // continue trying allocation until we run out of attempts
-        // (or attempts are set to 0, which means infinite)
-        --i;
-    } while ( i || !attempts_);
-
-    isc_throw(AllocFailed, "Failed to allocate address after " << attempts_
-              << " tries");
-}
-
-Lease6Ptr AllocEngine::createLease(const Subnet6Ptr& subnet,
-                                   const DuidPtr& duid,
-                                   uint32_t iaid,
-                                   const IOAddress& addr) {
-
-    Lease6Ptr lease(new Lease6(Lease6::LEASE_IA_NA, addr, duid, iaid,
-                               subnet->getPreferred(), subnet->getValid(),
-                               subnet->getT1(), subnet->getT2(), subnet->getID()));
-
-    bool status = LeaseMgr::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 (Lease6Ptr());
-    }
-}
-
-AllocEngine::~AllocEngine() {
-    if (allocator_) {
-        delete allocator_;
-        allocator_ = NULL;
-    }
-}
-
-}; // end of isc::dhcp namespace
-}; // end of isc namespace
diff --git a/src/bin/dhcp6/alloc_engine.h b/src/bin/dhcp6/alloc_engine.h
deleted file mode 100644
index baefe92..0000000
--- a/src/bin/dhcp6/alloc_engine.h
+++ /dev/null
@@ -1,140 +0,0 @@
-// Copyright (C) 2012 Internet Systems Consortium, Inc. ("ISC")
-//
-// Permission to use, copy, modify, and/or distribute this software for any
-// purpose with or without fee is hereby granted, provided that the above
-// copyright notice and this permission notice appear in all copies.
-//
-// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
-// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
-// AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
-// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
-// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
-// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
-// PERFORMANCE OF THIS SOFTWARE.
-
-#ifndef ALLOC_ENGINE_H
-#define ALLOC_ENGINE_H
-
-#include <boost/noncopyable.hpp>
-#include <dhcp/duid.h>
-#include <dhcp/subnet.h>
-#include <asiolink/io_address.h>
-#include <dhcp/lease_mgr.h>
-#include <iostream>
-
-namespace isc {
-namespace dhcp {
-
-/// An exception that is thrown when allocation module fails (e.g. due to
-/// lack of available addresses)
-class AllocFailed : public isc::Exception {
-public:
-
-/// @brief constructor
-///
-/// @param file name of the file, where exception occurred
-/// @param line line of the file, where exception occurred
-/// @param what text description of the issue that caused exception
-AllocFailed(const char* file, size_t line, const char* what)
-    : isc::Exception(file, line, what) {}
-};
-
-/// @brief DHCPv4 and DHCPv6 allocation engine
-///
-/// This class represents DHCP allocation engine. It is responsible
-/// for picking subnets, choosing and allocating a lease, extending,
-/// renewing, releasing and possibly expiring leases.
-
-class AllocEngine : public boost::noncopyable {
-protected:
-
-    class Allocator {
-    public:
-        virtual isc::asiolink::IOAddress
-            pickAddress(const Subnet6Ptr& subnet,
-                        const DuidPtr& duid,
-                        const isc::asiolink::IOAddress& hint) = 0;
-    protected:
-        Allocator() {
-        }
-    };
-
-    class IterativeAllocator : public Allocator {
-    public:
-        IterativeAllocator();
-        virtual isc::asiolink::IOAddress
-            pickAddress(const Subnet6Ptr& subnet,
-                        const DuidPtr& duid,
-                        const isc::asiolink::IOAddress& hint);
-    private:
-        isc::asiolink::IOAddress increaseAddress(const isc::asiolink::IOAddress& addr);
-
-    };
-
-#if 0
-    class HashedAllocator {
-    public:
-        IterativeAllocator(unsigned int attempts);
-        virtual isc::asiolink::IOAddress allocateAddress(const Subnet6Ptr& subnet,
-                                                         const DuidPtr& duid,
-                                                         const DUIOAddress& hint);
-    }
-
-    class RandomAllocator() {
-    public:
-        IterativeAllocator(unsigned int attempts);
-        virtual isc::asiolink::IOAddress
-        allocateAddress(const Subnet6Ptr& subnet,
-                        const DuidPtr& duid,
-                        const DUIOAddress& hint);
-    }
-#endif
-
-    public:
-
-    /// @brief specifies allocation type
-    typedef enum {
-        ALLOC_ITERATIVE, // iterative - one address after another
-        ALLOC_HASHED,    // hashed - client's DUID/client-id is hashed
-        ALLOC_RANDOM     // random - an address is randomly selected
-    } AllocType;
-
-
-    /// @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
-    AllocEngine(AllocType engine_type, unsigned int attempts);
-
-    Lease6Ptr
-    allocateAddress6(const Subnet6Ptr& subnet,
-                     const DuidPtr& duid,
-                     uint32_t iaid,
-                     const isc::asiolink::IOAddress& hint);
-
-    /// @brief Destructor. Used during DHCPv6 service shutdown.
-    virtual ~AllocEngine();
-private:
-    isc::asiolink::IOAddress
-    allocateAddress(const Subnet6Ptr& subnet,
-                    const DuidPtr& duid,
-                    const isc::asiolink::IOAddress& hint);
-
-    Lease6Ptr createLease(const Subnet6Ptr& subnet,
-                          const DuidPtr& duid,
-                          uint32_t iaid,
-                          const isc::asiolink::IOAddress& addr);
-
-    Allocator* allocator_;
-
-    unsigned int attempts_;
-};
-
-}; // namespace isc::dhcp
-}; // namespace isc
-
-#endif // DHCP6_SRV_H
diff --git a/src/lib/dhcp/Makefile.am b/src/lib/dhcp/Makefile.am
index 6585a38..3167635 100644
--- a/src/lib/dhcp/Makefile.am
+++ b/src/lib/dhcp/Makefile.am
@@ -16,7 +16,6 @@ CLEANFILES = *.gcno *.gcda
 lib_LTLIBRARIES = libb10-dhcp++.la libb10-dhcpsrv.la
 libb10_dhcp___la_SOURCES  =
 libb10_dhcp___la_SOURCES += libdhcp++.cc libdhcp++.h
-libb10_dhcp___la_SOURCES += lease_mgr.cc lease_mgr.h
 libb10_dhcp___la_SOURCES += iface_mgr.cc iface_mgr.h
 libb10_dhcp___la_SOURCES += iface_mgr_linux.cc
 libb10_dhcp___la_SOURCES += iface_mgr_bsd.cc
@@ -35,7 +34,9 @@ libb10_dhcpsrv_la_SOURCES  = cfgmgr.cc cfgmgr.h
 libb10_dhcpsrv_la_SOURCES += pool.cc pool.h
 libb10_dhcpsrv_la_SOURCES += subnet.cc subnet.h
 libb10_dhcpsrv_la_SOURCES += triplet.h
+libb10_dhcpsrv_la_SOURCES += lease_mgr.cc lease_mgr.h
 libb10_dhcpsrv_la_SOURCES += addr_utilities.cc addr_utilities.h
+libb10_dhcpsrv_la_SOURCES += alloc_engine.cc alloc_engine.h
 libb10_dhcpsrv_la_CXXFLAGS = $(AM_CXXFLAGS)
 libb10_dhcpsrv_la_CPPFLAGS = $(AM_CPPFLAGS) $(LOG4CPLUS_INCLUDES)
 libb10_dhcpsrv_la_LIBADD   = $(top_builddir)/src/lib/asiolink/libb10-asiolink.la
diff --git a/src/lib/dhcp/alloc_engine.cc b/src/lib/dhcp/alloc_engine.cc
new file mode 100644
index 0000000..8964c1b
--- /dev/null
+++ b/src/lib/dhcp/alloc_engine.cc
@@ -0,0 +1,239 @@
+// Copyright (C) 2012 Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#include <asiolink/io_address.h>
+#include <alloc_engine.h>
+
+using namespace isc::asiolink;
+
+namespace isc {
+namespace dhcp {
+
+AllocEngine::IterativeAllocator::IterativeAllocator()
+    :Allocator() {
+}
+
+isc::asiolink::IOAddress
+AllocEngine::IterativeAllocator::increaseAddress(const isc::asiolink::IOAddress& addr) {
+    uint8_t packed[V6ADDRESS_LEN];
+    int len;
+
+    // First we copy the whole address as 16 bytes.
+    if (addr.getFamily()==AF_INET) {
+        // IPv4
+        memcpy(packed, addr.getAddress().to_v4().to_bytes().data(), 4);
+        len = 4;
+    } else {
+        // IPv6
+        memcpy(packed, addr.getAddress().to_v6().to_bytes().data(), 16);
+        len = 16;
+    }
+
+    for (int i = len; i >=0; --i) {
+        packed[i]++;
+        if (packed[i] != 0) {
+            break;
+        }
+    }
+
+    return (IOAddress::from_bytes(addr.getFamily(), packed));
+}
+
+
+isc::asiolink::IOAddress
+AllocEngine::IterativeAllocator::pickAddress(const Subnet6Ptr& subnet,
+                                             const DuidPtr&,
+                                             const IOAddress&) {
+
+    // Let's get the last allocated address. It is usually be set correctly,
+    // but there are times when it won't be (like after removing a pool or
+    // perhaps restaring the server).
+    IOAddress last = subnet->getLastAllocated();
+
+    const Pool6Collection& pools = subnet->getPools();
+
+    if (pools.size() == 0) {
+        isc_throw(AllocFailed, "No pools defined in selected subnet");
+    }
+
+    Pool6Ptr pool = Pool6Ptr(); // null
+
+    // first we need to find a pool the last address belongs to.
+    Pool6Collection::const_iterator it;
+    for (it = pools.begin(); it != pools.end(); ++it) {
+        if ((*it)->inRange(last)) {
+            break;
+        }
+    }
+
+    // last one was bogus for one of several reasons:
+    // - we just booted up and that's the first address we're allocating
+    // - a subnet was removed or other reconfiguration just completed
+    // - perhaps allocation algorithm was changed
+    if (it == pools.end()) {
+        // ok to access first element directly. We checked that pools is non-empty
+        IOAddress next = pools[0]->getFirstAddress();
+        subnet->setLastAllocated(next);
+        return (next);
+    }
+
+    // Ok, we have a pool that the last address belonged to, let's use it.
+
+    IOAddress next = increaseAddress(last); // basically addr++
+    if ((*it)->inRange(next)) {
+        // the next one it in pool as well, so we haven't hit pool boundary yet
+        subnet->setLastAllocated(next);
+        return (next);
+    }
+
+    // We hit pool boundary, let's try to jump to the next pool and try again
+    ++it;
+    if (it == pools.end()) {
+        // Really out of luck today. That was the last pool. Let's rewind
+        // to the beginning.
+        next = pools[0]->getFirstAddress();
+        subnet->setLastAllocated(next);
+        return (next);
+    }
+
+    // there is a next pool, let's try first adddress from it
+    next = (*it)->getFirstAddress();
+    subnet->setLastAllocated(next);
+    return (next);
+}
+
+
+AllocEngine::AllocEngine(AllocType engine_type, unsigned int attempts)
+    :attempts_(attempts) {
+    switch (engine_type) {
+    case ALLOC_ITERATIVE:
+        allocator_ = new IterativeAllocator();
+        break;
+#if 0
+    case ALLOC_HASHED:
+        allocator_ = new HashedAllocator();
+        break;
+    case ALLOC_RANDOM:
+        allocator_ = new RandomAllocator();
+        break;
+#endif
+
+    default:
+        isc_throw(BadValue, "Invalid/unsupported allocation algorithm");
+    }
+}
+
+Lease6Ptr
+AllocEngine::allocateAddress6(const Subnet6Ptr& subnet,
+                              const DuidPtr& duid,
+                              uint32_t iaid,
+                              const IOAddress& hint,
+                              bool fake /* = false */ ) {
+    // That check is not necessary. We create allocator in AllocEngine
+    // constructor
+    if (!allocator_) {
+        isc_throw(InvalidOperation, "No allocator selected");
+    }
+
+    // check if there's existing lease for that subnet/duid/iaid combination.
+    Lease6Ptr existing = LeaseMgr::instance().getLease6(*duid, iaid, subnet->getID());
+    if (existing) {
+        // we have a lease already. This is a returning client, probably after
+        // his reboot.
+        return (existing);
+    }
+
+    // check if the hint is available
+    existing = LeaseMgr::instance().getLease6(hint);
+    if (!existing) {
+        // the hint is good, let's create a lease for it
+    }
+
+    unsigned int i = attempts_;
+    do {
+        IOAddress candidate = allocator_->pickAddress(subnet, duid, hint);
+
+        /// @todo: check if the address is reserved once we have host support
+        /// implemented
+
+        Lease6Ptr existing = LeaseMgr::instance().getLease6(candidate);
+        // there's no existing lease for selected candidate, so it is
+        // free. Let's allocate it.
+        if (!existing) {
+            Lease6Ptr lease = createLease(subnet, duid, iaid, candidate, fake);
+            if (lease) {
+                return (lease);
+            }
+
+            // Although the address was free just microseconds ago, it may have
+            // been taken just now. If the lease insertion fails, we continue
+            // allocation attempts.
+        }
+
+        // continue trying allocation until we run out of attempts
+        // (or attempts are set to 0, which means infinite)
+        --i;
+    } while ( i || !attempts_);
+
+    isc_throw(AllocFailed, "Failed to allocate address after " << attempts_
+              << " tries");
+}
+
+Lease6Ptr AllocEngine::createLease(const Subnet6Ptr& subnet,
+                                   const DuidPtr& duid,
+                                   uint32_t iaid,
+                                   const IOAddress& addr,
+                                   bool fake /*= false */ ) {
+
+    Lease6Ptr lease(new Lease6(Lease6::LEASE_IA_NA, addr, duid, iaid,
+                               subnet->getPreferred(), subnet->getValid(),
+                               subnet->getT1(), subnet->getT2(), subnet->getID()));
+
+    if (!fake) {
+        // That is a real (REQUEST) allocation
+        bool status = LeaseMgr::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 (Lease6Ptr());
+        }
+    } else {
+        // That is only fake (SOLICIT without rapid-commit) allocation
+
+        // It is for advertise only. We should not insert the lease into LeaseMgr,
+        // but rather check that we could have inserted it.
+        Lease6Ptr existing = LeaseMgr::instance().getLease6(addr);
+        if (!existing) {
+            return (lease);
+        } else {
+            return (Lease6Ptr());
+        }
+    }
+}
+
+AllocEngine::~AllocEngine() {
+    if (allocator_) {
+        delete allocator_;
+        allocator_ = NULL;
+    }
+}
+
+}; // end of isc::dhcp namespace
+}; // end of isc namespace
diff --git a/src/lib/dhcp/alloc_engine.h b/src/lib/dhcp/alloc_engine.h
new file mode 100644
index 0000000..e02bcc5
--- /dev/null
+++ b/src/lib/dhcp/alloc_engine.h
@@ -0,0 +1,142 @@
+// Copyright (C) 2012 Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#ifndef ALLOC_ENGINE_H
+#define ALLOC_ENGINE_H
+
+#include <boost/noncopyable.hpp>
+#include <dhcp/duid.h>
+#include <dhcp/subnet.h>
+#include <asiolink/io_address.h>
+#include <dhcp/lease_mgr.h>
+#include <iostream>
+
+namespace isc {
+namespace dhcp {
+
+/// An exception that is thrown when allocation module fails (e.g. due to
+/// lack of available addresses)
+class AllocFailed : public isc::Exception {
+public:
+
+/// @brief constructor
+///
+/// @param file name of the file, where exception occurred
+/// @param line line of the file, where exception occurred
+/// @param what text description of the issue that caused exception
+AllocFailed(const char* file, size_t line, const char* what)
+    : isc::Exception(file, line, what) {}
+};
+
+/// @brief DHCPv4 and DHCPv6 allocation engine
+///
+/// This class represents DHCP allocation engine. It is responsible
+/// for picking subnets, choosing and allocating a lease, extending,
+/// renewing, releasing and possibly expiring leases.
+
+class AllocEngine : public boost::noncopyable {
+protected:
+
+    class Allocator {
+    public:
+        virtual isc::asiolink::IOAddress
+            pickAddress(const Subnet6Ptr& subnet,
+                        const DuidPtr& duid,
+                        const isc::asiolink::IOAddress& hint) = 0;
+    protected:
+        Allocator() {
+        }
+    };
+
+    class IterativeAllocator : public Allocator {
+    public:
+        IterativeAllocator();
+        virtual isc::asiolink::IOAddress
+            pickAddress(const Subnet6Ptr& subnet,
+                        const DuidPtr& duid,
+                        const isc::asiolink::IOAddress& hint);
+    private:
+        isc::asiolink::IOAddress increaseAddress(const isc::asiolink::IOAddress& addr);
+
+    };
+
+#if 0
+    class HashedAllocator {
+    public:
+        IterativeAllocator(unsigned int attempts);
+        virtual isc::asiolink::IOAddress allocateAddress(const Subnet6Ptr& subnet,
+                                                         const DuidPtr& duid,
+                                                         const DUIOAddress& hint);
+    }
+
+    class RandomAllocator() {
+    public:
+        IterativeAllocator(unsigned int attempts);
+        virtual isc::asiolink::IOAddress
+        allocateAddress(const Subnet6Ptr& subnet,
+                        const DuidPtr& duid,
+                        const DUIOAddress& hint);
+    }
+#endif
+
+    public:
+
+    /// @brief specifies allocation type
+    typedef enum {
+        ALLOC_ITERATIVE, // iterative - one address after another
+        ALLOC_HASHED,    // hashed - client's DUID/client-id is hashed
+        ALLOC_RANDOM     // random - an address is randomly selected
+    } AllocType;
+
+
+    /// @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
+    AllocEngine(AllocType engine_type, unsigned int attempts);
+
+    Lease6Ptr
+    allocateAddress6(const Subnet6Ptr& subnet,
+                     const DuidPtr& duid,
+                     uint32_t iaid,
+                     const isc::asiolink::IOAddress& hint,
+                     bool fake);
+
+    /// @brief Destructor. Used during DHCPv6 service shutdown.
+    virtual ~AllocEngine();
+private:
+    isc::asiolink::IOAddress
+    allocateAddress(const Subnet6Ptr& subnet,
+                    const DuidPtr& duid,
+                    const isc::asiolink::IOAddress& hint);
+
+    Lease6Ptr createLease(const Subnet6Ptr& subnet,
+                          const DuidPtr& duid,
+                          uint32_t iaid,
+                          const isc::asiolink::IOAddress& addr,
+                          bool fake = false);
+
+    Allocator* allocator_;
+
+    unsigned int attempts_;
+};
+
+}; // namespace isc::dhcp
+}; // namespace isc
+
+#endif // DHCP6_SRV_H
diff --git a/src/lib/dhcp/lease_mgr.cc b/src/lib/dhcp/lease_mgr.cc
index 338a5cd..fb74435 100644
--- a/src/lib/dhcp/lease_mgr.cc
+++ b/src/lib/dhcp/lease_mgr.cc
@@ -39,6 +39,10 @@ Lease6::Lease6(LeaseType type, const isc::asiolink::IOAddress& addr, DuidPtr dui
      preferred_lft_(preferred), valid_lft_(valid), t1_(t1), t2_(t2),
      subnet_id_(subnet_id), fixed_(false), fqdn_fwd_(false),
      fqdn_rev_(false) {
+    if (duid == DuidPtr()) {
+        isc_throw(InvalidOperation, "DUID must be specified for a lease");
+    }
+
     cltt_ = time(NULL);
 }
 
diff --git a/src/lib/dhcp/lease_mgr.h b/src/lib/dhcp/lease_mgr.h
index de14171..8ac7cc4 100644
--- a/src/lib/dhcp/lease_mgr.h
+++ b/src/lib/dhcp/lease_mgr.h
@@ -12,6 +12,9 @@
 // OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
 // PERFORMANCE OF THIS SOFTWARE.
 
+#ifndef LEASE_MGR_H
+#define LEASE_MGR_H
+
 #include <string>
 #include <fstream>
 #include <vector>
@@ -272,6 +275,12 @@ typedef std::vector< boost::shared_ptr<Lease6Ptr> > Lease6Collection;
 /// interface to all backends. As this is an abstract class, it should not
 /// be used directly, but rather specialized derived class should be used
 /// instead.
+///
+/// This class is a meta-singleton. At any given time, there is only one
+/// instance of any classes derived from that class. That is achieved with
+/// defining only a single protected constructor, so every derived class has
+/// to use it. Furthermore, this sole constructor registers the first instance
+/// (and throws InvalidOperation if there is an attempt to create a second one).
 class LeaseMgr : public boost::noncopyable {
 public:
     /// Client Hardware address
@@ -286,8 +295,6 @@ public:
     /// @throw InvalidOperation if LeaseMgr not instantiated
     static LeaseMgr& instance();
 
-    void instantiate(const std::string& config);
-
     /// @brief Destructor
     virtual ~LeaseMgr();
 
@@ -471,11 +478,15 @@ public:
 protected:
     /// @brief The sole lease manager constructor
     ///
-    /// dbconfig is a generic way of passing parameters. Parameters
-    /// are passed in the "name=value" format, separated by spaces.
-    /// Values may be enclosed in double quotes, if needed.
+    /// dbconfig is a generic way of passing parameters. Parameters are passed
+    /// in the "name=value" format, separated by spaces. Values may be enclosed
+    /// in double quotes, if needed. This ctor guarantees that there will be
+    /// only one instance of any derived classes. If there is a second instance
+    /// being created with the first one still around, it will throw
+    /// InvalidOperation.
     ///
     /// @param dbconfig database configuration
+    /// @throw InvalidOperation when trying to create second LeaseMgr
     LeaseMgr(const std::string& dbconfig);
 
     /// @brief returns value of the parameter
@@ -494,3 +505,5 @@ protected:
 }; // end of isc::dhcp namespace
 
 }; // end of isc namespace
+
+#endif // LEASE_MGR_H
diff --git a/src/lib/dhcp/tests/Makefile.am b/src/lib/dhcp/tests/Makefile.am
index a15d957..1f15112 100644
--- a/src/lib/dhcp/tests/Makefile.am
+++ b/src/lib/dhcp/tests/Makefile.am
@@ -46,6 +46,8 @@ libdhcpsrv_unittests_SOURCES  = run_unittests.cc
 libdhcpsrv_unittests_SOURCES += cfgmgr_unittest.cc triplet_unittest.cc
 libdhcpsrv_unittests_SOURCES += pool_unittest.cc subnet_unittest.cc
 libdhcpsrv_unittests_SOURCES += addr_utilities_unittest.cc
+libdhcpsrv_unittests_SOURCES += alloc_engine_unittest.cc
+
 
 libdhcpsrv_unittests_CPPFLAGS = $(AM_CPPFLAGS) $(GTEST_INCLUDES) $(LOG4CPLUS_INCLUDES)
 libdhcpsrv_unittests_LDFLAGS  = $(AM_LDFLAGS)  $(GTEST_LDFLAGS)
diff --git a/src/lib/dhcp/tests/alloc_engine_unittest.cc b/src/lib/dhcp/tests/alloc_engine_unittest.cc
new file mode 100644
index 0000000..5f0a4ba
--- /dev/null
+++ b/src/lib/dhcp/tests/alloc_engine_unittest.cc
@@ -0,0 +1,53 @@
+// Copyright (C) 2011-2012 Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#include <config.h>
+#include <iostream>
+#include <sstream>
+#include <gtest/gtest.h>
+#include <asiolink/io_address.h>
+#include <dhcp/lease_mgr.h>
+#include <dhcp/duid.h>
+#include <dhcp/alloc_engine.h>
+
+using namespace std;
+using namespace isc;
+using namespace isc::asiolink;
+using namespace isc::dhcp;
+
+
+namespace {
+// empty class for now, but may be extended once Addr6 becomes bigger
+class AllocEngineTest : public ::testing::Test {
+public:
+    AllocEngineTest() {
+    }
+};
+
+// This test checks if the Allocation Engine can be instantiated and that it
+// parses parameters string properly.
+TEST_F(AllocEngineTest, constructor) {
+
+    AllocEngine* x = NULL;
+
+    // Hashed and random allocators are not supported yet
+    ASSERT_THROW(x = new AllocEngine(AllocEngine::ALLOC_HASHED, 5), BadValue);
+    ASSERT_THROW(x = new AllocEngine(AllocEngine::ALLOC_RANDOM, 5), BadValue);
+
+    ASSERT_NO_THROW(x = new AllocEngine(AllocEngine::ALLOC_ITERATIVE, 100));
+
+    delete x;
+}
+
+}; // end of anonymous namespace
diff --git a/src/lib/dhcp/tests/lease_mgr_unittest.cc b/src/lib/dhcp/tests/lease_mgr_unittest.cc
index 0c5d7e0..35436ff 100644
--- a/src/lib/dhcp/tests/lease_mgr_unittest.cc
+++ b/src/lib/dhcp/tests/lease_mgr_unittest.cc
@@ -385,4 +385,59 @@ TEST_F(LeaseMgrTest, addGetDelete) {
     delete leaseMgr;
 }
 
+// This test checks there that leaseMgr is really a singleton and that
+// no more than one can be created.
+TEST_F(LeaseMgrTest, singleton) {
+    Memfile_LeaseMgr* leaseMgr1 = NULL;
+    Memfile_LeaseMgr* leaseMgr2 = NULL;
+
+    EXPECT_THROW(LeaseMgr::instance(), InvalidOperation);
+
+    EXPECT_NO_THROW( leaseMgr1 = new Memfile_LeaseMgr("") );
+
+    EXPECT_NO_THROW(LeaseMgr::instance());
+
+    // There can be only one instance of any LeaseMgr derived
+    // objects instantiated at any time.
+    ASSERT_THROW(leaseMgr2 = new Memfile_LeaseMgr(""), InvalidOperation);
+
+    delete leaseMgr1;
+
+    ASSERT_NO_THROW(leaseMgr2 = new Memfile_LeaseMgr("") );
+
+    delete leaseMgr2;
+}
+
+// This test checks if the Lease6 structure can be instantiated correctly
+TEST(Lease6, ctor) {
+
+    IOAddress addr("2001:db8:1::456");
+
+    uint8_t llt[] = {0, 1, 2, 3, 4, 5, 6, 0xa, 0xb, 0xc, 0xd, 0xe, 0xf};
+    DuidPtr duid(new DUID(llt, sizeof(llt)));
+
+    uint32_t iaid = 7; // just a number
+
+    SubnetID subnet_id = 8; // just another number
+
+    Lease6Ptr x(new Lease6(Lease6::LEASE_IA_NA, addr,
+                           duid, iaid, 100, 200, 50, 80,
+                           subnet_id));
+
+    EXPECT_TRUE(x->addr_ == addr);
+    EXPECT_TRUE(*x->duid_ == *duid);
+    EXPECT_TRUE(x->iaid_ == iaid);
+    EXPECT_TRUE(x->subnet_id_ == subnet_id);
+    EXPECT_TRUE(x->type_ == Lease6::LEASE_IA_NA);
+    EXPECT_TRUE(x->preferred_lft_ == 100);
+    EXPECT_TRUE(x->valid_lft_ == 200);
+    EXPECT_TRUE(x->t1_ == 50);
+    EXPECT_TRUE(x->t2_ == 80);
+
+    // Lease6 must be instantiated with a DUID, not with NULL pointer
+    EXPECT_THROW(new Lease6(Lease6::LEASE_IA_NA, addr,
+                            DuidPtr(), iaid, 100, 200, 50, 80,
+                            subnet_id), InvalidOperation);
+}
+
 }; // end of anonymous namespace



More information about the bind10-changes mailing list