Parcourir la source

[master] Merge branch 'trac3711'

Conflicts:
	src/lib/asiolink/io_address.h
	src/lib/asiolink/tests/io_address_unittest.cc
Marcin Siodelski il y a 10 ans
Parent
commit
cb5533a1bf

+ 51 - 1
src/lib/asiolink/io_address.cc

@@ -1,4 +1,4 @@
-// Copyright (C) 2010  Internet Systems Consortium, Inc. ("ISC")
+// Copyright (C) 2010-2012, 2014-2015 Internet Systems Consortium, Inc. ("ISC")
 //
 //
 // Permission to use, copy, modify, and/or distribute this software for any
 // Permission to use, copy, modify, and/or distribute this software for any
 // purpose with or without fee is hereby granted, provided that the above
 // purpose with or without fee is hereby granted, provided that the above
@@ -131,5 +131,55 @@ operator<<(std::ostream& os, const IOAddress& address) {
     return (os);
     return (os);
 }
 }
 
 
+IOAddress
+IOAddress::subtract(const IOAddress& a, const IOAddress& b) {
+    if (a.getFamily() != b.getFamily()) {
+        isc_throw(BadValue, "Both addresses have to be the same family");
+    }
+    if (a.isV4()) {
+        // Subtracting v4 is easy. We have uint32_t operator.
+        return (IOAddress(static_cast<uint32_t>(a) - static_cast<uint32_t>(b)));
+    } else {
+        // v6 is more involved.
+
+        // Let's extract the raw data first.
+        vector<uint8_t> a_vec = a.toBytes();
+        vector<uint8_t> b_vec = b.toBytes();
+
+        // ... and prepare the result
+        vector<uint8_t> result(V6ADDRESS_LEN,0);
+
+        // Carry is a boolean, but to avoid its frequent casting, let's
+        // use uint8_t. Also, some would prefer to call it borrow, but I prefer
+        // carry. Somewhat relevant discussion here:
+        // http://en.wikipedia.org/wiki/Carry_flag#Carry_flag_vs._Borrow_flag
+        uint8_t carry = 0;
+
+        // Now perform subtraction with borrow.
+        for (int i = a_vec.size() - 1; i >= 0; --i) {
+            result[i] = a_vec[i] - b_vec[i] - carry;
+            carry = (a_vec[i] < b_vec[i] + carry);
+        }
+
+        return (fromBytes(AF_INET6, &result[0]));
+    }
+}
+
+IOAddress
+IOAddress::increase(const IOAddress& addr) {
+    std::vector<uint8_t> packed(addr.toBytes());
+
+    // Start increasing the least significant byte
+    for (int i = packed.size() - 1; i >= 0; --i) {
+        // if we haven't overflowed (0xff -> 0x0), than we are done
+        if (++packed[i] != 0) {
+            break;
+        }
+    }
+
+    return (IOAddress::fromBytes(addr.getFamily(), &packed[0]));
+}
+
+
 } // namespace asiolink
 } // namespace asiolink
 } // namespace isc
 } // namespace isc

+ 41 - 0
src/lib/asiolink/io_address.h

@@ -221,6 +221,47 @@ public:
         return (nequals(other));
         return (nequals(other));
     }
     }
 
 
+    /// @brief Subtracts one address from another (a - b)
+    ///
+    /// Treats addresses as integers and subtracts them. For example:
+    /// 192.0.2.5 - 192.0.2.0 = 0.0.0.5
+    /// fe80::abcd - fe80:: = ::abcd
+    ///
+    /// It is possible to subtract greater from lesser address, e.g.
+    /// 192.168.56.10 - 192.168.67.20, but please do understand that
+    /// the address space is a finite field in mathematical sense, so
+    /// you may end up with a result that is greater then any of the
+    /// addresses you specified. Also, subtraction is not commutative,
+    /// so a - b != b - a.
+    ///
+    /// This operation is essential for calculating the number of
+    /// leases in a pool, where we need to calculate (max - min).
+    /// @throw BadValue if addresses are of different family
+    /// @param a address to be subtracted from
+    /// @param b address to be subtracted
+    /// @return IOAddress object that represents the difference
+    static IOAddress subtract(const IOAddress& a, const IOAddress& b);
+
+    /// @brief Returns an address increased by one
+    ///
+    /// This method works for both IPv4 and IPv6 addresses. For example,
+    /// increase 192.0.2.255 will become 192.0.3.0.
+    ///
+    /// Address space is a finite field in the mathematical sense, so keep
+    /// in mind that the address space "loops". 255.255.255.255 increased
+    /// by one gives 0.0.0.0. The same is true for maximum value of IPv6
+    /// (all 1's) looping to ::.
+    ///
+    /// @todo Determine if we have a use-case for increasing the address
+    /// by more than one. Increase by one is used in AllocEngine. This method
+    /// could take extra parameter that specifies the value by which the
+    /// address should be increased.
+    ///
+    /// @param addr address to be increased
+    /// @return address increased by one
+    static IOAddress
+    increase(const IOAddress& addr);
+
     /// \brief Converts IPv4 address to uint32_t
     /// \brief Converts IPv4 address to uint32_t
     ///
     ///
     /// Will throw BadValue exception if that is not IPv4
     /// Will throw BadValue exception if that is not IPv4

+ 77 - 2
src/lib/asiolink/tests/io_address_unittest.cc

@@ -1,4 +1,4 @@
-// Copyright (C) 2011, 2015 Internet Systems Consortium, Inc. ("ISC")
+// Copyright (C) 2011-2012,2014-2015  Internet Systems Consortium, Inc. ("ISC")
 //
 //
 // Permission to use, copy, modify, and/or distribute this software for any
 // Permission to use, copy, modify, and/or distribute this software for any
 // purpose with or without fee is hereby granted, provided that the above
 // purpose with or without fee is hereby granted, provided that the above
@@ -17,6 +17,7 @@
 
 
 #include <asiolink/io_error.h>
 #include <asiolink/io_error.h>
 #include <asiolink/io_address.h>
 #include <asiolink/io_address.h>
+#include <exceptions/exceptions.h>
 
 
 #include <algorithm>
 #include <algorithm>
 #include <cstring>
 #include <cstring>
