]> git.ipfire.org Git - thirdparty/kea.git/commitdiff
[5425] Checkpoint: code one, tests todo
authorFrancis Dupont <fdupont@isc.org>
Sat, 4 Nov 2017 09:12:17 +0000 (10:12 +0100)
committerFrancis Dupont <fdupont@isc.org>
Sat, 4 Nov 2017 09:12:17 +0000 (10:12 +0100)
src/lib/dhcpsrv/alloc_engine.cc
src/lib/dhcpsrv/alloc_engine.h
src/lib/dhcpsrv/parsers/dhcp_parsers.cc
src/lib/dhcpsrv/parsers/shared_network_parser.cc
src/lib/dhcpsrv/pool.cc
src/lib/dhcpsrv/pool.h
src/lib/dhcpsrv/subnet.cc
src/lib/dhcpsrv/subnet.h
src/lib/dhcpsrv/tests/alloc_engine4_unittest.cc
src/lib/dhcpsrv/tests/alloc_engine6_unittest.cc
src/lib/dhcpsrv/tests/alloc_engine_utils.h

index c7c08c371a7568bad7c9f37bde17f2a1c3e24889..e6945c0aa777aab20f5254d0a78957c171090294 100644 (file)
@@ -144,19 +144,33 @@ AllocEngine::IterativeAllocator::increasePrefix(const isc::asiolink::IOAddress&
     return (IOAddress::fromBytes(AF_INET6, packed));
 }
 
