From: Tomek Mrugalski Date: Wed, 17 Oct 2012 16:06:46 +0000 (+0200) Subject: [2324] Initial, rough version of allocation engine implemented. X-Git-Tag: trac2487_base~21^2~9^2~15 X-Git-Url: http://git.ipfire.org/gitweb.cgi?a=commitdiff_plain;h=0c3a609fdde1fe14de8d2c032e1350c3f10c3058;p=thirdparty%2Fkea.git [2324] Initial, rough version of allocation engine implemented. --- diff --git a/src/bin/dhcp6/alloc_engine.cc b/src/bin/dhcp6/alloc_engine.cc new file mode 100644 index 0000000000..456022154e --- /dev/null +++ b/src/bin/dhcp6/alloc_engine.cc @@ -0,0 +1,184 @@ +// 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 +#include + +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 diff --git a/src/bin/dhcp6/alloc_engine.h b/src/bin/dhcp6/alloc_engine.h new file mode 100644 index 0000000000..8fa0bbdcf0 --- /dev/null +++ b/src/bin/dhcp6/alloc_engine.h @@ -0,0 +1,140 @@ +// 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 +#include +#include +#include +#include +#include + +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