@@ -102,7 +103,7 @@ TEST(IOAddressTest, toBytesV6) {
     const char* V6STRING = "2001:db8:1::dead:beef";
     const char* V6STRING = "2001:db8:1::dead:beef";
     uint8_t V6[] = {
     uint8_t V6[] = {
         0x20, 0x01, 0x0d, 0xb8, 0x00, 0x01, 0x00, 0x00,
         0x20, 0x01, 0x0d, 0xb8, 0x00, 0x01, 0x00, 0x00,
-        0x00, 0x00, 0x00, 0x00, 0xde, 0xad, 0xbe, 0xef 
+        0x00, 0x00, 0x00, 0x00, 0xde, 0xad, 0xbe, 0xef
     };
     };
 
 
     std::vector<uint8_t> actual = IOAddress(V6STRING).toBytes();
     std::vector<uint8_t> actual = IOAddress(V6STRING).toBytes();
@@ -223,3 +224,77 @@ TEST(IOAddressTest, staticAddresses) {
     EXPECT_EQ(IOAddress("255.255.255.255"), IOAddress::IPV4_BCAST_ADDRESS());
     EXPECT_EQ(IOAddress("255.255.255.255"), IOAddress::IPV4_BCAST_ADDRESS());
     EXPECT_EQ(IOAddress("::"), IOAddress::IPV6_ZERO_ADDRESS());
     EXPECT_EQ(IOAddress("::"), IOAddress::IPV6_ZERO_ADDRESS());
 }
 }
+
+// Tests whether address subtraction works correctly.
+TEST(IOAddressTest, subtract) {
+    IOAddress addr1("192.0.2.12");
+    IOAddress addr2("192.0.2.5");
+    IOAddress addr3("192.0.2.0");
+    IOAddress addr4("0.0.2.1");
+    IOAddress any4("0.0.0.0");
+    IOAddress bcast("255.255.255.255");
+
+    EXPECT_EQ("0.0.0.7", IOAddress::subtract(addr1, addr2).toText());
+    EXPECT_EQ("0.0.0.12", IOAddress::subtract(addr1, addr3).toText());
+
+    // Subtracting 0.0.0.0 is like subtracting 0.
+    EXPECT_EQ("192.0.2.12", IOAddress::subtract(addr1, any4).toText());
+    EXPECT_EQ("192.0.2.13", IOAddress::subtract(addr1, bcast).toText());
+    EXPECT_EQ("191.255.255.255", IOAddress::subtract(addr3, addr4).toText());
+
+    // Let's check if we can subtract greater address from smaller.
+    // This will check if we can "loop"
+    EXPECT_EQ("255.255.255.251", IOAddress::subtract(addr3, addr2).toText());
+
+    IOAddress addr6("fe80::abcd");
+    IOAddress addr7("fe80::");
+    IOAddress addr8("fe80::1234");
+    IOAddress addr9("2001:db8::face");
+    IOAddress addr10("2001:db8::ffff:ffff:ffff:ffff");
+    IOAddress addr11("::1");
+    IOAddress any6("::");
+
+    EXPECT_EQ("::abcd", IOAddress::subtract(addr6, addr7).toText());
+    EXPECT_EQ("::9999", IOAddress::subtract(addr6, addr8).toText());
+    EXPECT_EQ("::ffff:ffff:ffff:531", IOAddress::subtract(addr10, addr9).toText());
+
+    // Subtract with borrow, extreme edition. Need to borrow one bit
+    // 112 times.
+    EXPECT_EQ("fe7f:ffff:ffff:ffff:ffff:ffff:ffff:ffff",
+              IOAddress::subtract(addr7, addr11).toText());
+
+    // Now check if we can loop beyond :: (:: - ::1 is a lot of F's)
+    EXPECT_EQ("ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff",
+              IOAddress::subtract(any6, addr11).toText());
+
+    // Subtracting :: is like subtracting 0.
+    EXPECT_EQ("2001:db8::face", IOAddress::subtract(addr9, any6).toText());
+
+    // Let's check if we can subtract greater address from smaller.
+    // This will check if we can "loop"
+    EXPECT_EQ("ffff:ffff:ffff:ffff:ffff:ffff:ffff:edcc",
+              IOAddress::subtract(addr7, addr8).toText());
+
+    // Inter-family relations are not allowed.
+    EXPECT_THROW(IOAddress::subtract(addr1, addr6), isc::BadValue);
+    EXPECT_THROW(IOAddress::subtract(addr6, addr1), isc::BadValue);
+}
+
+// Test checks whether an address can be increased.
+TEST(IOAddressTest, increaseAddr) {
+    IOAddress addr1("192.0.2.12");
+    IOAddress any4("0.0.0.0");
+    IOAddress bcast("255.255.255.255");
+    IOAddress addr6("2001:db8::ffff:ffff:ffff:ffff");
+    IOAddress addr11("::1");
+    IOAddress any6("::");
+    IOAddress the_last_one("ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff");
+
+    EXPECT_EQ("192.0.2.13", IOAddress::increase(addr1).toText());
+    EXPECT_EQ("0.0.0.1", IOAddress::increase(any4).toText());
+    EXPECT_EQ("0.0.0.0", IOAddress::increase(bcast).toText());
+    EXPECT_EQ("2001:db8:0:1::", IOAddress::increase(addr6).toText());
+    EXPECT_EQ("::2", IOAddress::increase(addr11).toText());
+    EXPECT_EQ("::1", IOAddress::increase(any6).toText());
+    EXPECT_EQ("::", IOAddress::increase(the_last_one).toText());
+}

+ 98 - 1
src/lib/dhcpsrv/addr_utilities.cc

@@ -1,4 +1,4 @@
-// Copyright (C) 2012 Internet Systems Consortium, Inc. ("ISC")
+// Copyright (C) 2012,2015 Internet Systems Consortium, Inc. ("ISC")
 //
 //
 // Permission to use, copy, modify, and/or distribute this software for any
 // Permission to use, copy, modify, and/or distribute this software for any
 // purpose with or without fee is hereby granted, provided that the above
 // purpose with or without fee is hereby granted, provided that the above
@@ -15,6 +15,8 @@
 #include <dhcpsrv/addr_utilities.h>
 #include <dhcpsrv/addr_utilities.h>
 #include <exceptions/exceptions.h>
 #include <exceptions/exceptions.h>
 
 
+#include <vector>
+#include <limits>
 #include <string.h>
 #include <string.h>
 
 
 using namespace isc;
 using namespace isc;
@@ -205,5 +207,100 @@ isc::asiolink::IOAddress getNetmask4(uint8_t len) {
     return (IOAddress(x));
     return (IOAddress(x));
 }
 }
 
 