+isc::asiolink::IOAddress
+AllocEngine::IterativeAllocator::increaseAddress(const isc::asiolink::IOAddress& address,
+                                                 bool prefix,
+                                                 const uint8_t prefix_len) {
+    if (!prefix) {
+        return (IOAddress::increase(address));
+    } else {
+        return (increasePrefix(address, prefix_len));
+    }
+}
 
 isc::asiolink::IOAddress
 AllocEngine::IterativeAllocator::pickAddress(const SubnetPtr& subnet,
+                                             const ClientClasses& client_classes,
                                              const DuidPtr&,
                                              const IOAddress&) {
 
     // Is this prefix allocation?
     bool prefix = pool_type_ == Lease::TYPE_PD;
+    uint8_t prefix_len = 0;
 
     // Let's get the last allocated address. It is usually set correctly,
     // but there are times when it won't be (like after removing a pool or
     // perhaps restarting the server).
     IOAddress last = subnet->getLastAllocated(pool_type_);
+    bool valid = true;
+    bool retrying = false;
 
     const PoolCollection& pools = subnet->getPools(pool_type_);
 
@@ -166,58 +180,108 @@ AllocEngine::IterativeAllocator::pickAddress(const SubnetPtr& subnet,
 
     // first we need to find a pool the last address belongs to.
     PoolCollection::const_iterator it;
+    PoolCollection::const_iterator first = pools.end();
+    PoolPtr first_pool;
     for (it = pools.begin(); it != pools.end(); ++it) {
+        if (!(*it)->clientSupported(client_classes)) {
+            continue;
+        }
+        if (first == pools.end()) {
+            first = it;
+        }
         if ((*it)->inRange(last)) {
             break;
         }
     }
 
+    // Caller checked this cannot happen
+    if (first == pools.end()) {
+        isc_throw(AllocFailed, "No allowed pools defined in selected subnet");
+    }
+
     // 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
+    // - last pool does not allow this client
     if (it == pools.end()) {
-        // ok to access first element directly. We checked that pools is non-empty
-        IOAddress next = pools[0]->getFirstAddress();
-        subnet->setLastAllocated(pool_type_, next);
-        return (next);
+        it = first;
     }
 
-    // Ok, we have a pool that the last address belonged to, let's use it.
+    for (;;) {
+        // Trying next pool
+        if (retrying) {
+            for (; it != pools.end(); ++it) {
+                if ((*it)->clientSupported(client_classes)) {
+                    break;
+                }
+            }
+            if (it == pools.end()) {
+                // Really out of luck today. That was the last pool.
+                break;
+            }
+        }
 
-    IOAddress next("::");
-    if (!prefix) {
-        next = IOAddress::increase(last); // basically addr++
-    } else {
-        Pool6Ptr pool6 = boost::dynamic_pointer_cast<Pool6>(*it);
-        if (!pool6) {
-            // Something is gravely wrong here
-            isc_throw(Unexpected, "Wrong type of pool: " << (*it)->toText()
-                      << " is not Pool6");
+        last = (*it)->getLastAllocated();
+        valid = (*it)->isLastAllocatedValid();
+        if (!valid && (last == (*it)->getFirstAddress())) {
+            // Pool was (re)initialized
+            (*it)->setLastAllocated(last);
+            subnet->setLastAllocated(pool_type_, last);
+            return (last);
         }
-        // Get the next prefix
-        next = increasePrefix(last, pool6->getLength());
-    }
-    if ((*it)->inRange(next)) {
-        // the next one is in the pool as well, so we haven't hit pool boundary yet
-        subnet->setLastAllocated(pool_type_, next);
-        return (next);
+        // still can be bogus
+        if (valid && !(*it)->inRange(last)) {
+            valid = false;
+            (*it)->resetLastAllocated();
+            (*it)->setLastAllocated((*it)->getFirstAddress());
+        }
+
+        if (valid) {
+            // Ok, we have a pool that the last address belonged to, let's use it.
+            if (prefix) {
+                Pool6Ptr pool6 = boost::dynamic_pointer_cast<Pool6>(*it);
+
+                if (!pool6) {
+                    // Something is gravely wrong here
+                    isc_throw(Unexpected, "Wrong type of pool: "
+                              << (*it)->toText()
+                              << " is not Pool6");
+                }
+                // Get the prefix length
+                prefix_len = pool6->getLength();
+            }
+
+            IOAddress next = increaseAddress(last, prefix, prefix_len);
+            if ((*it)->inRange(next)) {
+                // the next one is in the pool as well, so we haven't hit
+                // pool boundary yet
+                (*it)->setLastAllocated(next);
+                subnet->setLastAllocated(pool_type_, next);
+                return (next);
+            }
+
+            valid = false;
+           (*it)->resetLastAllocated();
+        }
+        // We hit pool boundary, let's try to jump to the next pool and try again
+       ++it;
+        retrying = true;
     }
 
-    // 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(pool_type_, next);
-        return (next);
+    // Let's rewind to the beginning.
+    for (it = first; it != pools.end(); ++it) {
+        if ((*it)->clientSupported(client_classes)) {
+            (*it)->setLastAllocated((*it)->getFirstAddress());
+            (*it)->resetLastAllocated();
+        }
     }
 
-    // there is a next pool, let's try first address from it
-    next = (*it)->getFirstAddress();
-    subnet->setLastAllocated(pool_type_, next);
-    return (next);
+    // ok to access first element directly. We checked that pools is non-empty
+    last = (*first)->getLastAllocated();
+    (*first)->setLastAllocated(last);
+    subnet->setLastAllocated(pool_type_, last);
+    return (last);
 }
 
 AllocEngine::HashedAllocator::HashedAllocator(Lease::Type lease_type)
@@ -228,6 +292,7 @@ AllocEngine::HashedAllocator::HashedAllocator(Lease::Type lease_type)
 
 isc::asiolink::IOAddress
 AllocEngine::HashedAllocator::pickAddress(const SubnetPtr&,
+                                          const ClientClasses&,
                                           const DuidPtr&,
                                           const IOAddress&) {
     isc_throw(NotImplemented, "Hashed allocator is not implemented");
@@ -241,6 +306,7 @@ AllocEngine::RandomAllocator::RandomAllocator(Lease::Type lease_type)
 
 isc::asiolink::IOAddress
 AllocEngine::RandomAllocator::pickAddress(const SubnetPtr&,
+                                          const ClientClasses&,
                                           const DuidPtr&,
                                           const IOAddress&) {
     isc_throw(NotImplemented, "Random allocator is not implemented");
@@ -677,7 +743,12 @@ AllocEngine::allocateUnreservedLeases6(ClientContext6& ctx) {
         // check if the hint is in pool and is available
         // This is equivalent of subnet->inPool(hint), but returns the pool
         pool = boost::dynamic_pointer_cast<Pool6>
-            (subnet->getPool(ctx.currentIA().type_, hint, false));
+            (subnet->getPool(ctx.currentIA().type_, ctx.query_->getClasses(), hint));
+
+        // check if the pool is allowed
+        if (pool && !pool->clientSupported(ctx.query_->getClasses())) {
+            pool.reset();
+        }
 
         if (pool) {
 
@@ -775,14 +846,23 @@ AllocEngine::allocateUnreservedLeases6(ClientContext6& ctx) {
         // - we find a free address
         // - we find an address for which the lease has expired
         // - we exhaust number of tries
-        uint64_t max_attempts = (attempts_ > 0 ? attempts_  :
-                                 subnet->getPoolCapacity(ctx.currentIA().type_));
+        uint64_t possible_attempts =
+            subnet->getPoolCapacity(ctx.currentIA().type_,
+                                    ctx.query_->getClasses());
+        // Try next subnet if there is no chance to get something
+        if (possible_attempts == 0) {
+            subnet = subnet->getNextSubnet(original_subnet);
+            continue;
+        }
+        uint64_t max_attempts = (attempts_ > 0 ? attempts_  : possible_attempts);
 
         for (uint64_t i = 0; i < max_attempts; ++i) {
 
             ++total_attempts;
 
-            IOAddress candidate = allocator->pickAddress(subnet, ctx.duid_,
+            IOAddress candidate = allocator->pickAddress(subnet,
+                                                         ctx.query_->getClasses(),
+                                                         ctx.duid_,
                                                          hint);
 
             /// In-pool reservations: Check if this address is reserved for someone
@@ -800,7 +880,7 @@ AllocEngine::allocateUnreservedLeases6(ClientContext6& ctx) {
             uint8_t prefix_len = 128;
             if (ctx.currentIA().type_ == Lease::TYPE_PD) {
                 pool = boost::dynamic_pointer_cast<Pool6>(
-                    subnet->getPool(ctx.currentIA().type_, candidate, false));
+                        subnet->getPool(ctx.currentIA().type_, ctx.query_->getClasses(), candidate));
                 if (pool) {
                     prefix_len = pool->getLength();
                 }
@@ -3214,10 +3294,19 @@ AllocEngine::allocateUnreservedLease4(ClientContext4& ctx) {
             client_id = ctx.clientid_;
         }
 
-        const uint64_t max_attempts = (attempts_ > 0 ? attempts_ :
-                                       subnet->getPoolCapacity(Lease::TYPE_V4));
+        uint64_t possible_attempts =
+            subnet->getPoolCapacity(Lease::TYPE_V4,
+                                    ctx.query_->getClasses());
+        uint64_t max_attempts = (attempts_ > 0 ? attempts_ : possible_attempts);
+        // Skip trying if there is no chance to get something
+        if (possible_attempts == 0) {
+            max_attempts = 0;
+        }
+
         for (uint64_t i = 0; i < max_attempts; ++i) {
-            IOAddress candidate = allocator->pickAddress(subnet, client_id,
+            IOAddress candidate = allocator->pickAddress(subnet,
+                                                         ctx.query_->getClasses(),
+                                                         client_id,
                                                          ctx.requested_address_);
             // If address is not reserved for another client, try to allocate it.
             if (!addressReserved(candidate, ctx)) {
index e33b2f7056ac2245851f833bd31c400add7bb301..d30e96008ccbe78a60e29954f1016dae4f4b8c61 100644 (file)
@@ -8,6 +8,7 @@
 #define ALLOC_ENGINE_H
 
 #include <asiolink/io_address.h>
+#include <dhcp/classify.h>
 #include <dhcp/duid.h>
 #include <dhcp/hwaddr.h>
 #include <dhcp/pkt4.h>
@@ -78,13 +79,19 @@ protected:
         /// than pickResource(), because nobody would immediately know what the
         /// resource means in this context.
         ///
+        /// Pools which are not allowed for client classes are skipped.
+        ///
         /// @param subnet next address will be returned from pool of that subnet
+        /// @param client_classes list of classes client belongs to
+
         /// @param duid Client's DUID
         /// @param hint client's hint
         ///
         /// @return the next address
         virtual isc::asiolink::IOAddress
-        pickAddress(const SubnetPtr& subnet, const DuidPtr& duid,
+        pickAddress(const SubnetPtr& subnet,
+                    const ClientClasses& client_classes,
+                    const DuidPtr& duid,
                     const isc::asiolink::IOAddress& hint) = 0;
 
         /// @brief Default constructor.
@@ -125,11 +132,13 @@ protected:
         /// @brief returns the next address from pools in a subnet
         ///
         /// @param subnet next address will be returned from pool of that subnet
+        /// @param client_classes list of classes client belongs to
         /// @param duid Client's DUID (ignored)
         /// @param hint client's hint (ignored)
         /// @return the next address
         virtual isc::asiolink::IOAddress
             pickAddress(const SubnetPtr& subnet,
+                        const ClientClasses& client_classes,
                         const DuidPtr& duid,
                         const isc::asiolink::IOAddress& hint);
     protected:
@@ -147,6 +156,20 @@ protected:
         static isc::asiolink::IOAddress
         increasePrefix(const isc::asiolink::IOAddress& prefix,
                        const uint8_t prefix_len);
+
+        /// @brief Returns the next address or prefix
+        ///
+        /// This method works for IPv4 addresses, IPv6 addresses and
+        /// IPv6 prefixes.
+        ///
+        /// @param address address or prefix to be increased
+        /// @param prefix true when the previous argument is a prefix
+        /// @param prefix_len length of the prefix
+        /// @return result address or prefix
+        static isc::asiolink::IOAddress
+        increaseAddress(const isc::asiolink::IOAddress& address,
+                        bool prefix, const uint8_t prefix_len);
+
     };
 
     /// @brief Address/prefix allocator that gets an address based on a hash
@@ -164,12 +187,15 @@ protected:
         /// @todo: Implement this method
         ///
         /// @param subnet an address will be picked from pool of that subnet
+        /// @param client_classes list of classes client belongs to
         /// @param duid Client's DUID
         /// @param hint a hint (last address that was picked)
         /// @return selected address
-        virtual isc::asiolink::IOAddress pickAddress(const SubnetPtr& subnet,
-                                                     const DuidPtr& duid,
-                                                     const isc::asiolink::IOAddress& hint);
+        virtual isc::asiolink::IOAddress
+            pickAddress(const SubnetPtr& subnet,
+                        const ClientClasses& client_classes,
+                        const DuidPtr& duid,
+                        const isc::asiolink::IOAddress& hint);
     };
 
     /// @brief Random allocator that picks address randomly
@@ -191,7 +217,9 @@ protected:
         /// @param hint the last address that was picked (ignored)
         /// @return a random address from the pool
         virtual isc::asiolink::IOAddress
-        pickAddress(const SubnetPtr& subnet, const DuidPtr& duid,
+        pickAddress(const SubnetPtr& subnet,
+                    const ClientClasses& client_classes,
+                    const DuidPtr& duid,
                     const isc::asiolink::IOAddress& hint);
     };
 
index a2926ecf06134a1167a8b2268a3dbf3ecdeedfbe..d57b94a894db32ecdd6d0ff31dd3892ebff7c0bf 100644 (file)
@@ -378,6 +378,12 @@ PoolParser::parse(PoolStoragePtr pools,
                       << " (" << option_data->getPosition() << ")");
         }
     }
+
+    // Client-class.
+    string client_class = getString(pool_structure, "client-class");
+    if (!client_class.empty()) {
+        pool->allowClientClass(client_class);
+    }
 }
 
 //****************************** Pool4Parser *************************
@@ -870,6 +876,11 @@ PdPoolParser::parse(PoolStoragePtr pools, ConstElementPtr pd_pool_) {
         pool_->setContext(user_context_);
     }
 
+    string client_class = getString(pd_pool_, "client-class");
+    if (!client_class.empty()) {
+        pool_->allowClientClass(client_class);
+    }
+        
     // Add the local pool to the external storage ptr.
     pools->push_back(pool_);
 }
index 8a3cb14b31893c8687f29ac5a8d33a31d10a6e72..65f201e43972ee9b5fa67d80697d8d32e33f1ba4 100644 (file)
@@ -71,8 +71,8 @@ SharedNetwork4Parser::parse(const data::ConstElementPtr& shared_network_data) {
         }
 
     } catch (const DhcpConfigError&) {
-       // Position was already added
-       throw;
+        // Position was already added
+        throw;
     } catch (const std::exception& ex) {
         isc_throw(DhcpConfigError, ex.what() << " ("
                   << shared_network_data->getPosition() << ")");
index 77cdc461214aed9a6042a7dac302c6a4ed81191c..1e3b6221603887254e4ad3452703a75db65c2622 100644 (file)
@@ -18,13 +18,35 @@ namespace dhcp {
 Pool::Pool(Lease::Type type, const isc::asiolink::IOAddress& first,
            const isc::asiolink::IOAddress& last)
     :id_(getNextID()), first_(first), last_(last), type_(type),
-     capacity_(0), cfg_option_(new CfgOption()) {
+     capacity_(0), cfg_option_(new CfgOption()), white_list_(),
+     last_allocated_(first), last_allocated_valid_(false) {
 }
 
 bool Pool::inRange(const isc::asiolink::IOAddress& addr) const {
     return (first_.smallerEqual(addr) && addr.smallerEqual(last_));
 }
 
+bool Pool::clientSupported(const ClientClasses& classes) const {
+    if (white_list_.empty()) {
+        // There is no class defined for this pool, so we do
+        // support everyone.
+        return (true);
+    }
+
+    for (ClientClasses::const_iterator it = white_list_.begin();
+         it != white_list_.end(); ++it) {
+        if (classes.contains(*it)) {
+            return (true);
+        }
+    }
+
+    return (false);
+}
+
+void Pool::allowClientClass(const ClientClass& class_name) {
+    white_list_.insert(class_name);
+}
+
 std::string
 Pool::toText() const {
     std::stringstream tmp;
@@ -90,6 +112,15 @@ Pool::toElement() const {
     ConstCfgOptionPtr opts = getCfgOption();
     map->set("option-data", opts->toElement());
     return (map);
+
+    // Set client-class
+    const ClientClasses& cclasses = getClientClasses();
+    if (cclasses.size() > 1) {
+        isc_throw(ToElementError, "client-class has too many items: "
+                  << cclasses.size());
+    } else if (!cclasses.empty()) {
+        map->set("client-class", Element::create(*cclasses.cbegin()));
+    }
 }
 
 data::ElementPtr
index 56d769891d6899c8134f7f39b3e0bb5ddc71db46..5254ea5ca5b6ef5d534d47a9796f4238464e8582 100644 (file)
@@ -8,6 +8,7 @@
 #define POOL_H
 
 #include <asiolink/io_address.h>
+#include <dhcp/classify.h>
 #include <dhcp/option6_pdexclude.h>
 #include <boost/shared_ptr.hpp>
 #include <cc/data.h>
@@ -106,6 +107,58 @@ public:
         user_context_ = ctx;
     }
 
+    /// @Checks whether this pool supports client that belongs to
+    /// specified classes.
+    ///
+    /// @todo: currently doing the same than network which
+    /// is known to be improved.
+    ///
+    /// @param client_classes list of all classes the client belongs to
+    /// @return true if client can be supported, false otherwise
+    bool clientSupported(const ClientClasses& client_classes) const;
+
+    /// @brief Adds class class_name to the list of supported classes
+    ///
+    /// @param class_name client class to be supported by this pool
+    void allowClientClass(const ClientClass& class_name);
+
+    /// @brief returns the client class white list
+    ///
+    /// @note Currently white list is empty or has one element
+    /// @note The returned reference is only valid as long as the object
+    /// returned is valid.
+    ///
+    /// @return client classes @ref white_list_
+    const ClientClasses& getClientClasses() const {
+        return (white_list_);
+    }
+
+    /// @brief returns the last address that was tried from this pool
+    ///
+    /// @return address/prefix that was last tried from this pool
+    isc::asiolink::IOAddress getLastAllocated() const {
+        return last_allocated_;
+    }
+
+    /// @brief checks if the last address is valid
+    /// @return true if the last address is valid
+    bool isLastAllocatedValid() const {
+        return last_allocated_valid_;
+    }
+
+    /// @brief sets the last address that was tried from this pool
+    ///
+    /// @param addr address/prefix to that was tried last
+    void setLastAllocated(const isc::asiolink::IOAddress& addr) {
+        last_allocated_ = addr;
+        last_allocated_valid_ = true;
+    }
+
+    /// @brief resets the last address to invalid
+    void resetLastAllocated() {
+        last_allocated_valid_ = false;
+    }
+
     /// @brief Unparse a pool object.
     ///
     /// @return A pointer to unparsed pool configuration.
@@ -164,8 +217,22 @@ protected:
     /// @brief Pointer to the option data configuration for this pool.
     CfgOptionPtr cfg_option_;
 
+    /// @brief Optional definition of a client class
+    ///
+    /// @ref Network::white_list_
+    ClientClasses white_list_;
+
     /// @brief Pointer to the user context (may be NULL)
     data::ConstElementPtr user_context_;
+
+    /// @brief Last allocated address
+    /// See @ref isc::dhcp::subnet::last_allocated_ia_
+    /// Initialized and reset to first
+    isc::asiolink::IOAddress last_allocated_;
+
+    /// @brief Status of last allocated address
+    bool last_allocated_valid_;
+
 };
 
 /// @brief Pool information for IPv4 addresses
index 837faed17171e0b03457af52ac56b3dd72ad5f21..0e0f0baca7a07d5e66df9fce7da197e5d8759e7e 100644 (file)
@@ -134,6 +134,23 @@ Subnet::getPoolCapacity(Lease::Type type) const {
     }
 }
 
+uint64_t
+Subnet::getPoolCapacity(Lease::Type type,
+                        const ClientClasses& client_classes) const {
+    switch (type) {
+    case Lease::TYPE_V4:
+    case Lease::TYPE_NA:
+        return sumPoolCapacity(pools_, client_classes);
+    case Lease::TYPE_TA:
+        return sumPoolCapacity(pools_ta_, client_classes);
+    case Lease::TYPE_PD:
+        return sumPoolCapacity(pools_pd_, client_classes);
+    default:
+        isc_throw(BadValue, "Unsupported pool type: "
+                  << static_cast<int>(type));
+    }
+}
+
 uint64_t
 Subnet::sumPoolCapacity(const PoolCollection& pools) const {
     uint64_t sum = 0;
@@ -152,6 +169,28 @@ Subnet::sumPoolCapacity(const PoolCollection& pools) const {
     return (sum);
 }
 
+uint64_t
+Subnet::sumPoolCapacity(const PoolCollection& pools,
+                        const ClientClasses& client_classes) const {
+    uint64_t sum = 0;
+    for (PoolCollection::const_iterator p = pools.begin(); p != pools.end(); ++p) {
+        if (!(*p)->clientSupported(client_classes)) {
+            continue;
+        }
+        uint64_t x = (*p)->getCapacity();
+
+        // Check if we can add it. If sum + x > uint64::max, then we would have
+        // overflown if we tried to add it.
+        if (x > std::numeric_limits<uint64_t>::max() - sum) {
+            return (std::numeric_limits<uint64_t>::max());
+        }
+
+        sum += x;
+    }
+
+    return (sum);
+}
+
 void Subnet4::checkType(Lease::Type type) const {
     if (type != Lease::TYPE_V4) {
         isc_throw(BadValue, "Only TYPE_V4 is allowed for Subnet4");
@@ -329,6 +368,33 @@ const PoolPtr Subnet::getPool(Lease::Type type, const isc::asiolink::IOAddress&
     return (candidate);
 }
 
+const PoolPtr Subnet::getPool(Lease::Type type,
+                              const ClientClasses& client_classes,
+                              const isc::asiolink::IOAddress& hint) const {
+    // check if the type is valid (and throw if it isn't)
+    checkType(type);
+
+    const PoolCollection& pools = getPools(type);
+
+    PoolPtr candidate;
+
+    if (!pools.empty()) {
+        PoolCollection::const_iterator ub =
+            std::upper_bound(pools.begin(), pools.end(), hint,
+                             prefixLessThanFirstAddress);
+
+        if (ub != pools.begin()) {
+            --ub;
+            if ((*ub)->inRange(hint) && (*ub)->clientSupported(client_classes)) {
+                candidate = *ub;
+            }
+        }
+    }
+
+    // Return a pool or NULL if no match found.
+    return (candidate);
+}
+
 void
 Subnet::addPool(const PoolPtr& pool) {
     // check if the type is valid (and throw if it isn't)
index 03b1f7784c57415f6df0ee1285142a73a810fdb6..883aa229b6c2492ad4da995fe111d67a508ccfbd 100644 (file)
@@ -54,26 +54,26 @@ public:
     /// @return true if the address is in any of the pools
     bool inPool(Lease::Type type, const isc::asiolink::IOAddress& addr) const;
 
-    /// @brief returns the last address that was tried from this pool
+    /// @brief returns the last address that was tried from this subnet
     ///
     /// This method returns the last address that was attempted to be allocated
     /// from this subnet. This is used as helper information for the next
     /// iteration of the allocation algorithm.
     ///
-    /// @todo: Define map<SubnetID, IOAddress> somewhere in the
+    /// @todo: Define map<SubnetID, ClientClass, IOAddress> somewhere in the
     ///        AllocEngine::IterativeAllocator and keep the data there
     ///
     /// @param type lease type to be returned
-    /// @return address/prefix that was last tried from this pool
+    /// @return address/prefix that was last tried from this subnet
     isc::asiolink::IOAddress getLastAllocated(Lease::Type type) const;
 
-    /// @brief sets the last address that was tried from this pool
+    /// @brief sets the last address that was tried from this subnet
     ///
     /// This method sets the last address that was attempted to be allocated
     /// from this subnet. This is used as helper information for the next
     /// iteration of the allocation algorithm.
     ///
-    /// @todo: Define map<SubnetID, IOAddress> somewhere in the
+    /// @todo: Define map<SubnetID, ClientClass, IOAddress> somewhere in the
     ///        AllocEngine::IterativeAllocator and keep the data there
     /// @param addr address/prefix to that was tried last
     /// @param type lease type to be set
@@ -142,6 +142,17 @@ public:
     const PoolPtr getPool(Lease::Type type, const isc::asiolink::IOAddress& addr,
                           bool anypool = true) const;
 
+    /// @brief Returns a pool that specified address belongs to with classes
+    ///
+    /// Variant using only pools allowing given classes
+    ///
+    /// @param type pool type that the pool is looked for
+    /// @param client_classes client class list which must be allowed
+    /// @param addr address that the returned pool should cover (optional)
+    const PoolPtr getPool(Lease::Type type,
+                          const ClientClasses& client_classes,
+                          const isc::asiolink::IOAddress& addr) const;
+
     /// @brief Returns a pool without any address specified
     ///
     /// @param type pool type that the pool is looked for
@@ -169,6 +180,14 @@ public:
     /// @param type type of the lease
     uint64_t getPoolCapacity(Lease::Type type) const;
 
+    /// @brief Returns the number of possible leases for specified lease type
+    /// allowed for a client which belongs to classes.
+    ///
+    /// @param type type of the lease
+    /// @param client_classes List of classes the client belongs to.
+    uint64_t getPoolCapacity(Lease::Type type,
+                             const ClientClasses& client_classes) const;
+
     /// @brief Returns textual representation of the subnet (e.g.
     /// "2001:db8::/64")
     ///
@@ -300,6 +319,13 @@ protected:
     /// @return sum of possible leases
     uint64_t sumPoolCapacity(const PoolCollection& pools) const;
 
+    /// @brief returns a sum of possible leases in all pools allowing classes
+    /// @param pools list of pools
+    /// @param client_classes list of classes
+    /// @return sum of possible/allowed leases
+    uint64_t sumPoolCapacity(const PoolCollection& pools,
+                             const ClientClasses& client_classes) const;
+
     /// @brief Checks if the specified pool overlaps with an existing pool.
     ///
     /// @param pool_type Pool type.
index ff216a6a77957e609cde4127bbebebd6cecfc1de..ab02757247b65d06beb9b8e5cdab83c9e0ae581f 100644 (file)
@@ -326,7 +326,7 @@ TEST_F(AllocEngine4Test, IterativeAllocator) {
         alloc(new NakedAllocEngine::IterativeAllocator(Lease::TYPE_V4));
 
     for (int i = 0; i < 1000; ++i) {
-        IOAddress candidate = alloc->pickAddress(subnet_, clientid_,
+        IOAddress candidate = alloc->pickAddress(subnet_, cc_, clientid_,
                                                  IOAddress("0.0.0.0"));
         EXPECT_TRUE(subnet_->inPool(Lease::TYPE_V4, candidate));
     }
@@ -359,7 +359,7 @@ TEST_F(AllocEngine4Test, IterativeAllocator_manyPools4) {
     std::set<IOAddress> generated_addrs;
     int cnt = 0;
     while (++cnt) {
-        IOAddress candidate = alloc.pickAddress(subnet_, clientid_, IOAddress("0.0.0.0"));
+        IOAddress candidate = alloc.pickAddress(subnet_, cc_, clientid_, IOAddress("0.0.0.0"));
         EXPECT_TRUE(subnet_->inPool(Lease::TYPE_V4, candidate));
 
         // One way to easily verify that the iterative allocator really works is
index 1002958cfb44a1b86a608483d5fc65c98fb8f48e..a8d80109eade4e961715dc4006821cd1c0141e89 100644 (file)
@@ -189,7 +189,7 @@ TEST_F(AllocEngine6Test, IterativeAllocator) {
         alloc(new NakedAllocEngine::IterativeAllocator(Lease::TYPE_NA));
 
     for (int i = 0; i < 1000; ++i) {
-        IOAddress candidate = alloc->pickAddress(subnet_, duid_, IOAddress("::"));
+        IOAddress candidate = alloc->pickAddress(subnet_, cc_, duid_, IOAddress("::"));
         EXPECT_TRUE(subnet_->inPool(Lease::TYPE_NA, candidate));
     }
 }
@@ -210,23 +210,23 @@ TEST_F(AllocEngine6Test, IterativeAllocatorAddrStep) {
     subnet_->addPool(pool3);
 
     // Let's check the first pool (5 addresses here)
-    EXPECT_EQ("2001:db8:1::1", alloc.pickAddress(subnet_, duid_, IOAddress("::")).toText());
-    EXPECT_EQ("2001:db8:1::2", alloc.pickAddress(subnet_, duid_, IOAddress("::")).toText());
-    EXPECT_EQ("2001:db8:1::3", alloc.pickAddress(subnet_, duid_, IOAddress("::")).toText());
-    EXPECT_EQ("2001:db8:1::4", alloc.pickAddress(subnet_, duid_, IOAddress("::")).toText());
-    EXPECT_EQ("2001:db8:1::5", alloc.pickAddress(subnet_, duid_, IOAddress("::")).toText());
+    EXPECT_EQ("2001:db8:1::1", alloc.pickAddress(subnet_, cc_, duid_, IOAddress("::")).toText());
+    EXPECT_EQ("2001:db8:1::2", alloc.pickAddress(subnet_, cc_, duid_, IOAddress("::")).toText());
+    EXPECT_EQ("2001:db8:1::3", alloc.pickAddress(subnet_, cc_, duid_, IOAddress("::")).toText());
+    EXPECT_EQ("2001:db8:1::4", alloc.pickAddress(subnet_, cc_, duid_, IOAddress("::")).toText());
+    EXPECT_EQ("2001:db8:1::5", alloc.pickAddress(subnet_, cc_, duid_, IOAddress("::")).toText());
 
     // The second pool is easy - only one address here
-    EXPECT_EQ("2001:db8:1::100", alloc.pickAddress(subnet_, duid_, IOAddress("::")).toText());
+    EXPECT_EQ("2001:db8:1::100", alloc.pickAddress(subnet_, cc_, duid_, IOAddress("::")).toText());
 
     // This is the third and last pool, with 2 addresses in it
-    EXPECT_EQ("2001:db8:1::105", alloc.pickAddress(subnet_, duid_, IOAddress("::")).toText());
-    EXPECT_EQ("2001:db8:1::106", alloc.pickAddress(subnet_, duid_, IOAddress("::")).toText());
+    EXPECT_EQ("2001:db8:1::105", alloc.pickAddress(subnet_, cc_, duid_, IOAddress("::")).toText());
+    EXPECT_EQ("2001:db8:1::106", alloc.pickAddress(subnet_, cc_, duid_, IOAddress("::")).toText());
 
     // We iterated over all addresses and reached to the end of the last pool.
     // Let's wrap around and start from the beginning
-    EXPECT_EQ("2001:db8:1::1", alloc.pickAddress(subnet_, duid_, IOAddress("::")).toText());
-    EXPECT_EQ("2001:db8:1::2", alloc.pickAddress(subnet_, duid_, IOAddress("::")).toText());
+    EXPECT_EQ("2001:db8:1::1", alloc.pickAddress(subnet_, cc_, duid_, IOAddress("::")).toText());
+    EXPECT_EQ("2001:db8:1::2", alloc.pickAddress(subnet_, cc_, duid_, IOAddress("::")).toText());
 }
 
 TEST_F(AllocEngine6Test, IterativeAllocatorPrefixStep) {
@@ -247,41 +247,41 @@ TEST_F(AllocEngine6Test, IterativeAllocatorPrefixStep) {
     // 2001:db8:2::/56 split into /64 prefixes (256 leases) (or 2001:db8:2:XX::)
 
     // First pool check (Let's check over all 16 leases)
-    EXPECT_EQ("2001:db8::", alloc.pickAddress(subnet_, duid_, IOAddress("::")).toText());
-    EXPECT_EQ("2001:db8:0:10::", alloc.pickAddress(subnet_, duid_, IOAddress("::")).toText());
-    EXPECT_EQ("2001:db8:0:20::", alloc.pickAddress(subnet_, duid_, IOAddress("::")).toText());
-    EXPECT_EQ("2001:db8:0:30::", alloc.pickAddress(subnet_, duid_, IOAddress("::")).toText());
-    EXPECT_EQ("2001:db8:0:40::", alloc.pickAddress(subnet_, duid_, IOAddress("::")).toText());
-    EXPECT_EQ("2001:db8:0:50::", alloc.pickAddress(subnet_, duid_, IOAddress("::")).toText());
-    EXPECT_EQ("2001:db8:0:60::", alloc.pickAddress(subnet_, duid_, IOAddress("::")).toText());
-    EXPECT_EQ("2001:db8:0:70::", alloc.pickAddress(subnet_, duid_, IOAddress("::")).toText());
-    EXPECT_EQ("2001:db8:0:80::", alloc.pickAddress(subnet_, duid_, IOAddress("::")).toText());
-    EXPECT_EQ("2001:db8:0:90::", alloc.pickAddress(subnet_, duid_, IOAddress("::")).toText());
-    EXPECT_EQ("2001:db8:0:a0::", alloc.pickAddress(subnet_, duid_, IOAddress("::")).toText());
-    EXPECT_EQ("2001:db8:0:b0::", alloc.pickAddress(subnet_, duid_, IOAddress("::")).toText());
-    EXPECT_EQ("2001:db8:0:c0::", alloc.pickAddress(subnet_, duid_, IOAddress("::")).toText());
-    EXPECT_EQ("2001:db8:0:d0::", alloc.pickAddress(subnet_, duid_, IOAddress("::")).toText());
-    EXPECT_EQ("2001:db8:0:e0::", alloc.pickAddress(subnet_, duid_, IOAddress("::")).toText());
-    EXPECT_EQ("2001:db8:0:f0::", alloc.pickAddress(subnet_, duid_, IOAddress("::")).toText());
+    EXPECT_EQ("2001:db8::", alloc.pickAddress(subnet_, cc_, duid_, IOAddress("::")).toText());
+    EXPECT_EQ("2001:db8:0:10::", alloc.pickAddress(subnet_, cc_, duid_, IOAddress("::")).toText());
+    EXPECT_EQ("2001:db8:0:20::", alloc.pickAddress(subnet_, cc_, duid_, IOAddress("::")).toText());
+    EXPECT_EQ("2001:db8:0:30::", alloc.pickAddress(subnet_, cc_, duid_, IOAddress("::")).toText());
+    EXPECT_EQ("2001:db8:0:40::", alloc.pickAddress(subnet_, cc_, duid_, IOAddress("::")).toText());
+    EXPECT_EQ("2001:db8:0:50::", alloc.pickAddress(subnet_, cc_, duid_, IOAddress("::")).toText());
+    EXPECT_EQ("2001:db8:0:60::", alloc.pickAddress(subnet_, cc_, duid_, IOAddress("::")).toText());
+    EXPECT_EQ("2001:db8:0:70::", alloc.pickAddress(subnet_, cc_, duid_, IOAddress("::")).toText());
+    EXPECT_EQ("2001:db8:0:80::", alloc.pickAddress(subnet_, cc_, duid_, IOAddress("::")).toText());
+    EXPECT_EQ("2001:db8:0:90::", alloc.pickAddress(subnet_, cc_, duid_, IOAddress("::")).toText());
+    EXPECT_EQ("2001:db8:0:a0::", alloc.pickAddress(subnet_, cc_, duid_, IOAddress("::")).toText());
+    EXPECT_EQ("2001:db8:0:b0::", alloc.pickAddress(subnet_, cc_, duid_, IOAddress("::")).toText());
+    EXPECT_EQ("2001:db8:0:c0::", alloc.pickAddress(subnet_, cc_, duid_, IOAddress("::")).toText());
+    EXPECT_EQ("2001:db8:0:d0::", alloc.pickAddress(subnet_, cc_, duid_, IOAddress("::")).toText());
+    EXPECT_EQ("2001:db8:0:e0::", alloc.pickAddress(subnet_, cc_, duid_, IOAddress("::")).toText());
+    EXPECT_EQ("2001:db8:0:f0::", alloc.pickAddress(subnet_, cc_, duid_, IOAddress("::")).toText());
 
     // Second pool (just one lease here)
-    EXPECT_EQ("2001:db8:1::", alloc.pickAddress(subnet_, duid_, IOAddress("::")).toText());
+    EXPECT_EQ("2001:db8:1::", alloc.pickAddress(subnet_, cc_, duid_, IOAddress("::")).toText());
 
     // Third pool (256 leases, let's check first and last explicitly and the
     // rest over in a pool
-    EXPECT_EQ("2001:db8:2::", alloc.pickAddress(subnet_, duid_, IOAddress("::")).toText());
+    EXPECT_EQ("2001:db8:2::", alloc.pickAddress(subnet_, cc_, duid_, IOAddress("::")).toText());
     for (int i = 1; i < 255; i++) {
         stringstream exp;
         exp << "2001:db8:2:" << hex << i << dec << "::";
-        EXPECT_EQ(exp.str(), alloc.pickAddress(subnet_, duid_, IOAddress("::")).toText());
+        EXPECT_EQ(exp.str(), alloc.pickAddress(subnet_, cc_, duid_, IOAddress("::")).toText());
 
     }
-    EXPECT_EQ("2001:db8:2:ff::", alloc.pickAddress(subnet_, duid_, IOAddress("::")).toText());
+    EXPECT_EQ("2001:db8:2:ff::", alloc.pickAddress(subnet_, cc_, duid_, IOAddress("::")).toText());
 
     // Ok, we've iterated over all prefixes in all pools. We now wrap around.
     // We're looping over now (iterating over first pool again)
-    EXPECT_EQ("2001:db8::", alloc.pickAddress(subnet_, duid_, IOAddress("::")).toText());
-    EXPECT_EQ("2001:db8:0:10::", alloc.pickAddress(subnet_, duid_, IOAddress("::")).toText());
+    EXPECT_EQ("2001:db8::", alloc.pickAddress(subnet_, cc_, duid_, IOAddress("::")).toText());
+    EXPECT_EQ("2001:db8:0:10::", alloc.pickAddress(subnet_, cc_, duid_, IOAddress("::")).toText());
 }
 
 // This test verifies that the iterative allocator can step over addresses
@@ -289,7 +289,7 @@ TEST_F(AllocEngine6Test, IterativeAllocatorAddressIncrease) {
     NakedAllocEngine::NakedIterativeAllocator alloc(Lease::TYPE_NA);
 
     // Let's pick the first address
-    IOAddress addr1 = alloc.pickAddress(subnet_, duid_, IOAddress("2001:db8:1::10"));
+    IOAddress addr1 = alloc.pickAddress(subnet_, cc_, duid_, IOAddress("2001:db8:1::10"));
 
     // Check that we can indeed pick the first address from the pool
     EXPECT_EQ("2001:db8:1::10", addr1.toText());
@@ -379,7 +379,7 @@ TEST_F(AllocEngine6Test, IterativeAllocator_manyPools6) {
     std::set<IOAddress> generated_addrs;
     int cnt = 0;
     while (++cnt) {
-        IOAddress candidate = alloc.pickAddress(subnet_, duid_, IOAddress("::"));
+        IOAddress candidate = alloc.pickAddress(subnet_, cc_, duid_, IOAddress("::"));
         EXPECT_TRUE(subnet_->inPool(Lease::TYPE_NA, candidate));
 
         // One way to easily verify that the iterative allocator really works is
index 426189eaaaab92efa7903b64d8dcb729f506eb2b..de3d8a28861a7775de61685e8b77217e06857912 100644 (file)
@@ -409,6 +409,7 @@ public:
     bool fqdn_fwd_;           ///< Perform forward update for a lease.
     bool fqdn_rev_;           ///< Perform reverse update for a lease.
     LeaseMgrFactory factory_; ///< pointer to LeaseMgr factory
+    ClientClasses cc_;        ///< client classes
 };
 
 /// @brief Used in Allocation Engine tests for IPv4
@@ -510,6 +511,7 @@ public:
     Pool4Ptr pool_;             ///< Pool belonging to subnet_
     LeaseMgrFactory factory_;   ///< Pointer to LeaseMgr factory
     AllocEngine::ClientContext4 ctx_; ///< Context information passed to various
+    ClientClasses cc_;          ///< Client classes
                                      ///< allocation engine functions.
 };