Browse Source

[master] Merge branch 'trac4321'

Marcin Siodelski 8 years ago
parent
commit
0bfb2a9093

+ 7 - 7
src/bin/dhcp6/tests/confirm_unittest.cc

@@ -1,4 +1,4 @@
-// Copyright (C) 2014-2015 Internet Systems Consortium, Inc. ("ISC")
+// Copyright (C) 2014-2016 Internet Systems Consortium, Inc. ("ISC")
 //
 // This Source Code Form is subject to the terms of the Mozilla Public
 // License, v. 2.0. If a copy of the MPL was not distributed with this
@@ -115,7 +115,7 @@ TEST_F(ConfirmTest, sanityCheck) {
 TEST_F(ConfirmTest, directClientSameIAID) {
     Dhcp6Client client;
     // Configure client to request IA_NA.
-    client.useNA();
+    client.requestAddress();
     // Make 4-way exchange to get the lease.
     ASSERT_NO_FATAL_FAILURE(requestLease(CONFIRM_CONFIGS[0], 2, client));
     // Keep the client's lease for future reference.
@@ -157,7 +157,7 @@ TEST_F(ConfirmTest, directClientSameIAID) {
 TEST_F(ConfirmTest, directClientDifferentIAID) {
     Dhcp6Client client;
     // Configure client to request IA_NA.
-    client.useNA();
+    client.requestAddress();
     // Make 4-way exchange to get the lease.
     ASSERT_NO_FATAL_FAILURE(requestLease(CONFIRM_CONFIGS[0], 2, client));
     // Keep the client's lease for future reference.
@@ -205,7 +205,7 @@ TEST_F(ConfirmTest, relayedClient) {
     // Client to send relayed message.
     client.useRelay();
     // Configure client to request IA_NA.
-    client.useNA();
+    client.requestAddress();
     // Make 4-way exchange to get the lease.
     ASSERT_NO_FATAL_FAILURE(requestLease(CONFIRM_CONFIGS[1], 2, client));
     // Keep the client's lease for future reference.
@@ -265,7 +265,7 @@ TEST_F(ConfirmTest, relayedClientNoSubnet) {
     // Client to send relayed message.
     client.useRelay();
     // Configure client to request IA_NA.
-    client.useNA();
+    client.requestAddress();
     // Make 4-way exchange to get the lease.
     ASSERT_NO_FATAL_FAILURE(requestLease(CONFIRM_CONFIGS[1], 2, client));
     // Now that the client has a lease, let's remove any subnets to check
@@ -305,7 +305,7 @@ TEST_F(ConfirmTest, relayedUnicast) {
     // Client to send relayed message.
     client.useRelay();
     // Configure client to request IA_NA.
-    client.useNA();
+    client.requestAddress();
     // Make 4-way exchange to get the lease.
     ASSERT_NO_FATAL_FAILURE(requestLease(CONFIRM_CONFIGS[1], 2, client));
     // Make sure we have got the lease.
@@ -329,7 +329,7 @@ TEST_F(ConfirmTest, relayedUnicast) {
 TEST_F(ConfirmTest, unicast) {
     Dhcp6Client client;
     // Configure client to request IA_NA.
-    client.useNA();
+    client.requestAddress();
     // Make 4-way exchange to get the lease.
     ASSERT_NO_FATAL_FAILURE(requestLease(CONFIRM_CONFIGS[0], 2, client));
     // Make sure the client has got the lease.

+ 9 - 6
src/bin/dhcp6/tests/decline_unittest.cc

@@ -1,4 +1,4 @@
-// Copyright (C) 2015 Internet Systems Consortium, Inc. ("ISC")
+// Copyright (C) 2015-2016 Internet Systems Consortium, Inc. ("ISC")
 //
 // This Source Code Form is subject to the terms of the Mozilla Public
 // License, v. 2.0. If a copy of the MPL was not distributed with this
@@ -57,7 +57,6 @@ public:
 
     /// @brief IAID used for IA_NA.
     uint32_t na_iaid_;
-
 };
 
 };
@@ -78,7 +77,7 @@ Dhcpv6SrvTest::acquireAndDecline(Dhcp6Client& client,
     StatsMgr::instance().setValue("declined-addresses", static_cast<int64_t>(0));
 
     client.setDUID(duid1);
-    client.useNA(iaid1);
+    client.requestAddress(iaid1);
 
     // Configure the server with a configuration.
     ASSERT_NO_THROW(configure(DECLINE_CONFIGS[0], *client.getServer()));
@@ -117,6 +116,9 @@ Dhcpv6SrvTest::acquireAndDecline(Dhcp6Client& client,
     ASSERT_TRUE(declined_global);
     uint64_t before_global = declined_cnt->getInteger().first;
 
+    /// Determines if the client will include address in the messages it sends.
+    bool include_address_ = true;
+
     // Let's tamper with the address if necessary.
     switch (addr_type) {
     case VALID_ADDR:
@@ -129,11 +131,12 @@ Dhcpv6SrvTest::acquireAndDecline(Dhcp6Client& client,
         break;
     case NO_ADDR:
         // Tell the client to not include an address in its IA_NA
-        client.includeAddress(false);
+        include_address_ = false;
         break;
     case NO_IA:
         // Tell the client to not include IA_NA at all
-        client.useNA(false);
+        client.config_.clear();
+        client.clearRequestedIAs();
     }
 
     // Use the second duid
@@ -143,7 +146,7 @@ Dhcpv6SrvTest::acquireAndDecline(Dhcp6Client& client,
     client.config_.leases_[0].iaid_ = iaid2;
 
     // Ok, let's decline the lease.
-    ASSERT_NO_THROW(client.doDecline());
+    ASSERT_NO_THROW(client.doDecline(include_address_));
 
     // Let's check if there's a lease
     Lease6Ptr lease = LeaseMgrFactory::instance().getLease6(Lease::TYPE_NA,

+ 221 - 96
src/bin/dhcp6/tests/dhcp6_client.cc

@@ -1,4 +1,4 @@
-// Copyright (C) 2014-2015 Internet Systems Consortium, Inc. ("ISC")
+// Copyright (C) 2014-2016 Internet Systems Consortium, Inc. ("ISC")
 //
 // This Source Code Form is subject to the terms of the Mozilla Public
 // License, v. 2.0. If a copy of the MPL was not distributed with this
@@ -13,6 +13,7 @@
 #include <dhcp/option_int_array.h>
 #include <dhcp/pkt6.h>
 #include <dhcpsrv/lease.h>
+#include <dhcpsrv/pool.h>
 #include <dhcp6/tests/dhcp6_client.h>
 #include <util/buffer.h>
 #include <boost/foreach.hpp>
@@ -20,6 +21,7 @@
 #include <cstdlib>
 #include <time.h>
 
+using namespace isc::asiolink;
 using namespace isc::dhcp;
 using namespace isc::dhcp::test;
 
@@ -53,7 +55,7 @@ struct getLeasesByPropertyFun {
         for (typename std::vector<Lease6>::const_iterator lease =
                  config.leases_.begin(); lease != config.leases_.end();
              ++lease) {
-            // Check if fulfils the condition.
+            // Check if fulfills the condition.
             if ((equals && ((*lease).*MemberPointer) == property) ||
                 (!equals && ((*lease).*MemberPointer) != property)) {
                 // Found the matching lease.
@@ -63,6 +65,25 @@ struct getLeasesByPropertyFun {
     }
 };
 
+/// @brief Returns leases which belong to specified pool.
+///
+/// @param config DHCP client configuration structure holding leases.
+/// @param pool Pool to which returned leases belong.
+/// @param [out] leases A vector in which the function will store leases
+/// found.
+void getLeasesByPool(const Dhcp6Client::Configuration& config,
+                     const Pool6& pool, std::vector<Lease6>& leases) {
+    for (std::vector<Lease6>::const_iterator lease =
+             config.leases_.begin(); lease != config.leases_.end();
+         ++lease) {
+        // Check if prefix in range.
+        if (pool.inRange(lease->addr_)) {
+            // Found the matching lease.
+            leases.push_back(*lease);
+        }
+    }
+}
+
 }; // end of anonymous namespace
 
 namespace isc {
@@ -77,18 +98,12 @@ Dhcp6Client::Dhcp6Client() :
     link_local_("fe80::3a60:77ff:fed5:cdef"),
     iface_name_("eth0"),
     srv_(boost::shared_ptr<NakedDhcpv6Srv>(new NakedDhcpv6Srv(0))),
-    use_na_(false),
-    use_pd_(false),
     use_relay_(false),
     use_oro_(false),
     use_client_id_(true),
     use_rapid_commit_(false),
-    address_hint_(),
-    prefix_hint_(),
-    fqdn_(),
-    na_iaid_(1234),
-    pd_iaid_(5678),
-    include_address_(true) {
+    client_ias_(),
+    fqdn_() {
 }
 
 Dhcp6Client::Dhcp6Client(boost::shared_ptr<NakedDhcpv6Srv>& srv) :
@@ -99,18 +114,12 @@ Dhcp6Client::Dhcp6Client(boost::shared_ptr<NakedDhcpv6Srv>& srv) :
     link_local_("fe80::3a60:77ff:fed5:cdef"),
     iface_name_("eth0"),
     srv_(srv),
-    use_na_(false),
-    use_pd_(false),
     use_relay_(false),
     use_oro_(false),
     use_client_id_(true),
     use_rapid_commit_(false),
-    address_hint_(),
-    prefix_hint_(),
-    fqdn_(),
-    na_iaid_(1234),
-    pd_iaid_(5678),
-    include_address_(true) {
+    client_ias_(),
+    fqdn_() {
 }
 
 void
@@ -240,52 +249,70 @@ Dhcp6Client::appendFQDN() {
 
 void
 Dhcp6Client::appendRequestedIAs(const Pkt6Ptr& query) const {
-    if (use_na_) {
-        conditionallyAppendRequestedIA(query, D6O_IA_NA, na_iaid_);
-    }
-
-    if (use_pd_) {
-        conditionallyAppendRequestedIA(query, D6O_IA_PD, pd_iaid_);
-    }
-}
-
-void
-Dhcp6Client::conditionallyAppendRequestedIA(const Pkt6Ptr& query,
-                                            const uint8_t ia_type,
-                                            const uint32_t iaid) const {
-    // Get existing options of the specified type.
-    OptionCollection options = query->getOptions(ia_type);
-    std::pair<unsigned int, OptionPtr> option_pair;
-
-    // Check if the option we want to add is already present.
-    BOOST_FOREACH(option_pair, options) {
-        Option6IAPtr ia = boost::dynamic_pointer_cast<Option6IA>(option_pair.second);
-        // This shouldn't happen.
-        if (!ia) {
-            isc_throw(Unexpected, "Dhcp6Client: IA option has an invalid C++ type;"
-                      " this is a programming issue");
+    BOOST_FOREACH(const ClientIA& ia, client_ias_) {
+        OptionCollection options =
+            query->getOptions(ia.type_ == Lease::TYPE_NA ?
+                              D6O_IA_NA : D6O_IA_PD);
+        std::pair<unsigned int, OptionPtr> option_pair;
+        Option6IAPtr existing_ia;
+        BOOST_FOREACH(option_pair, options) {
+            Option6IAPtr ia_opt =
+                boost::dynamic_pointer_cast<Option6IA>(option_pair.second);
+            // This shouldn't happen.
+            if (!ia_opt) {
+                isc_throw(Unexpected,
+                          "Dhcp6Client: IA option has an invalid C++ type;"
+                          " this is a programming issue");
+            }
+            if (ia_opt->getIAID() == ia.iaid_) {
+                existing_ia = ia_opt;
+            }
         }
-        // There is an option of the specific type already. If it has our
-        // IAID we return here, because we don't want to duplicate the IA.
-        // If IAID is different, we check other existing IAs.
-        if (ia->getIAID() == iaid) {
-            return;
+        if (!existing_ia) {
+            existing_ia.reset(new Option6IA(ia.type_ == Lease::TYPE_NA ?
+                                            D6O_IA_NA : D6O_IA_PD, ia.iaid_));
+            query->addOption(existing_ia);
         }
-    }
 
-    // If we're here, it means that there is no instance of our IA yet.
-    Option6IAPtr requested_ia(new Option6IA(ia_type, iaid));
-    // Add prefix hint if specified.
-    if (prefix_hint_ && (ia_type == D6O_IA_PD)) {
-        requested_ia->addOption(prefix_hint_);
+        bool option_exists = false;
+        if ((ia.type_ == Lease::TYPE_NA) && !ia.prefix_.isV6Zero()) {
+            Option6IAAddrPtr ia_addr(new Option6IAAddr(D6O_IAADDR, ia.prefix_,
+                                                       0, 0));
+            BOOST_FOREACH(option_pair, existing_ia->getOptions()) {
+                Option6IAAddrPtr existing_addr = boost::dynamic_pointer_cast<
+                    Option6IAAddr>(option_pair.second);
+                if (existing_addr &&
+                    (existing_addr->getAddress() == ia.prefix_)) {
+                    option_exists = true;
+                }
+            }
 
-    } else if (address_hint_ && (ia_type == D6O_IA_NA)) {
-        requested_ia->addOption(address_hint_);
-    }
+            if (!option_exists) {
+                existing_ia->addOption(ia_addr);
+            }
 
-    query->addOption(requested_ia);
-}
+        } else if ((ia.type_ == Lease::TYPE_PD) &&
+                   (!ia.prefix_.isV6Zero() || (ia.prefix_len_ > 0))) {
+            Option6IAPrefixPtr ia_prefix(new Option6IAPrefix(D6O_IAPREFIX,
+                                                             ia.prefix_,
+                                                             ia.prefix_len_,
+                                                             0, 0));
+            BOOST_FOREACH(option_pair, existing_ia->getOptions()) {
+                Option6IAPrefixPtr existing_prefix =
+                    boost::dynamic_pointer_cast<Option6IAPrefix>(option_pair.second);
+                if (existing_prefix &&
+                    (existing_prefix->getAddress() == ia.prefix_) &&
+                    existing_prefix->getLength()) {
+                    option_exists = true;
+                }
+            }
 
+            if (!option_exists) {
+                existing_ia->addOption(ia_prefix);
+            }
+        }
+    }
+}
 
 void
 Dhcp6Client::copyIAs(const Pkt6Ptr& source, const Pkt6Ptr& dest) {
@@ -390,7 +417,7 @@ Dhcp6Client::doSARR() {
 }
 
 void
-Dhcp6Client::doSolicit() {
+Dhcp6Client::doSolicit(const bool always_apply_config) {
     context_.query_ = createMsg(DHCPV6_SOLICIT);
     if (forced_server_id_) {
         context_.query_->addOption(forced_server_id_);
@@ -410,9 +437,12 @@ Dhcp6Client::doSolicit() {
     context_.response_ = receiveOneMsg();
 
     // If using Rapid Commit and the server has responded with Reply,
-    // let's apply received configuration.
-    if (use_rapid_commit_ && context_.response_ &&
-        context_.response_->getType() == DHCPV6_REPLY) {
+    // let's apply received configuration. We also apply the configuration
+    // for the Advertise if instructed to do so.
+    if (context_.response_ &&
+        (always_apply_config ||
+         (use_rapid_commit_ &&
+          context_.response_->getType() == DHCPV6_REPLY))) {
         config_.clear();
         applyRcvdConfiguration(context_.response_);
     }
@@ -451,21 +481,7 @@ Dhcp6Client::doInfRequest() {
 
     // IA_NA, IA_TA and IA_PD options are not allowed in INF-REQUEST,
     // but hey! Let's test it.
-    if (use_na_) {
-        // Insert IA_NA option.
-        context_.query_->addOption(Option6IAPtr(new Option6IA(D6O_IA_NA,
-                                                              na_iaid_)));
-    }
-
-    // IA-PD is also not allowed. So it may be useful in testing, too.
-    if (use_pd_) {
-        // Insert IA_PD option.
-        Option6IAPtr ia(new Option6IA(D6O_IA_PD, pd_iaid_));
-        if (prefix_hint_) {
-            ia->addOption(prefix_hint_);
-        }
-        context_.query_->addOption(ia);
-    }
+    appendRequestedIAs(context_.query_);
 
     sendMsg(context_.query_);
     context_.response_ = receiveOneMsg();
@@ -490,8 +506,10 @@ Dhcp6Client::doRenew() {
     appendFQDN();
 
     context_.query_ = query;
+
     sendMsg(context_.query_);
     context_.response_ = receiveOneMsg();
+
     // Apply configuration only if the server has responded.
     if (context_.response_) {
         config_.clear();
@@ -536,7 +554,7 @@ Dhcp6Client::doConfirm() {
 }
 
 void
-Dhcp6Client::doDecline() {
+Dhcp6Client::doDecline(const bool include_address) {
     Pkt6Ptr query = createMsg(DHCPV6_DECLINE);
     if (!forced_server_id_) {
         query->addOption(context_.response_->getOption(D6O_SERVERID));
@@ -544,7 +562,7 @@ Dhcp6Client::doDecline() {
         query->addOption(forced_server_id_);
     }
 
-    generateIAFromLeases(query);
+    generateIAFromLeases(query, include_address);
 
     context_.query_ = query;
     sendMsg(context_.query_);
@@ -558,14 +576,10 @@ Dhcp6Client::doDecline() {
 }
 
 void
-Dhcp6Client::generateIAFromLeases(const Pkt6Ptr& query) {
+Dhcp6Client::generateIAFromLeases(const Pkt6Ptr& query,
+                                  const bool include_address) {
     /// @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();
          lease != config_.leases_.end(); ++lease) {
         if (lease->type_ != Lease::TYPE_NA) {
@@ -574,7 +588,7 @@ Dhcp6Client::generateIAFromLeases(const Pkt6Ptr& query) {
 
         Option6IAPtr ia(new Option6IA(D6O_IA_NA, lease->iaid_));
 
-        if (include_address_) {
+        if (include_address) {
             ia->addOption(OptionPtr(new Option6IAAddr(D6O_IAADDR,
                   lease->addr_, lease->preferred_lft_, lease->valid_lft_)));
         }
@@ -665,6 +679,118 @@ Dhcp6Client::getLeasesWithZeroLifetime() const {
     return (leases);
 }
 
+std::vector<Lease6>
+Dhcp6Client::getLeasesByAddress(const IOAddress& address) const {
+    std::vector<Lease6> leases;
+    getLeasesByProperty<Lease, IOAddress, &Lease::addr_>(address, true, leases);
+    return (leases);
+}
+
+std::vector<Lease6>
+Dhcp6Client::getLeasesByAddressRange(const IOAddress& first,
+                                     const IOAddress& second) const {
+    std::vector<Lease6> leases;
+    getLeasesByPool(config_, Pool6(Lease::TYPE_NA, first, second), leases);
+    return (leases);
+}
+
+std::vector<Lease6>
+Dhcp6Client::getLeasesByPrefixPool(const asiolink::IOAddress& prefix,
+                                   const uint8_t prefix_len,
+                                   const uint8_t delegated_len) const {
+    std::vector<Lease6> leases;
+    getLeasesByPool(config_, Pool6(Lease::TYPE_PD, prefix, prefix_len,
+                                   delegated_len), leases);
+    return (leases);
+}
+
+bool
+Dhcp6Client::hasLeaseForAddress(const asiolink::IOAddress& address) const {
+    std::vector<Lease6> leases = getLeasesByAddress(address);
+    return (!leases.empty());
+}
+
+bool
+Dhcp6Client::hasLeaseForAddress(const asiolink::IOAddress& address,
+                                const IAID& 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,
+                                     const asiolink::IOAddress& last) const {
+    std::vector<Lease6> leases = getLeasesByAddressRange(first, last);
+    return (!leases.empty());
+}
+
+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,
+                               const uint8_t prefix_len) const {
+    std::vector<Lease6> leases = getLeasesByAddress(prefix);
+    BOOST_FOREACH(const Lease6& lease, leases) {
+        if (lease.prefixlen_ == prefix_len) {
+            return (true);
+        }
+    }
+    return (false);
+}
+
+bool
+Dhcp6Client::hasLeaseForPrefix(const asiolink::IOAddress& prefix,
+                               const uint8_t prefix_len,
+                               const IAID& 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,
+                                   const uint8_t prefix_len,
+                                   const uint8_t delegated_len) const {
+    std::vector<Lease6> leases = getLeasesByPrefixPool(prefix, prefix_len,
+                                                       delegated_len);
+    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
 Dhcp6Client::getStatusCode(const uint32_t iaid) const {
     std::map<uint32_t, uint16_t>::const_iterator status_code =
@@ -732,30 +858,29 @@ Dhcp6Client::sendMsg(const Pkt6Ptr& msg) {
     }
     // Repack the message to simulate wire-data parsing.
     msg->pack();
+
     Pkt6Ptr msg_copy(new Pkt6(static_cast<const uint8_t*>
                               (msg->getBuffer().getData()),
                               msg->getBuffer().getLength()));
     msg_copy->setRemoteAddr(link_local_);
     msg_copy->setLocalAddr(dest_addr_);
     msg_copy->setIface(iface_name_);
+
     srv_->fakeReceive(msg_copy);
     srv_->run();
 }
 
 void
-Dhcp6Client::useHint(const uint32_t pref_lft, const uint32_t valid_lft,
-                     const std::string& address) {
-    address_hint_.reset(new Option6IAAddr(D6O_IAADDR,
-                                          asiolink::IOAddress(address),
-                                          pref_lft, valid_lft));
+Dhcp6Client::requestAddress(const uint32_t iaid,
+                            const asiolink::IOAddress& address) {
+    client_ias_.push_back(ClientIA(Lease::TYPE_NA, iaid, address, 128));
 }
 
 void
-Dhcp6Client::useHint(const uint32_t pref_lft, const uint32_t valid_lft,
-                     const uint8_t len, const std::string& prefix) {
-    prefix_hint_.reset(new Option6IAPrefix(D6O_IAPREFIX,
-                                           asiolink::IOAddress(prefix),
-                                           len, pref_lft, valid_lft));
+Dhcp6Client::requestPrefix(const uint32_t iaid,
+                           const uint8_t prefix_len,
+                           const asiolink::IOAddress& prefix) {
+    client_ias_.push_back(ClientIA(Lease::TYPE_PD, iaid, prefix, prefix_len));
 }
 
 void

+ 196 - 108
src/bin/dhcp6/tests/dhcp6_client.h

@@ -11,9 +11,12 @@
 #include <dhcp/duid.h>
 #include <dhcp/option.h>
 #include <dhcp/option6_client_fqdn.h>
+#include <dhcpsrv/lease.h>
 #include <dhcp6/tests/dhcp6_test_utils.h>
+#include <util/staged_value.h>
 #include <boost/noncopyable.hpp>
 #include <boost/shared_ptr.hpp>
+#include <list>
 #include <set>
 #include <vector>
 
@@ -142,6 +145,27 @@ public:
         Pkt6Ptr response_;
     };
 
+    /// @brief Structure holding information to be placed in client's IA.
+    struct ClientIA {
+        Lease::Type type_;            ///< IA type
+        uint32_t iaid_;               ///< IAID
+        asiolink::IOAddress prefix_;  ///< prefix or address
+        uint8_t prefix_len_;          ///< prefix length
+
+        /// @brief Constructor.
+        ///
+        /// @param type IA type.
+        /// @param iaid IAID.
+        /// @param prefix Address or prefix.
+        /// @param prefix_len Prefix length.
+        ClientIA(const Lease::Type& type, const uint32_t iaid,
+                 const asiolink::IOAddress& prefix,
+                 const uint8_t prefix_len)
+            : type_(type), iaid_(iaid), prefix_(prefix),
+              prefix_len_(prefix_len) {
+        }
+    };
+
     /// @brief Creates a new client.
     ///
     /// This constructor initializes the class members to default values:
@@ -203,12 +227,15 @@ public:
     /// i.e. sends a Solicit to the server and receives Advertise. It doesn't
     /// set the lease configuration in the @c config_.
     ///
+    /// @param always_apply_config Apply received configuration even if the
+    /// Advertise message is received. Default value is false.
+    ///
     /// @throw This function doesn't throw exceptions on its own, but it calls
     /// functions that are not exception safe, so it may throw exceptions if
     /// error occurs.
     ///
     /// @todo Perform sanity checks on returned messages.
-    void doSolicit();
+    void doSolicit(const bool always_apply_config = false);
 
     /// @brief Sends a Renew to the server and receives the Reply.
     ///
@@ -265,13 +292,14 @@ public:
     ///
     /// This function simulates sending the Decline message to the server and
     /// receiving the server's response.
-    void doDecline();
+    /// @param include_address should the address be included?
+    void doDecline(const bool include_address = true);
 
     /// @brief Performs stateless (inf-request / reply) exchange.
     ///
     /// This function generates Information-request message, sends it
     /// to the server and then receives the reply. Contents of the Inf-Request
-    /// are controlled by use_na_, use_pd_, use_client_id_ and use_oro_
+    /// are controlled by client_ias_, use_client_id_ and use_oro_
     /// fields. This method does not process the response in any specific
     /// way, just stores it.
     void doInfRequest();
@@ -335,6 +363,110 @@ public:
     /// @brief Returns leases with zero lifetimes.
     std::vector<Lease6> getLeasesWithZeroLifetime() const;
 
+    /// @brief Returns leases by lease address/prefix.
+    ///
+    /// @param address Leased address.
+    ///
+    /// @return Vector containing leases for the specified address.
+    std::vector<Lease6>
+    getLeasesByAddress(const asiolink::IOAddress& address) const;
+
+    /// @brief Returns leases belonging to specified address range.
+    ///
+    /// @param first Lower bound of the address range.
+    /// @param second Upper bound of the address range.
+    ///
+    /// @return Vector containing leases belonging to specified address range.
+    std::vector<Lease6>
+    getLeasesByAddressRange(const asiolink::IOAddress& first,
+                            const asiolink::IOAddress& second) const;
+
+    /// @brief Returns leases belonging to prefix pool.
+    ///
+    /// @param prefix Prefix of the pool.
+    /// @param prefix_len Prefix length.
+    /// @param delegated_len Delegated prefix length.
+    ///
+    /// @return Vector containing leases belonging to specified prefix pool.
+    std::vector<Lease6>
+    getLeasesByPrefixPool(const asiolink::IOAddress& prefix,
+                          const uint8_t prefix_len,
+                          const uint8_t delegated_len) const;
+
+    /// @brief Checks if client has lease for the specified address.
+    ///
+    /// @param address Address for which lease should be found.
+    ///
+    /// @return true if client has lease for the address, false otherwise.
+    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 IAID& iaid) const;
+
+    /// @brief Checks if client has a lease for an address within range.
+    ///
+    /// @param first Lower bound of the address range.
+    /// @param last Upper bound of the address range.
+    ///
+    /// @return true if client has lease for the address within the range,
+    /// false otherwise.
+    bool hasLeaseForAddressRange(const asiolink::IOAddress& first,
+                                 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.
+    ///
+    /// @param prefix Prefix.
+    /// @param prefix_len Prefix length.
+    ///
+    /// @return true if client has a lease for the specified prefix, false
+    /// otherwise.
+    bool hasLeaseForPrefix(const asiolink::IOAddress& prefix,
+                           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 IAID& iaid) const;
+
+    /// @brief Checks if client has a lease belonging to a prefix pool.
+    ///
+    /// @param prefix Pool prefix.
+    /// @param prefix_len Prefix length.
+    /// @param delegated_len Delegated prefix length.
+    ///
+    /// @return true if client has a lease belonging to specified pool,
+    /// false otherwise.
+    bool hasLeaseForPrefixPool(const asiolink::IOAddress& prefix,
+                               const uint8_t prefix_len,
+                               const uint8_t delegated_len) const;
+
+    /// @brief Checks if client has a lease with zero lifetimes for a prefix.
+    ///
+    /// @param prefix Prefix.
+    /// @param prefix_len Prefix length.
+    ///
+    /// @return true if client has a lease with zero lifetimes for a prefix.
+    bool hasLeaseWithZeroLifetimeForPrefix(const asiolink::IOAddress& prefix,
+                                           const uint8_t prefix_len) const;
+
     /// @brief Returns the value of the global status code for the last
     /// transaction.
     uint16_t getStatusCode() const {
@@ -419,67 +551,61 @@ public:
         link_local_ = link_local;
     }
 
-    /// @brief Set an address hint to be sent to a server.
-    ///
-    /// @param pref_lft Preferred lifetime.
-    /// @param valid_lft Valid lifetime.
-    /// @param address Address for which the client has a preference.
-    void useHint(const uint32_t pref_lft, const uint32_t valid_lft,
-                 const std::string& address);
-
-    /// @brief Sets a prefix hint to be sent to a server.
-    ///
-    /// @param pref_lft Preferred lifetime.
-    /// @param valid_lft Valid lifetime.
-    /// @param len Prefix length.
-    /// @param prefix Prefix for which the client has a preference.
-    void useHint(const uint32_t pref_lft, const uint32_t valid_lft,
-                 const uint8_t len, const std::string& prefix);
-
-    /// @brief Place IA_NA options to request address assignment.
-    ///
-    /// This function configures the client to place IA_NA options in its
-    /// Solicit messages to request the IPv6 address assignment.
-    ///
-    /// @param iaid IAID to be used in the IA_NA.
-    void useNA(const uint32_t iaid) {
-        useNA(true, iaid);
-    }
-
-    /// @brief Place IA_NA options to request address assignment.
-    ///
-    /// This function configures the client to place IA_NA options in its
-    /// Solicit messages to request the IPv6 address assignment.
-    ///
-    /// @param use Parameter which 'true' value indicates that client should
-    /// request address assignment.
-    /// @param iaid IAID to be used in the IA_NA.
-    void useNA(const bool use = true, const uint32_t iaid = 1234) {
-        use_na_ = use;
-        na_iaid_ = iaid;
-    }
-
-    /// @brief Place IA_PD options to request address assignment.
-    ///
-    /// This function configures the client to place IA_NA options in its
-    /// Solicit messages to request the IPv6 address assignment.
-    ///
-    /// @param iaid IAID to be used in the IA_PD.
-    void usePD(const uint32_t iaid) {
-        usePD(true, iaid);
-    }
-
-    /// @brief Place IA_PD options to request prefix assignment.
-    ///
-    /// This function configures the client to place IA_PD options in its
-    /// Solicit messages to request the IPv6 address assignment.
-    ///
-    /// @param use Parameter which 'true' value indicates that client should
-    /// request prefix assignment.
-    /// @param iaid IAID to be used in the IA_NA.
-    void usePD(const bool use = true, const uint32_t iaid = 5678) {
-        use_pd_ = use;
-        pd_iaid_ = iaid;
+    /// @brief Specifies address to be included in client's message.
+    ///
+    /// This method specifies IPv6 address to be included within IA_NA
+    /// option sent by the client. In order to specify multiple addresses
+    /// to be included in a particular IA_NA, this method must be called
+    /// multiple times to specify each address separately. In such case,
+    /// the value of the IAID should remain the same across all calls to
+    /// this method.
+    ///
+    /// This method is typically called to specify IA_NA options to be
+    /// sent to the server during 4-way handshakes and during lease
+    /// renewal to request allocation of new leases (as per RFC7550).
+    ///
+    /// @param iaid IAID.
+    /// @param address IPv6 address to be included in the IA_NA. It defaults
+    /// to IPv6 zero address, which indicates that no address should be
+    /// included in the IA_NA (empty IA_NA will be sent).
+    void requestAddress(const uint32_t iaid = 1234,
+                        const asiolink::IOAddress& address =
+                        asiolink::IOAddress::IPV6_ZERO_ADDRESS());
+
+    /// @brief Specifies IPv6 prefix to be included in client's message.
+    ///
+    /// This method specifies IPv6 prefix to be included within IA_PD
+    /// option sent by the client. In order to specify multiple prefixes
+    /// to be included in a particular IA_PD, this method must be called
+    /// multiple times to specify each prefix separately. In such case,
+    /// the value of the IAID should remain the same across all calls to
+    /// this method.
+    ///
+    /// This method is typically called to specify IA_PD options to be
+    /// sent to the server during 4-way handshakes and during lease
+    /// renewal to request allocation of new leases (as per RFC7550).
+    ///
+    /// @param iaid IAID.
+    /// @param prefix_len Prefix length.
+    /// @param prefix Prefix to be included. This value defaults to the
+    /// IPv6 zero address. If zero address is specified and prefix_len is
+    /// set to 0, the IA Prefix option will not be included in the IA_PD.
+    /// If the prefix_len is non-zero and the prefix is IPv6 zero address
+    /// the prefix length hint will be included in the IA Prefix option.
+    void requestPrefix(const uint32_t iaid = 5678,
+                       const uint8_t prefix_len = 0,
+                       const asiolink::IOAddress& prefix =
+                       asiolink::IOAddress::IPV6_ZERO_ADDRESS());
+
+    /// @brief Removes IAs specified by @ref requestAddress and
+    /// @ref requestPrefix methods.
+    ///
+    /// If this method is called and the client initiates an exchange with
+    /// a server the client will only include IAs for which it has leases.
+    /// If the client has no leases (e.g. a Solicit case), no IAs will be
+    /// included in the client's message.
+    void clearRequestedIAs() {
+        client_ias_.clear();
     }
 
     /// @brief Simulate sending messages through a relay.
@@ -499,14 +625,6 @@ public:
         use_client_id_ = send;
     }
 
-    /// @brief Controls whether the client should send an addres in IA_NA
-    ///
-    /// @todo: For now, this flag is only used in Decline
-    /// @param send should the address be included?
-    void includeAddress(const bool send) {
-        include_address_ = send;
-    }
-
     /// @brief Specifies if the Rapid Commit option should be included in
     /// the Solicit message.
     ///
@@ -575,8 +693,9 @@ public:
     /// @brief Generates IA_NA based on lease information
     ///
     /// @param query generated IA_NA options will be added here
-    void
-    generateIAFromLeases(const Pkt6Ptr& query);
+    /// @param include_address should the address be included?
+    void generateIAFromLeases(const Pkt6Ptr& query,
+                              const bool include_address = true);
 
     /// @brief Adds extra option (an option the client will always send)
     ///
@@ -621,27 +740,12 @@ private:
 
     /// @brief Includes IAs to be requested.
     ///
-    /// This method checks if @c use_na_ and/or @c use_pd_ are specified and
-    /// includes appropriate IA types, if they are not already included.
+    /// This method includes IAs explicitly requested using client_ias_
     ///
     /// @param query Pointer to the client's message to which IAs should be
     /// added.
     void appendRequestedIAs(const Pkt6Ptr& query) const;
 
-    /// @brief Include IA of the specified type if it doesn't exist yet.
-    ///
-    /// This methods includes an IA option of the specific type, and
-    /// having a given IAID to the query message, if this IA hasn't
-    /// been added yet.
-    ///
-    /// @param query Pointer to the client's message to which IA should be
-    /// added.
-    /// @param ia_type One of the D6O_IA_NA or D6O_IA_PD
-    /// @param iaid IAID of the IA.
-    void conditionallyAppendRequestedIA(const Pkt6Ptr& query,
-                                        const uint8_t ia_type,
-                                        const uint32_t iaid) const;
-
     /// @brief Copy IA options from one message to another.
     ///
     /// This method copies IA_NA and IA_PD options from one message to another.
@@ -733,19 +837,14 @@ private:
     /// @brief Pointer to the server that the client is communicating with.
     boost::shared_ptr<isc::dhcp::test::NakedDhcpv6Srv> srv_;
 
-    bool use_na_;    ///< Enable address assignment.
-    bool use_pd_;    ///< Enable prefix delegation.
     bool use_relay_; ///< Enable relaying messages to the server.
 
     bool use_oro_;  ///< Conth
     bool use_client_id_;
     bool use_rapid_commit_;
 
-    /// @brief Pointer to the option holding an address hint.
-    Option6IAAddrPtr address_hint_;
-
-    /// @brief Pointer to the option holding a prefix hint.
-    Option6IAPrefixPtr prefix_hint_;
+    /// @brief List holding information to be sent in client's IAs.
+    std::list<ClientIA> client_ias_;
 
     /// @brief List of options to be requested
     ///
@@ -761,17 +860,6 @@ private:
 
     /// @brief FQDN requested by the client.
     Option6ClientFqdnPtr fqdn_;
-
-    /// @bref IAID used by the client when requesting address assignment.
-    uint32_t na_iaid_;
-    /// @brief IAID used by the client when requesting prefix delegation.
-    uint32_t pd_iaid_;
-
-    /// @brief Determines if the client will include address in the messages
-    ///        it sends.
-    ///
-    /// @todo this flag is currently supported in Decline only.
-    bool include_address_;
 };
 
 } // end of namespace isc::dhcp::test

+ 70 - 0
src/bin/dhcp6/tests/dhcp6_test_utils.h

@@ -34,6 +34,76 @@ namespace isc {
 namespace dhcp {
 namespace test {
 
+/// @brief Generic wrapper to provide strongly typed values.
+///
+/// In many cases, the test fixture class methods require providing many
+/// parameters, of which some are 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 readability 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 strongly 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 for DHCPv6 server testing.
 ///
 /// Currently it configures the test data path directory in

File diff suppressed because it is too large
+ 1012 - 18
src/bin/dhcp6/tests/host_unittest.cc


+ 26 - 23
src/bin/dhcp6/tests/rebind_unittest.cc

@@ -1,4 +1,4 @@
-// Copyright (C) 2014-2015 Internet Systems Consortium, Inc. ("ISC")
+// Copyright (C) 2014-2016 Internet Systems Consortium, Inc. ("ISC")
 //
 // This Source Code Form is subject to the terms of the Mozilla Public
 // License, v. 2.0. If a copy of the MPL was not distributed with this
@@ -259,7 +259,7 @@ TEST_F(RebindTest, sanityCheck) {
 TEST_F(RebindTest, directClient) {
     Dhcp6Client client;
     // Configure client to request IA_NA.
-    client.useNA();
+    client.requestAddress();
     // Make 4-way exchange to get the lease.
     ASSERT_NO_FATAL_FAILURE(requestLease(REBIND_CONFIGS[0], 2, client));
     // Keep the client's lease for future reference.
@@ -288,7 +288,7 @@ TEST_F(RebindTest, directClient) {
 TEST_F(RebindTest, directClientChangingSubnet) {
     Dhcp6Client client;
     // Configure client to request IA_NA.
-    client.useNA();
+    client.requestAddress();
     // Make 4-way exchange to get the lease.
     ASSERT_NO_FATAL_FAILURE(requestLease(REBIND_CONFIGS[0], 2, client));
     // Keep the client's lease for future reference.
@@ -325,15 +325,16 @@ TEST_F(RebindTest, directClientChangingSubnet) {
 TEST_F(RebindTest, directClientChangingIAID) {
     Dhcp6Client client;
     // Configure client to request IA_NA.
-    client.useNA();
+    client.requestAddress();
     // Make 4-way exchange to get the lease.
     ASSERT_NO_FATAL_FAILURE(requestLease(REBIND_CONFIGS[0], 2, client));
     // Keep the client's lease for future reference.
     Lease6 lease_client = client.getLease(0);
     // Modify the IAID of the lease record that client stores. By adding
     // one to IAID we guarantee that the IAID will change.
+    client.clearRequestedIAs();
     client.config_.leases_[0].iaid_ = 1235;
-    client.useNA(true, 1235);
+    client.requestAddress(1235);
 
     // Try to Rebind. The server should allocate new lease for this IAID.
     ASSERT_NO_THROW(client.doRebind());
@@ -358,7 +359,7 @@ TEST_F(RebindTest, directClientChangingIAID) {
 TEST_F(RebindTest, directClientLostLease) {
     Dhcp6Client client;
     // Configure client to request IA_NA.
-    client.useNA();
+    client.requestAddress();
     // Make 4-way exchange to get the lease.
     ASSERT_NO_FATAL_FAILURE(requestLease(REBIND_CONFIGS[0], 2, client));
     // Keep the client's lease for future reference.
@@ -383,7 +384,7 @@ TEST_F(RebindTest, directClientLostLease) {
 TEST_F(RebindTest, relayedClient) {
     Dhcp6Client client;
     // Configure client to request IA_NA.
-    client.useNA();
+    client.requestAddress();
     // Configure DHCPv6 client to simulate sending the message through a relay
     // agent. The default link-addr is 3001:1::1. This address should be used
     // by the server to pick the suitable subnet.
@@ -416,7 +417,7 @@ TEST_F(RebindTest, relayedClient) {
 TEST_F(RebindTest, relayedClientChangingSubnet) {
     Dhcp6Client client;
     // Configure client to request IA_NA.
-    client.useNA();
+    client.requestAddress();
     // Configure DHCPv6 client to simulate sending the message through a relay
     // agent. The default link-addr is 3001:1::1. This address should be used
     // by the server to pick the suitable subnet.
@@ -447,7 +448,7 @@ TEST_F(RebindTest, relayedClientChangingSubnet) {
 TEST_F(RebindTest, relayedClientChangingIAID) {
     Dhcp6Client client;
     // Configure client to request IA_NA.
-    client.useNA();
+    client.requestAddress();
     // Configure DHCPv6 client to simulate sending the message through a relay
     // agent. The default link-addr is 3001:1::1. This address should be used
     // by the server to pick the suitable subnet.
@@ -459,8 +460,9 @@ TEST_F(RebindTest, relayedClientChangingIAID) {
 
     // Modify the IAID of the lease record that client stores. By adding
     // one to IAID we guarantee that the IAID will change.
+    client.clearRequestedIAs();
     client.config_.leases_[0].iaid_ = 1235;
-    client.useNA(true, 1235);
+    client.requestAddress(1235);
 
     // Try to Rebind. The server should allocate new lease for this IAID.
     ASSERT_NO_THROW(client.doRebind());
@@ -485,7 +487,7 @@ TEST_F(RebindTest, relayedClientChangingIAID) {
 TEST_F(RebindTest, relayedClientLostLease) {
     Dhcp6Client client;
     // Configure client to request IA_NA.
-    client.useNA();
+    client.requestAddress();
     // Configure DHCPv6 client to simulate sending the message through a relay
     // agent. The default link-addr is 3001:1::1. This address should be used
     // by the server to pick the suitable subnet.
@@ -514,7 +516,7 @@ TEST_F(RebindTest, relayedClientLostLease) {
 TEST_F(RebindTest, relayedClientChangingAddress) {
     Dhcp6Client client;
     // Configure client to request IA_NA.
-    client.useNA();
+    client.requestAddress();
     // Make 4-way exchange to get the lease.
     ASSERT_NO_FATAL_FAILURE(requestLease(REBIND_CONFIGS[2], 2, client));
     // Keep the client's lease for future reference.
@@ -570,7 +572,7 @@ TEST_F(RebindTest, relayedClientChangingAddress) {
 TEST_F(RebindTest, directClientPD) {
     Dhcp6Client client;
     // Configure client to request IA_PD.
-    client.usePD();
+    client.requestPrefix();
     // Make 4-way exchange to get the lease.
     ASSERT_NO_FATAL_FAILURE(requestLease(REBIND_CONFIGS[4], 2, client));
     // Keep the client's lease for future reference.
@@ -597,7 +599,7 @@ TEST_F(RebindTest, directClientPD) {
 TEST_F(RebindTest, directClientPDChangingSubnet) {
     Dhcp6Client client;
     // Configure client to request IA_PD.
-    client.usePD();
+    client.requestPrefix();
     // Make 4-way exchange to get the lease.
     ASSERT_NO_FATAL_FAILURE(requestLease(REBIND_CONFIGS[4], 2, client));
     // Keep the client's lease for future reference.
@@ -634,7 +636,7 @@ TEST_F(RebindTest, directClientPDChangingSubnet) {
 TEST_F(RebindTest, directClientPDChangingIAID) {
     Dhcp6Client client;
     // Configure client to request IA_PD.
-    client.usePD();
+    client.requestPrefix();
     // Make 4-way exchange to get the lease.
     ASSERT_NO_FATAL_FAILURE(requestLease(REBIND_CONFIGS[4], 2, client));
     // Keep the client's lease for future reference.
@@ -642,8 +644,9 @@ TEST_F(RebindTest, directClientPDChangingIAID) {
 
     // Modify the IAID of the lease record that client stores. By adding
     // one to IAID we guarantee that the IAID will change.
+    client.clearRequestedIAs();
     client.config_.leases_[0].iaid_ = 5679;
-    client.usePD(true, 5679);
+    client.requestPrefix(5679);
 
     // Try to Rebind. The server should allocate new lease for this IAID.
     ASSERT_NO_THROW(client.doRebind());
@@ -668,7 +671,7 @@ TEST_F(RebindTest, directClientPDChangingIAID) {
 TEST_F(RebindTest, directClientPDChangingPrefix) {
     Dhcp6Client client;
     // Configure client to request IA_PD.
-    client.usePD();
+    client.requestPrefix();
     // Make 4-way exchange to get the lease.
     ASSERT_NO_FATAL_FAILURE(requestLease(REBIND_CONFIGS[4], 2, client));
     // Keep the client's lease for future reference.
@@ -724,7 +727,7 @@ TEST_F(RebindTest, directClientPDChangingPrefix) {
 TEST_F(RebindTest, unicast) {
     Dhcp6Client client;
     // Configure client to request IA_NA.
-    client.useNA();
+    client.requestAddress();
     // Make 4-way exchange to get the lease.
     ASSERT_NO_FATAL_FAILURE(requestLease(REBIND_CONFIGS[0], 2, client));
     // Keep the client's lease for future reference.
@@ -753,7 +756,7 @@ TEST_F(RebindTest, unicast) {
 TEST_F(RebindTest, relayedUnicast) {
     Dhcp6Client client;
     // Configure client to request IA_NA.
-    client.useNA();
+    client.requestAddress();
     // Configure DHCPv6 client to simulate sending the message through a relay
     // agent. The default link-addr is 3001:1::1. This address should be used
     // by the server to pick the suitable subnet.
@@ -788,8 +791,8 @@ TEST_F(RebindTest, requestPrefixInRebind) {
     Dhcp6Client client;
 
     // Configure client to request IA_NA and IA_PD.
-    client.useNA();
-    client.usePD();
+    client.requestAddress();
+    client.requestPrefix();
 
     // Configure the server with NA pools only.
     ASSERT_NO_THROW(configure(REBIND_CONFIGS[0], *client.getServer()));
@@ -843,8 +846,8 @@ TEST_F(RebindTest, requestAddressInRebind) {
     Dhcp6Client client;
 
     // Configure client to request IA_NA and IA_PD.
-    client.useNA();
-    client.usePD();
+    client.requestAddress();
+    client.requestPrefix();
 
     // Configure the server with PD pools only.
     ASSERT_NO_THROW(configure(REBIND_CONFIGS[4], *client.getServer()));

+ 10 - 13
src/bin/dhcp6/tests/renew_unittest.cc

@@ -116,8 +116,8 @@ TEST_F(RenewTest, requestPrefixInRenew) {
     Dhcp6Client client;
 
     // Configure client to request IA_NA and IA_PD.
-    client.useNA(na_iaid_);
-    client.usePD(pd_iaid_);
+    client.requestAddress(na_iaid_);
+    client.requestPrefix(pd_iaid_);
 
     // Configure the server with NA pools only.
     ASSERT_NO_THROW(configure(RENEW_CONFIGS[0], *client.getServer()));
@@ -175,8 +175,8 @@ TEST_F(RenewTest, requestPrefixInRenewUseHint) {
     Dhcp6Client client;
 
     // Configure client to request IA_NA and IA_PD.
-    client.useNA(na_iaid_);
-    client.usePD(pd_iaid_);
+    client.requestAddress(na_iaid_);
+    client.requestPrefix(pd_iaid_);
 
     // Configure the server with NA pools only.
     ASSERT_NO_THROW(configure(RENEW_CONFIGS[0], *client.getServer()));
@@ -208,7 +208,7 @@ TEST_F(RenewTest, requestPrefixInRenewUseHint) {
     EXPECT_EQ(STATUS_Success, client.getStatusCode(na_iaid_));
 
     // Specify the hint used for IA_PD.
-    client.useHint(0, 0, 64, "::");
+    client.requestPrefix(pd_iaid_, 64, IOAddress::IPV6_ZERO_ADDRESS());
 
     // Send Renew message to the server, including IA_NA and requesting IA_PD.
     ASSERT_NO_THROW(client.doRenew());
@@ -225,9 +225,6 @@ TEST_F(RenewTest, requestPrefixInRenewUseHint) {
     // Reconfigure the server to use both NA and PD pools.
     configure(RENEW_CONFIGS[2], *client.getServer());
 
-    // Specify the hint used for IA_PD.
-    client.useHint(0, 0, 64, "::");
-
     // Send Renew message to the server, including IA_NA and requesting IA_PD.
     ASSERT_NO_THROW(client.doRenew());
 
@@ -251,8 +248,8 @@ TEST_F(RenewTest, requestAddressInRenew) {
     Dhcp6Client client;
 
     // Configure client to request IA_NA and IA_PD.
-    client.useNA(na_iaid_);
-    client.usePD(pd_iaid_);
+    client.requestAddress(na_iaid_);
+    client.requestPrefix(pd_iaid_);
 
     // Configure the server with PD pools only.
     ASSERT_NO_THROW(configure(RENEW_CONFIGS[1], *client.getServer()));
@@ -311,8 +308,8 @@ TEST_F(RenewTest, requestAddressInRenewHint) {
     Dhcp6Client client;
 
     // Configure client to request IA_NA and IA_PD.
-    client.useNA(na_iaid_);
-    client.usePD(pd_iaid_);
+    client.requestAddress(na_iaid_);
+    client.requestPrefix(pd_iaid_);
 
     // Configure the server with PD pools only.
     ASSERT_NO_THROW(configure(RENEW_CONFIGS[1], *client.getServer()));
@@ -334,7 +331,7 @@ TEST_F(RenewTest, requestAddressInRenewHint) {
     ASSERT_EQ(0, leases_client_na.size());
     ASSERT_EQ(STATUS_NoAddrsAvail, client.getStatusCode(na_iaid_));
 
-    client.useHint(0, 0, "2001:db8:1::100");
+    client.requestAddress(na_iaid_, IOAddress("2001:db8:1::100"));
 
     // Send Renew message to the server, including IA_PD and requesting IA_NA.
     // The server should return NoAddrsAvail status code in this case.

+ 13 - 11
src/bin/dhcp6/tests/sarr_unittest.cc

@@ -1,4 +1,4 @@
-// Copyright (C) 2014-2015 Internet Systems Consortium, Inc. ("ISC")
+// Copyright (C) 2014-2016 Internet Systems Consortium, Inc. ("ISC")
 //
 // This Source Code Form is subject to the terms of the Mozilla Public
 // License, v. 2.0. If a copy of the MPL was not distributed with this
@@ -15,6 +15,7 @@
 #include <stats/stats_mgr.h>
 
 using namespace isc;
+using namespace isc::asiolink;
 using namespace isc::dhcp;
 using namespace isc::dhcp::test;
 
@@ -122,14 +123,14 @@ public:
 TEST_F(SARRTest, directClientPrefixHint) {
     Dhcp6Client client;
     // Configure client to request IA_PD.
-    client.usePD();
+    client.requestPrefix();
     configure(CONFIGS[0], *client.getServer());
     // Make sure we ended-up having expected number of subnets configured.
     const Subnet6Collection* subnets = CfgMgr::instance().getCurrentCfg()->
         getCfgSubnets6()->getAll();
     ASSERT_EQ(1, subnets->size());
     // Append IAPREFIX option to the client's message.
-    ASSERT_NO_THROW(client.useHint(100, 200, 64, "2001:db8:3:33::33"));
+    ASSERT_NO_THROW(client.requestPrefix(5678, 64, asiolink::IOAddress("2001:db8:3:33::33")));
     // Perform 4-way exchange.
     ASSERT_NO_THROW(client.doSARR());
     // Server should have assigned a prefix.
@@ -153,7 +154,8 @@ TEST_F(SARRTest, directClientPrefixHint) {
     client.modifyDUID();
 
     // Use the hint with some least significant bytes set.
-    ASSERT_NO_THROW(client.useHint(100, 200, 64, "2001:db8:3:33::34"));
+    client.clearRequestedIAs();
+    ASSERT_NO_THROW(client.requestPrefix(5678, 64, IOAddress("2001:db8:3:33::34")));
     ASSERT_NO_THROW(client.doSARR());
     // Server should assign a lease.
     ASSERT_EQ(1, client.getLeaseNum());
@@ -176,7 +178,7 @@ TEST_F(SARRTest, directClientPrefixHint) {
 TEST_F(SARRTest, rapidCommitEnable) {
     Dhcp6Client client;
     // Configure client to request IA_NA
-    client.useNA();
+    client.requestAddress();
     configure(CONFIGS[1], *client.getServer());
     ASSERT_NO_THROW(client.getServer()->startD2());
     // Make sure we ended-up having expected number of subnets configured.
@@ -214,7 +216,7 @@ TEST_F(SARRTest, rapidCommitEnable) {
 TEST_F(SARRTest, rapidCommitNoOption) {
     Dhcp6Client client;
     // Configure client to request IA_NA
-    client.useNA();
+    client.requestAddress();
     configure(CONFIGS[1], *client.getServer());
     ASSERT_NO_THROW(client.getServer()->startD2());
     // Make sure we ended-up having expected number of subnets configured.
@@ -247,7 +249,7 @@ TEST_F(SARRTest, rapidCommitDisable) {
     // The subnet assigned to eth1 has Rapid Commit disabled.
     client.setInterface("eth1");
     // Configure client to request IA_NA
-    client.useNA();
+    client.requestAddress();
     configure(CONFIGS[1], *client.getServer());
     ASSERT_NO_THROW(client.getServer()->startD2());
     // Make sure we ended-up having expected number of subnets configured.
@@ -283,7 +285,7 @@ TEST_F(SARRTest, sarrStats) {
     Dhcp6Client client;
     configure(CONFIGS[1], *client.getServer());
     client.setInterface("eth1");
-    client.useNA();
+    client.requestAddress();
 
     // Make sure we ended-up having expected number of subnets configured.
     const Subnet6Collection* subnets = CfgMgr::instance().getCurrentCfg()->
@@ -347,7 +349,7 @@ TEST_F(SARRTest, pkt6ReceiveDropStat1) {
     Dhcp6Client client;
     configure(CONFIGS[1], *client.getServer());
     client.setInterface("eth1");
-    client.useNA();
+    client.requestAddress();
 
     client.doSolicit();
     client.useServerId(bogus_srv_id);
@@ -372,7 +374,7 @@ TEST_F(SARRTest, pkt6ReceiveDropStat2) {
     Dhcp6Client client;
     configure(CONFIGS[1], *client.getServer());
     client.setInterface("eth1");
-    client.useNA();
+    client.requestAddress();
 
     client.setDestAddress(asiolink::IOAddress("2001:db8::1")); // Pretend it's unicast
     client.doSolicit();
@@ -397,7 +399,7 @@ TEST_F(SARRTest, pkt6ReceiveDropStat3) {
     Dhcp6Client client;
     configure(CONFIGS[1], *client.getServer());
     client.setInterface("eth1");
-    client.useNA();
+    client.requestAddress();
 
     // Let's send our client-id as server-id. That will result in the
     // packet containing the client-id twice. That should cause RFCViolation

+ 80 - 44
src/lib/dhcpsrv/alloc_engine.cc

@@ -587,9 +587,10 @@ AllocEngine::allocateUnreservedLeases6(ClientContext6& ctx) {
         Pool6>(ctx.subnet_->getPool(ctx.currentIA().type_, hint, false));
 
     if (pool) {
+
         /// @todo: We support only one hint for now
-        Lease6Ptr lease = LeaseMgrFactory::instance().getLease6(ctx.currentIA().type_,
-                                                                hint);
+        Lease6Ptr lease =
+            LeaseMgrFactory::instance().getLease6(ctx.currentIA().type_, hint);
         if (!lease) {
 
             // In-pool reservations: Check if this address is reserved for someone
@@ -758,34 +759,46 @@ AllocEngine::allocateReservedLeases6(ClientContext6& ctx,
     IPv6Resrv::Type type = ctx.currentIA().type_ == Lease::TYPE_NA ?
         IPv6Resrv::TYPE_NA : IPv6Resrv::TYPE_PD;
 
-    // Get the IPv6 reservations of specified type.
-    const IPv6ResrvRange& reservs = ctx.host_->getIPv6Reservations(type);
-    for (IPv6ResrvIterator resv = reservs.first; resv != reservs.second; ++resv) {
-        // We do have a reservation for addr.
-        IOAddress addr = resv->second.getPrefix();
-        uint8_t prefix_len = resv->second.getPrefixLen();
-
-        // Check if already have this lease on the existing_leases list.
-        for (Lease6Collection::iterator l = existing_leases.begin();
-             l != existing_leases.end(); ++l) {
-
-            // Ok, we already have a lease for this reservation and it's usable
-            if (((*l)->addr_ == addr) && (*l)->valid_lft_ != 0) {
+    // We want to avoid allocating new lease for an IA if there is already
+    // a valid lease for which client has reservation. So, we first check if
+    // we already have a lease for a reserved address or prefix.
+    BOOST_FOREACH(const Lease6Ptr& lease, existing_leases) {
+        if ((lease->valid_lft_ != 0)) {
+            if (ctx.host_->hasReservation(IPv6Resrv(type, lease->addr_,
+                                                    lease->prefixlen_))) {
+                // We found existing lease for a reserved address or prefix.
+                // We'll simply extend the lifetime of the lease.
                 LOG_DEBUG(alloc_engine_logger, ALLOC_ENGINE_DBG_TRACE,
                           ALLOC_ENGINE_V6_ALLOC_HR_LEASE_EXISTS)
                     .arg(ctx.query_->getLabel())
-                    .arg((*l)->typeToText((*l)->type_))
-                    .arg((*l)->addr_.toText());
+                    .arg(lease->typeToText(lease->type_))
+                    .arg(lease->addr_.toText());
 
                 // If this is a real allocation, we may need to extend the lease
                 // lifetime.
-                if (!ctx.fake_allocation_ && conditionalExtendLifetime(**l)) {
-                    LeaseMgrFactory::instance().updateLease6(*l);
+                if (!ctx.fake_allocation_ && conditionalExtendLifetime(*lease)) {
+                    LeaseMgrFactory::instance().updateLease6(lease);
                 }
-
                 return;
             }
         }
+    }
+
+    // There is no lease for a reservation in this IA. So, let's now iterate
+    // over reservations specified and try to allocate one of them for the IA.
+
+    // Get the IPv6 reservations of specified type.
+    const IPv6ResrvRange& reservs = ctx.host_->getIPv6Reservations(type);
+    BOOST_FOREACH(IPv6ResrvTuple type_lease_tuple, reservs) {
+        // We do have a reservation for address or prefix.
+        const IOAddress& addr = type_lease_tuple.second.getPrefix();
+        uint8_t prefix_len = type_lease_tuple.second.getPrefixLen();
+
+        // We have allocated this address/prefix while processing one of the
+        // previous IAs, so let's try another reservation.
+        if (ctx.isAllocated(addr, prefix_len)) {
+            continue;
+        }
 
         // If there's a lease for this address, let's not create it.
         // It doesn't matter whether it is for this client or for someone else.
@@ -838,37 +851,60 @@ AllocEngine::removeNonmatchingReservedLeases6(ClientContext6& ctx,
     // so the operation shouldn't be that expensive.
     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(),
-                                                     (*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;
         }
 
-        // 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.
-        queueNCR(CHG_REMOVE, *candidate);
+        queueNCR(CHG_REMOVE, candidate);
 
         // Need to decrease statistic for assigned addresses.
         StatsMgr::instance().addValue(
@@ -883,10 +919,10 @@ AllocEngine::removeNonmatchingReservedLeases6(ClientContext6& ctx,
         // should not interfere with it.
 
         // 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
-        removeLeases(existing_leases, (*candidate)->addr_);
+        removeLeases(existing_leases, candidate->addr_);
     }
 }
 

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

@@ -479,12 +479,12 @@ struct Lease6 : public Lease {
     /// @param fqdn_rev If true, reverse DNS update is performed for a lease.
     /// @param hostname FQDN of the client which gets the lease.
     /// @param hwaddr hardware address (MAC), may be NULL
-    /// @param prefixlen An address prefix length.
+    /// @param prefixlen An address prefix length (optional, defaults to 128)
     Lease6(Lease::Type type, const isc::asiolink::IOAddress& addr, DuidPtr duid,
            uint32_t iaid, uint32_t preferred, uint32_t valid, uint32_t t1,
            uint32_t t2, SubnetID subnet_id, const bool fqdn_fwd,
            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
     ///

+ 1 - 1
src/lib/dhcpsrv/tests/csv_lease_file6_unittest.cc

@@ -270,7 +270,7 @@ TEST_F(CSVLeaseFile6Test, recreate) {
               "lease_type,iaid,prefix_len,fqdn_fwd,fqdn_rev,hostname,hwaddr,"
               "state\n"
               "2001:db8:1::1,00:01:02:03:04:05:06:0a:0b:0c:0d:0e:0f,"
-              "200,200,8,100,0,7,0,1,1,host.example.com,,0\n"
+              "200,200,8,100,0,7,128,1,1,host.example.com,,0\n"
               "2001:db8:2::10,01:01:01:01:0a:01:02:03:04:05"
               ",300,300,6,150,0,8,128,0,0,,,0\n"
               "3000:1:1::,00:01:02:03:04:05:06:0a:0b:0c:0d:0e:0f,"