+uint64_t
+addrsInRange(const isc::asiolink::IOAddress& min,
+             const isc::asiolink::IOAddress& max) {
+    if (min.getFamily() != max.getFamily()) {
+        isc_throw(BadValue, "Both addresses have to be the same family");
+    }
+
+    if (max < min) {
+        isc_throw(BadValue, min.toText() << " must not be greater than "
+                  << max.toText());
+    }
+
+    if (min.isV4()) {
+        // Let's explicitly cast last_ and first_ (IOAddress). This conversion is
+        // automatic, but let's explicitly cast it show that we moved to integer
+        // domain and addresses are now substractable.
+        uint64_t max_numeric = static_cast<uint32_t>(max);
+        uint64_t min_numeric = static_cast<uint32_t>(min);
+
+        // We can simply subtract the values. We need to increase the result
+        // by one, as both min and max are included in the range. So even if
+        // min == max, there's one address.
+        return (max_numeric - min_numeric + 1);
+    } else {
+
+        // Calculating the difference in v6 is more involved. Let's subtract
+        // one from the other. By subtracting min from max, we move the
+        // [a, b] range to the [0, (b-a)] range. We don't care about the beginning
+        // of the new range (it's always zero). The upper bound now specifies
+        // the number of addresses minus one.
+        IOAddress count = IOAddress::subtract(max, min);
+
+        // There's one very special case. Someone is trying to check how many
+        // IPv6 addresses are in IPv6 address space. He called this method
+        // with ::, ffff:ffff:ffff:fffff:ffff:ffff:ffff:ffff. The diff is also
+        // all 1s. Had we increased it by one, the address would flip to all 0s.
+        // This will not happen in a real world. Apparently, unit-tests are
+        // sometimes nastier then a real world.
+        static IOAddress max6("ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff");
+        if (count == max6) {
+            return (std::numeric_limits<uint64_t>::max());
+        }
+
+        // Increase it by one (a..a range still contains one address, even though
+        // a subtracted from a is zero).
+        count = IOAddress::increase(count);
+
+        // We don't have uint128, so for anything greater than 2^64, we'll just
+        // assume numeric_limits<uint64_t>::max. Let's do it the manual way.
+        const std::vector<uint8_t>& bin(count.toBytes());
+
+        // If any of the most significant 64 bits is set, we have more than
+        // 2^64 addresses and can't represent it even on uint64_t.
+        for (int i = 0 ; i < 8; i++) {
+            if (bin[i]) {
+                return (std::numeric_limits<uint64_t>::max());
+            }
+        }
+
+        // Ok, we're good. The pool is sanely sized. It may be huge, but at least
+        // that's something we can represent on uint64_t.
+        uint64_t numeric = 0;
+        for (int i = 8; i < 16; i++) {
+            numeric <<= 8;
+            numeric += bin[i];
+        }
+
+        return (numeric);
+    }
+}
+
+uint64_t prefixesInRange(const uint8_t pool_len, const uint8_t delegated_len) {
+    if (delegated_len < pool_len) {
+        return (0);
+    }
+
+    uint64_t count = delegated_len - pool_len;
+
+    if (count == 0) {
+        // If we want to delegate /64 out of /64 pool, we have only
+        // one prefix.
+        return (1);
+    } else if (count >= 64) {
+        // If the difference is greater than or equal 64, e.g. we want to
+        // delegate /96 out of /16 pool, the number is bigger than we can
+        // express, so we'll stick with maximum value of uint64_t.
+        return (std::numeric_limits<uint64_t>::max());
+    } else {
+        // Now count specifies the exponent (e.g. if the difference between the
+        // delegated and pool length is 4, we have 16 prefixes), so we need
+        // to calculate 2^(count - 1)
+        return ((static_cast<uint64_t>(2)) << (count - 1));
+    }
+}
+
 };
 };
 };
 };

+ 28 - 2
src/lib/dhcpsrv/addr_utilities.h

@@ -1,4 +1,4 @@
-// Copyright (C) 2012 Internet Systems Consortium, Inc. ("ISC")
+// Copyright (C) 2012,2014-2015 Internet Systems Consortium, Inc. ("ISC")
 //
 //
 // Permission to use, copy, modify, and/or distribute this software for any
 // Permission to use, copy, modify, and/or distribute this software for any
 // purpose with or without fee is hereby granted, provided that the above
 // purpose with or without fee is hereby granted, provided that the above
@@ -52,11 +52,37 @@ isc::asiolink::IOAddress firstAddrInPrefix(const isc::asiolink::IOAddress& prefi
 isc::asiolink::IOAddress lastAddrInPrefix(const isc::asiolink::IOAddress& prefix,
 isc::asiolink::IOAddress lastAddrInPrefix(const isc::asiolink::IOAddress& prefix,
                                            uint8_t len);
                                            uint8_t len);
 
 
-/// @brief generates an IPv4 netmask of specified length
+/// @brief Generates an IPv4 netmask of specified length
 /// @throw BadValue if len is greater than 32
 /// @throw BadValue if len is greater than 32
 /// @return netmask
 /// @return netmask
 isc::asiolink::IOAddress getNetmask4(uint8_t len);
 isc::asiolink::IOAddress getNetmask4(uint8_t len);
 
 
+
+/// @brief Returns number of available addresses in the specified range (min - max).
+///
+/// Note that for min equal max case, the number of addresses is one.
+///
+/// @throw BadValue if min and max are not of the same family (both must be
+///        either IPv4 or IPv6) or if max < min.
+///
+/// @param min the first address in range
+/// @param max the last address in range
+/// @return number of addresses in range
+uint64_t addrsInRange(const isc::asiolink::IOAddress& min,
+                      const isc::asiolink::IOAddress& max);
+
+/// @brief Returns number of available IPv6 prefixes in the specified prefix.
+///
+/// Note that if the answer is bigger than uint64_t can hold, it will return
+/// the max value of uint64_t type.
+///
+/// Example: prefixesInRange(48, 64) returns 65536, as there are /48 pool
+/// can be split into 65536 prefixes, each /64 large.
+///
+/// @param pool_len length of the pool in bits
+/// @param delegated_len length of the prefixes to be delegated from the pool
+/// @return number of prefixes in range
+uint64_t prefixesInRange(const uint8_t pool_len, const uint8_t delegated_len);
 };
 };
 };
 };
 
 

+ 13 - 54
src/lib/dhcpsrv/alloc_engine.cc

@@ -67,34 +67,6 @@ AllocEngine::IterativeAllocator::IterativeAllocator(Lease::Type lease_type)
 }
 }
 
 
 isc::asiolink::IOAddress
 isc::asiolink::IOAddress
