BIND 10 master, updated. b1217e691027f310f47bb7b23b621d5b8656ea6c [master] Merge branch 'trac3150' (DHCP::CfgMgr support for PD)

BIND 10 source code commits bind10-changes at lists.isc.org
Tue Sep 17 15:40:33 UTC 2013


The branch, master has been updated
       via  b1217e691027f310f47bb7b23b621d5b8656ea6c (commit)
       via  e6f0e89162bac0adae3ce3141437a282d5183162 (commit)
       via  ecc58c5e3a9e10f06357fcffaf169568b507901f (commit)
       via  eeacc3ac637e400c5f1512fc434ab08523521e2e (commit)
       via  c37f3b910e63df55b46516943a7e2ffd51e14e5c (commit)
       via  22b8b335e30ab84bdbac60244d74553e5bf3b719 (commit)
       via  f6619e644f62317afb4259e2af7732caa9d10c25 (commit)
       via  154fbc8f1d2a8de45de50799228a52585726d823 (commit)
       via  48b5e32efb89d2c07b40c0d3e74200b56d984f75 (commit)
      from  7430591b4ae4c7052cab86ed17d0221db3b524a8 (commit)

Those revisions listed above that are new to this repository have
not appeared on any other notification email; so we list those
revisions in full, below.

- Log -----------------------------------------------------------------
commit b1217e691027f310f47bb7b23b621d5b8656ea6c
Merge: 7430591 e6f0e89
Author: Tomek Mrugalski <tomasz at isc.org>
Date:   Tue Sep 17 17:40:16 2013 +0200

    [master] Merge branch 'trac3150' (DHCP::CfgMgr support for PD)
    
    Conflicts:
    	ChangeLog

-----------------------------------------------------------------------

Summary of changes:
 ChangeLog                                      |    7 +-
 src/bin/dhcp4/dhcp4_srv.cc                     |    3 +-
 src/bin/dhcp6/config_parser.cc                 |   12 +-
 src/lib/dhcpsrv/alloc_engine.cc                |   36 +++--
 src/lib/dhcpsrv/alloc_engine.h                 |   23 ++-
 src/lib/dhcpsrv/pool.cc                        |   46 ++++--
 src/lib/dhcpsrv/pool.h                         |  113 ++++++++++----
 src/lib/dhcpsrv/subnet.cc                      |  173 +++++++++++++++++----
 src/lib/dhcpsrv/subnet.h                       |   86 ++++++++---
 src/lib/dhcpsrv/tests/alloc_engine_unittest.cc |   53 ++++---
 src/lib/dhcpsrv/tests/pool_unittest.cc         |   63 ++++++++
 src/lib/dhcpsrv/tests/subnet_unittest.cc       |  194 ++++++++++++++++++++++--
 12 files changed, 653 insertions(+), 156 deletions(-)

