Browse Source

[4321] Extended host tests for multiple IAs.

Tests now use common function to verify the server's response.
Also, improved commentary in the tests.
Marcin Siodelski 8 years ago
parent
commit
de5f1831ed

+ 56 - 5
src/bin/dhcp6/tests/dhcp6_client.cc

@@ -495,8 +495,10 @@ Dhcp6Client::doRenew() {
     appendFQDN();
     appendFQDN();
 
 
     context_.query_ = query;
     context_.query_ = query;
+
     sendMsg(context_.query_);
     sendMsg(context_.query_);
     context_.response_ = receiveOneMsg();
     context_.response_ = receiveOneMsg();
+
     // Apply configuration only if the server has responded.
     // Apply configuration only if the server has responded.
     if (context_.response_) {
     if (context_.response_) {
         config_.clear();
         config_.clear();
@@ -566,11 +568,6 @@ void
 Dhcp6Client::generateIAFromLeases(const Pkt6Ptr& query) {
 Dhcp6Client::generateIAFromLeases(const Pkt6Ptr& query) {
     /// @todo: add support for IAPREFIX here.
     /// @todo: add support for IAPREFIX here.
 
 
-/*    if (!use_na_) {
-        // If we're told to not use IA_NA at all, there's nothing to be done here
-        return;
-    } */
-
     for (std::vector<Lease6>::const_iterator lease = config_.leases_.begin();
     for (std::vector<Lease6>::const_iterator lease = config_.leases_.begin();
          lease != config_.leases_.end(); ++lease) {
          lease != config_.leases_.end(); ++lease) {
         if (lease->type_ != Lease::TYPE_NA) {
         if (lease->type_ != Lease::TYPE_NA) {
@@ -702,6 +699,18 @@ Dhcp6Client::hasLeaseForAddress(const asiolink::IOAddress& address) const {
 }
 }
 
 
 bool
 bool
+Dhcp6Client::hasLeaseForAddress(const asiolink::IOAddress& address,
+                                const uint32_t iaid) const {
+    std::vector<Lease6> leases = getLeasesByAddress(address);
+    BOOST_FOREACH(const Lease6& lease, leases) {
+        if (lease.iaid_ == iaid) {
+            return (true);
+        }
+    }
+    return (false);
+}
+
+bool
 Dhcp6Client::hasLeaseForAddressRange(const asiolink::IOAddress& first,
 Dhcp6Client::hasLeaseForAddressRange(const asiolink::IOAddress& first,
                                      const asiolink::IOAddress& last) const {
                                      const asiolink::IOAddress& last) const {
     std::vector<Lease6> leases = getLeasesByAddressRange(first, last);
     std::vector<Lease6> leases = getLeasesByAddressRange(first, last);
@@ -709,6 +718,19 @@ Dhcp6Client::hasLeaseForAddressRange(const asiolink::IOAddress& first,
 }
 }
 
 
 bool
 bool
+Dhcp6Client::
+hasLeaseWithZeroLifetimeForAddress(const asiolink::IOAddress& address) const {
+    std::vector<Lease6> leases = getLeasesByAddress(address);
+    BOOST_FOREACH(const Lease6& lease, leases) {
+        if ((lease.preferred_lft_ == 0) && (lease.valid_lft_ == 0)) {
+            return (true);
+        }
+    }
+    return (false);
+}
+
+
+bool
 Dhcp6Client::hasLeaseForPrefix(const asiolink::IOAddress& prefix,
 Dhcp6Client::hasLeaseForPrefix(const asiolink::IOAddress& prefix,
                                const uint8_t prefix_len) const {
                                const uint8_t prefix_len) const {
     std::vector<Lease6> leases = getLeasesByAddress(prefix);
     std::vector<Lease6> leases = getLeasesByAddress(prefix);
@@ -721,6 +743,20 @@ Dhcp6Client::hasLeaseForPrefix(const asiolink::IOAddress& prefix,
 }
 }
 
 
 bool
 bool
+Dhcp6Client::hasLeaseForPrefix(const asiolink::IOAddress& prefix,
+                               const uint8_t prefix_len,
+                               const uint32_t iaid) const {
+    std::vector<Lease6> leases = getLeasesByAddress(prefix);
+    BOOST_FOREACH(const Lease6& lease, leases) {
+        if ((lease.prefixlen_ == prefix_len) &&
+            (lease.iaid_ == iaid)) {
+            return (true);
+        }
+    }
+    return (false);
+}
+
+bool
 Dhcp6Client::hasLeaseForPrefixPool(const asiolink::IOAddress& prefix,
 Dhcp6Client::hasLeaseForPrefixPool(const asiolink::IOAddress& prefix,
                                    const uint8_t prefix_len,
                                    const uint8_t prefix_len,
                                    const uint8_t delegated_len) const {
                                    const uint8_t delegated_len) const {
@@ -729,6 +765,19 @@ Dhcp6Client::hasLeaseForPrefixPool(const asiolink::IOAddress& prefix,
     return (!leases.empty());
     return (!leases.empty());
 }
 }
 
 
+bool
+Dhcp6Client::hasLeaseWithZeroLifetimeForPrefix(const asiolink::IOAddress& prefix,
+                                               const uint8_t prefix_len) const {
+    std::vector<Lease6> leases = getLeasesByAddress(prefix);
+    BOOST_FOREACH(const Lease6& lease, leases) {
+        if ((lease.prefixlen_ == prefix_len) && (lease.preferred_lft_ == 0) &&
+            (lease.valid_lft_ == 0)) {
+            return (true);
+        }
+    }
+    return (false);
+}
+
 
 
 uint16_t
 uint16_t
 Dhcp6Client::getStatusCode(const uint32_t iaid) const {
 Dhcp6Client::getStatusCode(const uint32_t iaid) const {
@@ -797,12 +846,14 @@ Dhcp6Client::sendMsg(const Pkt6Ptr& msg) {
     }
     }
     // Repack the message to simulate wire-data parsing.
     // Repack the message to simulate wire-data parsing.
     msg->pack();
     msg->pack();
+
     Pkt6Ptr msg_copy(new Pkt6(static_cast<const uint8_t*>
     Pkt6Ptr msg_copy(new Pkt6(static_cast<const uint8_t*>
                               (msg->getBuffer().getData()),
                               (msg->getBuffer().getData()),
                               msg->getBuffer().getLength()));
                               msg->getBuffer().getLength()));
     msg_copy->setRemoteAddr(link_local_);
     msg_copy->setRemoteAddr(link_local_);
     msg_copy->setLocalAddr(dest_addr_);
     msg_copy->setLocalAddr(dest_addr_);
     msg_copy->setIface(iface_name_);
     msg_copy->setIface(iface_name_);
+
     srv_->fakeReceive(msg_copy);
     srv_->fakeReceive(msg_copy);
     srv_->run();
     srv_->run();
 }
 }

+ 29 - 0
src/bin/dhcp6/tests/dhcp6_client.h

@@ -396,6 +396,14 @@ public:
     /// @return true if client has lease for the address, false otherwise.
     /// @return true if client has lease for the address, false otherwise.
     bool hasLeaseForAddress(const asiolink::IOAddress& address) const;
     bool hasLeaseForAddress(const asiolink::IOAddress& address) const;
 
 
+    /// @brief Checks if client has lease for the specified address in the
+    /// IA_NA identified by IAID.
+    ///
+    /// @param address Address for which lease should be found.
+    /// @param iaid IAID of the IA_NA in which the lease is expected.
+    bool hasLeaseForAddress(const asiolink::IOAddress& address,
+                            const uint32_t iaid) const;
+
     /// @brief Checks if client has a lease for an address within range.
     /// @brief Checks if client has a lease for an address within range.
     ///
     ///
     /// @param first Lower bound of the address range.
     /// @param first Lower bound of the address range.
@@ -406,6 +414,14 @@ public:
     bool hasLeaseForAddressRange(const asiolink::IOAddress& first,
     bool hasLeaseForAddressRange(const asiolink::IOAddress& first,
                                  const asiolink::IOAddress& last) const;
                                  const asiolink::IOAddress& last) const;
 
 
+    /// @brief Checks if client has a lease with zero lifetimes for the
+    /// specified address.
+    ///
+    /// @param address Address for which lease should be found.
+    ///
+    /// @return true if client has a lease, false otherwise.
+    bool hasLeaseWithZeroLifetimeForAddress(const asiolink::IOAddress& address) const;
+
     /// @brief Checks if client has a lease for a prefix.
     /// @brief Checks if client has a lease for a prefix.
     ///
     ///
     /// @param prefix Prefix.
     /// @param prefix Prefix.
@@ -416,6 +432,16 @@ public:
     bool hasLeaseForPrefix(const asiolink::IOAddress& prefix,
     bool hasLeaseForPrefix(const asiolink::IOAddress& prefix,
                            const uint8_t prefix_len) const;
                            const uint8_t prefix_len) const;
 
 
+    /// @brief Checks if client as a lease for prefix in the IA_PD identified
+    /// by specified IAID.
+    ///
+    /// @param prefix Prefix.
+    /// @param prefix_len Prefix length.
+    /// @param iaid IAID of the IA_PD in which the lease is expected.
+    bool hasLeaseForPrefix(const asiolink::IOAddress& prefix,
+                           const uint8_t prefix_len,
+                           const uint32_t iaid) const;
+
     /// @brief Checks if client has a lease belonging to a prefix pool.
     /// @brief Checks if client has a lease belonging to a prefix pool.
     ///
     ///
     /// @param prefix Pool prefix.
     /// @param prefix Pool prefix.
@@ -428,6 +454,9 @@ public:
                                const uint8_t prefix_len,
                                const uint8_t prefix_len,
                                const uint8_t delegated_len) const;
                                const uint8_t delegated_len) const;
 
 
+    bool hasLeaseWithZeroLifetimeForPrefix(const asiolink::IOAddress& prefix,
+                                           const uint8_t prefix_len) const;
+
     /// @brief Returns the value of the global status code for the last
     /// @brief Returns the value of the global status code for the last
     /// transaction.
     /// transaction.
     uint16_t getStatusCode() const {
     uint16_t getStatusCode() const {

+ 676 - 128
src/bin/dhcp6/tests/host_unittest.cc

@@ -10,6 +10,8 @@
 #include <dhcp6/tests/dhcp6_test_utils.h>
 #include <dhcp6/tests/dhcp6_test_utils.h>
 #include <dhcp6/tests/dhcp6_client.h>
 #include <dhcp6/tests/dhcp6_client.h>
 #include <boost/algorithm/string/join.hpp>
 #include <boost/algorithm/string/join.hpp>
+#include <boost/bind.hpp>
+#include <boost/function.hpp>
 #include <boost/lexical_cast.hpp>
 #include <boost/lexical_cast.hpp>
 #include <list>
 #include <list>
 #include <sstream>
 #include <sstream>
@@ -111,53 +113,129 @@ const char* CONFIGS[] = {
         "        \"ip-addresses\": [ \"2001:db8:1::2\" ]"
         "        \"ip-addresses\": [ \"2001:db8:1::2\" ]"
         "    } ]"
         "    } ]"
         " } ]"
         " } ]"
-    "}",
-
-    // Configuration 3:
-    "{ "
-        "\"interfaces-config\": {"
-        "  \"interfaces\": [ \"*\" ]"
-        "},"
-        "\"valid-lifetime\": 4000, "
-        "\"preferred-lifetime\": 3000,"
-        "\"rebind-timer\": 2000, "
-        "\"renew-timer\": 1000, "
-        "\"subnet6\": [ "
-        " { "
-        "    \"subnet\": \"2001:db8:1::/48\", "
-        "    \"pools\": [ { \"pool\": \"2001:db8:1::1 - 2001:db8:1::10\" } ],"
-        "    \"interface\" : \"eth0\","
-        "    \"reservations\": ["
-        "    {"
-        "        \"duid\": \"01:02:03:04\","
-        "        \"ip-addresses\": [ \"2001:db8:1:1::1\", \"2001:db8:1:1::2\","
-                                    "\"2001:db8:1:1::3\" ],"
-        "        \"prefixes\": [  \"3000:1:1::/32\", \"3000:1:2::/32\","
-                                 "\"3000:1:3::/32\" ]"
-        "    } ]"
-        " } ]"
     "}"
     "}"
 };
 };
 
 
-class Reservation {
+/// @brief Generic wrapper to provide strongly typed values.
+///
+/// In many cases, the test fixture class methods require providing many
+/// paramaters, of which some ore optional. Some of the parameters may also
+/// be implicitly converted to other types. Non-careful test implementer
+/// may often "shift by one" or swap two values on the arguments list, which
+/// will be accepted by the compiler but will result in troubles running the
+/// function. Sometimes it takes non trivial amount of debugging to find out
+/// why the particular function fails until we find that the arguments were
+/// swapped or shifted. In addition, the use of classes wrapping simple types
+/// results in better readbility of the test code.
+///
+/// @tparam ValueType Type of the wrapped value.
+template<typename ValueType>
+struct SpecializedTypeWrapper {
+
+    /// @brief Constructor
+    ///
+    /// @param value Wrapped value
+    explicit SpecializedTypeWrapper(const ValueType& value)
+        : value_(value) { }
+
+    /// @brief Operator returning a wrapped value.
+    operator ValueType () const {
+        return (value_);
+    }
+
+    /// @brief Wrapped value.
+    ValueType value_;
+};
+
+
+/// @brief Class representing strongly typed IAID.
+struct IAID : public SpecializedTypeWrapper<uint32_t> {
+    /// @brief Constructor
+    ///
+    /// @param iaid IAID.
+    explicit IAID(const uint32_t iaid)
+        : SpecializedTypeWrapper(iaid) { }
+};
+
+/// @brief Class representing stronly typed value for strict IAID checks.
+///
+/// Strict IAID checks are used to verify that  the particular address has been
+/// assign to a specific IA. In many cases we don't check that because it may
+/// not be possible to predict to which IA the specific lease will be assigned.
+struct StrictIAIDChecking : public SpecializedTypeWrapper<bool> {
+    /// @brief Constructor.
+    ///
+    /// @param strict_check Boolean value indicating if strict checking should
+    /// be performed.
+    explicit StrictIAIDChecking(const bool strict_check)
+        : SpecializedTypeWrapper(strict_check) { }
+
+    /// @brief Convenience function returning an object indicating that strict
+    /// checks should be performed.
+    static const StrictIAIDChecking YES() {
+        static StrictIAIDChecking strict_check(true);
+        return (strict_check);
+    }
+
+    /// @brief Convenience function returning an object indicating that strict
+    /// checks should not be performed.
+    static StrictIAIDChecking NO() {
+        static StrictIAIDChecking strict_check(false);
+        return (strict_check);
+    }
+
+};
+
+/// @brief Base class representing leases and hints coveyed within IAs.
+///
+/// This is a base class for @ref Reservation and @ref Hint classes.
+class IAResource {
 public:
 public:
-    Reservation(const std::string& resource);
 
 
+    /// @brief Constructor.
+    ///
+    /// Creates a resource instance from a string. The string is provided in
+    /// one of the following formats:
+    /// - "2001:db8:1::1" for addresses.
+    /// - "2001:db8::/64" for prefixes.
+    /// - "::/0" to mark lease or hint as unspecified (empty).
+    IAResource(const std::string& resource);
+
+    /// @brief Checks if resource is unspecified.
+    ///
+    /// @return true if resource is unspecified.
     bool isEmpty() const;
     bool isEmpty() const;
 
 
+    /// @brief Checks if resource is a prefix.
+    ///
+    /// @return true if resource is a prefix.
     bool isPrefix() const;
     bool isPrefix() const;
 
 
-    static const Reservation& UNSPEC();
+    /// @brief Returns prefix or address (depending on resource type).
+    const IOAddress& getPrefix() const;
 
 
+    /// @brief Returns prefix length.
+    uint8_t getPrefixLen() const;
+
+    /// @brief Returns textual representation of the resource.
+    std::string toText() const;
+
+    /// @brief Operator converting resource to string.
     operator std::string() const;
     operator std::string() const;
 
 
 private:
 private:
+
+    /// @brief Holds prefix or address (depending on resource type).
     IOAddress prefix_;
     IOAddress prefix_;
+
+    /// @brief Holds prefix length (for prefixes).
     uint8_t prefix_len_;
     uint8_t prefix_len_;
+
 };
 };
 
 
-Reservation::Reservation(const std::string& resource)
+IAResource::IAResource(const std::string& resource)
     : prefix_(IOAddress::IPV6_ZERO_ADDRESS()), prefix_len_(0) {
     : prefix_(IOAddress::IPV6_ZERO_ADDRESS()), prefix_len_(0) {
+    // Check if resource is a prefix, i.e. search for slash.
     size_t slash_pos = resource.find("/");
     size_t slash_pos = resource.find("/");
     if ((slash_pos != std::string::npos) && (slash_pos < resource.size() - 1)) {
     if ((slash_pos != std::string::npos) && (slash_pos < resource.size() - 1)) {
         prefix_len_ = boost::lexical_cast<unsigned int>(resource.substr(slash_pos + 1));
         prefix_len_ = boost::lexical_cast<unsigned int>(resource.substr(slash_pos + 1));
@@ -166,21 +244,27 @@ Reservation::Reservation(const std::string& resource)
 }
 }
 
 
 bool
 bool
-Reservation::isEmpty() const {
-    return (prefix_.isV6Zero());
+IAResource::isEmpty() const {
+    return (prefix_.isV6Zero() && (prefix_len_ == 0));
 }
 }
 
 
 bool
 bool
-Reservation::isPrefix() const {
+IAResource::isPrefix() const {
     return (!isEmpty() && (prefix_len_ > 0));
     return (!isEmpty() && (prefix_len_ > 0));
 }
 }
 
 
-const Reservation& Reservation::UNSPEC() {
-    static Reservation unspec("::/0");
-    return (unspec);
+const IOAddress&
+IAResource::getPrefix() const {
+    return (prefix_);
 }
 }
 
 
-Reservation::operator std::string() const {
+uint8_t
+IAResource::getPrefixLen() const {
+    return (prefix_len_);
+}
+
+std::string
+IAResource::toText() const {
     std::ostringstream s;
     std::ostringstream s;
     s << "\"" << prefix_;
     s << "\"" << prefix_;
     if (prefix_len_ > 0) {
     if (prefix_len_ > 0) {
@@ -190,6 +274,67 @@ Reservation::operator std::string() const {
     return (s.str());
     return (s.str());
 }
 }
 
 
+IAResource::operator std::string() const {
+    return (toText());
+}
+
+/// @brief Address or prefix reservation.
+class Reservation : public IAResource {
+public:
+
+    /// @brief Constructor
+    ///
+    /// @param resource Resource string as for @ref IAResource constructor.
+    Reservation(const std::string& resource)
+        : IAResource(resource) {
+    }
+
+    /// @brief Convenience function returning unspecified resource.
+    static const Reservation& UNSPEC();
+
+};
+
+const Reservation& Reservation::UNSPEC() {
+    static Reservation unspec("::/0");
+    return (unspec);
+}
+
+/// @brief Address or prefix hint.
+class Hint : public IAResource {
+public:
+
+    /// @brief Constructor.
+    ///
+    /// Includes IAID of an IA in which hint should be placed.
+    ///
+    /// @param iaid IAID of IA in which hint should be placed.
+    /// @param resource Resource string as for @ref IAResource constructor.
+    Hint(const IAID& iaid, const std::string& resource)
+        : IAResource(resource), iaid_(iaid.value_) {
+    }
+
+    /// @brief Returns IAID as 32-bit unsigned value.
+    uint32_t getIAID() const;
+
+    /// @brief Convenience function returning unspecified hint.
+    static const Hint& UNSPEC();
+
+private:
+
+    /// @brief Holds IAID as 32-bit unsigned integer.
+    uint32_t iaid_;
+};
+
+uint32_t
+Hint::getIAID() const {
+    return (iaid_);
+}
+
+const Hint& Hint::UNSPEC() {
+    static Hint unspec(IAID(0), "::/0");
+    return (unspec);
+}
+
 /// @brief Test fixture class for testing host reservations
 /// @brief Test fixture class for testing host reservations
 class HostTest : public Dhcpv6SrvTest {
 class HostTest : public Dhcpv6SrvTest {
 public:
 public:
@@ -200,7 +345,10 @@ public:
     /// Sets up fake interfaces.
     /// Sets up fake interfaces.
     HostTest()
     HostTest()
         : Dhcpv6SrvTest(),
         : Dhcpv6SrvTest(),
-          iface_mgr_test_config_(true) {
+          iface_mgr_test_config_(true),
+          client_(),
+          do_solicit_(boost::bind(&Dhcp6Client::doSolicit, &client_, true)),
+          do_solicit_request_(boost::bind(&Dhcp6Client::doSARR, &client_)) {
     }
     }
 
 
     /// @brief Verifies that the reservation is retrieved by the server
     /// @brief Verifies that the reservation is retrieved by the server
@@ -235,10 +383,113 @@ public:
         EXPECT_EQ(exp_ip_address, lease_client.addr_.toText());
         EXPECT_EQ(exp_ip_address, lease_client.addr_.toText());
     }
     }
 
 
+    /// @brief Checks if the client obtained lease for specified reservation.
+    ///
+    /// @param r Reservation.
+    /// @param [out] address_count This value is incremented if the client
+    /// obtained the address lease.
+    /// @param [out] prefix_count This value is incremented if the client
+    /// obtained the prefix lease.
+    void testLeaseForIA(const Reservation& r, size_t& address_count,
+                        size_t& prefix_count);
+
+    /// @brief Checks if the client obtined lease for specified hint.
+    ///
+    /// The hint belongs to a specific IA (identified by IAID) and is expected
+    /// to be returned in this IA by the server.
+    ///
+    /// @param h Hint.
+    void testLeaseForIA(const Hint& h);
+
+    /// @brief A generic test for assigning multiple reservations to a single
+    /// client sending multiple IAs.
+    ///
+    /// This test creates a server configuration which includes one subnet,
+    /// address pool of "2001:db8:1::1 - 2001:db8:1::10" and the prefix pool
+    /// of 3001::/32, with delegated prefix length of 64. The configuration
+    /// may include between 0 and 6 reservations for a client with DUID of
+    /// "01:02:03:04".
+    ///
+    /// The test performs an exchange with a server, typically 4-way exchange
+    /// or Solicit-Advertise. The client's message includes 3 IA_NAs (with
+    /// IAIDs in range of 1..3) and 3 IA_PDs (with IAIDs in range of 4..6).
+    ///
+    /// It is possible to specify hints for selected IAs. The IA is in such
+    /// case identified by the IAID.
+    ///
+    /// The test expects that the server returns 6 leases. It checks if those
+    /// leases contain all reserved addresses and prefixes specified as
+    /// arguments of the test. If the number of IAs is greater than the
+    /// number of reservations it checks that for the remaining IAs the
+    /// leases from dynamic pools are assigned.
+    ///
+    /// The strict_iaid_check flag controlls whether the test should verify
+    /// that the address or prefix specified as a hint is assigned by the
+    /// server to the IA in which the hint was placed by the client.
+    ///
+    /// @param client_operation Dhcp6Client function to be executed to
+    /// perform an exchange with the server.
+    /// @param r1 Reservation 1. Default value is "unspecified", in which
+    /// case the reservation will not be created.
+    /// @param r2 Reservation 2.
+    /// @param r3 Reservation 3.
+    /// @param r4 Reservation 4.
+    /// @param r5 Reservation 5.
+    /// @param r6 Reservation 6.
+    /// @param strict_iaid_check Indicates if the test should check if the
+    /// hints sent by the client have been allocated by the server to the
+    /// particular IAs. Default value is NO (no checks).
+    /// @param h1 Hint 1. Default value is "unspecified", in which case the
+    /// hint will not be included.
+    /// @param h2 Hint 2.
+    /// @param h3 Hint 3.
+    /// @param h4 Hint 4.
+    /// @param h5 Hint 5.
+    /// @param h6 Hint 6.
+    void testMultipleIAs(const boost::function<void ()>& client_operation,
+                         const Reservation& r1 = Reservation::UNSPEC(),
+                         const Reservation& r2 = Reservation::UNSPEC(),
+                         const Reservation& r3 = Reservation::UNSPEC(),
+                         const Reservation& r4 = Reservation::UNSPEC(),
+                         const Reservation& r5 = Reservation::UNSPEC(),
+                         const Reservation& r6 = Reservation::UNSPEC(),
+                         const StrictIAIDChecking& strict_iaid_check =
+                         StrictIAIDChecking::NO(),
+                         const Hint& h1 = Hint::UNSPEC(),
+                         const Hint& h2 = Hint::UNSPEC(),
+                         const Hint& h3 = Hint::UNSPEC(),
+                         const Hint& h4 = Hint::UNSPEC(),
+                         const Hint& h5 = Hint::UNSPEC(),
+                         const Hint& h6 = Hint::UNSPEC());
+
+    /// @brief Checks if specified reservation is for address or prefix and
+    /// stores reservation in the textual format on one of the lists.
+    ///
+    /// @param [out] address_list Reference to a list containing address
+    /// reservations.
+    /// @param [out] prefix_list Reference to a list containing prefix
+    /// reservations.
     static void storeReservation(const Reservation& r,
     static void storeReservation(const Reservation& r,
                                  std::list<std::string>& address_list,
                                  std::list<std::string>& address_list,
                                  std::list<std::string>& prefix_list);
                                  std::list<std::string>& prefix_list);
 
 
+    /// @brief Creates configuration for testing processing multiple IAs.
+    ///
+    /// This method creates a server configuration which includes one subnet,
+    /// address pool of "2001:db8:1::1 - 2001:db8:1::10" and the prefix pool
+    /// of 3001::/32, with delegated prefix length of 64. The configuration
+    /// may include between 0 and 6 reservations for a client with DUID of
+    /// "01:02:03:04".
+    ///
+    /// @param r1 Reservation 1. Default value is "unspecified" in which case
+    /// the reservation will not be included in the configruation.
+    /// @param r2 Reservation 2.
+    /// @param r3 Reservation 3.
+    /// @param r4 Reservation 4.
+    /// @param r5 Reservation 5.
+    /// @param r6 Reservation 6.
+    ///
+    /// @return Text containing server configuration in JSON format.
     std::string configString(const DUID& duid,
     std::string configString(const DUID& duid,
                              const Reservation& r1 = Reservation::UNSPEC(),
                              const Reservation& r1 = Reservation::UNSPEC(),
                              const Reservation& r2 = Reservation::UNSPEC(),
                              const Reservation& r2 = Reservation::UNSPEC(),
@@ -247,11 +498,133 @@ public:
                              const Reservation& r5 = Reservation::UNSPEC(),
                              const Reservation& r5 = Reservation::UNSPEC(),
                              const Reservation& r6 = Reservation::UNSPEC()) const;
                              const Reservation& r6 = Reservation::UNSPEC()) const;
 
 
+    /// @brief Configures client to include hint.
+    ///
+    /// @param client Reference to a client.
+    /// @param hint Const reference to an object holding the hint.
+    static void requestIA(Dhcp6Client& client, const Hint& hint);
+
+    /// @brief Configures client to include 6 IAs without hints.
+    ///
+    /// This method configures the client to include 3 IA_NAs and
+    /// 3 IA_PDs.
+    ///
+    /// @param client Reference to a client.
+    static void requestEmptyIAs(Dhcp6Client& client);
+
     /// @brief Interface Manager's fake configuration control.
     /// @brief Interface Manager's fake configuration control.
     IfaceMgrTestConfig iface_mgr_test_config_;
     IfaceMgrTestConfig iface_mgr_test_config_;
+
+    /// @brief Instance of the common DHCPv6 client.
+    Dhcp6Client client_;
+
+    /// @brief Pointer to the Dhcp6Client::doSolicit method.
+    boost::function<void() > do_solicit_;
+
+    /// @brief Pointer to the Dhcp6Client::doSARR method.
+    boost::function<void() > do_solicit_request_;
 };
 };
 
 
 void
 void
+HostTest::testLeaseForIA(const Reservation& r, size_t& address_count,
+                         size_t& prefix_count) {
+    if (r.isPrefix()) {
+        ++prefix_count;
+        EXPECT_TRUE(client_.hasLeaseForPrefix(r.getPrefix(), r.getPrefixLen()));
+
+    } else if (!r.isEmpty()) {
+        ++address_count;
+        EXPECT_TRUE(client_.hasLeaseForAddress(r.getPrefix()));
+    }
+}
+
+void
+HostTest::testLeaseForIA(const Hint& h) {
+    if (h.isPrefix()) {
+        EXPECT_TRUE(client_.hasLeaseForPrefix(h.getPrefix(), h.getPrefixLen(),
+                                              h.getIAID()))
+            << "there is no lease for prefix " << h.toText()
+            << " and IAID = " << h.getIAID();
+
+    } else if (!h.isEmpty()) {
+        EXPECT_TRUE(client_.hasLeaseForAddress(h.getPrefix(), h.getIAID()))
+            << "there is no lease for address " << h.toText()
+            << " and IAID = " << h.getIAID();
+    }
+}
+
+void
+HostTest::testMultipleIAs(const boost::function<void ()>& client_operation,
+                          const Reservation& r1, const Reservation& r2,
+                          const Reservation& r3, const Reservation& r4,
+                          const Reservation& r5, const Reservation& r6,
+                          const StrictIAIDChecking& strict_iaid_check,
+                          const Hint& h1, const Hint& h2 ,
+                          const Hint& h3, const Hint& h4,
+                          const Hint& h5, const Hint& h6) {
+    client_.setDUID("01:02:03:04");
+
+    /// Create configuration with 0 to 6 reservations.
+    const std::string c = configString(*client_.getDuid(), r1, r2, r3,
+                                       r4, r5, r6);
+
+    ASSERT_NO_THROW(configure(c, *client_.getServer()));
+
+    // First includes all IAs. They are initially empty.
+    requestEmptyIAs(client_);
+
+    // For each specified hint, include it in the respective IA. Hints
+    // which are "unspecified" will not be included.
+    requestIA(client_, h1);
+    requestIA(client_, h2);
+    requestIA(client_, h3);
+    requestIA(client_, h4);
+    requestIA(client_, h5);
+    requestIA(client_, h6);
+
+
+    // Send Solicit and require that the client saves received configuration
+    // so as we can test that advertised configuration is correct.
+    ASSERT_NO_THROW(client_operation());
+
+    ASSERT_EQ(6, client_.getLeaseNum());
+
+    // Count reserved addresses and prefixes assigned from reservations.
+    size_t address_count = 0;
+    size_t prefix_count = 0;
+
+    testLeaseForIA(r1, address_count, prefix_count);
+    testLeaseForIA(r2, address_count, prefix_count);
+    testLeaseForIA(r3, address_count, prefix_count);
+    testLeaseForIA(r4, address_count, prefix_count);
+    testLeaseForIA(r5, address_count, prefix_count);
+    testLeaseForIA(r6, address_count, prefix_count);
+
+    // Get all addresses assigned from the dynamic pool (not reserved).
+    std::vector<Lease6> leases =
+        client_.getLeasesByAddressRange(IOAddress("2001:db8:1::1"),
+                                        IOAddress("2001:db8:1::10"));
+    // There are 3 IA_NAs and for a few we have assigned reserved addresses.
+    // The remaining ones should be assigned from the dynamic pool.
+    ASSERT_EQ(3 - address_count, leases.size());
+
+    // Get all prefixes assigned from the dynamic pool (not reserved).
+    leases =  client_.getLeasesByPrefixPool(IOAddress("3001::"), 32, 64);
+    ASSERT_EQ(3 - prefix_count, leases.size());
+
+    // Check that the hints have been allocated to respective IAs.
+    if (strict_iaid_check) {
+        testLeaseForIA(h1);
+        testLeaseForIA(h2);
+        testLeaseForIA(h3);
+        testLeaseForIA(h4);
+        testLeaseForIA(h5);
+        testLeaseForIA(h6);
+    }
+}
+
+
+void
 HostTest::storeReservation(const Reservation& r,
 HostTest::storeReservation(const Reservation& r,
                            std::list<std::string>& address_list,
                            std::list<std::string>& address_list,
                            std::list<std::string>& prefix_list) {
                            std::list<std::string>& prefix_list) {
@@ -295,6 +668,7 @@ HostTest::configString(const DUID& duid,
         "                      \"delegated-len\": 64 } ],"
         "                      \"delegated-len\": 64 } ],"
         "    \"interface\" : \"eth0\"";
         "    \"interface\" : \"eth0\"";
 
 
+    // Create reservations.
     if (!address_list.empty() || !prefix_list.empty()) {
     if (!address_list.empty() || !prefix_list.empty()) {
         s << ","
         s << ","
             "    \"reservations\": ["
             "    \"reservations\": ["
@@ -326,6 +700,29 @@ HostTest::configString(const DUID& duid,
     return (s.str());
     return (s.str());
 }
 }
 
 
+void
+HostTest::requestIA(Dhcp6Client& client, const Hint& hint) {
+    if ((hint.getIAID() != 0) && !hint.isEmpty()) {
+        if (hint.isPrefix()) {
+            client.requestPrefix(hint.getIAID(), hint.getPrefixLen(),
+                                 hint.getPrefix());
+        } else {
+            client.requestAddress(hint.getIAID(), hint.getPrefix());
+        }
+    }
+}
+
+void
+HostTest::requestEmptyIAs(Dhcp6Client& client) {
+    // Create IAs with IAIDs between 1 and 6.
+    client.requestAddress(1);
+    client.requestAddress(2);
+    client.requestAddress(3);
+    client.requestPrefix(4);
+    client.requestPrefix(5);
+    client.requestPrefix(6);
+}
+
 
 
 // Test basic SARR scenarios against a server configured with one subnet
 // Test basic SARR scenarios against a server configured with one subnet
 // containing two reservations.  One reservation with a hostname, one
 // containing two reservations.  One reservation with a hostname, one
@@ -546,107 +943,258 @@ TEST_F(HostTest, hostIdentifiersOrder) {
     testReservationByIdentifier(client, 2, "2001:db8:1::2");
     testReservationByIdentifier(client, 2, "2001:db8:1::2");
 }
 }
 
 
-TEST_F(HostTest, reservationMultipleIASolicit) {
-    Dhcp6Client client;
-    client.setDUID("01:02:03:04");
-
-    const std::string c = configString(*client.getDuid(),
-                                       Reservation("2001:db8:1:1::1"),
-                                       Reservation("2001:db8:1:1::2"),
-                                       Reservation("2001:db8:1:1::3"),
-                                       Reservation("3000:1:1::/32"),
-                                       Reservation("3000:1:2::/32"),
-                                       Reservation("3000:1:3::/32"));
-
-    ASSERT_NO_THROW(configure(c, *client.getServer()));
-
-    client.requestAddress(1234);
-    client.requestAddress(2345);
-    client.requestAddress(3456);
-    client.requestPrefix(5678);
-    client.requestPrefix(6789);
-    client.requestPrefix(7890);
-
-    // Send Solicit and require that the client saves received configuration
-    // so as we can test that advertised configuration is correct.
-    ASSERT_NO_THROW(client.doSolicit(true));
-
-    ASSERT_EQ(6, client.getLeaseNum());
-
-    EXPECT_TRUE(client.hasLeaseForAddress(IOAddress("2001:db8:1:1::1")));
-    EXPECT_TRUE(client.hasLeaseForAddress(IOAddress("2001:db8:1:1::2")));
-    EXPECT_TRUE(client.hasLeaseForAddress(IOAddress("2001:db8:1:1::3")));
-    EXPECT_TRUE(client.hasLeaseForPrefix(IOAddress("3000:1:1::"), 32));
-    EXPECT_TRUE(client.hasLeaseForPrefix(IOAddress("3000:1:2::"), 32));
-    EXPECT_TRUE(client.hasLeaseForPrefix(IOAddress("3000:1:3::"), 32));
+// In this test the client sends Solicit with 3 IA_NAs and 3 IA_PDs
+// without hints and the server should return those IAs with 3 reserved
+// addresses and 3 reserved prefixes.
+TEST_F(HostTest, multipleIAsSolicit) {
+    testMultipleIAs(do_solicit_,
+                    Reservation("2001:db8:1:1::1"),
+                    Reservation("2001:db8:1:1::2"),
+                    Reservation("2001:db8:1:1::3"),
+                    Reservation("3000:1:1::/64"),
+                    Reservation("3000:1:2::/64"),
+                    Reservation("3000:1:3::/64"));
 }
 }
 
 
-TEST_F(HostTest, reservationMultipleIARequest) {
-    Dhcp6Client client;
-    client.setDUID("01:02:03:04");
-
-    const std::string c = configString(*client.getDuid(),
-                                       Reservation("2001:db8:1:1::1"),
-                                       Reservation("2001:db8:1:1::2"),
-                                       Reservation("2001:db8:1:1::3"),
-                                       Reservation("3000:1:1::/32"),
-                                       Reservation("3000:1:2::/32"),
-                                       Reservation("3000:1:3::/32"));
-
-    ASSERT_NO_THROW(configure(c, *client.getServer()));
-
-    client.requestAddress(1234);
-    client.requestAddress(2345);
-    client.requestAddress(3456);
-    client.requestPrefix(5678);
-    client.requestPrefix(6789);
-    client.requestPrefix(7890);
-
-    ASSERT_NO_THROW(client.doSARR());
-
-    ASSERT_EQ(6, client.getLeaseNum());
-
-    EXPECT_TRUE(client.hasLeaseForAddress(IOAddress("2001:db8:1:1::1")));
-    EXPECT_TRUE(client.hasLeaseForAddress(IOAddress("2001:db8:1:1::2")));
-    EXPECT_TRUE(client.hasLeaseForAddress(IOAddress("2001:db8:1:1::3")));
-    EXPECT_TRUE(client.hasLeaseForPrefix(IOAddress("3000:1:1::"), 32));
-    EXPECT_TRUE(client.hasLeaseForPrefix(IOAddress("3000:1:2::"), 32));
-    EXPECT_TRUE(client.hasLeaseForPrefix(IOAddress("3000:1:3::"), 32));
+// In this test the client performs 4-way exchange, sending 3 IA_NAs
+// and 3 IA_PDs without hints. The server should return those IAs
+// with 3 reserved addresses and 3 reserved prefixes.
+TEST_F(HostTest, multipleIAsRequest) {
+    testMultipleIAs(do_solicit_request_,
+                    Reservation("2001:db8:1:1::1"),
+                    Reservation("2001:db8:1:1::2"),
+                    Reservation("2001:db8:1:1::3"),
+                    Reservation("3000:1:1::/64"),
+                    Reservation("3000:1:2::/64"),
+                    Reservation("3000:1:3::/64"));
 }
 }
 
 
-TEST_F(HostTest, reservationAndDynamicIAs) {
-    Dhcp6Client client;
-    client.setDUID("01:02:03:04");
-
-    const std::string c = configString(*client.getDuid(),
-                                       Reservation("2001:db8:1:1::2"),
-                                       Reservation("2001:db8:1:1::3"),
-                                       Reservation("3000:1:1::/32"),
-                                       Reservation("3000:1:3::/32"));
+// In this test the client sends Solicit with 3 IA_NAs and 3 IA_PDs
+// without hints. The server has 2 reservations for addresses and
+// 2 reservations for prefixes for this client. The server should
+// assign reserved addresses and prefixes to the client, and return
+// them in 2 IA_NAs and 2 IA_PDs. For the remaining IA_NA and IA_PD
+// the server should allocate address and prefix from a dynamic pools.
+TEST_F(HostTest, staticAndDynamicIAs) {
+    testMultipleIAs(do_solicit_,
+                    Reservation("2001:db8:1:1::2"),
+                    Reservation("2001:db8:1:1::3"),
+                    Reservation("3000:1:1::/64"),
+                    Reservation("3000:1:3::/64"));
+}
 
 
-    ASSERT_NO_THROW(configure(c, *client.getServer()));
+// In this test the client sends Solicit with 3 IA_NAs and 3 IA_PDs.
+// The client includes an address hint for IAID = 1, a prefix length
+// hint for the IAID = 5, and the prefix hint for IAID = 6. The hints
+// match the reserved resources and should be allocated for the client.
+TEST_F(HostTest, multipleIAsHintsForReservations) {
+    testMultipleIAs(do_solicit_,
+                    Reservation("2001:db8:1:1::1"),
+                    Reservation("2001:db8:1:1::2"),
+                    Reservation("2001:db8:1:1::3"),
+                    Reservation("3000:1:1::/64"),
+                    Reservation("3000:1:2::/64"),
+                    Reservation("3000:1:3::/64"),
+                    StrictIAIDChecking::NO(),
+                    Hint(IAID(1), "2001:db8:1:1::2"),
+                    Hint(IAID(5), "::/64"),
+                    Hint(IAID(6), "3000:1:1::/64"));
+}
 
 
-    client.requestAddress(1234);
-    client.requestAddress(2345);
-    client.requestAddress(3456);
-    client.requestPrefix(5678);
-    client.requestPrefix(6789);
-    client.requestPrefix(7890);
+// In this test the client sends Solicit with 3 IA_NAs and 3 IA_PDs.
+// The client includes one address hint for IAID = 1 and one
+// prefix hint for IAID = 6. The hints point to an address and prefix
+// from the dynamic pools, but because the server has reservations
+// for other addresses and prefixes outside the pool, the address
+// and prefix specified as hint should not be allocated. Instead
+// the server should allocate reserved leases.
+TEST_F(HostTest, multipleIAsHintsInPool) {
+    testMultipleIAs(do_solicit_,
+                    Reservation("2001:db8:1:1::1"),
+                    Reservation("2001:db8:1:1::2"),
+                    Reservation("2001:db8:1:1::3"),
+                    Reservation("3000:1:1::/64"),
+                    Reservation("3000:1:2::/64"),
+                    Reservation("3000:1:3::/64"),
+                    StrictIAIDChecking::NO(),
+                    Hint(IAID(1), "2001:db8:1::2"),
+                    Hint(IAID(6), "3001::/64"));
+}
 
 
-    // Send Solicit and require that the client saves received configuration
-    // so as we can test that advertised configuration is correct.
-    ASSERT_NO_THROW(client.doSolicit(true));
+// In this test, the client sends Solicit with 3 IA_NAs and 3 IA_PDs.
+// The client includes one address hint for which the client has
+// reservation, one prefix hint for which the client has reservation,
+// one hint for an address from the dynamic pool and one hint for a
+// prefix from a dynamic pool. The server has reservations for 2
+// addresses and 2 prefixes. The server should allocate reserved
+// leases and address and prefix from a dynamic pool, which client
+// included as hints.
+TEST_F(HostTest, staticAndDynamicIAsHints) {
+    testMultipleIAs(do_solicit_,
+                    Reservation("2001:db8:1:1::1"),
+                    Reservation("2001:db8:1:1::3"),
+                    Reservation("3000:1:1::/64"),
+                    Reservation("3000:1:2::/64"),
+                    Reservation::UNSPEC(),
+                    Reservation::UNSPEC(),
+                    StrictIAIDChecking::NO(),
+                    Hint(IAID(1), "2001:db8:1::2"),
+                    Hint(IAID(3), "2001:db8:1:1::1"),
+                    Hint(IAID(5), "3001::/64"),
+                    Hint(IAID(6), "3000::/64"));
+}
 
 
-    ASSERT_EQ(6, client.getLeaseNum());
+// In this test, the client sends Solicit with 3 IA_NAs and 3 IA_PDs.
+// The server has reservation for two addresses and two prefixes for
+// this client. The client includes address hint in the third IA_NA
+// and in the third IA_PD. The server should offer 2 addresses in the
+// first two IA_NAs and 2 prefixes in the two IA_PDs. The server should
+// respect hints provided within the 3rd IA_NA and 3rd IA_PD. The server
+// wouldn't respect hints if they were provided within 1st or 2nd IA of
+// a given type, because the server always tries to allocate the
+// reserved leases in the first place.
+TEST_F(HostTest, staticAndDynamicIAsHintsStrictIAIDCheck) {
+    testMultipleIAs(do_solicit_,
+                    Reservation("2001:db8:1:1::1"),
+                    Reservation("2001:db8:1:1::2"),
+                    Reservation("3000:1:1::/64"),
+                    Reservation("3000:1:2::/64"),
+                    Reservation::UNSPEC(),
+                    Reservation::UNSPEC(),
+                    StrictIAIDChecking::YES(),
+                    Hint(IAID(3), "2001:db8:1::5"),
+                    Hint(IAID(6), "3001:0:0:10::/64"));
+}
 
 
-    EXPECT_TRUE(client.hasLeaseForAddress(IOAddress("2001:db8:1:1::2")));
-    EXPECT_TRUE(client.hasLeaseForAddress(IOAddress("2001:db8:1:1::3")));
-    EXPECT_TRUE(client.hasLeaseForPrefix(IOAddress("3000:1:1::"), 32));
-    EXPECT_TRUE(client.hasLeaseForPrefix(IOAddress("3000:1:3::"), 32));
+// In this test, the client performs 4-way exchange and includes 3 IA_NAs
+// and 3 IA_PDs. The client provides no hints. The server has 3 address
+// reservations and 3 prefix reservations for this client and allocates them
+// as a result of 4-way exchange. The client then sends a Renew and the server
+// should renew all leases allocated for the client during the 4-way exchange.
+TEST_F(HostTest, multipleIAsRenew) {
+    // 4-way exchange
+    testMultipleIAs(do_solicit_request_,
+                    Reservation("2001:db8:1:1::1"),
+                    Reservation("2001:db8:1:1::2"),
+                    Reservation("2001:db8:1:1::3"),
+                    Reservation("3000:1:1::/64"),
+                    Reservation("3000:1:2::/64"),
+                    Reservation("3000:1:3::/64"));
+
+    // Renew
+    ASSERT_NO_THROW(client_.doRenew());
+
+    // Make sure that the client still has the same leases.
+    ASSERT_EQ(6, client_.getLeaseNum());
+
+    EXPECT_TRUE(client_.hasLeaseForAddress(IOAddress("2001:db8:1:1::1")));
+    EXPECT_TRUE(client_.hasLeaseForAddress(IOAddress("2001:db8:1:1::2")));
+    EXPECT_TRUE(client_.hasLeaseForAddress(IOAddress("2001:db8:1:1::3")));
+    EXPECT_TRUE(client_.hasLeaseForPrefix(IOAddress("3000:1:1::"), 64));
+    EXPECT_TRUE(client_.hasLeaseForPrefix(IOAddress("3000:1:2::"), 64));
+    EXPECT_TRUE(client_.hasLeaseForPrefix(IOAddress("3000:1:3::"), 64));
+}
 
 
-    EXPECT_TRUE(client.hasLeaseForAddressRange(IOAddress("2001:db8:1::1"),
-                                               IOAddress("2001:db8:1::10")));
-    EXPECT_TRUE(client.hasLeaseForPrefixPool(IOAddress("3001::"), 32, 64));
+// In this test, the client performs 4-way exchange and includes 3 IA_NAs and
+// 3 IA_PDs and includes no hints. The server has reservations for 2 addresses
+// and 2 prefixes for this client. The server allocates reserved leases and
+// an additional address and prefix from the dynamic pools. The server is
+// reconfigured to add 3rd address and 3rd prefix reservation for the client.
+// The client sends a Renew and the server should renew existing leases and
+// allocate newly reserved address and prefix, replacing the previously
+// allocated dynamic leases. For both dynamically allocated leases, the
+// server should return IAs with zero lifetimes.
+TEST_F(HostTest, additionalReservationDuringRenew) {
+    // 4-way exchange to acquire 4 reserved leases and 2 dynamic leases.
+    testMultipleIAs(do_solicit_request_,
+                    Reservation("2001:db8:1:1::1"),
+                    Reservation("2001:db8:1:1::2"),
+                    Reservation("3000:1:1::/64"),
+                    Reservation("3000:1:2::/64"));
+
+    // The server must have not lease for the address and prefix for which
+    // we will later make reservations, because these are outside of the
+    // dynamic pool.
+    ASSERT_FALSE(client_.hasLeaseForAddress(IOAddress("2001:db8:1:1::3")));
+    ASSERT_FALSE(client_.hasLeaseForPrefix(IOAddress("3000:1:3::"), 64));
+
+    // Retrieve leases from the dynamic pools and store them so as we can
+    // later check that they were returned with zero lifetimes when the
+    // reservations are added.
+    std::vector<Lease6> leases =
+        client_.getLeasesByAddressRange(IOAddress("2001:db8:1::1"),
+                                        IOAddress("2001:db8:1::10"));
+    ASSERT_EQ(1, leases.size());
+    IOAddress dynamic_address_lease = leases[0].addr_;
+
+    leases = client_.getLeasesByPrefixPool(IOAddress("3001::"), 32, 64);
+    ASSERT_EQ(1, leases.size());
+    IOAddress dynamic_prefix_lease = leases[0].addr_;
+
+    // Add two additional reservations.
+    std::string c = configString(*client_.getDuid(),
+                                 Reservation("2001:db8:1:1::1"),
+                                 Reservation("2001:db8:1:1::2"),
+                                 Reservation("2001:db8:1:1::3"),
+                                 Reservation("3000:1:1::/64"),
+                                 Reservation("3000:1:2::/64"),
+                                 Reservation("3000:1:3::/64"));
+
+    ASSERT_NO_THROW(configure(c, *client_.getServer()));
+
+    // Client renews and includes all leases it currently has in the IAs.
+    ASSERT_NO_THROW(client_.doRenew());
+
+    // The expectation is that the server allocated two new reserved leases to
+    // the client and removed leases allocated from the dynamic pools. The
+    // number if leases in the server configuration should include those that
+    // are returned with zero lifetimes. Hence, the total number of leases
+    // should be equal to 6 + 2 = 8.
+    ASSERT_EQ(8, client_.getLeaseNum());
+
+    EXPECT_TRUE(client_.hasLeaseForAddress(IOAddress("2001:db8:1:1::1")));
+    EXPECT_TRUE(client_.hasLeaseForAddress(IOAddress("2001:db8:1:1::2")));
+    EXPECT_TRUE(client_.hasLeaseForAddress(IOAddress("2001:db8:1:1::3")));
+    EXPECT_TRUE(client_.hasLeaseForPrefix(IOAddress("3000:1:1::"), 64));
+    EXPECT_TRUE(client_.hasLeaseForPrefix(IOAddress("3000:1:2::"), 64));
+    EXPECT_TRUE(client_.hasLeaseForPrefix(IOAddress("3000:1:3::"), 64));
+
+    // Make sure that the replaced leases have been returned with zero liftimes.
+    EXPECT_TRUE(client_.hasLeaseWithZeroLifetimeForAddress(dynamic_address_lease));
+    EXPECT_TRUE(client_.hasLeaseWithZeroLifetimeForPrefix(dynamic_prefix_lease, 64));
+
+    // Now let's test the scenario when all reservations are removed for this
+    // client.
+    c = configString(*client_.getDuid());
+
+    ASSERT_NO_THROW(configure(c, *client_.getServer()));
+
+    // An attempt to renew should result in removing all allocated leases,
+    // because these leases are no longer reserved and they don't belong to the
+    // dynamic pools.
+    ASSERT_NO_THROW(client_.doRenew());
+
+    // The total number of leases should include removed leases and newly
+    // allocated once, i.e. 6 + 6 = 12.
+    ASSERT_EQ(12, client_.getLeaseNum());
+
+    // All removed leases should be returned with zero liftimes.
+    EXPECT_TRUE(client_.hasLeaseWithZeroLifetimeForAddress(IOAddress("2001:db8:1:1::1")));
+    EXPECT_TRUE(client_.hasLeaseWithZeroLifetimeForAddress(IOAddress("2001:db8:1:1::2")));
+    EXPECT_TRUE(client_.hasLeaseWithZeroLifetimeForAddress(IOAddress("2001:db8:1:1::3")));
+    EXPECT_TRUE(client_.hasLeaseWithZeroLifetimeForPrefix(IOAddress("3000:1:1::"), 64));
+    EXPECT_TRUE(client_.hasLeaseWithZeroLifetimeForPrefix(IOAddress("3000:1:2::"), 64));
+    EXPECT_TRUE(client_.hasLeaseWithZeroLifetimeForPrefix(IOAddress("3000:1:3::"), 64));
+
+    // Make sure that all address leases are within the dynamic pool range.
+    leases = client_.getLeasesByAddressRange(IOAddress("2001:db8:1::1"),
+                                            IOAddress("2001:db8:1::10"));
+    EXPECT_EQ(3, leases.size());
+
+    // Make sure that all prefix leases are also within the dynamic pool range.
+    leases = client_.getLeasesByPrefixPool(IOAddress("3001::"), 32, 64);
+    EXPECT_EQ(3, leases.size());
 }
 }
 
 
+
 } // end of anonymous namespace
 } // end of anonymous namespace

+ 47 - 24
src/lib/dhcpsrv/alloc_engine.cc

@@ -845,37 +845,60 @@ AllocEngine::removeNonmatchingReservedLeases6(ClientContext6& ctx,
     // so the operation shouldn't be that expensive.
     // so the operation shouldn't be that expensive.
     Lease6Collection copy = existing_leases;
     Lease6Collection copy = existing_leases;
 
 
-    for (Lease6Collection::const_iterator candidate = copy.begin();
-         candidate != copy.end(); ++candidate) {
+    BOOST_FOREACH(const Lease6Ptr& candidate, copy) {
+        // If we have reservation we should check if the reservation is for
+        // the candidate lease. If so, we simply accept the lease.
+        if (ctx.host_) {
+            if (candidate->type_ == Lease6::TYPE_NA) {
+                if (ctx.host_->hasReservation(IPv6Resrv(IPv6Resrv::TYPE_NA,
+                                                        candidate->addr_))) {
+                    continue;
+                }
+            } else {
+                if (ctx.host_->hasReservation(IPv6Resrv(IPv6Resrv::TYPE_PD,
+                                                        candidate->addr_,
+                                                        candidate->prefixlen_))) {
+                    continue;
+                }
+            }
+        }
 
 
+        // The candidate address doesn't appear to be reserved for us.
+        // We have to make a bit more expensive operation here to retrieve
+        // the reservation for the candidate lease and see if it is
+        // reserved for someone else.
         ConstHostPtr host = HostMgr::instance().get6(ctx.subnet_->getID(),
         ConstHostPtr host = HostMgr::instance().get6(ctx.subnet_->getID(),
-                                                     (*candidate)->addr_);
-
-        if (!host || (host == ctx.host_)) {
-            // Not reserved or reserved for us. That's ok, let's check
-            // the next lease.
+                                                     candidate->addr_);
+        // If lease is not reserved to someone else, it means that it can
+        // be allocated to us from a dynamic pool, but we must check if
+        // this lease belongs to any pool. If it does, we can proceed to
+        // checking the next lease.
+        if (!host && ctx.subnet_->inPool(candidate->type_, candidate->addr_)) {
             continue;
             continue;
         }
         }
 
 
-        // Ok, we have a problem. This host has a lease that is reserved
-        // for someone else. We need to recover from this.
-        if (ctx.currentIA().type_ == Lease::TYPE_NA) {
-            LOG_INFO(alloc_engine_logger, ALLOC_ENGINE_V6_REVOKED_ADDR_LEASE)
-                .arg((*candidate)->addr_.toText()).arg(ctx.duid_->toText())
-                .arg(host->getIdentifierAsText());
-        } else {
-            LOG_INFO(alloc_engine_logger, ALLOC_ENGINE_V6_REVOKED_PREFIX_LEASE)
-                .arg((*candidate)->addr_.toText())
-                .arg(static_cast<int>((*candidate)->prefixlen_))
-                .arg(ctx.duid_->toText())
-                .arg(host->getIdentifierAsText());
+        if (host) {
+            // Ok, we have a problem. This host has a lease that is reserved
+            // for someone else. We need to recover from this.
+            if (ctx.currentIA().type_ == Lease::TYPE_NA) {
+                LOG_INFO(alloc_engine_logger, ALLOC_ENGINE_V6_REVOKED_ADDR_LEASE)
+                    .arg(candidate->addr_.toText()).arg(ctx.duid_->toText())
+                    .arg(host->getIdentifierAsText());
+            } else {
+                LOG_INFO(alloc_engine_logger, ALLOC_ENGINE_V6_REVOKED_PREFIX_LEASE)
+                    .arg(candidate->addr_.toText())
+                    .arg(static_cast<int>(candidate->prefixlen_))
+                    .arg(ctx.duid_->toText())
+                    .arg(host->getIdentifierAsText());
+            }
         }
         }
 
 
-        // Remove this lease from LeaseMgr
-        LeaseMgrFactory::instance().deleteLease((*candidate)->addr_);
+        // Remove this lease from LeaseMgr as it is reserved to someone
+        // else or doesn't belong to a pool.
+        LeaseMgrFactory::instance().deleteLease(candidate->addr_);
 
 
         // Update DNS if needed.
         // Update DNS if needed.
-        queueNCR(CHG_REMOVE, *candidate);
+        queueNCR(CHG_REMOVE, candidate);
 
 
         // Need to decrease statistic for assigned addresses.
         // Need to decrease statistic for assigned addresses.
         StatsMgr::instance().addValue(
         StatsMgr::instance().addValue(
@@ -890,10 +913,10 @@ AllocEngine::removeNonmatchingReservedLeases6(ClientContext6& ctx,
         // should not interfere with it.
         // should not interfere with it.
 
 
         // Add this to the list of removed leases.
         // Add this to the list of removed leases.
-        ctx.currentIA().old_leases_.push_back(*candidate);
+        ctx.currentIA().old_leases_.push_back(candidate);
 
 
         // Let's remove this candidate from existing leases
         // Let's remove this candidate from existing leases
-        removeLeases(existing_leases, (*candidate)->addr_);
+        removeLeases(existing_leases, candidate->addr_);
     }
     }
 }
 }
 
 

+ 1 - 1
src/lib/dhcpsrv/lease.h

@@ -484,7 +484,7 @@ struct Lease6 : public Lease {
            uint32_t iaid, uint32_t preferred, uint32_t valid, uint32_t t1,
            uint32_t iaid, uint32_t preferred, uint32_t valid, uint32_t t1,
            uint32_t t2, SubnetID subnet_id, const bool fqdn_fwd,
            uint32_t t2, SubnetID subnet_id, const bool fqdn_fwd,
            const bool fqdn_rev, const std::string& hostname,
            const bool fqdn_rev, const std::string& hostname,
-           const HWAddrPtr& hwaddr = HWAddrPtr(), uint8_t prefixlen = 0);
+           const HWAddrPtr& hwaddr = HWAddrPtr(), uint8_t prefixlen = 128);
 
 
     /// @brief Constructor
     /// @brief Constructor
     ///
     ///