-AllocEngine::IterativeAllocator::increaseAddress(const isc::asiolink::IOAddress& addr) {
-    // Get a buffer holding an address.
-    const std::vector<uint8_t>& vec = addr.toBytes();
-    // Get the address length.
-    const int len = vec.size();
-
-    // Since the same array will be used to hold the IPv4 and IPv6
-    // address we have to make sure that the size of the array
-    // we allocate will work for both types of address.
-    BOOST_STATIC_ASSERT(V4ADDRESS_LEN <= V6ADDRESS_LEN);
-    uint8_t packed[V6ADDRESS_LEN];
-
-    // Copy the address. It can be either V4 or V6.
-    std::memcpy(packed, &vec[0], len);
-
-    // Start increasing the least significant byte
-    for (int i = len - 1; i >= 0; --i) {
-        ++packed[i];
-        // if we haven't overflowed (0xff -> 0x0), than we are done
-        if (packed[i] != 0) {
-            break;
-        }
-    }
-
-    return (IOAddress::fromBytes(addr.getFamily(), packed));
-}
-
-isc::asiolink::IOAddress
 AllocEngine::IterativeAllocator::increasePrefix(const isc::asiolink::IOAddress& prefix,
 AllocEngine::IterativeAllocator::increasePrefix(const isc::asiolink::IOAddress& prefix,
                                                 const uint8_t prefix_len) {
                                                 const uint8_t prefix_len) {
     if (!prefix.isV6()) {
     if (!prefix.isV6()) {
@@ -193,7 +165,7 @@ AllocEngine::IterativeAllocator::pickAddress(const SubnetPtr& subnet,
 
 
     IOAddress next("::");
     IOAddress next("::");
     if (!prefix) {
     if (!prefix) {
-        next = increaseAddress(last); // basically addr++
+        next = IOAddress::increase(last); // basically addr++
     } else {
     } else {
         Pool6Ptr pool6 = boost::dynamic_pointer_cast<Pool6>(*it);
         Pool6Ptr pool6 = boost::dynamic_pointer_cast<Pool6>(*it);
         if (!pool6) {
         if (!pool6) {
@@ -566,21 +538,12 @@ AllocEngine::allocateUnreservedLeases6(ClientContext6& ctx) {
     // - we find an address for which the lease has expired
     // - we find an address for which the lease has expired
     // - we exhaust number of tries
     // - we exhaust number of tries
     //
     //
-    // @todo: Current code does not handle pool exhaustion well. It will be
-    // improved. Current problems:
-    // 1. with attempts set to too large value (e.g. 1000) and a small pool (e.g.
-    // 10 addresses), we will iterate over it 100 times before giving up
-    // 2. attempts 0 mean unlimited (this is really UINT_MAX, not infinite)
-    // 3. the whole concept of infinite attempts is just asking for infinite loop
-    // We may consider some form or reference counting (this pool has X addresses
-    // left), but this has one major problem. We exactly control allocation
-    // moment, but we currently do not control expiration time at all
-
-    // Initialize the maximum number of attempts to pick and allocate an
-    // address. The value of 0 means "infinite", which is maximum uint32_t
-    // value.
-    uint32_t max_attempts = (attempts_ == 0) ?
-        std::numeric_limits<uint32_t>::max() : attempts_;
+    /// @todo: We used to use hardcoded number of attempts (100). Now we dynamically
+    /// calculate the number of possible leases in all pools in this subnet and
+    /// try that number of times at most. It would be useful to that value if
+    /// attempts_, specified by the user could override that value (and keep
+    /// dynamic if they're set to 0).
+    uint32_t max_attempts = ctx.subnet_->getPoolCapacity(ctx.type_);
     for (uint32_t i = 0; i < max_attempts; ++i)
     for (uint32_t i = 0; i < max_attempts; ++i)
     {
     {
         IOAddress candidate = allocator->pickAddress(ctx.subnet_, ctx.duid_, hint);
         IOAddress candidate = allocator->pickAddress(ctx.subnet_, ctx.duid_, hint);
@@ -1039,16 +1002,12 @@ AllocEngine::allocateLease4(const SubnetPtr& subnet, const ClientIdPtr& clientid
         // - we find an address for which the lease has expired
         // - we find an address for which the lease has expired
         // - we exhaust the number of tries
         // - we exhaust the number of tries
         //
         //
-        /// @todo: Current code does not handle pool exhaustion well. It will be
-        /// improved. Current problems:
-        /// 1. with attempts set to too large value (e.g. 1000) and a small pool (e.g.
-        /// 10 addresses), we will iterate over it 100 times before giving up
-        /// 2. attempts 0 mean unlimited (this is really UINT_MAX, not infinite)
-        /// 3. the whole concept of infinite attempts is just asking for infinite loop
-        /// We may consider some form or reference counting (this pool has X addresses
-        /// left), but this has one major problem. We exactly control allocation
-        /// moment, but we currently do not control expiration time at all
-        unsigned int i = attempts_;
+        /// @todo: We used to use hardcoded number of attempts (100). Now we dynamically
+        /// calculate the number of possible leases in all pools in this subnet and
+        /// try that number of times at most. It would be useful to that value if
+        /// attempts_, specified by the user could override that value (and keep
+        /// dynamic if they're set to 0).
+        uint64_t i = subnet->getPoolCapacity(Lease::TYPE_V4);
         do {
         do {
             // Decrease the number of remaining attempts here so as we guarantee
             // Decrease the number of remaining attempts here so as we guarantee
             // that it is decreased when the code below uses "continue".
             // that it is decreased when the code below uses "continue".

+ 0 - 10
src/lib/dhcpsrv/alloc_engine.h

@@ -137,16 +137,6 @@ protected:
                         const isc::asiolink::IOAddress& hint);
                         const isc::asiolink::IOAddress& hint);
     protected:
     protected:
 
 
-        /// @brief Returns an address increased by one
-        ///
-        /// This method works for both IPv4 and IPv6 addresses. For example,
-        /// increase 192.0.2.255 will become 192.0.3.0.
-        ///
-        /// @param addr address to be increased
-        /// @return address increased by one
-        static isc::asiolink::IOAddress
-        increaseAddress(const isc::asiolink::IOAddress& addr);
-
         /// @brief Returns the next prefix
         /// @brief Returns the next prefix
         ///
         ///
         /// This method works for IPv6 addresses only. It increases
         /// This method works for IPv6 addresses only. It increases

+ 26 - 5
src/lib/dhcpsrv/pool.cc

@@ -1,4 +1,4 @@
-// Copyright (C) 2012-2013 Internet Systems Consortium, Inc. ("ISC")
+// Copyright (C) 2012-2015 Internet Systems Consortium, Inc. ("ISC")
 //
 //
 // Permission to use, copy, modify, and/or distribute this software for any
 // Permission to use, copy, modify, and/or distribute this software for any
 // purpose with or without fee is hereby granted, provided that the above
 // purpose with or without fee is hereby granted, provided that the above
@@ -24,7 +24,8 @@ namespace dhcp {
 
 
 Pool::Pool(Lease::Type type, const isc::asiolink::IOAddress& first,
 Pool::Pool(Lease::Type type, const isc::asiolink::IOAddress& first,
            const isc::asiolink::IOAddress& last)
            const isc::asiolink::IOAddress& last)
-    :id_(getNextID()), first_(first), last_(last), type_(type) {
+    :id_(getNextID()), first_(first), last_(last), type_(type),
+     capacity_(0) {
 }
 }
 
 
 bool Pool::inRange(const isc::asiolink::IOAddress& addr) const {
 bool Pool::inRange(const isc::asiolink::IOAddress& addr) const {
@@ -50,6 +51,12 @@ Pool4::Pool4(const isc::asiolink::IOAddress& first,
     if (last < first) {
     if (last < first) {
         isc_throw(BadValue, "Upper boundary is smaller than lower boundary.");
         isc_throw(BadValue, "Upper boundary is smaller than lower boundary.");
     }
     }
+
+    // This is IPv4 pool, which only has one type. We can calculate
+    // the number of theoretically possible leases in it. As there's 2^32
+    // possible IPv4 addresses, we'll be able to accurately store that
+    // info.
+    capacity_ = addrsInRange(first, last);
 }
 }
 
 
 Pool4::Pool4( const isc::asiolink::IOAddress& prefix, uint8_t prefix_len)
 Pool4::Pool4( const isc::asiolink::IOAddress& prefix, uint8_t prefix_len)
@@ -67,8 +74,13 @@ Pool4::Pool4( const isc::asiolink::IOAddress& prefix, uint8_t prefix_len)
 
 
     // Let's now calculate the last address in defined pool
     // Let's now calculate the last address in defined pool
     last_ = lastAddrInPrefix(prefix, prefix_len);
     last_ = lastAddrInPrefix(prefix, prefix_len);
-}
 
 
+    // This is IPv4 pool, which only has one type. We can calculate
+    // the number of theoretically possible leases in it. As there's 2^32
+    // possible IPv4 addresses, we'll be able to accurately store that
+    // info.
+    capacity_ = addrsInRange(prefix, last_);
+}
 
 
 Pool6::Pool6(Lease::Type type, const isc::asiolink::IOAddress& first,
 Pool6::Pool6(Lease::Type type, const isc::asiolink::IOAddress& first,
              const isc::asiolink::IOAddress& last)
              const isc::asiolink::IOAddress& last)
@@ -105,6 +117,11 @@ Pool6::Pool6(Lease::Type type, const isc::asiolink::IOAddress& first,
         isc_throw(BadValue, "Invalid Pool6 type specified:"
         isc_throw(BadValue, "Invalid Pool6 type specified:"
                   << static_cast<int>(type));
                   << static_cast<int>(type));
     }
     }
+
+    // Let's calculate the theoretical number of leases in this pool.
+    // If the pool is extremely large (i.e. contains more than 2^64 addresses,
+    // we'll just cap it at max value of uint64_t).
+    capacity_ = addrsInRange(first, last);
 }
 }
 
 
 Pool6::Pool6(Lease::Type type, const isc::asiolink::IOAddress& prefix,
 Pool6::Pool6(Lease::Type type, const isc::asiolink::IOAddress& prefix,
@@ -123,7 +140,7 @@ Pool6::Pool6(Lease::Type type, const isc::asiolink::IOAddress& prefix,
 
 
     if (prefix_len > delegated_len) {
     if (prefix_len > delegated_len) {
         isc_throw(BadValue, "Delegated length (" << static_cast<int>(delegated_len)
         isc_throw(BadValue, "Delegated length (" << static_cast<int>(delegated_len)
-                  << ") must be longer than prefix length ("
+                  << ") must be longer than or equal to prefix length ("
                   << static_cast<int>(prefix_len) << ")");
                   << static_cast<int>(prefix_len) << ")");
     }
     }
 
 
@@ -138,6 +155,11 @@ Pool6::Pool6(Lease::Type type, const isc::asiolink::IOAddress& prefix,
 
 
     // Let's now calculate the last address in defined pool
     // Let's now calculate the last address in defined pool
     last_ = lastAddrInPrefix(prefix, prefix_len);
     last_ = lastAddrInPrefix(prefix, prefix_len);
+
+    // Let's calculate the theoretical number of leases in this pool.
+    // For addresses, we could use addrsInRange(prefix, last_), but it's
+    // much faster to do calculations on prefix lengths.
+    capacity_ = prefixesInRange(prefix_len, delegated_len);
 }
 }
 
 
 std::string
 std::string
@@ -149,6 +171,5 @@ Pool6::toText() const {
     return (tmp.str());
     return (tmp.str());
 }
 }
 
 
-
 }; // end of isc::dhcp namespace
 }; // end of isc::dhcp namespace
 }; // end of isc namespace
 }; // end of isc namespace

+ 17 - 1
src/lib/dhcpsrv/pool.h

@@ -1,4 +1,4 @@
-// Copyright (C) 2012-2013 Internet Systems Consortium, Inc. ("ISC")
+// Copyright (C) 2012-2013,2015 Internet Systems Consortium, Inc. ("ISC")
 //
 //
 // Permission to use, copy, modify, and/or distribute this software for any
 // Permission to use, copy, modify, and/or distribute this software for any
 // purpose with or without fee is hereby granted, provided that the above
 // purpose with or without fee is hereby granted, provided that the above
@@ -79,6 +79,14 @@ public:
     virtual ~Pool() {
     virtual ~Pool() {
     }
     }
 
 
+    /// @brief Returns the number of all leases in this pool.
+    ///
+    /// Note that this is the upper bound, assuming that no leases are used
+    /// and there are no host reservations. This is just a theoretical calculation.
+    /// @return number of possible leases in this pool
+    uint64_t getCapacity() const {
+        return (capacity_);
+    }
 protected:
 protected:
 
 
     /// @brief protected constructor
     /// @brief protected constructor
@@ -120,6 +128,14 @@ protected:
 
 
     /// @brief defines a lease type that will be served from this pool
     /// @brief defines a lease type that will be served from this pool
     Lease::Type type_;
     Lease::Type type_;
+
+    /// @brief Stores number of possible leases.
+    ///
+    /// This could be calculated on the fly, but the calculations are somewhat
+    /// involved, so it is more efficient to calculate it once and just store
+    /// the result. Note that for very large pools, the number is capped at
+    /// max value of uint64_t.
+    uint64_t capacity_;
 };
 };
 
 
 /// @brief Pool information for IPv4 addresses
 /// @brief Pool information for IPv4 addresses

+ 34 - 0
src/lib/dhcpsrv/subnet.cc

@@ -132,6 +132,40 @@ Subnet::toText() const {
     return (tmp.str());
     return (tmp.str());
 }
 }
 
 
+uint64_t
+Subnet::getPoolCapacity(Lease::Type type) const {
+    switch (type) {
+    case Lease::TYPE_V4:
+    case Lease::TYPE_NA:
+        return sumPoolCapacity(pools_);
+    case Lease::TYPE_TA:
+        return sumPoolCapacity(pools_ta_);
+    case Lease::TYPE_PD:
+        return sumPoolCapacity(pools_pd_);
+    default:
+        isc_throw(BadValue, "Unsupported pool type: "
+                  << static_cast<int>(type));
+    }
+}
+
+uint64_t
+Subnet::sumPoolCapacity(const PoolCollection& pools) const {
+    uint64_t sum = 0;
+    for (PoolCollection::const_iterator p = pools.begin(); p != pools.end(); ++p) {
+        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 {
 void Subnet4::checkType(Lease::Type type) const {
     if (type != Lease::TYPE_V4) {
     if (type != Lease::TYPE_V4) {
         isc_throw(BadValue, "Only TYPE_V4 is allowed for Subnet4");
         isc_throw(BadValue, "Only TYPE_V4 is allowed for Subnet4");

+ 10 - 0
src/lib/dhcpsrv/subnet.h

@@ -233,6 +233,11 @@ public:
     /// @return a collection of all pools
     /// @return a collection of all pools
     const PoolCollection& getPools(Lease::Type type) const;
     const PoolCollection& getPools(Lease::Type type) const;
 
 
+    /// @brief Returns the number of possible leases for specified lease type
+    ///
+    /// @param type type of the lease
+    uint64_t getPoolCapacity(Lease::Type type) const;
+
     /// @brief Sets name of the network interface for directly attached networks
     /// @brief Sets name of the network interface for directly attached networks
     ///
     ///
     /// @param iface_name name of the interface
     /// @param iface_name name of the interface
@@ -410,6 +415,11 @@ protected:
     /// @throw BadValue if invalid value is used
     /// @throw BadValue if invalid value is used
     virtual void checkType(Lease::Type type) const = 0;
     virtual void checkType(Lease::Type type) const = 0;
 
 
+    /// @brief returns a sum of possible leases in all pools
+    /// @param pools list of pools
+    /// @return sum of possible leases
+    uint64_t sumPoolCapacity(const PoolCollection& pools) const;
+
     /// @brief subnet-id
     /// @brief subnet-id
     ///
     ///
     /// Subnet-id is a unique value that can be used to find or identify
     /// Subnet-id is a unique value that can be used to find or identify

+ 79 - 2
src/lib/dhcpsrv/tests/addr_utilities_unittest.cc

@@ -1,5 +1,4 @@
-
-// Copyright (C) 2012  Internet Systems Consortium, Inc. ("ISC")
+// Copyright (C) 2012-2013,2015  Internet Systems Consortium, Inc. ("ISC")
 //
 //
 // Permission to use, copy, modify, and/or distribute this software for any
 // Permission to use, copy, modify, and/or distribute this software for any
 // purpose with or without fee is hereby granted, provided that the above
 // purpose with or without fee is hereby granted, provided that the above
@@ -200,4 +199,82 @@ TEST(AddrUtilitiesTest, getNetmask4) {
     EXPECT_THROW(getNetmask4(33), isc::BadValue);
     EXPECT_THROW(getNetmask4(33), isc::BadValue);
 }
 }
 
 
+// Checks if the calculation for IPv4 addresses in range are correct.
+TEST(AddrUtilitiesTest, addrsInRange4) {
+
+    // Let's start with something simple
+    EXPECT_EQ(1, addrsInRange(IOAddress("192.0.2.0"), IOAddress("192.0.2.0")));
+    EXPECT_EQ(10, addrsInRange(IOAddress("192.0.2.10"), IOAddress("192.0.2.19")));
+    EXPECT_EQ(256, addrsInRange(IOAddress("192.0.2.0"), IOAddress("192.0.2.255")));
+    EXPECT_EQ(65536, addrsInRange(IOAddress("192.0.0.0"), IOAddress("192.0.255.255")));
+    EXPECT_EQ(16777216, addrsInRange(IOAddress("10.0.0.0"), IOAddress("10.255.255.255")));
+
+    // Let's check if the network boundaries are crossed correctly.
+    EXPECT_EQ(3, addrsInRange(IOAddress("10.0.0.255"), IOAddress("10.0.1.1")));
+
+    // Let's go a bit overboard with this! How many addresses are there in
+    // IPv4 address space? That's a slightly tricky question, as the answer
+    // cannot be stored in uint32_t.
+    EXPECT_EQ(uint64_t(std::numeric_limits<uint32_t>::max()) + 1,
+              addrsInRange(IOAddress("0.0.0.0"), IOAddress("255.255.255.255")));
+
+    // The upper bound cannot be smaller than the lower bound.
+    EXPECT_THROW(addrsInRange(IOAddress("192.0.2.5"), IOAddress("192.0.2.4")),
+                 isc::BadValue);
+}
+
+// Checks if the calculation for IPv6 addresses in range are correct.
+TEST(AddrUtilitiesTest, addrsInRange6) {
+
+    // Let's start with something simple
+    EXPECT_EQ(1, addrsInRange(IOAddress("::"), IOAddress("::")));
+    EXPECT_EQ(16, addrsInRange(IOAddress("fe80::1"), IOAddress("fe80::10")));
+    EXPECT_EQ(65536, addrsInRange(IOAddress("fe80::"), IOAddress("fe80::ffff")));
+    EXPECT_EQ(uint64_t(std::numeric_limits<uint32_t>::max()) + 1,
+              addrsInRange(IOAddress("fe80::"), IOAddress("fe80::ffff:ffff")));
+
+    // There's 2^80 addresses between those. Due to uint64_t limits, this method is
+    // capped at 2^64 -1.
+    EXPECT_EQ(std::numeric_limits<uint64_t>::max(),
+              addrsInRange(IOAddress("2001:db8:1::"), IOAddress("2001:db8:2::")));
+
+    // Let's check if the network boundaries are crossed correctly.
+    EXPECT_EQ(3, addrsInRange(IOAddress("2001:db8::ffff"), IOAddress("2001:db8::1:1")));
+
+    // Let's go a bit overboard with this! How many addresses are there in
+    // IPv6 address space? That's a really tricky question, as the answer
+    // wouldn't fit even in uint128_t (if we had it). This method is capped
+    // at max value of uint64_t.
+    EXPECT_EQ(std::numeric_limits<uint64_t>::max(), addrsInRange(IOAddress("::"),
+              IOAddress("ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff")));
+
+    EXPECT_THROW(addrsInRange(IOAddress("fe80::5"), IOAddress("fe80::4")),
+                 isc::BadValue);
+}
+
+// Checks if prefixInRange returns valid number of prefixes in specified range.
+TEST(AddrUtilitiesTest, prefixesInRange) {
+
+    // How many /64 prefixes are in /64 pool?
+    EXPECT_EQ(1, prefixesInRange(64, 64));
+
+    // How many /63 prefixes are in /64 pool?
+    EXPECT_EQ(2, prefixesInRange(63, 64));
+
+    // How many /64 prefixes are in /48 pool?
+    EXPECT_EQ(65536, prefixesInRange(48, 64));
+
+    // How many /127 prefixes are in /64 pool?
+    EXPECT_EQ(uint64_t(9223372036854775808u), prefixesInRange(64, 127));
+
+    // How many /128 prefixes are in /64 pool?
+    EXPECT_EQ(std::numeric_limits<uint64_t>::max(),
+              prefixesInRange(64, 128));
+
+    // Let's go overboard again. How many IPv6 addresses are there?
+    EXPECT_EQ(std::numeric_limits<uint64_t>::max(),
+              prefixesInRange(0, 128));
+
+}
+
 }; // end of anonymous namespace
 }; // end of anonymous namespace

+ 5 - 5
src/lib/dhcpsrv/tests/alloc_engine_utils.h

@@ -67,7 +67,6 @@ public:
             :IterativeAllocator(type) {
             :IterativeAllocator(type) {
         }
         }
 
 
-        using AllocEngine::IterativeAllocator::increaseAddress;
         using AllocEngine::IterativeAllocator::increasePrefix;
         using AllocEngine::IterativeAllocator::increasePrefix;
     };
     };
 };
 };
@@ -173,16 +172,17 @@ public:
 
 
     /// @brief Checks if specified address is increased properly
     /// @brief Checks if specified address is increased properly
     ///
     ///
-    /// Method uses gtest macros to mark check failure.
+    /// Method uses gtest macros to mark check failure. This is a proxy
+    /// method, since increaseAddress was moved to IOAddress class.
     ///
     ///
     /// @param alloc IterativeAllocator that is tested
     /// @param alloc IterativeAllocator that is tested
     /// @param input address to be increased
     /// @param input address to be increased
     /// @param exp_output expected address after increase
     /// @param exp_output expected address after increase
     void
     void
-    checkAddrIncrease(NakedAllocEngine::NakedIterativeAllocator& alloc,
+    checkAddrIncrease(NakedAllocEngine::NakedIterativeAllocator&,
                       std::string input, std::string exp_output) {
                       std::string input, std::string exp_output) {
-        EXPECT_EQ(exp_output, alloc.increaseAddress(asiolink::IOAddress(input))
-                  .toText());
+        EXPECT_EQ(exp_output, asiolink::IOAddress::increase(
+                      asiolink::IOAddress(input)).toText());
     }
     }
 
 
     /// @brief Checks if increasePrefix() works as expected
     /// @brief Checks if increasePrefix() works as expected

+ 27 - 1
src/lib/dhcpsrv/tests/pool_unittest.cc

@@ -1,4 +1,4 @@
-// Copyright (C) 2012 Internet Systems Consortium, Inc. ("ISC")
+// Copyright (C) 2012-2013,2015 Internet Systems Consortium, Inc. ("ISC")
 //
 //
 // Permission to use, copy, modify, and/or distribute this software for any
 // Permission to use, copy, modify, and/or distribute this software for any
 // purpose with or without fee is hereby granted, provided that the above
 // purpose with or without fee is hereby granted, provided that the above
@@ -81,6 +81,21 @@ TEST(Pool4Test, in_range) {
    EXPECT_FALSE(pool1.inRange(IOAddress("0.0.0.0")));
    EXPECT_FALSE(pool1.inRange(IOAddress("0.0.0.0")));
 }
 }
 
 
+// Checks if the number of possible leases in range is reported correctly.
+TEST(Pool4Test, leasesCount) {
+    Pool4 pool1(IOAddress("192.0.2.10"), IOAddress("192.0.2.20"));
+    EXPECT_EQ(11, pool1.getCapacity());
+
+    Pool4 pool2(IOAddress("192.0.2.0"), IOAddress("192.0.2.255"));
+    EXPECT_EQ(256, pool2.getCapacity());
+
+    Pool4 pool3(IOAddress("192.168.0.0"), IOAddress("192.168.255.255"));
+    EXPECT_EQ(65536, pool3.getCapacity());
+
+    Pool4 pool4(IOAddress("10.0.0.0"), IOAddress("10.255.255.255"));
+    EXPECT_EQ(16777216, pool4.getCapacity());
+}
+
 // This test creates 100 pools and verifies that their IDs are unique.
 // This test creates 100 pools and verifies that their IDs are unique.
 TEST(Pool4Test, unique_id) {
 TEST(Pool4Test, unique_id) {
 
 
@@ -263,4 +278,15 @@ TEST(Poo6Test,toText) {
               pool2.toText());
               pool2.toText());
 }
 }
 
 
+// Checks if the number of possible leases in range is reported correctly.
+TEST(Pool6Test, leasesCount) {
+    Pool6 pool1(Lease::TYPE_NA, IOAddress("2001:db8::1"),
+                IOAddress("2001:db8::2"));
+    EXPECT_EQ(2, pool1.getCapacity());
+
+    Pool6 pool2(Lease::TYPE_PD, IOAddress("2001:db8:1::"), 96, 112);
+    EXPECT_EQ(65536, pool2.getCapacity());
+}
+
+
 }; // end of anonymous namespace
 }; // end of anonymous namespace

