Browse Source

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

Conflicts:
	ChangeLog
Tomek Mrugalski 11 years ago
parent
commit
b1217e6910

+ 6 - 1
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
 676.	[bug]		muks
 	We now also allow the short name ("hmac-md5"), along with the long
 	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
 	name ("hmac-md5.sig-alg.reg.int") that was allowed before for
@@ -21,7 +26,7 @@
 	for IAPREFIX (Option6_IAPrefix) has been added.
 	for IAPREFIX (Option6_IAPrefix) has been added.
 	(Trac #3145, git 3a844e85ecc3067ccd1c01841f4a61366cb278f4)
 	(Trac #3145, git 3a844e85ecc3067ccd1c01841f4a61366cb278f4)
 
 
-672.	[func]	tmark
+672.	[func]		tmark
 	Added b10-dhcp-ddnsupdate transaction base class, NameChangeTransaction.
 	Added b10-dhcp-ddnsupdate transaction base class, NameChangeTransaction.
 	This class provides the common structure and methods to implement the state
 	This class provides the common structure and methods to implement the state
 	models described in the DHCP_DDNS design, plus integration with DNSClient
 	models described in the DHCP_DDNS design, plus integration with DNSClient

+ 2 - 1
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());
             .arg(LeaseMgrFactory::instance().getName());
 
 
         // Instantiate allocation engine
         // 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
         // Register hook points
         hook_index_pkt4_receive_   = Hooks.hook_index_pkt4_receive_;
         hook_index_pkt4_receive_   = Hooks.hook_index_pkt4_receive_;

+ 6 - 6
src/bin/dhcp6/config_parser.cc

@@ -141,13 +141,13 @@ protected:
     ///
     ///
     /// @param addr is the IPv6 prefix of the pool.
     /// @param addr is the IPv6 prefix of the pool.
     /// @param len is the prefix length.
     /// @param len is the prefix length.
-    /// @param ptype is the type of IPv6 pool (Pool6::Pool6Type). Note this is
+    /// @param ptype is the type of IPv6 pool (Pool::PoolType). Note this is
-    /// passed in as an int32_t and cast to Pool6Type to accommodate a
+    /// passed in as an int32_t and cast to PoolType to accommodate a
     /// polymorphic interface.
     /// polymorphic interface.
     /// @return returns a PoolPtr to the new Pool4 object.
     /// @return returns a PoolPtr to the new Pool4 object.
     PoolPtr poolMaker (IOAddress &addr, uint32_t len, int32_t ptype)
     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)));
                                   (ptype), addr, len)));
     }
     }
 
 
@@ -155,13 +155,13 @@ protected:
     ///
     ///
     /// @param min is the first IPv6 address in the pool.
     /// @param min is the first IPv6 address in the pool.
     /// @param max is the last 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
+    /// @param ptype is the type of IPv6 pool (Pool::PoolType). Note this is
-    /// passed in as an int32_t and cast to Pool6Type to accommodate a
+    /// passed in as an int32_t and cast to PoolType to accommodate a
     /// polymorphic interface.
     /// polymorphic interface.
     /// @return returns a PoolPtr to the new Pool4 object.
     /// @return returns a PoolPtr to the new Pool4 object.
     PoolPtr poolMaker (IOAddress &min, IOAddress &max, int32_t ptype)
     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)));
                                   (ptype), min, max)));
     }
     }
 };
 };

+ 20 - 16
src/lib/dhcpsrv/alloc_engine.cc