-----------------------------------------------------------------------
diff --git a/ChangeLog b/ChangeLog
index a75d547..2d6a086 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,8 @@
+677.	[func]		tomek
+	libdhcpsrv: CfgMgr is now able to store IA, TA and PD pools in
+	Subnet6 structures.
+	(Trac #3150, git e6f0e89162bac0adae3ce3141437a282d5183162)
+
 676.	[bug]		muks
 	We now also allow the short name ("hmac-md5"), along with the long
 	name ("hmac-md5.sig-alg.reg.int") that was allowed before for
@@ -21,7 +26,7 @@
 	for IAPREFIX (Option6_IAPrefix) has been added.
 	(Trac #3145, git 3a844e85ecc3067ccd1c01841f4a61366cb278f4)
 
-672.	[func]	tmark
+672.	[func]		tmark
 	Added b10-dhcp-ddnsupdate transaction base class, NameChangeTransaction.
 	This class provides the common structure and methods to implement the state
 	models described in the DHCP_DDNS design, plus integration with DNSClient
diff --git a/src/bin/dhcp4/dhcp4_srv.cc b/src/bin/dhcp4/dhcp4_srv.cc
index bf4f76b..7d13631 100644
--- a/src/bin/dhcp4/dhcp4_srv.cc
+++ b/src/bin/dhcp4/dhcp4_srv.cc
@@ -135,7 +135,8 @@ Dhcpv4Srv::Dhcpv4Srv(uint16_t port, const char* dbconfig, const bool use_bcast,
             .arg(LeaseMgrFactory::instance().getName());
 
         // Instantiate allocation engine
-        alloc_engine_.reset(new AllocEngine(AllocEngine::ALLOC_ITERATIVE, 100));
+        alloc_engine_.reset(new AllocEngine(AllocEngine::ALLOC_ITERATIVE, 100,
+                                            false /* false = IPv4 */));
 
         // Register hook points
         hook_index_pkt4_receive_   = Hooks.hook_index_pkt4_receive_;
diff --git a/src/bin/dhcp6/config_parser.cc b/src/bin/dhcp6/config_parser.cc
index 63bda52..a5c179f 100644
--- a/src/bin/dhcp6/config_parser.cc
+++ b/src/bin/dhcp6/config_parser.cc
@@ -141,13 +141,13 @@ protected:
     ///
     /// @param addr is the IPv6 prefix of the pool.
     /// @param len is the prefix length.
-    /// @param ptype is the type of IPv6 pool (Pool6::Pool6Type). Note this is
-    /// passed in as an int32_t and cast to Pool6Type to accommodate a
+    /// @param ptype is the type of IPv6 pool (Pool::PoolType). Note this is
+    /// passed in as an int32_t and cast to PoolType to accommodate a
     /// polymorphic interface.
     /// @return returns a PoolPtr to the new Pool4 object.
     PoolPtr poolMaker (IOAddress &addr, uint32_t len, int32_t ptype)
     {
-        return (PoolPtr(new Pool6(static_cast<isc::dhcp::Pool6::Pool6Type>
+        return (PoolPtr(new Pool6(static_cast<isc::dhcp::Pool::PoolType>
                                   (ptype), addr, len)));
     }
 
@@ -155,13 +155,13 @@ protected:
     ///
     /// @param min is the first IPv6 address in the pool.
     /// @param max is the last IPv6 address in the pool.
-    /// @param ptype is the type of IPv6 pool (Pool6::Pool6Type). Note this is
-    /// passed in as an int32_t and cast to Pool6Type to accommodate a
+    /// @param ptype is the type of IPv6 pool (Pool::PoolType). Note this is
+    /// passed in as an int32_t and cast to PoolType to accommodate a
     /// polymorphic interface.
     /// @return returns a PoolPtr to the new Pool4 object.
     PoolPtr poolMaker (IOAddress &min, IOAddress &max, int32_t ptype)
     {
-        return (PoolPtr(new Pool6(static_cast<isc::dhcp::Pool6::Pool6Type>
+        return (PoolPtr(new Pool6(static_cast<isc::dhcp::Pool::PoolType>
                                   (ptype), min, max)));
     }
 };
diff --git a/src/lib/dhcpsrv/alloc_engine.cc b/src/lib/dhcpsrv/alloc_engine.cc
index 151da67..923f7cc 100644
--- a/src/lib/dhcpsrv/alloc_engine.cc
+++ b/src/lib/dhcpsrv/alloc_engine.cc
@@ -53,8 +53,8 @@ AllocEngineHooks Hooks;
 namespace isc {
 namespace dhcp {
 
-AllocEngine::IterativeAllocator::IterativeAllocator()
-    :Allocator() {
+AllocEngine::IterativeAllocator::IterativeAllocator(Pool::PoolType lease_type)
+    :Allocator(lease_type) {
 }
 
 isc::asiolink::IOAddress
@@ -94,9 +94,9 @@ AllocEngine::IterativeAllocator::pickAddress(const SubnetPtr& subnet,
     // 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();
+    IOAddress last = subnet->getLastAllocated(pool_type_);
 
-    const PoolCollection& pools = subnet->getPools();
+    const PoolCollection& pools = subnet->getPools(pool_type_);
 
     if (pools.empty()) {
         isc_throw(AllocFailed, "No pools defined in selected subnet");
@@ -117,7 +117,7 @@ AllocEngine::IterativeAllocator::pickAddress(const SubnetPtr& subnet,
     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);
+        subnet->setLastAllocated(pool_type_, next);
         return (next);
     }
 
@@ -126,7 +126,7 @@ AllocEngine::IterativeAllocator::pickAddress(const SubnetPtr& subnet,
     IOAddress next = increaseAddress(last); // basically addr++
     if ((*it)->inRange(next)) {
         // the next one is in the pool as well, so we haven't hit pool boundary yet
-        subnet->setLastAllocated(next);
+        subnet->setLastAllocated(pool_type_, next);
         return (next);
     }
 
@@ -136,18 +136,18 @@ AllocEngine::IterativeAllocator::pickAddress(const SubnetPtr& subnet,
         // Really out of luck today. That was the last pool. Let's rewind
         // to the beginning.
         next = pools[0]->getFirstAddress();
-        subnet->setLastAllocated(next);
+        subnet->setLastAllocated(pool_type_, next);
         return (next);
     }
 
     // there is a next pool, let's try first address from it
     next = (*it)->getFirstAddress();
-    subnet->setLastAllocated(next);
+    subnet->setLastAllocated(pool_type_, next);
     return (next);
 }
 
-AllocEngine::HashedAllocator::HashedAllocator()
-    :Allocator() {
+AllocEngine::HashedAllocator::HashedAllocator(Pool::PoolType lease_type)
+    :Allocator(lease_type) {
     isc_throw(NotImplemented, "Hashed allocator is not implemented");
 }
 
@@ -159,8 +159,8 @@ AllocEngine::HashedAllocator::pickAddress(const SubnetPtr&,
     isc_throw(NotImplemented, "Hashed allocator is not implemented");
 }
 
-AllocEngine::RandomAllocator::RandomAllocator()
-    :Allocator() {
+AllocEngine::RandomAllocator::RandomAllocator(Pool::PoolType lease_type)
+    :Allocator(lease_type) {
     isc_throw(NotImplemented, "Random allocator is not implemented");
 }
 
@@ -173,17 +173,21 @@ AllocEngine::RandomAllocator::pickAddress(const SubnetPtr&,
 }
 
 
-AllocEngine::AllocEngine(AllocType engine_type, unsigned int attempts)
+AllocEngine::AllocEngine(AllocType engine_type, unsigned int attempts,
+                         bool ipv6)
     :attempts_(attempts) {
+
+    Pool::PoolType pool_type = ipv6?Pool::TYPE_IA:Pool::TYPE_V4;
+
     switch (engine_type) {
     case ALLOC_ITERATIVE:
-        allocator_ = boost::shared_ptr<Allocator>(new IterativeAllocator());
+        allocator_.reset(new IterativeAllocator(pool_type));
         break;
     case ALLOC_HASHED:
-        allocator_ = boost::shared_ptr<Allocator>(new HashedAllocator());
+        allocator_.reset(new HashedAllocator(pool_type));
         break;
     case ALLOC_RANDOM:
-        allocator_ = boost::shared_ptr<Allocator>(new RandomAllocator());
+        allocator_.reset(new RandomAllocator(pool_type));
         break;
 
     default:
diff --git a/src/lib/dhcpsrv/alloc_engine.h b/src/lib/dhcpsrv/alloc_engine.h
index 0b563a5..507d769 100644
--- a/src/lib/dhcpsrv/alloc_engine.h
+++ b/src/lib/dhcpsrv/alloc_engine.h
@@ -77,10 +77,21 @@ protected:
         pickAddress(const SubnetPtr& subnet, const DuidPtr& duid,
                     const isc::asiolink::IOAddress& hint) = 0;
 
+        /// @brief Default constructor.
+        ///
+        /// Specifies which type of leases this allocator will assign
+        /// @param pool_type specifies pool type (addresses, temp. addr or prefixes)
+        Allocator(Pool::PoolType pool_type)
+            :pool_type_(pool_type) {
+        }
+
         /// @brief virtual destructor
         virtual ~Allocator() {
         }
     protected:
+
+        /// @brief defines lease type allocation
+        Pool::PoolType pool_type_;
     };
 
     /// @brief Address/prefix allocator that iterates over all addresses
@@ -95,7 +106,8 @@ protected:
         /// @brief default constructor
         ///
         /// Does not do anything
-        IterativeAllocator();
+        /// @param type - specifies allocation type
+        IterativeAllocator(Pool::PoolType type);
 
         /// @brief returns the next address from pools in a subnet
         ///
@@ -123,7 +135,8 @@ protected:
     public:
 
         /// @brief default constructor (does nothing)
-        HashedAllocator();
+        /// @param type - specifies allocation type
+        HashedAllocator(Pool::PoolType type);
 
         /// @brief returns an address based on hash calculated from client's DUID.
         ///
@@ -145,7 +158,8 @@ protected:
     public:
 
         /// @brief default constructor (does nothing)
-        RandomAllocator();
+        /// @param type - specifies allocation type
+        RandomAllocator(Pool::PoolType type);
 
         /// @brief returns an random address from pool of specified subnet
         ///
@@ -180,7 +194,8 @@ protected:
     /// @param engine_type selects allocation algorithm
     /// @param attempts number of attempts for each lease allocation before
     ///        we give up (0 means unlimited)
-    AllocEngine(AllocType engine_type, unsigned int attempts);
+    /// @param ipv6 specifies if the engine should work for IPv4 or IPv6
+    AllocEngine(AllocType engine_type, unsigned int attempts, bool ipv6 = true);
 
     /// @brief Returns IPv4 lease.
     ///
diff --git a/src/lib/dhcpsrv/pool.cc b/src/lib/dhcpsrv/pool.cc
index 7104c61..7c01da0 100644
--- a/src/lib/dhcpsrv/pool.cc
+++ b/src/lib/dhcpsrv/pool.cc
@@ -21,9 +21,9 @@ using namespace isc::asiolink;
 namespace isc {
 namespace dhcp {
 
-Pool::Pool(const isc::asiolink::IOAddress& first,
+Pool::Pool(PoolType type, const isc::asiolink::IOAddress& first,
            const isc::asiolink::IOAddress& last)
-    :id_(getNextID()), first_(first), last_(last) {
+    :id_(getNextID()), first_(first), last_(last), type_(type) {
 }
 
 bool Pool::inRange(const isc::asiolink::IOAddress& addr) const {
@@ -32,7 +32,7 @@ bool Pool::inRange(const isc::asiolink::IOAddress& addr) const {
 
 Pool4::Pool4(const isc::asiolink::IOAddress& first,
              const isc::asiolink::IOAddress& last)
-    :Pool(first, last) {
+:Pool(Pool::TYPE_V4, first, last) {
     // check if specified address boundaries are sane
     if (!first.isV4() || !last.isV4()) {
         isc_throw(BadValue, "Invalid Pool4 address boundaries: not IPv4");
@@ -43,9 +43,8 @@ Pool4::Pool4(const isc::asiolink::IOAddress& first,
     }
 }
 
-Pool4::Pool4(const isc::asiolink::IOAddress& prefix,
-             uint8_t prefix_len)
-    :Pool(prefix, IOAddress("0.0.0.0")) {
+Pool4::Pool4( const isc::asiolink::IOAddress& prefix, uint8_t prefix_len)
+:Pool(Pool::TYPE_V4, prefix, IOAddress("0.0.0.0")) {
 
     // check if the prefix is sane
     if (!prefix.isV4()) {
@@ -62,15 +61,21 @@ Pool4::Pool4(const isc::asiolink::IOAddress& prefix,
 }
 
 
-Pool6::Pool6(Pool6Type type, const isc::asiolink::IOAddress& first,
+Pool6::Pool6(PoolType type, const isc::asiolink::IOAddress& first,
              const isc::asiolink::IOAddress& last)
-    :Pool(first, last), type_(type) {
+    :Pool(type, first, last), prefix_len_(128) {
 
     // check if specified address boundaries are sane
     if (!first.isV6() || !last.isV6()) {
         isc_throw(BadValue, "Invalid Pool6 address boundaries: not IPv6");
     }
 
+    if ( (type != Pool::TYPE_IA) && (type != Pool::TYPE_TA) &&
+         (type != Pool::TYPE_PD)) {
+        isc_throw(BadValue, "Invalid Pool6 type: " << static_cast<int>(type)
+                  << ", must be TYPE_IA, TYPE_TA or TYPE_PD");
+    }
+
     if (last < first) {
         isc_throw(BadValue, "Upper boundary is smaller than lower boundary.");
         // This check is a bit strict. If we decide that it is too strict,
@@ -88,23 +93,34 @@ Pool6::Pool6(Pool6Type type, const isc::asiolink::IOAddress& first,
     // parameters are for IA and TA only. There is another dedicated
     // constructor for that (it uses prefix/length)
     if ((type != TYPE_IA) && (type != TYPE_TA)) {
-        isc_throw(BadValue, "Invalid Pool6 type specified");
+        isc_throw(BadValue, "Invalid Pool6 type specified:"
+                  << static_cast<int>(type));
     }
 }
 
-Pool6::Pool6(Pool6Type type, const isc::asiolink::IOAddress& prefix,
-             uint8_t prefix_len)
-    :Pool(prefix, IOAddress("::")),
-     type_(type) {
+Pool6::Pool6(PoolType type, const isc::asiolink::IOAddress& prefix,
+             uint8_t prefix_len, uint8_t delegated_len /* = 128 */)
+    :Pool(type, prefix, IOAddress("::")), prefix_len_(delegated_len) {
 
     // check if the prefix is sane
     if (!prefix.isV6()) {
         isc_throw(BadValue, "Invalid Pool6 address boundaries: not IPv6");
     }
 
-    // check if the prefix length is sane 
+    // check if the prefix length is sane
     if (prefix_len == 0 || prefix_len > 128) {
-        isc_throw(BadValue, "Invalid prefix length");
+        isc_throw(BadValue, "Invalid prefix length: " << prefix_len);
+    }
+
+    if (prefix_len > delegated_len) {
+        isc_throw(BadValue, "Delegated length (" << static_cast<int>(delegated_len)
+                  << ") must be longer than prefix length ("
+                  << static_cast<int>(prefix_len) << ")");
+    }
+
+    if ( ( (type == TYPE_IA) || (type == TYPE_TA)) && (delegated_len != 128)) {
+        isc_throw(BadValue, "For IA or TA pools, delegated prefix length must "
+                  << " be 128.");
     }
 
     /// @todo: We should probably implement checks against weird addresses
diff --git a/src/lib/dhcpsrv/pool.h b/src/lib/dhcpsrv/pool.h
index e0a6f3c..199fe0b 100644
--- a/src/lib/dhcpsrv/pool.h
+++ b/src/lib/dhcpsrv/pool.h
@@ -32,6 +32,25 @@ class Pool {
 
 public:
 
+    /// @brief specifies Pool type
+    ///
+    /// Currently there are 3 pool types defined in DHCPv6:
+    /// - Non-temporary addresses (conveyed in IA_NA)
+    /// - Temporary addresses (conveyed in IA_TA)
+    /// - Delegated Prefixes (conveyed in IA_PD)
+    ///
+    /// The fourth one (TYPE_V4) is used in DHCPv4 use cases when getPool()
+    /// code is shared between v4 and v6 code.
+    ///
+    /// There is a new one being worked on (IA_PA, see draft-ietf-dhc-host-gen-id), but
+    /// support for it is not planned for now.
+    typedef enum {
+        TYPE_IA,
+        TYPE_TA,
+        TYPE_PD,
+        TYPE_V4
+    }  PoolType;
+
     /// @brief returns Pool-id
     ///
     /// @return pool-id value
@@ -58,6 +77,20 @@ public:
     /// @return true, if the address is in pool
     bool inRange(const isc::asiolink::IOAddress& addr) const;
 
+    /// @brief Returns pool type (v4, v6 non-temporary, v6 temp, v6 prefix)
+    /// @return returns pool type
+    PoolType getType() const {
+        return (type_);
+    }
+
+    /// @brief virtual destructor
+    ///
+    /// We need Pool to be a polymorphic class, so we could dynamic cast
+    /// from PoolPtr to Pool6Ptr if we need to. A class becomes polymorphic,
+    /// when there is at least one virtual method.
+    virtual ~Pool() {
+    }
+
 protected:
 
     /// @brief protected constructor
@@ -65,7 +98,12 @@ protected:
     /// This constructor is protected to prevent anyone from instantiating
     /// Pool class directly. Instances of Pool4 and Pool6 should be created
     /// instead.
-    Pool(const isc::asiolink::IOAddress& first,
+    ///
+    /// @param type type of the pool
+    /// @param first first address of a range
+    /// @param last last address of a range
+    Pool(PoolType type,
+         const isc::asiolink::IOAddress& first,
          const isc::asiolink::IOAddress& last);
 
     /// @brief returns the next unique Pool-ID
@@ -91,6 +129,9 @@ protected:
     ///
     /// @todo: This field is currently not used.
     std::string comments_;
+
+    /// @brief defines a pool type
+    PoolType type_;
 };
 
 /// @brief Pool information for IPv4 addresses
@@ -117,9 +158,6 @@ public:
 /// @brief a pointer an IPv4 Pool
 typedef boost::shared_ptr<Pool4> Pool4Ptr;
 
-/// @brief a container for IPv4 Pools
-typedef std::vector<Pool4Ptr> Pool4Collection;
-
 /// @brief Pool information for IPv6 addresses and prefixes
 ///
 /// It holds information about pool6, i.e. a range of IPv6 address space that
@@ -127,55 +165,70 @@ typedef std::vector<Pool4Ptr> Pool4Collection;
 class Pool6 : public Pool {
 public:
 
-    /// @brief specifies Pool type
-    ///
-    /// Currently there are 3 pool types defined in DHCPv6:
-    /// - Non-temporary addresses (conveyed in IA_NA)
-    /// - Temporary addresses (conveyed in IA_TA)
-    /// - Delegated Prefixes (conveyed in IA_PD)
-    /// There is a new one being worked on (IA_PA, see draft-ietf-dhc-host-gen-id), but
-    /// support for it is not planned for now.
-    typedef enum {
-        TYPE_IA,
-        TYPE_TA,
-        TYPE_PD
-    }  Pool6Type;
-
     /// @brief the constructor for Pool6 "min-max" style definition
     ///
-    /// @param type type of the pool (IA, TA or PD)
+    /// @throw BadValue if PD is define (PD can be only prefix/len)
+    ///
+    /// @param type type of the pool (IA or TA)
     /// @param first the first address in a pool
     /// @param last the last address in a pool
-    Pool6(Pool6Type type, const isc::asiolink::IOAddress& first,
+    Pool6(PoolType type, const isc::asiolink::IOAddress& first,
           const isc::asiolink::IOAddress& last);
 
     /// @brief the constructor for Pool6 "prefix/len" style definition
     ///
+    /// For addressed, this is just a prefix/len definition. For prefixes,
+    /// there is one extra additional parameter delegated_len. It specifies
+    /// a size of delegated prefixes that the pool will be split into. For
+    /// example pool 2001:db8::/56, delegated_len=64 means that there is a
+    /// pool 2001:db8::/56. It will be split into 256 prefixes of length /64,
+    /// e.g. 2001:db8:0:1::/64, 2001:db8:0:2::/64 etc.
+    ///
+    /// Naming convention:
+    /// A smaller prefix length yields a shorter prefix which describes a larger
+    /// set of addresses. A larger length yields a longer prefix which describes
+    /// a smaller set of addresses.
+    ///
+    /// Obviously, prefix_len must define shorter or equal prefix length than
+    /// delegated_len, so prefix_len <= delegated_len. Note that it is slightly
+    /// confusing: bigger (larger) prefix actually has smaller prefix length,
+    /// e.g. /56 is a bigger prefix than /64, but has shorter (smaller) prefix
+    /// length.
+    ///
+    /// @throw BadValue if delegated_len is defined for non-PD types or
+    ///        when delegated_len < prefix_len
+    ///
     /// @param type type of the pool (IA, TA or PD)
     /// @param prefix specifies prefix of the pool
-    /// @param prefix_len specifies length of the prefix of the pool
-    Pool6(Pool6Type type, const isc::asiolink::IOAddress& prefix,
-          uint8_t prefix_len);
+    /// @param prefix_len specifies prefix length of the pool
+    /// @param delegated_len specifies lenght of the delegated prefixes
+    Pool6(PoolType type, const isc::asiolink::IOAddress& prefix,
+          uint8_t prefix_len, uint8_t delegated_len = 128);
 
     /// @brief returns pool type
     ///
     /// @return pool type
-    Pool6Type getType() const {
+    PoolType getType() const {
         return (type_);
     }
 
-private:
-    /// @brief defines a pool type
-    Pool6Type type_;
+    /// @brief returns delegated prefix length
+    ///
+    /// This may be useful for "prefix/len" style definition for
+    /// addresses, but is mostly useful for prefix pools.
+    /// @return prefix length (1-128)
+    uint8_t getLength() {
+        return (prefix_len_);
+    }
 
+private:
+    /// @brief Defines prefix length (for TYPE_PD only)
+    uint8_t prefix_len_;
 };
 
 /// @brief a pointer an IPv6 Pool
 typedef boost::shared_ptr<Pool6> Pool6Ptr;
 
-/// @brief a container for IPv6 Pools
-typedef std::vector<Pool6Ptr> Pool6Collection;
-
 /// @brief a pointer to either IPv4 or IPv6 Pool
 typedef boost::shared_ptr<Pool> PoolPtr;
 
diff --git a/src/lib/dhcpsrv/subnet.cc b/src/lib/dhcpsrv/subnet.cc
index 50a0fee..b491454 100644
--- a/src/lib/dhcpsrv/subnet.cc
+++ b/src/lib/dhcpsrv/subnet.cc
@@ -30,15 +30,17 @@ Subnet::Subnet(const isc::asiolink::IOAddress& prefix, uint8_t len,
                const Triplet<uint32_t>& valid_lifetime)
     :id_(getNextID()), prefix_(prefix), prefix_len_(len), t1_(t1),
      t2_(t2), valid_(valid_lifetime),
-     last_allocated_(lastAddrInPrefix(prefix, len)) {
+     last_allocated_ia_(lastAddrInPrefix(prefix, len)),
+     last_allocated_ta_(lastAddrInPrefix(prefix, len)),
+     last_allocated_pd_(lastAddrInPrefix(prefix, len)) {
     if ((prefix.isV6() && len > 128) ||
         (prefix.isV4() && len > 32)) {
-        isc_throw(BadValue, 
+        isc_throw(BadValue,
                   "Invalid prefix length specified for subnet: " << len);
     }
 }
 
-bool 
+bool
 Subnet::inRange(const isc::asiolink::IOAddress& addr) const {
     IOAddress first = firstAddrInPrefix(prefix_, prefix_len_);
     IOAddress last = lastAddrInPrefix(prefix_, prefix_len_);
@@ -86,13 +88,58 @@ Subnet::getOptionDescriptor(const std::string& option_space,
     return (*range.first);
 }
 
-std::string 
+isc::asiolink::IOAddress Subnet::getLastAllocated(Pool::PoolType type) const {
+    // check if the type is valid (and throw if it isn't)
+    checkType(type);
+
+    switch (type) {
+    case Pool::TYPE_V4:
+    case Pool::TYPE_IA:
+        return last_allocated_ia_;
+    case Pool::TYPE_TA:
+        return last_allocated_ta_;
+    case Pool::TYPE_PD:
+        return last_allocated_pd_;
+    default:
+        isc_throw(BadValue, "Pool type " << type << " not supported");
+    }
+}
+
+void Subnet::setLastAllocated(Pool::PoolType type,
+                              const isc::asiolink::IOAddress& addr) {
+
+    // check if the type is valid (and throw if it isn't)
+    checkType(type);
+
+    switch (type) {
+    case Pool::TYPE_V4:
+    case Pool::TYPE_IA:
+        last_allocated_ia_ = addr;
+        return;
+    case Pool::TYPE_TA:
+        last_allocated_ta_ = addr;
+        return;
+    case Pool::TYPE_PD:
+        last_allocated_pd_ = addr;
+        return;
+    default:
+        isc_throw(BadValue, "Pool type " << type << " not supported");
+    }
+}
+
+std::string
 Subnet::toText() const {
     std::stringstream tmp;
     tmp << prefix_.toText() << "/" << static_cast<unsigned int>(prefix_len_);
     return (tmp.str());
 }
 
+void Subnet4::checkType(Pool::PoolType type) const {
+    if (type != Pool::TYPE_V4) {
+        isc_throw(BadValue, "Only TYPE_V4 is allowed for Subnet4");
+    }
+}
+
 Subnet4::Subnet4(const isc::asiolink::IOAddress& prefix, uint8_t length,
                  const Triplet<uint32_t>& t1,
                  const Triplet<uint32_t>& t2,
@@ -104,35 +151,55 @@ Subnet4::Subnet4(const isc::asiolink::IOAddress& prefix, uint8_t length,
     }
 }
 
-void 
-Subnet::addPool(const PoolPtr& pool) {
-    IOAddress first_addr = pool->getFirstAddress();
-    IOAddress last_addr = pool->getLastAddress();
-
-    if (!inRange(first_addr) || !inRange(last_addr)) {
-        isc_throw(BadValue, "Pool (" << first_addr.toText() << "-" 
-                  << last_addr.toText()
-                  << " does not belong in this (" << prefix_.toText() << "/"
-                  << static_cast<int>(prefix_len_) << ") subnet4");
+const PoolCollection& Subnet::getPools(Pool::PoolType type) const {
+    // check if the type is valid (and throw if it isn't)
+    checkType(type);
+
+    switch (type) {
+    case Pool::TYPE_V4:
+    case Pool::TYPE_IA:
+        return (pools_);
+    case Pool::TYPE_TA:
+        return (pools_ta_);
+    case Pool::TYPE_PD:
+        return (pools_pd_);
+    default:
+        isc_throw(BadValue, "Unsupported pool type: " << type);
     }
-
-    /// @todo: Check that pools do not overlap
-
-    pools_.push_back(pool);
 }
 
-PoolPtr Subnet::getPool(isc::asiolink::IOAddress hint) {
+PoolPtr Subnet::getPool(Pool::PoolType type, isc::asiolink::IOAddress hint) {
+    // check if the type is valid (and throw if it isn't)
+    checkType(type);
+
+    PoolCollection* pools = NULL;
+
+    switch (type) {
+    case Pool::TYPE_V4:
+    case Pool::TYPE_IA:
+        pools = &pools_;
+        break;
+    case Pool::TYPE_TA:
+        pools = &pools_ta_;
+        break;
+    case Pool::TYPE_PD:
+        pools = &pools_pd_;
+        break;
+    default:
+        isc_throw(BadValue, "Failed to select pools. Unknown pool type: "
+                  << type);
+    }
 
     PoolPtr candidate;
-    for (PoolCollection::iterator pool = pools_.begin(); 
-         pool != pools_.end(); ++pool) {
+    for (PoolCollection::const_iterator pool = pools->begin();
+         pool != pools->end(); ++pool) {
 
-        // If we won't find anything better, then let's just use the first pool
+        // if we won't find anything better, then let's just use the first pool
         if (!candidate) {
             candidate = *pool;
         }
 
-        // If the client provided a pool and there's a pool that hint is valid 
+        // if the client provided a pool and there's a pool that hint is valid
         // in, then let's use that pool
         if ((*pool)->inRange(hint)) {
             return (*pool);
@@ -141,30 +208,62 @@ PoolPtr Subnet::getPool(isc::asiolink::IOAddress hint) {
     return (candidate);
 }
 
-void 
+void
+Subnet::addPool(const PoolPtr& pool) {
+    IOAddress first_addr = pool->getFirstAddress();
+    IOAddress last_addr = pool->getLastAddress();
+
+    if (!inRange(first_addr) || !inRange(last_addr)) {
+        isc_throw(BadValue, "Pool (" << first_addr.toText() << "-"
+                  << last_addr.toText()
+                  << " does not belong in this (" << prefix_.toText() << "/"
+                  << static_cast<int>(prefix_len_) << ") subnet");
+    }
+
+    /// @todo: Check that pools do not overlap
+
+    // check if the type is valid (and throw if it isn't)
+    checkType(pool->getType());
+
+    switch (pool->getType()) {
+    case Pool::TYPE_V4:
+    case Pool::TYPE_IA:
+        pools_.push_back(pool);
+        return;
+    case Pool6::TYPE_TA:
+        pools_ta_.push_back(pool);
+        return;
+    case Pool6::TYPE_PD:
+        pools_pd_.push_back(pool);
+        return;
+    default:
+        isc_throw(BadValue, "Invalid pool type specified: "
+                  << static_cast<int>(pool->getType()));
+    }
+}
+
+void
 Subnet::setIface(const std::string& iface_name) {
     iface_ = iface_name;
 }
 
-std::string 
+std::string
 Subnet::getIface() const {
     return (iface_);
 }
 
-
-
 void
 Subnet4::validateOption(const OptionPtr& option) const {
     if (!option) {
-        isc_throw(isc::BadValue, 
+        isc_throw(isc::BadValue,
                   "option configured for subnet must not be NULL");
     } else if (option->getUniverse() != Option::V4) {
-        isc_throw(isc::BadValue, 
+        isc_throw(isc::BadValue,
                   "expected V4 option to be added to the subnet");
     }
 }
 
-bool 
+bool
 Subnet::inPool(const isc::asiolink::IOAddress& addr) const {
 
     // Let's start with checking if it even belongs to that subnet.
@@ -172,7 +271,7 @@ Subnet::inPool(const isc::asiolink::IOAddress& addr) const {
         return (false);
     }
 
-    for (PoolCollection::const_iterator pool = pools_.begin(); 
+    for (PoolCollection::const_iterator pool = pools_.begin();
          pool != pools_.end(); ++pool) {
         if ((*pool)->inRange(addr)) {
             return (true);
@@ -195,13 +294,21 @@ Subnet6::Subnet6(const isc::asiolink::IOAddress& prefix, uint8_t length,
     }
 }
 
+void Subnet6::checkType(Pool::PoolType type) const {
+    if ( (type != Pool::TYPE_IA) && (type != Pool::TYPE_TA) &&
+         (type != Pool::TYPE_PD)) {
+        isc_throw(BadValue, "Invalid Pool type: " << static_cast<int>(type)
+                  << ", must be TYPE_IA, TYPE_TA or TYPE_PD for Subnet6");
+    }
+}
+
 void
 Subnet6::validateOption(const OptionPtr& option) const {
     if (!option) {
-        isc_throw(isc::BadValue, 
+        isc_throw(isc::BadValue,
                   "option configured for subnet must not be NULL");
     } else if (option->getUniverse() != Option::V6) {
-        isc_throw(isc::BadValue, 
+        isc_throw(isc::BadValue,
                   "expected V6 option to be added to the subnet");
     }
 }
diff --git a/src/lib/dhcpsrv/subnet.h b/src/lib/dhcpsrv/subnet.h
index 0ac5109..7f88a2e 100644
--- a/src/lib/dhcpsrv/subnet.h
+++ b/src/lib/dhcpsrv/subnet.h
@@ -240,10 +240,9 @@ public:
     /// @todo: Define map<SubnetID, IOAddress> somewhere in the
     ///        AllocEngine::IterativeAllocator and keep the data there
     ///
-    /// @return address that was last tried from this pool
-    isc::asiolink::IOAddress getLastAllocated() const {
-        return (last_allocated_);
-    }
+    /// @param type lease type to be returned
+    /// @return address/prefix that was last tried from this pool
+    isc::asiolink::IOAddress getLastAllocated(Pool::PoolType type) const;
 
     /// @brief sets the last address that was tried from this pool
     ///
@@ -253,9 +252,10 @@ public:
     ///
     /// @todo: Define map<SubnetID, IOAddress> somewhere in the
     ///        AllocEngine::IterativeAllocator and keep the data there
-    void setLastAllocated(const isc::asiolink::IOAddress& addr) {
-        last_allocated_ = addr;
-    }
+    /// @param addr address/prefix to that was tried last
+    /// @param type lease type to be set
+    void setLastAllocated(Pool::PoolType type,
+                          const isc::asiolink::IOAddress& addr);
 
     /// @brief returns unique ID for that subnet
     /// @return unique ID for that subnet
@@ -274,14 +274,20 @@ public:
 
     /// @brief Returns a pool that specified address belongs to
     ///
+    /// If there is no pool that the address belongs to (hint is invalid), other
+    /// pool of specified type will be returned.
+    ///
+    /// @param type pool type that the pool is looked for
     /// @param addr address that the returned pool should cover (optional)
-    /// @return Pointer to found Pool4 or Pool6 (or NULL)
-    PoolPtr getPool(isc::asiolink::IOAddress addr);
+    /// @return found pool (or NULL)
+    PoolPtr getPool(Pool::PoolType type, isc::asiolink::IOAddress addr);
 
     /// @brief Returns a pool without any address specified
+    ///
+    /// @param type pool type that the pool is looked for
     /// @return returns one of the pools defined
-    PoolPtr getPool() {
-        return (getPool(default_pool()));
+    PoolPtr getAnyPool(Pool::PoolType type) {
+        return (getPool(type, default_pool()));
     }
 
     /// @brief Returns the default address that will be used for pool selection
@@ -294,22 +300,21 @@ public:
     ///
     /// The reference is only valid as long as the object that returned it.
     ///
+    /// @param type lease type to be set
     /// @return a collection of all pools
-    const PoolCollection& getPools() const {
-        return pools_;
-    }
+    const PoolCollection& getPools(Pool::PoolType type) const;
 
     /// @brief sets name of the network interface for directly attached networks
     ///
     /// @param iface_name name of the interface
     void setIface(const std::string& iface_name);
 
-    /// @brief network interface name used to reach subnet (or "" for remote 
+    /// @brief network interface name used to reach subnet (or "" for remote
     /// subnets)
     /// @return network interface name for directly attached subnets or ""
     std::string getIface() const;
 
-    /// @brief returns textual representation of the subnet (e.g. 
+    /// @brief returns textual representation of the subnet (e.g.
     /// "2001:db8::/64")
     ///
     /// @return textual representation
@@ -339,6 +344,16 @@ protected:
         return (id++);
     }
 
+    /// @brief Checks if used pool type is valid
+    ///
+    /// Allowed type for Subnet4 is Pool::TYPE_V4.
+    /// Allowed types for Subnet6 are Pool::TYPE_{IA,TA,PD}.
+    /// This method is implemented in derived classes.
+    ///
+    /// @param type type to be checked
+    /// @throw BadValue if invalid value is used
+    virtual void checkType(Pool::PoolType type) const = 0;
+
     /// @brief Check if option is valid and can be added to a subnet.
     ///
     /// @param option option to be validated.
@@ -350,9 +365,15 @@ protected:
     /// a Subnet4 or Subnet6.
     SubnetID id_;
 
-    /// @brief collection of pools in that list
+    /// @brief collection of IPv4 or non-temporary IPv6 pools in that subnet
     PoolCollection pools_;
 
+    /// @brief collection of IPv6 temporary address pools in that subnet
+    PoolCollection pools_ta_;
+
+    /// @brief collection of IPv6 prefix pools in that subnet
+    PoolCollection pools_pd_;
+
     /// @brief a prefix of the subnet
     isc::asiolink::IOAddress prefix_;
 
@@ -377,7 +398,17 @@ protected:
     /// removing a pool, restarting or changing allocation algorithms. For
     /// that purpose it should be only considered a help that should not be
     /// fully trusted.
-    isc::asiolink::IOAddress last_allocated_;
+    isc::asiolink::IOAddress last_allocated_ia_;
+
+    /// @brief last allocated temporary address
+    ///
+    /// See @ref last_allocated_ia_ for details.
+    isc::asiolink::IOAddress last_allocated_ta_;
+
+    /// @brief last allocated IPv6 prefix
+    ///
+    /// See @ref last_allocated_ia_ for details.
+    isc::asiolink::IOAddress last_allocated_pd_;
 
     /// @brief Name of the network interface (if connected directly)
     std::string iface_;
@@ -426,6 +457,14 @@ protected:
     virtual isc::asiolink::IOAddress default_pool() const {
         return (isc::asiolink::IOAddress("0.0.0.0"));
     }
+
+    /// @brief Checks if used pool type is valid
+    ///
+    /// Allowed type for Subnet4 is Pool::TYPE_V4.
+    ///
+    /// @param type type to be checked
+    /// @throw BadValue if invalid value is used
+    virtual void checkType(Pool::PoolType type) const;
 };
 
 /// @brief A pointer to a Subnet4 object
@@ -490,12 +529,17 @@ protected:
         return (isc::asiolink::IOAddress("::"));
     }
 
+    /// @brief Checks if used pool type is valid
+    ///
+    /// allowed types for Subnet6 are Pool::TYPE_{IA,TA,PD}.
+    ///
+    /// @param type type to be checked
+    /// @throw BadValue if invalid value is used
+    virtual void checkType(Pool::PoolType type) const;
+
     /// @brief specifies optional interface-id
     OptionPtr interface_id_;
 
-    /// @brief collection of pools in that list
-    Pool6Collection pools_;
-
     /// @brief a triplet with preferred lifetime (in seconds)
     Triplet<uint32_t> preferred_;
 };
diff --git a/src/lib/dhcpsrv/tests/alloc_engine_unittest.cc b/src/lib/dhcpsrv/tests/alloc_engine_unittest.cc
index 9eb873f..66fb8c1 100644
--- a/src/lib/dhcpsrv/tests/alloc_engine_unittest.cc
+++ b/src/lib/dhcpsrv/tests/alloc_engine_unittest.cc
@@ -55,8 +55,10 @@ public:
     /// @brief the sole constructor
     /// @param engine_type specifies engine type (e.g. iterative)
     /// @param attempts number of lease selection attempts before giving up
-    NakedAllocEngine(AllocEngine::AllocType engine_type, unsigned int attempts)
-        :AllocEngine(engine_type, attempts) {
+    /// @param ipv6 specifies if the engine is IPv6 or IPv4
+    NakedAllocEngine(AllocEngine::AllocType engine_type,
+                     unsigned int attempts, bool ipv6 = true)
+        :AllocEngine(engine_type, attempts, ipv6) {
     }
 
     // Expose internal classes for testing purposes
@@ -399,7 +401,7 @@ TEST_F(AllocEngine6Test, allocateAddress6Nulls) {
 // pool
 TEST_F(AllocEngine6Test, IterativeAllocator) {
     boost::scoped_ptr<NakedAllocEngine::Allocator>
-        alloc(new NakedAllocEngine::IterativeAllocator());
+        alloc(new NakedAllocEngine::IterativeAllocator(Pool6::TYPE_IA));
 
     for (int i = 0; i < 1000; ++i) {
         IOAddress candidate = alloc->pickAddress(subnet_, duid_, IOAddress("::"));
@@ -412,7 +414,7 @@ TEST_F(AllocEngine6Test, IterativeAllocator) {
 // in all pools in specified subnet. It also must not pick the same address twice
 // unless it runs out of pool space and must start over.
 TEST_F(AllocEngine6Test, IterativeAllocator_manyPools6) {
-    NakedAllocEngine::IterativeAllocator alloc;
+    NakedAllocEngine::IterativeAllocator alloc(Pool6::TYPE_IA);
 
     // let's start from 2, as there is 2001:db8:1::10 - 2001:db8:1::20 pool already.
     for (int i = 2; i < 10; ++i) {
@@ -632,7 +634,8 @@ TEST_F(AllocEngine6Test, requestReuseExpiredLease6) {
 // This test checks if the simple IPv4 allocation can succeed
 TEST_F(AllocEngine4Test, simpleAlloc4) {
     boost::scoped_ptr<AllocEngine> engine;
-    ASSERT_NO_THROW(engine.reset(new AllocEngine(AllocEngine::ALLOC_ITERATIVE, 100)));
+    ASSERT_NO_THROW(engine.reset(new AllocEngine(AllocEngine::ALLOC_ITERATIVE,
+                                                 100, false)));
     ASSERT_TRUE(engine);
 
     Lease4Ptr lease = engine->allocateAddress4(subnet_, clientid_, hwaddr_,
@@ -661,7 +664,8 @@ TEST_F(AllocEngine4Test, simpleAlloc4) {
 // This test checks if the fake allocation (for DISCOVER) can succeed
 TEST_F(AllocEngine4Test, fakeAlloc4) {
     boost::scoped_ptr<AllocEngine> engine;
-    ASSERT_NO_THROW(engine.reset(new AllocEngine(AllocEngine::ALLOC_ITERATIVE, 100)));
+    ASSERT_NO_THROW(engine.reset(new AllocEngine(AllocEngine::ALLOC_ITERATIVE,
+                                                 100, false)));
     ASSERT_TRUE(engine);
 
     Lease4Ptr lease = engine->allocateAddress4(subnet_, clientid_, hwaddr_,
@@ -689,7 +693,8 @@ TEST_F(AllocEngine4Test, fakeAlloc4) {
 // in pool and free) can succeed
 TEST_F(AllocEngine4Test, allocWithValidHint4) {
     boost::scoped_ptr<AllocEngine> engine;
-    ASSERT_NO_THROW(engine.reset(new AllocEngine(AllocEngine::ALLOC_ITERATIVE, 100)));
+    ASSERT_NO_THROW(engine.reset(new AllocEngine(AllocEngine::ALLOC_ITERATIVE,
+                                                 100, false)));
     ASSERT_TRUE(engine);
 
     Lease4Ptr lease = engine->allocateAddress4(subnet_, clientid_, hwaddr_,
@@ -722,7 +727,8 @@ TEST_F(AllocEngine4Test, allocWithValidHint4) {
 // in pool, but is currently used) can succeed
 TEST_F(AllocEngine4Test, allocWithUsedHint4) {
     boost::scoped_ptr<AllocEngine> engine;
-    ASSERT_NO_THROW(engine.reset(new AllocEngine(AllocEngine::ALLOC_ITERATIVE, 100)));
+    ASSERT_NO_THROW(engine.reset(new AllocEngine(AllocEngine::ALLOC_ITERATIVE,
+                                                 100, false)));
     ASSERT_TRUE(engine);
 
     // Let's create a lease and put it in the LeaseMgr
@@ -770,7 +776,8 @@ TEST_F(AllocEngine4Test, allocWithUsedHint4) {
 // can succeed. The invalid hint should be ignored completely.
 TEST_F(AllocEngine4Test, allocBogusHint4) {
     boost::scoped_ptr<AllocEngine> engine;
-    ASSERT_NO_THROW(engine.reset(new AllocEngine(AllocEngine::ALLOC_ITERATIVE, 100)));
+    ASSERT_NO_THROW(engine.reset(new AllocEngine(AllocEngine::ALLOC_ITERATIVE,
+                                                 100, false)));
     ASSERT_TRUE(engine);
 
     // Client would like to get a 3000::abc lease, which does not belong to any
@@ -805,7 +812,8 @@ TEST_F(AllocEngine4Test, allocBogusHint4) {
 // This test checks that NULL values are handled properly
 TEST_F(AllocEngine4Test, allocateAddress4Nulls) {
     boost::scoped_ptr<AllocEngine> engine;
-    ASSERT_NO_THROW(engine.reset(new AllocEngine(AllocEngine::ALLOC_ITERATIVE, 100)));
+    ASSERT_NO_THROW(engine.reset(new AllocEngine(AllocEngine::ALLOC_ITERATIVE,
+                                                 100, false)));
     ASSERT_TRUE(engine);
 
     // Allocations without subnet are not allowed
@@ -854,7 +862,7 @@ TEST_F(AllocEngine4Test, allocateAddress4Nulls) {
 // pool
 TEST_F(AllocEngine4Test, IterativeAllocator) {
     boost::scoped_ptr<NakedAllocEngine::Allocator>
-        alloc(new NakedAllocEngine::IterativeAllocator());
+        alloc(new NakedAllocEngine::IterativeAllocator(Pool6::TYPE_V4));
 
     for (int i = 0; i < 1000; ++i) {
         IOAddress candidate = alloc->pickAddress(subnet_, clientid_,
@@ -868,7 +876,7 @@ TEST_F(AllocEngine4Test, IterativeAllocator) {
 // in all pools in specified subnet. It also must not pick the same address twice
 // unless it runs out of pool space and must start over.
 TEST_F(AllocEngine4Test, IterativeAllocator_manyPools4) {
-    NakedAllocEngine::IterativeAllocator alloc;
+    NakedAllocEngine::IterativeAllocator alloc(Pool6::TYPE_V4);
 
     // Let's start from 2, as there is 2001:db8:1::10 - 2001:db8:1::20 pool already.
     for (int i = 2; i < 10; ++i) {
@@ -923,7 +931,8 @@ TEST_F(AllocEngine4Test, IterativeAllocator_manyPools4) {
 // This test checks if really small pools are working
 TEST_F(AllocEngine4Test, smallPool4) {
     boost::scoped_ptr<AllocEngine> engine;
-    ASSERT_NO_THROW(engine.reset(new AllocEngine(AllocEngine::ALLOC_ITERATIVE, 100)));
+    ASSERT_NO_THROW(engine.reset(new AllocEngine(AllocEngine::ALLOC_ITERATIVE,
+                                                 100, false)));
     ASSERT_TRUE(engine);
 
     IOAddress addr("192.0.2.17");
@@ -965,7 +974,8 @@ TEST_F(AllocEngine4Test, smallPool4) {
 // to find out a new lease fails.
 TEST_F(AllocEngine4Test, outOfAddresses4) {
     boost::scoped_ptr<AllocEngine> engine;
-    ASSERT_NO_THROW(engine.reset(new AllocEngine(AllocEngine::ALLOC_ITERATIVE, 100)));
+    ASSERT_NO_THROW(engine.reset(new AllocEngine(AllocEngine::ALLOC_ITERATIVE,
+                                                 100, false)));
     ASSERT_TRUE(engine);
 
     IOAddress addr("192.0.2.17");
@@ -1003,7 +1013,8 @@ TEST_F(AllocEngine4Test, outOfAddresses4) {
 // This test checks if an expired lease can be reused in DISCOVER (fake allocation)
 TEST_F(AllocEngine4Test, discoverReuseExpiredLease4) {
     boost::scoped_ptr<AllocEngine> engine;
-    ASSERT_NO_THROW(engine.reset(new AllocEngine(AllocEngine::ALLOC_ITERATIVE, 100)));
+    ASSERT_NO_THROW(engine.reset(new AllocEngine(AllocEngine::ALLOC_ITERATIVE,
+                                                 100, false)));
     ASSERT_TRUE(engine);
 
     IOAddress addr("192.0.2.15");
@@ -1070,7 +1081,8 @@ TEST_F(AllocEngine4Test, discoverReuseExpiredLease4) {
 // This test checks if an expired lease can be reused in REQUEST (actual allocation)
 TEST_F(AllocEngine4Test, requestReuseExpiredLease4) {
     boost::scoped_ptr<AllocEngine> engine;
-    ASSERT_NO_THROW(engine.reset(new AllocEngine(AllocEngine::ALLOC_ITERATIVE, 100)));
+    ASSERT_NO_THROW(engine.reset(new AllocEngine(AllocEngine::ALLOC_ITERATIVE,
+                                                 100, false)));
     ASSERT_TRUE(engine);
 
     IOAddress addr("192.0.2.105");
@@ -1123,7 +1135,8 @@ TEST_F(AllocEngine4Test, renewLease4) {
     boost::scoped_ptr<AllocEngine> engine;
     CalloutHandlePtr callout_handle = HooksManager::createCalloutHandle();
 
-    ASSERT_NO_THROW(engine.reset(new AllocEngine(AllocEngine::ALLOC_ITERATIVE, 100)));
+    ASSERT_NO_THROW(engine.reset(new AllocEngine(AllocEngine::ALLOC_ITERATIVE,
+                                                 100, false)));
     ASSERT_TRUE(engine);
 
     IOAddress addr("192.0.2.102");
@@ -1500,7 +1513,8 @@ TEST_F(HookAllocEngine4Test, lease4_select) {
 
     // Create allocation engine (hook names are registered in its ctor)
     boost::scoped_ptr<AllocEngine> engine;
-    ASSERT_NO_THROW(engine.reset(new AllocEngine(AllocEngine::ALLOC_ITERATIVE, 100)));
+    ASSERT_NO_THROW(engine.reset(new AllocEngine(AllocEngine::ALLOC_ITERATIVE,
+                                                 100, false)));
     ASSERT_TRUE(engine);
 
     // Initialize Hooks Manager
@@ -1563,7 +1577,8 @@ TEST_F(HookAllocEngine4Test, change_lease4_select) {
 
     // Create allocation engine (hook names are registered in its ctor)
     boost::scoped_ptr<AllocEngine> engine;
-    ASSERT_NO_THROW(engine.reset(new AllocEngine(AllocEngine::ALLOC_ITERATIVE, 100)));
+    ASSERT_NO_THROW(engine.reset(new AllocEngine(AllocEngine::ALLOC_ITERATIVE,
+                                                 100, false)));
     ASSERT_TRUE(engine);
 
     // Initialize Hooks Manager
diff --git a/src/lib/dhcpsrv/tests/pool_unittest.cc b/src/lib/dhcpsrv/tests/pool_unittest.cc
index 4e18a3c..e7d8540 100644
--- a/src/lib/dhcpsrv/tests/pool_unittest.cc
+++ b/src/lib/dhcpsrv/tests/pool_unittest.cc
@@ -146,6 +146,10 @@ TEST(Pool6Test, constructor_prefix_len) {
     // This is Pool6, IPv4 addresses do not belong here
     EXPECT_THROW(Pool6(Pool6::TYPE_IA, IOAddress("192.168.0.2"), 96),
                  BadValue);
+
+    // Delegated prefix length for addresses must be /128
+    EXPECT_THROW(Pool6(Pool6::TYPE_IA, IOAddress("2001:db8:1::"), 96, 125),
+                 BadValue);
 }
 
 TEST(Pool6Test, in_range) {
@@ -160,6 +164,65 @@ TEST(Pool6Test, in_range) {
    EXPECT_FALSE(pool1.inRange(IOAddress("::")));
 }
 
+// Checks that Prefix Delegation pools are handled properly
+TEST(Pool6Test, PD) {
+
+    // Let's construct 2001:db8:1::/96 PD pool, split into /112 prefixes
+    Pool6 pool1(Pool6::TYPE_PD, IOAddress("2001:db8:1::"), 96, 112);
+
+    EXPECT_EQ(Pool6::TYPE_PD, pool1.getType());
+    EXPECT_EQ(112, pool1.getLength());
+    EXPECT_EQ("2001:db8:1::", pool1.getFirstAddress().toText());
+    EXPECT_EQ("2001:db8:1::ffff:ffff", pool1.getLastAddress().toText());
+
+    // Check that it's not possible to have min-max range for PD
+    EXPECT_THROW(Pool6 pool2(Pool6::TYPE_PD, IOAddress("2001:db8:1::1"),
+                             IOAddress("2001:db8:1::f")), BadValue);
+
+    // Check that it's not allowed to delegate bigger prefix than the pool
+    // Let's try to split /64 prefix into /56 chunks (should be impossible)
+    EXPECT_THROW(Pool6 pool3(Pool6::TYPE_PD, IOAddress("2001:db8:1::"),
+                             64, 56), BadValue);
+
+    // It should be possible to have a pool split into just a single chunk
+    // Let's try to split 2001:db8:1::/77 into a single /77 delegated prefix
+    EXPECT_NO_THROW(Pool6 pool4(Pool6::TYPE_PD, IOAddress("2001:db8:1::"),
+                                77, 77));
+}
+
+// Checks that temporary address pools are handled properly
+TEST(Pool6Test, TA) {
+    // Note: since we defined TA pool types during PD work, we can test it
+    // now. Although the configuration to take advantage of it is not
+    // planned for now, we will support it some day.
+
+    // Let's construct 2001:db8:1::/96 temporary addresses
+    Pool6Ptr pool1;
+    EXPECT_NO_THROW(pool1.reset(new Pool6(Pool6::TYPE_TA,
+                                          IOAddress("2001:db8:1::"), 96)));
+
+    // Check that TA range can be only defined for single addresses
+    EXPECT_THROW(Pool6(Pool6::TYPE_TA, IOAddress("2001:db8:1::"), 96, 127),
+                 BadValue);
+
+    ASSERT_TRUE(pool1);
+    EXPECT_EQ(Pool6::TYPE_TA, pool1->getType());
+    EXPECT_EQ(128, pool1->getLength()); // singular addresses, not prefixes
+    EXPECT_EQ("2001:db8:1::", pool1->getFirstAddress().toText());
+    EXPECT_EQ("2001:db8:1::ffff:ffff", pool1->getLastAddress().toText());
+
+    // Check that it's possible to have min-max range for TA
+    Pool6Ptr pool2;
+    EXPECT_NO_THROW(pool2.reset(new Pool6(Pool6::TYPE_TA,
+                                          IOAddress("2001:db8:1::1"),
+                                          IOAddress("2001:db8:1::f"))));
+    ASSERT_TRUE(pool2);
+    EXPECT_EQ(Pool6::TYPE_TA, pool2->getType());
+    EXPECT_EQ(128, pool2->getLength()); // singular addresses, not prefixes
+    EXPECT_EQ("2001:db8:1::1", pool2->getFirstAddress().toText());
+    EXPECT_EQ("2001:db8:1::f", pool2->getLastAddress().toText());
+}
+
 // This test creates 100 pools and verifies that their IDs are unique.
 TEST(Pool6Test, unique_id) {
 
diff --git a/src/lib/dhcpsrv/tests/subnet_unittest.cc b/src/lib/dhcpsrv/tests/subnet_unittest.cc
index fbcebcb..4ebc508 100644
--- a/src/lib/dhcpsrv/tests/subnet_unittest.cc
+++ b/src/lib/dhcpsrv/tests/subnet_unittest.cc
@@ -66,24 +66,25 @@ TEST(Subnet4Test, Pool4InSubnet4) {
     PoolPtr pool2(new Pool4(IOAddress("192.1.2.128"), 26));
     PoolPtr pool3(new Pool4(IOAddress("192.1.2.192"), 30));
 
-    subnet->addPool(pool1);
+    EXPECT_NO_THROW(subnet->addPool(pool1));
 
     // If there's only one pool, get that pool
-    PoolPtr mypool = subnet->getPool();
+    PoolPtr mypool = subnet->getAnyPool(Pool::TYPE_V4);
     EXPECT_EQ(mypool, pool1);
 
 
-    subnet->addPool(pool2);
-    subnet->addPool(pool3);
+    EXPECT_NO_THROW(subnet->addPool(pool2));
+    EXPECT_NO_THROW(subnet->addPool(pool3));
 
     // If there are more than one pool and we didn't provide hint, we
     // should get the first pool
-    mypool = subnet->getPool();
+    EXPECT_NO_THROW(mypool = subnet->getAnyPool(Pool::TYPE_V4));
 
     EXPECT_EQ(mypool, pool1);
 
     // If we provide a hint, we should get a pool that this hint belongs to
-    mypool = subnet->getPool(IOAddress("192.1.2.195"));
+    EXPECT_NO_THROW(mypool = subnet->getPool(Pool::TYPE_V4,
+                                             IOAddress("192.1.2.195")));
 
     EXPECT_EQ(mypool, pool3);
 
@@ -175,6 +176,74 @@ TEST(Subnet4Test, get) {
     EXPECT_EQ(28, subnet->get().second);
 }
 
+
+// Checks if last allocated address/prefix is stored/retrieved properly
+TEST(Subnet4Test, lastAllocated) {
+    IOAddress addr("192.0.2.17");
+
+    IOAddress last("192.0.2.255");
+
+    Subnet4Ptr subnet(new Subnet4(IOAddress("192.0.2.0"), 24, 1, 2, 3));
+
+    // Check initial conditions (all should be set to the last address in range)
+    EXPECT_EQ(last.toText(), subnet->getLastAllocated(Pool::TYPE_V4).toText());
+
+    // Now set last allocated for IA
+    EXPECT_NO_THROW(subnet->setLastAllocated(Pool::TYPE_V4, addr));
+    EXPECT_EQ(addr.toText(), subnet->getLastAllocated(Pool::TYPE_V4).toText());
+
+    // No, you can't set the last allocated IPv6 address in IPv4 subnet
+    EXPECT_THROW(subnet->setLastAllocated(Pool::TYPE_IA, addr), BadValue);
+    EXPECT_THROW(subnet->setLastAllocated(Pool::TYPE_TA, addr), BadValue);
+    EXPECT_THROW(subnet->setLastAllocated(Pool::TYPE_PD, addr), BadValue);
+}
+
+// Checks if the V4 is the only allowed type for Pool4 and if getPool()
+// is working properly.
+TEST(Subnet4Test, PoolType) {
+
+    Subnet4Ptr subnet(new Subnet4(IOAddress("192.2.0.0"), 16, 1, 2, 3));
+
+    PoolPtr pool1(new Pool4(IOAddress("192.2.1.0"), 24));
+    PoolPtr pool2(new Pool4(IOAddress("192.2.2.0"), 24));
+    PoolPtr pool3(new Pool6(Pool6::TYPE_IA, IOAddress("2001:db8:1:3::"), 64));
+    PoolPtr pool4(new Pool6(Pool6::TYPE_TA, IOAddress("2001:db8:1:4::"), 64));
+    PoolPtr pool5(new Pool6(Pool6::TYPE_PD, IOAddress("2001:db8:1:1::"), 64));
+
+    // There should be no pools of any type by default
+    EXPECT_EQ(PoolPtr(), subnet->getAnyPool(Pool::TYPE_V4));
+
+    // It should not be possible to ask for V6 pools in Subnet4
+    EXPECT_THROW(subnet->getAnyPool(Pool::TYPE_IA), BadValue);
+    EXPECT_THROW(subnet->getAnyPool(Pool::TYPE_TA), BadValue);
+    EXPECT_THROW(subnet->getAnyPool(Pool::TYPE_PD), BadValue);
+
+    // Let's add a single V4 pool and check that it can be retrieved
+    EXPECT_NO_THROW(subnet->addPool(pool1));
+
+    // If there's only one IA pool, get that pool (without and with hint)
+    EXPECT_EQ(pool1, subnet->getAnyPool(Pool::TYPE_V4));
+    EXPECT_EQ(pool1, subnet->getPool(Pool::TYPE_V4, IOAddress("192.0.1.167")));
+
+    // Let's add additional V4 pool
+    EXPECT_NO_THROW(subnet->addPool(pool2));
+
+    // Try without hints
+    EXPECT_EQ(pool1, subnet->getAnyPool(Pool::TYPE_V4));
+
+    // Try with valid hints
+    EXPECT_EQ(pool1, subnet->getPool(Pool::TYPE_V4, IOAddress("192.2.1.5")));
+    EXPECT_EQ(pool2, subnet->getPool(Pool::TYPE_V4, IOAddress("192.2.2.254")));
+
+    // Try with bogus hints (hints should be ingored)
+    EXPECT_EQ(pool1, subnet->getPool(Pool::TYPE_V4, IOAddress("10.1.1.1")));
+
+    // Trying to add Pool6 to Subnet4 is a big no,no!
+    EXPECT_THROW(subnet->addPool(pool3), BadValue);
+    EXPECT_THROW(subnet->addPool(pool4), BadValue);
+    EXPECT_THROW(subnet->addPool(pool5), BadValue);
+}
+
 // Tests for Subnet6
 
 TEST(Subnet6Test, constructor) {
@@ -215,25 +284,95 @@ TEST(Subnet6Test, Pool6InSubnet6) {
     subnet->addPool(pool1);
 
     // If there's only one pool, get that pool
-    PoolPtr mypool = subnet->getPool();
+    PoolPtr mypool = subnet->getAnyPool(Pool::TYPE_IA);
     EXPECT_EQ(mypool, pool1);
 
-
     subnet->addPool(pool2);
     subnet->addPool(pool3);
 
     // If there are more than one pool and we didn't provide hint, we
     // should get the first pool
-    mypool = subnet->getPool();
+    mypool = subnet->getAnyPool(Pool::TYPE_IA);
 
     EXPECT_EQ(mypool, pool1);
 
     // If we provide a hint, we should get a pool that this hint belongs to
-    mypool = subnet->getPool(IOAddress("2001:db8:1:3::dead:beef"));
+    mypool = subnet->getPool(Pool::TYPE_IA, IOAddress("2001:db8:1:3::dead:beef"));
 
     EXPECT_EQ(mypool, pool3);
 }
 
+// Check if Subnet6 supports different types of pools properly.
+TEST(Subnet6Test, PoolTypes) {
+
+    Subnet6Ptr subnet(new Subnet6(IOAddress("2001:db8:1::"), 56, 1, 2, 3, 4));
+
+    PoolPtr pool1(new Pool6(Pool6::TYPE_IA, IOAddress("2001:db8:1:1::"), 64));
+    PoolPtr pool2(new Pool6(Pool6::TYPE_TA, IOAddress("2001:db8:1:2::"), 64));
+    PoolPtr pool3(new Pool6(Pool6::TYPE_PD, IOAddress("2001:db8:1:3::"), 64));
+    PoolPtr pool4(new Pool6(Pool6::TYPE_PD, IOAddress("2001:db8:1:4::"), 64));
+
+    PoolPtr pool5(new Pool4(IOAddress("192.0.2.0"), 24));
+
+    // There should be no pools of any type by default
+    EXPECT_EQ(PoolPtr(), subnet->getAnyPool(Pool::TYPE_IA));
+    EXPECT_EQ(PoolPtr(), subnet->getAnyPool(Pool::TYPE_TA));
+    EXPECT_EQ(PoolPtr(), subnet->getAnyPool(Pool::TYPE_PD));
+
+    // Trying to get IPv4 pool from Subnet6 is not allowed
+    EXPECT_THROW(subnet->getAnyPool(Pool::TYPE_V4), BadValue);
+
+    // Let's add a single IA pool and check that it can be retrieved
+    EXPECT_NO_THROW(subnet->addPool(pool1));
+
+    // If there's only one IA pool, get that pool
+    EXPECT_EQ(pool1, subnet->getAnyPool(Pool::TYPE_IA));
+    EXPECT_EQ(pool1, subnet->getPool(Pool::TYPE_IA, IOAddress("2001:db8:1:1::1")));
+
+    // Check if pools of different type are not returned
+    EXPECT_EQ(PoolPtr(), subnet->getAnyPool(Pool::TYPE_TA));
+    EXPECT_EQ(PoolPtr(), subnet->getAnyPool(Pool::TYPE_PD));
+
+    // We ask with good hints, but wrong types, should return nothing
+    EXPECT_EQ(PoolPtr(), subnet->getPool(Pool::TYPE_PD, IOAddress("2001:db8:1:2::1")));
+    EXPECT_EQ(PoolPtr(), subnet->getPool(Pool::TYPE_TA, IOAddress("2001:db8:1:3::1")));
+
+    // Let's add TA and PD pools
+    EXPECT_NO_THROW(subnet->addPool(pool2));
+    EXPECT_NO_THROW(subnet->addPool(pool3));
+
+    // Try without hints
+    EXPECT_EQ(pool1, subnet->getAnyPool(Pool::TYPE_IA));
+    EXPECT_EQ(pool2, subnet->getAnyPool(Pool::TYPE_TA));
+    EXPECT_EQ(pool3, subnet->getAnyPool(Pool::TYPE_PD));
+
+    // Try with valid hints
+    EXPECT_EQ(pool1, subnet->getPool(Pool::TYPE_IA, IOAddress("2001:db8:1:1::1")));
+    EXPECT_EQ(pool2, subnet->getPool(Pool::TYPE_TA, IOAddress("2001:db8:1:2::1")));
+    EXPECT_EQ(pool3, subnet->getPool(Pool::TYPE_PD, IOAddress("2001:db8:1:3::1")));
+
+    // Try with bogus hints (hints should be ingored)
+    EXPECT_EQ(pool1, subnet->getPool(Pool::TYPE_IA, IOAddress("2001:db8:1:7::1")));
+    EXPECT_EQ(pool2, subnet->getPool(Pool::TYPE_TA, IOAddress("2001:db8:1:7::1")));
+    EXPECT_EQ(pool3, subnet->getPool(Pool::TYPE_PD, IOAddress("2001:db8:1:7::1")));
+
+    // Let's add a second PD pool
+    EXPECT_NO_THROW(subnet->addPool(pool4));
+
+    // Without hints, it should return the first pool
+    EXPECT_EQ(pool3, subnet->getAnyPool(Pool::TYPE_PD));
+
+    // With valid hint, it should return that hint
+    EXPECT_EQ(pool3, subnet->getPool(Pool::TYPE_PD, IOAddress("2001:db8:1:3::1")));
+    EXPECT_EQ(pool4, subnet->getPool(Pool::TYPE_PD, IOAddress("2001:db8:1:4::1")));
+
+    // With invalid hint, it should return the first pool
+    EXPECT_EQ(pool3, subnet->getPool(Pool::TYPE_PD, IOAddress("2001:db8::123")));
+
+    // Adding Pool4 to Subnet6 is a big no, no!
+    EXPECT_THROW(subnet->addPool(pool5), BadValue);
+}
+
 TEST(Subnet6Test, Subnet6_Pool6_checks) {
 
     Subnet6Ptr subnet(new Subnet6(IOAddress("2001:db8:1::"), 56, 1, 2, 3, 4));
@@ -532,4 +671,39 @@ TEST(Subnet6Test, interfaceId) {
 
 }
 
+// Checks if last allocated address/prefix is stored/retrieved properly
+TEST(Subnet6Test, lastAllocated) {
+    IOAddress ia("2001:db8:1::1");
+    IOAddress ta("2001:db8:1::abcd");
+    IOAddress pd("2001:db8:1::1234:5678");
+
+    IOAddress last("2001:db8:1::ffff:ffff:ffff:ffff");
+
+    Subnet6Ptr subnet(new Subnet6(IOAddress("2001:db8:1::"), 64, 1, 2, 3, 4));
+
+    // Check initial conditions (all should be set to the last address in range)
+    EXPECT_EQ(last.toText(), subnet->getLastAllocated(Pool::TYPE_IA).toText());
+    EXPECT_EQ(last.toText(), subnet->getLastAllocated(Pool::TYPE_TA).toText());
+    EXPECT_EQ(last.toText(), subnet->getLastAllocated(Pool::TYPE_PD).toText());
+
+    // Now set last allocated for IA
+    EXPECT_NO_THROW(subnet->setLastAllocated(Pool::TYPE_IA, ia));
+    EXPECT_EQ(ia.toText(), subnet->getLastAllocated(Pool::TYPE_IA).toText());
+
+    // TA and PD should be unchanged
+    EXPECT_EQ(last.toText(), subnet->getLastAllocated(Pool::TYPE_TA).toText());
+    EXPECT_EQ(last.toText(), subnet->getLastAllocated(Pool::TYPE_PD).toText());
+
+    // Now set TA and PD
+    EXPECT_NO_THROW(subnet->setLastAllocated(Pool::TYPE_TA, ta));
+    EXPECT_NO_THROW(subnet->setLastAllocated(Pool::TYPE_PD, pd));
+
+    EXPECT_EQ(ia.toText(), subnet->getLastAllocated(Pool::TYPE_IA).toText());
+    EXPECT_EQ(ta.toText(), subnet->getLastAllocated(Pool::TYPE_TA).toText());
+    EXPECT_EQ(pd.toText(), subnet->getLastAllocated(Pool::TYPE_PD).toText());
+
+    // No, you can't set the last allocated IPv4 address in IPv6 subnet
+    EXPECT_THROW(subnet->setLastAllocated(Pool::TYPE_V4, ia), BadValue);
+}
+
 };



More information about the bind10-changes mailing list