+ 105 - 0
src/lib/dhcpsrv/tests/subnet_unittest.cc

@@ -22,6 +22,7 @@
 
 
 #include <boost/scoped_ptr.hpp>
 #include <boost/scoped_ptr.hpp>
 #include <gtest/gtest.h>
 #include <gtest/gtest.h>
+#include <limits>
 
 
 // don't import the entire boost namespace.  It will unexpectedly hide uint8_t
 // don't import the entire boost namespace.  It will unexpectedly hide uint8_t
 // for some systems.
 // for some systems.
@@ -147,6 +148,32 @@ TEST(Subnet4Test, Pool4InSubnet4) {
 
 
 }
 }
 
 
+// Check if it's possible to get specified number of possible leases for
+// an IPv4 subnet.
+TEST(Subnet4Test, getCapacity) {
+
+    // There's one /24 pool.
+    Subnet4Ptr subnet(new Subnet4(IOAddress("192.1.2.0"), 24, 1, 2, 3));
+
+    // There are no pools defined, so the total number of available addrs is 0.
+    EXPECT_EQ(0, subnet->getPoolCapacity(Lease::TYPE_V4));
+
+    // Let's add a /25 pool. That's 128 addresses.
+    PoolPtr pool1(new Pool4(IOAddress("192.1.2.0"), 25));
+    subnet->addPool(pool1);
+    EXPECT_EQ(128, subnet->getPoolCapacity(Lease::TYPE_V4));
+
+    // Let's add another /26 pool. That's extra 64 addresses.
+    PoolPtr pool2(new Pool4(IOAddress("192.1.2.128"), 26));
+    subnet->addPool(pool2);
+    EXPECT_EQ(192, subnet->getPoolCapacity(Lease::TYPE_V4));
+
+    // Let's add a third pool /30. This one has 4 addresses.
+    PoolPtr pool3(new Pool4(IOAddress("192.1.2.192"), 30));
+    subnet->addPool(pool3);
+    EXPECT_EQ(196, subnet->getPoolCapacity(Lease::TYPE_V4));
+}
+
 TEST(Subnet4Test, Subnet4_Pool4_checks) {
 TEST(Subnet4Test, Subnet4_Pool4_checks) {
 
 
     Subnet4Ptr subnet(new Subnet4(IOAddress("192.0.2.0"), 8, 1, 2, 3));
     Subnet4Ptr subnet(new Subnet4(IOAddress("192.0.2.0"), 8, 1, 2, 3));
@@ -434,6 +461,84 @@ TEST(Subnet6Test, relay) {
     EXPECT_EQ("2001:ffff::1", subnet.getRelayInfo().addr_.toText());
     EXPECT_EQ("2001:ffff::1", subnet.getRelayInfo().addr_.toText());
 }
 }
 
 
+// Test checks whether the number of addresses available in the pools are
+// calculated properly.
+TEST(Subnet6Test, Pool6getCapacity) {
+
+    Subnet6Ptr subnet(new Subnet6(IOAddress("2001:db8:1::"), 56, 1, 2, 3, 4));
+
+    // There's 2^16 = 65536 addresses in this one.
+    PoolPtr pool1(new Pool6(Lease::TYPE_NA, IOAddress("2001:db8:1:1::"), 112));
+
+    // There's 2^32 = 4294967296 addresses in each of those.
+    PoolPtr pool2(new Pool6(Lease::TYPE_NA, IOAddress("2001:db8:1:2::"), 96));
+    PoolPtr pool3(new Pool6(Lease::TYPE_NA, IOAddress("2001:db8:1:3::"), 96));
+
+    EXPECT_EQ(0, subnet->getPoolCapacity(Lease::TYPE_NA));
+    EXPECT_EQ(0, subnet->getPoolCapacity(Lease::TYPE_TA));
+    EXPECT_EQ(0, subnet->getPoolCapacity(Lease::TYPE_PD));
+
+    subnet->addPool(pool1);
+    EXPECT_EQ(65536, subnet->getPoolCapacity(Lease::TYPE_NA));
+
+    subnet->addPool(pool2);
+    EXPECT_EQ(uint64_t(4294967296 + 65536), subnet->getPoolCapacity(Lease::TYPE_NA));
+
+    subnet->addPool(pool3);
+    EXPECT_EQ(uint64_t(4294967296 + 4294967296 + 65536),
+              subnet->getPoolCapacity(Lease::TYPE_NA));
+
+    // This is 2^64 prefixes. We're overflown uint64_t.
+    PoolPtr pool4(new Pool6(Lease::TYPE_NA, IOAddress("2001:db8:1:4::"), 64));
+    subnet->addPool(pool4);
+    EXPECT_EQ(std::numeric_limits<uint64_t>::max(),
+              subnet->getPoolCapacity(Lease::TYPE_NA));
+
+    PoolPtr pool5(new Pool6(Lease::TYPE_NA, IOAddress("2001:db8:1:5::"), 64));
+    subnet->addPool(pool5);
+    EXPECT_EQ(std::numeric_limits<uint64_t>::max(),
+              subnet->getPoolCapacity(Lease::TYPE_NA));
+}
+
+// Test checks whether the number of prefixes available in the pools are
+// calculated properly.
+TEST(Subnet6Test, Pool6PdgetPoolCapacity) {
+
+    Subnet6Ptr subnet(new Subnet6(IOAddress("2001:db8::"), 32, 1, 2, 3, 4));
+
+    // There's 2^16 = 65536 addresses in this one.
+    PoolPtr pool1(new Pool6(Lease::TYPE_PD, IOAddress("2001:db8:1::"), 48, 64));
+
+    // There's 2^32 = 4294967296 addresses in each of those.
+    PoolPtr pool2(new Pool6(Lease::TYPE_PD, IOAddress("2001:db8:2::"), 48, 80));
+    PoolPtr pool3(new Pool6(Lease::TYPE_PD, IOAddress("2001:db8:3::"), 48, 80));
+
+    EXPECT_EQ(0, subnet->getPoolCapacity(Lease::TYPE_NA));
+    EXPECT_EQ(0, subnet->getPoolCapacity(Lease::TYPE_TA));
+    EXPECT_EQ(0, subnet->getPoolCapacity(Lease::TYPE_PD));
+
+    subnet->addPool(pool1);
+    EXPECT_EQ(65536, subnet->getPoolCapacity(Lease::TYPE_PD));
+
+    subnet->addPool(pool2);
+    EXPECT_EQ(uint64_t(4294967296 + 65536), subnet->getPoolCapacity(Lease::TYPE_PD));
+
+    subnet->addPool(pool3);
+    EXPECT_EQ(uint64_t(4294967296 + 4294967296 + 65536),
+              subnet->getPoolCapacity(Lease::TYPE_PD));
+
+    // This is 2^64.
+    PoolPtr pool4(new Pool6(Lease::TYPE_PD, IOAddress("2001:db8:4::"), 48, 112));
+    subnet->addPool(pool4);
+    EXPECT_EQ(std::numeric_limits<uint64_t>::max(),
+              subnet->getPoolCapacity(Lease::TYPE_PD));
+
+    PoolPtr pool5(new Pool6(Lease::TYPE_PD, IOAddress("2001:db8:5::"), 48, 112));
+    subnet->addPool(pool5);
+    EXPECT_EQ(std::numeric_limits<uint64_t>::max(),
+              subnet->getPoolCapacity(Lease::TYPE_PD));
+}
+
 TEST(Subnet6Test, Pool6InSubnet6) {
 TEST(Subnet6Test, Pool6InSubnet6) {
 
 
     Subnet6Ptr subnet(new Subnet6(IOAddress("2001:db8:1::"), 56, 1, 2, 3, 4));
     Subnet6Ptr subnet(new Subnet6(IOAddress("2001:db8:1::"), 56, 1, 2, 3, 4));