Parcourir la source

[2324] Initial, rough version of allocation engine implemented.

Tomek Mrugalski il y a 12 ans
Parent
commit
0c3a609fdd
2 fichiers modifiés avec 324 ajouts et 0 suppressions
  1. 184 0
      src/bin/dhcp6/alloc_engine.cc
  2. 140 0
      src/bin/dhcp6/alloc_engine.h

+ 184 - 0
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 <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

+ 140 - 0
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 <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