--- /dev/null
+// 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::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");
+ }
+
+ 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, iaid,
+ duid, 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
--- /dev/null
+// 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(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