@@ -53,8 +53,8 @@ AllocEngineHooks Hooks;
 namespace isc {
 namespace isc {
 namespace dhcp {
 namespace dhcp {
 
 
-AllocEngine::IterativeAllocator::IterativeAllocator()
+AllocEngine::IterativeAllocator::IterativeAllocator(Pool::PoolType lease_type)
-    :Allocator() {
+    :Allocator(lease_type) {
 }
 }
 
 
 isc::asiolink::IOAddress
 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,
     // 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
     // but there are times when it won't be (like after removing a pool or
     // perhaps restarting the server).
     // 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()) {
     if (pools.empty()) {
         isc_throw(AllocFailed, "No pools defined in selected subnet");
         isc_throw(AllocFailed, "No pools defined in selected subnet");
@@ -117,7 +117,7 @@ AllocEngine::IterativeAllocator::pickAddress(const SubnetPtr& subnet,
     if (it == pools.end()) {
     if (it == pools.end()) {
         // ok to access first element directly. We checked that pools is non-empty
         // ok to access first element directly. We checked that pools is non-empty
         IOAddress next = pools[0]->getFirstAddress();
         IOAddress next = pools[0]->getFirstAddress();
-        subnet->setLastAllocated(next);
+        subnet->setLastAllocated(pool_type_, next);
         return (next);
         return (next);
     }
     }
 
 
@@ -126,7 +126,7 @@ AllocEngine::IterativeAllocator::pickAddress(const SubnetPtr& subnet,
     IOAddress next = increaseAddress(last); // basically addr++
     IOAddress next = increaseAddress(last); // basically addr++
     if ((*it)->inRange(next)) {
     if ((*it)->inRange(next)) {
         // the next one is in the pool as well, so we haven't hit pool boundary yet
         // 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);
         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
         // Really out of luck today. That was the last pool. Let's rewind
         // to the beginning.
         // to the beginning.
         next = pools[0]->getFirstAddress();
         next = pools[0]->getFirstAddress();
-        subnet->setLastAllocated(next);
+        subnet->setLastAllocated(pool_type_, next);
         return (next);
         return (next);
     }
     }
 
 
     // there is a next pool, let's try first address from it
     // there is a next pool, let's try first address from it
     next = (*it)->getFirstAddress();
     next = (*it)->getFirstAddress();
-    subnet->setLastAllocated(next);
+    subnet->setLastAllocated(pool_type_, next);
     return (next);
     return (next);
 }
 }
 
 
-AllocEngine::HashedAllocator::HashedAllocator()
+AllocEngine::HashedAllocator::HashedAllocator(Pool::PoolType lease_type)
-    :Allocator() {
+    :Allocator(lease_type) {
     isc_throw(NotImplemented, "Hashed allocator is not implemented");
     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");
     isc_throw(NotImplemented, "Hashed allocator is not implemented");
 }
 }
 
 
-AllocEngine::RandomAllocator::RandomAllocator()
+AllocEngine::RandomAllocator::RandomAllocator(Pool::PoolType lease_type)
-    :Allocator() {
+    :Allocator(lease_type) {
     isc_throw(NotImplemented, "Random allocator is not implemented");
     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) {
     :attempts_(attempts) {
+
+    Pool::PoolType pool_type = ipv6?Pool::TYPE_IA:Pool::TYPE_V4;
+
     switch (engine_type) {
     switch (engine_type) {
     case ALLOC_ITERATIVE:
     case ALLOC_ITERATIVE:
-        allocator_ = boost::shared_ptr<Allocator>(new IterativeAllocator());
+        allocator_.reset(new IterativeAllocator(pool_type));
         break;
         break;
     case ALLOC_HASHED:
     case ALLOC_HASHED:
-        allocator_ = boost::shared_ptr<Allocator>(new HashedAllocator());
+        allocator_.reset(new HashedAllocator(pool_type));
         break;
         break;
     case ALLOC_RANDOM:
     case ALLOC_RANDOM:
-        allocator_ = boost::shared_ptr<Allocator>(new RandomAllocator());
+        allocator_.reset(new RandomAllocator(pool_type));
         break;
         break;
 
 
     default:
     default:

+ 19 - 4
src/lib/dhcpsrv/alloc_engine.h

@@ -77,10 +77,21 @@ protected:
         pickAddress(const SubnetPtr& subnet, const DuidPtr& duid,
         pickAddress(const SubnetPtr& subnet, const DuidPtr& duid,
                     const isc::asiolink::IOAddress& hint) = 0;
                     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
         /// @brief virtual destructor
         virtual ~Allocator() {
         virtual ~Allocator() {
         }
         }
     protected:
     protected:
+
+        /// @brief defines lease type allocation
+        Pool::PoolType pool_type_;
     };
     };
 
 
     /// @brief Address/prefix allocator that iterates over all addresses
     /// @brief Address/prefix allocator that iterates over all addresses
@@ -95,7 +106,8 @@ protected:
         /// @brief default constructor
         /// @brief default constructor
         ///
         ///
         /// Does not do anything
         /// Does not do anything
-        IterativeAllocator();
+        /// @param type - specifies allocation type
+        IterativeAllocator(Pool::PoolType type);
 
 
         /// @brief returns the next address from pools in a subnet
         /// @brief returns the next address from pools in a subnet
         ///
         ///
@@ -123,7 +135,8 @@ protected:
     public:
     public:
 
 
         /// @brief default constructor (does nothing)
         /// @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.
         /// @brief returns an address based on hash calculated from client's DUID.
         ///
         ///
@@ -145,7 +158,8 @@ protected:
     public:
     public:
 
 
         /// @brief default constructor (does nothing)
         /// @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
         /// @brief returns an random address from pool of specified subnet
         ///
         ///
@@ -180,7 +194,8 @@ protected:
     /// @param engine_type selects allocation algorithm
     /// @param engine_type selects allocation algorithm
     /// @param attempts number of attempts for each lease allocation before
     /// @param attempts number of attempts for each lease allocation before
     ///        we give up (0 means unlimited)
     ///        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.
     /// @brief Returns IPv4 lease.
     ///
     ///

+ 31 - 15
src/lib/dhcpsrv/pool.cc

@@ -21,9 +21,9 @@ using namespace isc::asiolink;
 namespace isc {
 namespace isc {
 namespace dhcp {
 namespace dhcp {
 
 
-Pool::Pool(const isc::asiolink::IOAddress& first,
+Pool::Pool(PoolType type, const isc::asiolink::IOAddress& first,
            const isc::asiolink::IOAddress& last)
            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 {
 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,
 Pool4::Pool4(const isc::asiolink::IOAddress& first,
              const isc::asiolink::IOAddress& last)
              const isc::asiolink::IOAddress& last)
-    :Pool(first, last) {
+:Pool(Pool::TYPE_V4, first, last) {
     // check if specified address boundaries are sane
     // check if specified address boundaries are sane
     if (!first.isV4() || !last.isV4()) {
     if (!first.isV4() || !last.isV4()) {
         isc_throw(BadValue, "Invalid Pool4 address boundaries: not IPv4");
         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,
+Pool4::Pool4( const isc::asiolink::IOAddress& prefix, uint8_t prefix_len)
-             uint8_t prefix_len)
+:Pool(Pool::TYPE_V4, prefix, IOAddress("0.0.0.0")) {
-    :Pool(prefix, IOAddress("0.0.0.0")) {
 
 
     // check if the prefix is sane
     // check if the prefix is sane
     if (!prefix.isV4()) {
     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)
              const isc::asiolink::IOAddress& last)
-    :Pool(first, last), type_(type) {
+    :Pool(type, first, last), prefix_len_(128) {
 
 
     // check if specified address boundaries are sane
     // check if specified address boundaries are sane
     if (!first.isV6() || !last.isV6()) {
     if (!first.isV6() || !last.isV6()) {
         isc_throw(BadValue, "Invalid Pool6 address boundaries: not IPv6");
         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) {
     if (last < first) {
         isc_throw(BadValue, "Upper boundary is smaller than lower boundary.");
         isc_throw(BadValue, "Upper boundary is smaller than lower boundary.");
         // This check is a bit strict. If we decide that it is too strict,
         // 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
     // parameters are for IA and TA only. There is another dedicated
     // constructor for that (it uses prefix/length)
     // constructor for that (it uses prefix/length)
     if ((type != TYPE_IA) && (type != TYPE_TA)) {
     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,
+Pool6::Pool6(PoolType type, const isc::asiolink::IOAddress& prefix,
-             uint8_t prefix_len)
+             uint8_t prefix_len, uint8_t delegated_len /* = 128 */)
-    :Pool(prefix, IOAddress("::")),
+    :Pool(type, prefix, IOAddress("::")), prefix_len_(delegated_len) {
-     type_(type) {
 
 
     // check if the prefix is sane
     // check if the prefix is sane
     if (!prefix.isV6()) {
     if (!prefix.isV6()) {
         isc_throw(BadValue, "Invalid Pool6 address boundaries: not IPv6");
         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) {
     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
     /// @todo: We should probably implement checks against weird addresses

+ 83 - 30
src/lib/dhcpsrv/pool.h

@@ -32,6 +32,25 @@ class Pool {
 
 
 public:
 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
     /// @brief returns Pool-id
     ///
     ///
     /// @return pool-id value
     /// @return pool-id value
@@ -58,6 +77,20 @@ public:
     /// @return true, if the address is in pool
     /// @return true, if the address is in pool
     bool inRange(const isc::asiolink::IOAddress& addr) const;
     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:
 protected:
 
 
     /// @brief protected constructor
     /// @brief protected constructor
@@ -65,7 +98,12 @@ protected:
     /// This constructor is protected to prevent anyone from instantiating
     /// This constructor is protected to prevent anyone from instantiating
     /// Pool class directly. Instances of Pool4 and Pool6 should be created
     /// Pool class directly. Instances of Pool4 and Pool6 should be created
     /// instead.
     /// 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);
          const isc::asiolink::IOAddress& last);
 
 
     /// @brief returns the next unique Pool-ID
     /// @brief returns the next unique Pool-ID
@@ -91,6 +129,9 @@ protected:
     ///
     ///
     /// @todo: This field is currently not used.
     /// @todo: This field is currently not used.
     std::string comments_;
     std::string comments_;
+
+    /// @brief defines a pool type
+    PoolType type_;
 };
 };
 
 
 /// @brief Pool information for IPv4 addresses
 /// @brief Pool information for IPv4 addresses
@@ -117,9 +158,6 @@ public:
 /// @brief a pointer an IPv4 Pool
 /// @brief a pointer an IPv4 Pool
 typedef boost::shared_ptr<Pool4> Pool4Ptr;
 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
 /// @brief Pool information for IPv6 addresses and prefixes
 ///
 ///
 /// It holds information about pool6, i.e. a range of IPv6 address space that
 /// 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 {
 class Pool6 : public Pool {
 public:
 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
     /// @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 first the first address in a pool
     /// @param last the last 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);
           const isc::asiolink::IOAddress& last);
 
 
     /// @brief the constructor for Pool6 "prefix/len" style definition
     /// @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 type type of the pool (IA, TA or PD)
     /// @param prefix specifies prefix of the pool
     /// @param prefix specifies prefix of the pool
-    /// @param prefix_len specifies length of the prefix of the pool
+    /// @param prefix_len specifies prefix length of the pool
-    Pool6(Pool6Type type, const isc::asiolink::IOAddress& prefix,
+    /// @param delegated_len specifies lenght of the delegated prefixes
-          uint8_t prefix_len);
+    Pool6(PoolType type, const isc::asiolink::IOAddress& prefix,
+          uint8_t prefix_len, uint8_t delegated_len = 128);
 
 
     /// @brief returns pool type
     /// @brief returns pool type
     ///
     ///
     /// @return pool type
     /// @return pool type
-    Pool6Type getType() const {
+    PoolType getType() const {
         return (type_);
         return (type_);
     }
     }
 
 
-private:
+    /// @brief returns delegated prefix length
-    /// @brief defines a pool type
+    ///
-    Pool6Type type_;
+    /// 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
 /// @brief a pointer an IPv6 Pool
 typedef boost::shared_ptr<Pool6> Pool6Ptr;
 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
 /// @brief a pointer to either IPv4 or IPv6 Pool
 typedef boost::shared_ptr<Pool> PoolPtr;
 typedef boost::shared_ptr<Pool> PoolPtr;
 
 

+ 140 - 33
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)
                const Triplet<uint32_t>& valid_lifetime)
     :id_(getNextID()), prefix_(prefix), prefix_len_(len), t1_(t1),
     :id_(getNextID()), prefix_(prefix), prefix_len_(len), t1_(t1),
      t2_(t2), valid_(valid_lifetime),
      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) ||
     if ((prefix.isV6() && len > 128) ||
         (prefix.isV4() && len > 32)) {
         (prefix.isV4() && len > 32)) {
-        isc_throw(BadValue, 
+        isc_throw(BadValue,
                   "Invalid prefix length specified for subnet: " << len);
                   "Invalid prefix length specified for subnet: " << len);
     }
     }
 }
 }
 
 
-bool 
+bool
 Subnet::inRange(const isc::asiolink::IOAddress& addr) const {
 Subnet::inRange(const isc::asiolink::IOAddress& addr) const {
     IOAddress first = firstAddrInPrefix(prefix_, prefix_len_);
     IOAddress first = firstAddrInPrefix(prefix_, prefix_len_);
     IOAddress last = lastAddrInPrefix(prefix_, prefix_len_);
     IOAddress last = lastAddrInPrefix(prefix_, prefix_len_);
@@ -86,13 +88,58 @@ Subnet::getOptionDescriptor(const std::string& option_space,
     return (*range.first);
     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 {
 Subnet::toText() const {
     std::stringstream tmp;
     std::stringstream tmp;
     tmp << prefix_.toText() << "/" << static_cast<unsigned int>(prefix_len_);
     tmp << prefix_.toText() << "/" << static_cast<unsigned int>(prefix_len_);
     return (tmp.str());
     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,
 Subnet4::Subnet4(const isc::asiolink::IOAddress& prefix, uint8_t length,
                  const Triplet<uint32_t>& t1,
                  const Triplet<uint32_t>& t1,
                  const Triplet<uint32_t>& t2,
                  const Triplet<uint32_t>& t2,
@@ -104,35 +151,55 @@ Subnet4::Subnet4(const isc::asiolink::IOAddress& prefix, uint8_t length,
     }
     }
 }
 }
 
 
-void 
+const PoolCollection& Subnet::getPools(Pool::PoolType type) const {
-Subnet::addPool(const PoolPtr& pool) {
+    // check if the type is valid (and throw if it isn't)
-    IOAddress first_addr = pool->getFirstAddress();
+    checkType(type);
-    IOAddress last_addr = pool->getLastAddress();
+
-
+    switch (type) {
-    if (!inRange(first_addr) || !inRange(last_addr)) {
+    case Pool::TYPE_V4:
-        isc_throw(BadValue, "Pool (" << first_addr.toText() << "-" 
+    case Pool::TYPE_IA:
-                  << last_addr.toText()
+        return (pools_);
-                  << " does not belong in this (" << prefix_.toText() << "/"
+    case Pool::TYPE_TA:
-                  << static_cast<int>(prefix_len_) << ") subnet4");
+        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;
     PoolPtr candidate;
-    for (PoolCollection::iterator pool = pools_.begin(); 
+    for (PoolCollection::const_iterator pool = pools->begin();
-         pool != pools_.end(); ++pool) {
+         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) {
         if (!candidate) {
             candidate = *pool;
             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
         // in, then let's use that pool
         if ((*pool)->inRange(hint)) {
         if ((*pool)->inRange(hint)) {
             return (*pool);
             return (*pool);
@@ -141,30 +208,62 @@ PoolPtr Subnet::getPool(isc::asiolink::IOAddress hint) {
     return (candidate);
     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) {
 Subnet::setIface(const std::string& iface_name) {
     iface_ = iface_name;
     iface_ = iface_name;
 }
 }
 
 
-std::string 
+std::string
 Subnet::getIface() const {
 Subnet::getIface() const {
     return (iface_);
     return (iface_);
 }
 }
 
 
-
-
 void
 void
 Subnet4::validateOption(const OptionPtr& option) const {
 Subnet4::validateOption(const OptionPtr& option) const {
     if (!option) {
     if (!option) {
-        isc_throw(isc::BadValue, 
+        isc_throw(isc::BadValue,
                   "option configured for subnet must not be NULL");
                   "option configured for subnet must not be NULL");
     } else if (option->getUniverse() != Option::V4) {
     } else if (option->getUniverse() != Option::V4) {
-        isc_throw(isc::BadValue, 
+        isc_throw(isc::BadValue,
                   "expected V4 option to be added to the subnet");
                   "expected V4 option to be added to the subnet");
     }
     }
 }
 }
 
 
-bool 
+bool
 Subnet::inPool(const isc::asiolink::IOAddress& addr) const {
 Subnet::inPool(const isc::asiolink::IOAddress& addr) const {
 
 
     // Let's start with checking if it even belongs to that subnet.
     // 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);
         return (false);
     }
     }
 
 
-    for (PoolCollection::const_iterator pool = pools_.begin(); 
+    for (PoolCollection::const_iterator pool = pools_.begin();
          pool != pools_.end(); ++pool) {
          pool != pools_.end(); ++pool) {
         if ((*pool)->inRange(addr)) {
         if ((*pool)->inRange(addr)) {
             return (true);
             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
 void
 Subnet6::validateOption(const OptionPtr& option) const {
 Subnet6::validateOption(const OptionPtr& option) const {
     if (!option) {
     if (!option) {
-        isc_throw(isc::BadValue, 
+        isc_throw(isc::BadValue,
                   "option configured for subnet must not be NULL");
                   "option configured for subnet must not be NULL");
     } else if (option->getUniverse() != Option::V6) {
     } else if (option->getUniverse() != Option::V6) {
-        isc_throw(isc::BadValue, 
+        isc_throw(isc::BadValue,
                   "expected V6 option to be added to the subnet");
                   "expected V6 option to be added to the subnet");
     }
     }
 }
 }

+ 65 - 21
src/lib/dhcpsrv/subnet.h

@@ -240,10 +240,9 @@ public:
     /// @todo: Define map<SubnetID, IOAddress> somewhere in the
     /// @todo: Define map<SubnetID, IOAddress> somewhere in the
     ///        AllocEngine::IterativeAllocator and keep the data there
     ///        AllocEngine::IterativeAllocator and keep the data there
     ///
     ///
-    /// @return address that was last tried from this pool
+    /// @param type lease type to be returned
-    isc::asiolink::IOAddress getLastAllocated() const {
+    /// @return address/prefix that was last tried from this pool
-        return (last_allocated_);
+    isc::asiolink::IOAddress getLastAllocated(Pool::PoolType type) const;
-    }
 
 
     /// @brief sets the last address that was tried from this pool
     /// @brief sets the last address that was tried from this pool
     ///
     ///
@@ -253,9 +252,10 @@ public:
     ///
     ///
     /// @todo: Define map<SubnetID, IOAddress> somewhere in the
     /// @todo: Define map<SubnetID, IOAddress> somewhere in the
     ///        AllocEngine::IterativeAllocator and keep the data there
     ///        AllocEngine::IterativeAllocator and keep the data there
-    void setLastAllocated(const isc::asiolink::IOAddress& addr) {
+    /// @param addr address/prefix to that was tried last
-        last_allocated_ = addr;
+    /// @param type lease type to be set
-    }
+    void setLastAllocated(Pool::PoolType type,
+                          const isc::asiolink::IOAddress& addr);
 
 
     /// @brief returns unique ID for that subnet
     /// @brief returns unique ID for that subnet
     /// @return 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
     /// @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)
     /// @param addr address that the returned pool should cover (optional)
-    /// @return Pointer to found Pool4 or Pool6 (or NULL)
+    /// @return found pool (or NULL)
-    PoolPtr getPool(isc::asiolink::IOAddress addr);
+    PoolPtr getPool(Pool::PoolType type, isc::asiolink::IOAddress addr);
 
 
     /// @brief Returns a pool without any address specified
     /// @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
     /// @return returns one of the pools defined
-    PoolPtr getPool() {
+    PoolPtr getAnyPool(Pool::PoolType type) {
-        return (getPool(default_pool()));
+        return (getPool(type, default_pool()));
     }
     }
 
 
     /// @brief Returns the default address that will be used for pool selection
     /// @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.
     /// 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
     /// @return a collection of all pools
-    const PoolCollection& getPools() const {
+    const PoolCollection& getPools(Pool::PoolType type) const;
-        return pools_;
-    }
 
 
     /// @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
     void setIface(const std::string& iface_name);
     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)
     /// subnets)
     /// @return network interface name for directly attached subnets or ""
     /// @return network interface name for directly attached subnets or ""
     std::string getIface() const;
     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")
     /// "2001:db8::/64")
     ///
     ///
     /// @return textual representation
     /// @return textual representation
@@ -339,6 +344,16 @@ protected:
         return (id++);
         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.
     /// @brief Check if option is valid and can be added to a subnet.
     ///
     ///
     /// @param option option to be validated.
     /// @param option option to be validated.
@@ -350,9 +365,15 @@ protected:
     /// a Subnet4 or Subnet6.
     /// a Subnet4 or Subnet6.
     SubnetID id_;
     SubnetID id_;
 
 
-    /// @brief collection of pools in that list
+    /// @brief collection of IPv4 or non-temporary IPv6 pools in that subnet
     PoolCollection pools_;
     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
     /// @brief a prefix of the subnet
     isc::asiolink::IOAddress prefix_;
     isc::asiolink::IOAddress prefix_;
 
 
@@ -377,7 +398,17 @@ protected:
     /// removing a pool, restarting or changing allocation algorithms. For
     /// removing a pool, restarting or changing allocation algorithms. For
     /// that purpose it should be only considered a help that should not be
     /// that purpose it should be only considered a help that should not be
     /// fully trusted.
     /// 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)
     /// @brief Name of the network interface (if connected directly)
     std::string iface_;
     std::string iface_;
@@ -426,6 +457,14 @@ protected:
     virtual isc::asiolink::IOAddress default_pool() const {
     virtual isc::asiolink::IOAddress default_pool() const {
         return (isc::asiolink::IOAddress("0.0.0.0"));
         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
 /// @brief A pointer to a Subnet4 object
@@ -490,12 +529,17 @@ protected:
         return (isc::asiolink::IOAddress("::"));
         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
     /// @brief specifies optional interface-id
     OptionPtr interface_id_;
     OptionPtr interface_id_;
 
 
-    /// @brief collection of pools in that list
-    Pool6Collection pools_;
-
     /// @brief a triplet with preferred lifetime (in seconds)
     /// @brief a triplet with preferred lifetime (in seconds)
     Triplet<uint32_t> preferred_;
     Triplet<uint32_t> preferred_;
 };
 };

+ 34 - 19
src/lib/dhcpsrv/tests/alloc_engine_unittest.cc

@@ -55,8 +55,10 @@ public:
     /// @brief the sole constructor
     /// @brief the sole constructor
     /// @param engine_type specifies engine type (e.g. iterative)
     /// @param engine_type specifies engine type (e.g. iterative)
     /// @param attempts number of lease selection attempts before giving up
     /// @param attempts number of lease selection attempts before giving up
-    NakedAllocEngine(AllocEngine::AllocType engine_type, unsigned int attempts)
+    /// @param ipv6 specifies if the engine is IPv6 or IPv4
-        :AllocEngine(engine_type, attempts) {
+    NakedAllocEngine(AllocEngine::AllocType engine_type,
+                     unsigned int attempts, bool ipv6 = true)
+        :AllocEngine(engine_type, attempts, ipv6) {
     }
     }
 
 
     // Expose internal classes for testing purposes
     // Expose internal classes for testing purposes
@@ -399,7 +401,7 @@ TEST_F(AllocEngine6Test, allocateAddress6Nulls) {
 // pool
 // pool
 TEST_F(AllocEngine6Test, IterativeAllocator) {
 TEST_F(AllocEngine6Test, IterativeAllocator) {
     boost::scoped_ptr<NakedAllocEngine::Allocator>
     boost::scoped_ptr<NakedAllocEngine::Allocator>
-        alloc(new NakedAllocEngine::IterativeAllocator());
+        alloc(new NakedAllocEngine::IterativeAllocator(Pool6::TYPE_IA));
 
 
     for (int i = 0; i < 1000; ++i) {
     for (int i = 0; i < 1000; ++i) {
         IOAddress candidate = alloc->pickAddress(subnet_, duid_, IOAddress("::"));
         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
 // 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.
 // unless it runs out of pool space and must start over.
 TEST_F(AllocEngine6Test, IterativeAllocator_manyPools6) {
 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.
     // 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) {
     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
 // This test checks if the simple IPv4 allocation can succeed
 TEST_F(AllocEngine4Test, simpleAlloc4) {
 TEST_F(AllocEngine4Test, simpleAlloc4) {
     boost::scoped_ptr<AllocEngine> engine;
     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);
     ASSERT_TRUE(engine);
 
 
     Lease4Ptr lease = engine->allocateAddress4(subnet_, clientid_, hwaddr_,
     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
 // This test checks if the fake allocation (for DISCOVER) can succeed
 TEST_F(AllocEngine4Test, fakeAlloc4) {
 TEST_F(AllocEngine4Test, fakeAlloc4) {
     boost::scoped_ptr<AllocEngine> engine;
     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);
     ASSERT_TRUE(engine);
 
 
     Lease4Ptr lease = engine->allocateAddress4(subnet_, clientid_, hwaddr_,
     Lease4Ptr lease = engine->allocateAddress4(subnet_, clientid_, hwaddr_,
@@ -689,7 +693,8 @@ TEST_F(AllocEngine4Test, fakeAlloc4) {
 // in pool and free) can succeed
 // in pool and free) can succeed
 TEST_F(AllocEngine4Test, allocWithValidHint4) {
 TEST_F(AllocEngine4Test, allocWithValidHint4) {
     boost::scoped_ptr<AllocEngine> engine;
     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);
     ASSERT_TRUE(engine);
 
 
     Lease4Ptr lease = engine->allocateAddress4(subnet_, clientid_, hwaddr_,
     Lease4Ptr lease = engine->allocateAddress4(subnet_, clientid_, hwaddr_,
@@ -722,7 +727,8 @@ TEST_F(AllocEngine4Test, allocWithValidHint4) {
 // in pool, but is currently used) can succeed
 // in pool, but is currently used) can succeed
 TEST_F(AllocEngine4Test, allocWithUsedHint4) {
 TEST_F(AllocEngine4Test, allocWithUsedHint4) {
     boost::scoped_ptr<AllocEngine> engine;
     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);
     ASSERT_TRUE(engine);
 
 
     // Let's create a lease and put it in the LeaseMgr
     // 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.
 // can succeed. The invalid hint should be ignored completely.
 TEST_F(AllocEngine4Test, allocBogusHint4) {
 TEST_F(AllocEngine4Test, allocBogusHint4) {
     boost::scoped_ptr<AllocEngine> engine;
     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);
     ASSERT_TRUE(engine);
 
 
     // Client would like to get a 3000::abc lease, which does not belong to any
     // 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
 // This test checks that NULL values are handled properly
 TEST_F(AllocEngine4Test, allocateAddress4Nulls) {
 TEST_F(AllocEngine4Test, allocateAddress4Nulls) {
     boost::scoped_ptr<AllocEngine> engine;
     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);
     ASSERT_TRUE(engine);
 
 
     // Allocations without subnet are not allowed
     // Allocations without subnet are not allowed
@@ -854,7 +862,7 @@ TEST_F(AllocEngine4Test, allocateAddress4Nulls) {
 // pool
 // pool
 TEST_F(AllocEngine4Test, IterativeAllocator) {
 TEST_F(AllocEngine4Test, IterativeAllocator) {
     boost::scoped_ptr<NakedAllocEngine::Allocator>
     boost::scoped_ptr<NakedAllocEngine::Allocator>
-        alloc(new NakedAllocEngine::IterativeAllocator());
+        alloc(new NakedAllocEngine::IterativeAllocator(Pool6::TYPE_V4));
 
 
     for (int i = 0; i < 1000; ++i) {
     for (int i = 0; i < 1000; ++i) {
         IOAddress candidate = alloc->pickAddress(subnet_, clientid_,
         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
 // 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.
 // unless it runs out of pool space and must start over.
 TEST_F(AllocEngine4Test, IterativeAllocator_manyPools4) {
 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.
     // 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) {
     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
 // This test checks if really small pools are working
 TEST_F(AllocEngine4Test, smallPool4) {
 TEST_F(AllocEngine4Test, smallPool4) {
     boost::scoped_ptr<AllocEngine> engine;
     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);
     ASSERT_TRUE(engine);
 
 
     IOAddress addr("192.0.2.17");
     IOAddress addr("192.0.2.17");
@@ -965,7 +974,8 @@ TEST_F(AllocEngine4Test, smallPool4) {
 // to find out a new lease fails.
 // to find out a new lease fails.
 TEST_F(AllocEngine4Test, outOfAddresses4) {
 TEST_F(AllocEngine4Test, outOfAddresses4) {
     boost::scoped_ptr<AllocEngine> engine;
     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);
     ASSERT_TRUE(engine);
 
 
     IOAddress addr("192.0.2.17");
     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)
 // This test checks if an expired lease can be reused in DISCOVER (fake allocation)
 TEST_F(AllocEngine4Test, discoverReuseExpiredLease4) {
 TEST_F(AllocEngine4Test, discoverReuseExpiredLease4) {
     boost::scoped_ptr<AllocEngine> engine;
     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);
     ASSERT_TRUE(engine);
 
 
     IOAddress addr("192.0.2.15");
     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)
 // This test checks if an expired lease can be reused in REQUEST (actual allocation)
 TEST_F(AllocEngine4Test, requestReuseExpiredLease4) {
 TEST_F(AllocEngine4Test, requestReuseExpiredLease4) {
     boost::scoped_ptr<AllocEngine> engine;
     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);
     ASSERT_TRUE(engine);
 
 
     IOAddress addr("192.0.2.105");
     IOAddress addr("192.0.2.105");
@@ -1123,7 +1135,8 @@ TEST_F(AllocEngine4Test, renewLease4) {
     boost::scoped_ptr<AllocEngine> engine;
     boost::scoped_ptr<AllocEngine> engine;
     CalloutHandlePtr callout_handle = HooksManager::createCalloutHandle();
     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);
     ASSERT_TRUE(engine);
 
 
     IOAddress addr("192.0.2.102");
     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)
     // Create allocation engine (hook names are registered in its ctor)
     boost::scoped_ptr<AllocEngine> engine;
     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);
     ASSERT_TRUE(engine);
 
 
     // Initialize Hooks Manager
     // Initialize Hooks Manager
@@ -1563,7 +1577,8 @@ TEST_F(HookAllocEngine4Test, change_lease4_select) {
 
 
     // Create allocation engine (hook names are registered in its ctor)
     // Create allocation engine (hook names are registered in its ctor)
     boost::scoped_ptr<AllocEngine> engine;
     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);
     ASSERT_TRUE(engine);
 
 
     // Initialize Hooks Manager
     // Initialize Hooks Manager

+ 63 - 0
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
     // This is Pool6, IPv4 addresses do not belong here
     EXPECT_THROW(Pool6(Pool6::TYPE_IA, IOAddress("192.168.0.2"), 96),
     EXPECT_THROW(Pool6(Pool6::TYPE_IA, IOAddress("192.168.0.2"), 96),
                  BadValue);
                  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) {
 TEST(Pool6Test, in_range) {
@@ -160,6 +164,65 @@ TEST(Pool6Test, in_range) {
    EXPECT_FALSE(pool1.inRange(IOAddress("::")));
    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.
 // This test creates 100 pools and verifies that their IDs are unique.
 TEST(Pool6Test, unique_id) {
 TEST(Pool6Test, unique_id) {
 
 

+ 184 - 10
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 pool2(new Pool4(IOAddress("192.1.2.128"), 26));
     PoolPtr pool3(new Pool4(IOAddress("192.1.2.192"), 30));
     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
     // If there's only one pool, get that pool
-    PoolPtr mypool = subnet->getPool();
+    PoolPtr mypool = subnet->getAnyPool(Pool::TYPE_V4);
     EXPECT_EQ(mypool, pool1);
     EXPECT_EQ(mypool, pool1);
 
 
 
 
-    subnet->addPool(pool2);
+    EXPECT_NO_THROW(subnet->addPool(pool2));
-    subnet->addPool(pool3);
+    EXPECT_NO_THROW(subnet->addPool(pool3));
 
 
     // If there are more than one pool and we didn't provide hint, we
     // If there are more than one pool and we didn't provide hint, we
     // should get the first pool
     // should get the first pool
-    mypool = subnet->getPool();
+    EXPECT_NO_THROW(mypool = subnet->getAnyPool(Pool::TYPE_V4));
 
 
     EXPECT_EQ(mypool, pool1);
     EXPECT_EQ(mypool, pool1);
 
 
     // If we provide a hint, we should get a pool that this hint belongs to
     // 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);
     EXPECT_EQ(mypool, pool3);
 
 
@@ -175,6 +176,74 @@ TEST(Subnet4Test, get) {
     EXPECT_EQ(28, subnet->get().second);
     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
 // Tests for Subnet6
 
 
 TEST(Subnet6Test, constructor) {
 TEST(Subnet6Test, constructor) {
@@ -215,25 +284,95 @@ TEST(Subnet6Test, Pool6InSubnet6) {
     subnet->addPool(pool1);
     subnet->addPool(pool1);
 
 
     // If there's only one pool, get that pool
     // If there's only one pool, get that pool
-    PoolPtr mypool = subnet->getPool();
+    PoolPtr mypool = subnet->getAnyPool(Pool::TYPE_IA);
     EXPECT_EQ(mypool, pool1);
     EXPECT_EQ(mypool, pool1);
 
 
-
     subnet->addPool(pool2);
     subnet->addPool(pool2);
     subnet->addPool(pool3);
     subnet->addPool(pool3);
 
 
     // If there are more than one pool and we didn't provide hint, we
     // If there are more than one pool and we didn't provide hint, we
     // should get the first pool
     // should get the first pool
-    mypool = subnet->getPool();
+    mypool = subnet->getAnyPool(Pool::TYPE_IA);
 
 
     EXPECT_EQ(mypool, pool1);
     EXPECT_EQ(mypool, pool1);
 
 
     // If we provide a hint, we should get a pool that this hint belongs to
     // 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);
     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) {
 TEST(Subnet6Test, Subnet6_Pool6_checks) {
 
 
     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));
@@ -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);
+}
+
 };
 };