Browse Source

[master] Merge branch 'trac3947'

Marcin Siodelski 9 years ago
parent
commit
c06ab97a4e

+ 54 - 0
doc/guide/dhcp6-srv.xml

@@ -2379,6 +2379,53 @@ should include options from the isc option space:
       even if it is not used.</para>
     </section>
 
+    <section id="dhcp6-rfc7550">
+      <title>Support for RFC 7550</title>
+      <para>The <ulink url="http://tools.ietf.org/html/rfc7550">RFC 7550</ulink>
+      has introduced some changes to the DHCPv6 protocol to resolve a few issues
+      with the coexistence of multiple stateful options in the messages sent
+      between the clients and servers.</para>
+
+      <para>The typical example is when the client, such as a requesting
+      router, requests an allocation of both addresses and prefixes when
+      it performs the 4-way (SARR) exchange with the server. If the
+      server is not configured to allocate any prefixes but it can allocate
+      some addresses, it will respond with the IA_NA(s) containing allocated
+      addresses and the IA_PD(s) containing the NoPrefixAvail status code. If
+      the client can operate without prefixes it may transition to the
+      'bound' state when it sends Renew/Rebind messages to the server,
+      according to the T1 and T2 times, to extend the lifetimes of the
+      allocated addresses. If the client is still interested in obtaining
+      prefixes from the server it may also include IA_PD in the Renew/Rebind
+      to request allocation of the prefixes. If the server still cannot
+      allocate the prefixes, it will respond with the IA_PD(s) containing
+      NoPrefixAvail status code. However, if the server can now allocate
+      the prefixes it will do so, and send them in the IA_PD(s) to the client.
+      Allocation of leases during the Renew/Rebind was not supported in the
+      <ulink url="http://tools.ietf.org/html/rfc3315">RFC 3315</ulink>
+      and <ulink url="http://tools.ietf.org/html/rfc3633">RFC 3633</ulink>,
+      and has been introduced in
+      <ulink url="http://tools.ietf.org/html/rfc7550">RFC 7550</ulink>.
+      Kea supports this new behavior and it doesn't provide any configuration
+      mechanisms to disable it.
+      </para>
+
+      <para>
+        The following are the other behaviors specified in the
+        <ulink url="http://tools.ietf.org/html/rfc7550">RFC 7550</ulink>
+        supported by the Kea DHCPv6 server:
+        <itemizedlist>
+          <listitem><simpara>set T1/T2 timers to the same value for all
+          stateful (IA_NA and IA_PD) options to facilitate renewal of all
+          client's leases at the same time (in a single message exchange),
+          </simpara></listitem>
+          <listitem><simpara>NoAddrsAvail and NoPrefixAvail status codes
+          are placed in the IA_NA and IA_PD options in the Advertise message,
+          rather than as the top level options.</simpara></listitem>
+        </itemizedlist>
+      </para>
+    </section>
+
     <section id="dhcp6-relay-override">
       <title>Using specific relay agent for a subnet</title>
       <para>
@@ -2964,6 +3011,13 @@ should include options from the isc option space:
             6939</ulink>: Supported option is client link-layer
             address option.</simpara>
           </listitem>
+          <listitem>
+            <simpara><emphasis>Issues and Recommendations with Multiple
+            Stateful DHCPv6 Options</emphasis>,
+            <ulink url="http://tools.ietf.org/html/rfc7550">RFC
+            7550</ulink>: All recommendations related to the DHCPv6 server
+            operation are supported.</simpara>
+          </listitem>
       </itemizedlist>
     </section>
 

+ 0 - 18
src/bin/dhcp6/dhcp6_messages.mes

@@ -224,24 +224,6 @@ as a result of receiving SIGHUP signal.
 This is an error message logged when the dynamic reconfiguration of the
 DHCP server failed.
 
-% DHCP6_EXTEND_NA_UNKNOWN %1: received unknown IA_NA with iaid=%2 in subnet %3
-This warning message is printed when client attempts to extend the lease
-for the address (in the IA_NA option) but no such lease is known by the server.
-It typically means that client has attempted to use its lease past its
-lifetime: causes of this include a adjustment of the client's date/time
-setting or poor support on the client for sleep/recovery. A properly
-implemented client will recover from such a situation by restarting the
-lease allocation process after receiving a negative reply from the server.
-The first argument includes the client and the transaction identification
-information. The second argument holds IAID. The third argument holds the
-subnet information.
-
-An alternative cause could be that the server has lost its database
-recently and does not recognize its well-behaving clients. This is more
-probable if you see many such messages. Clients will recover from this,
-but they will most likely get a different IP addresses and experience
-a brief service interruption.
-
 % DHCP6_HANDLE_SIGNAL_EXCEPTION An exception was thrown while handing signal: %1
 This error message is printed when an exception was raised during signal
 processing. This likely indicates a coding error and should be reported to ISC.

+ 21 - 75
src/bin/dhcp6/dhcp6_srv.cc

@@ -497,7 +497,7 @@ bool Dhcpv6Srv::run() {
         LOG_DEBUG(packet6_logger, DBG_DHCP6_BASIC_DATA, DHCP6_PACKET_RECEIVED)
             .arg(query->getLabel())
             .arg(query->getName())
-            .arg(query->getType())
+            .arg(static_cast<int>(query->getType()))
             .arg(query->getRemoteAddr())
             .arg(query->getLocalAddr())
             .arg(query->getIface());
@@ -1754,19 +1754,6 @@ Dhcpv6Srv::extendIA_NA(const Pkt6Ptr& query, const Pkt6Ptr& answer,
         ctx.hints_.push_back(make_pair(iaaddr->getAddress(), 128));
     }
 
-    // We need to remember it as we'll be removing hints from this list as
-    // we extend, cancel or otherwise deal with the leases.
-    bool hints_present = !ctx.hints_.empty();
-
-    /// @todo: This was clarified in draft-ietf-dhc-dhcpv6-stateful-issues that
-    /// the server is allowed to assign new leases in both Renew and Rebind. For
-    /// now, we only support it in Renew, because it breaks a lot of Rebind
-    /// unit-tests. Ultimately, whether we allow it or not, should be exposed
-    /// as configurable policy. See ticket #3717.
-    if (query->getType() == DHCPV6_RENEW) {
-        ctx.allow_new_leases_in_renewals_ = true;
-    }
-
     Lease6Collection leases = alloc_engine_->renewLeases6(ctx);
 
     // Ok, now we have the leases extended. We have:
@@ -1825,26 +1812,13 @@ Dhcpv6Srv::extendIA_NA(const Pkt6Ptr& query, const Pkt6Ptr& answer,
 
     // All is left is to insert the status code.
     if (leases.empty()) {
-        // We did not assign anything. If client has sent something, then
-        // the status code is NoBinding, if he sent an empty IA_NA, then it's
-        // NoAddrsAvailable
-        if (hints_present) {
-            // Insert status code NoBinding to indicate that the lease does not
-            // exist for this client.
-            ia_rsp->addOption(createStatusCode(*query, *ia_rsp, STATUS_NoBinding,
-                              "Sorry, no known leases for this duid/iaid/subnet."));
 
-            LOG_DEBUG(lease6_logger, DBG_DHCP6_DETAIL, DHCP6_EXTEND_NA_UNKNOWN)
-                .arg(query->getLabel())
-                .arg(ia->getIAID())
-                .arg(subnet->toText());
-        } else {
-            ia_rsp->addOption(createStatusCode(*query, *ia_rsp, STATUS_NoAddrsAvail,
-                              "Sorry, no addresses could be assigned at this time."));
-        }
-    } else {
-        // Yay, the client still has something. For now, let's not insert
-        // status-code=success to conserve bandwidth.
+        // The server wasn't able allocate new lease and renew an exising
+        // lease. In that case, the server sends NoAddrsAvail per RFC7550.
+        ia_rsp->addOption(createStatusCode(*query, *ia_rsp,
+                                           STATUS_NoAddrsAvail,
+                                           "Sorry, no addresses could be"
+                                           " assigned at this time."));
     }
 
     return (ia_rsp);
@@ -1931,15 +1905,6 @@ Dhcpv6Srv::extendIA_PD(const Pkt6Ptr& query,
         // Put the client's prefix into the hints list.
         ctx.hints_.push_back(make_pair(prf->getAddress(), prf->getLength()));
     }
-    // We need to remember it as we'll be removing hints from this list as
-    // we extend, cancel or otherwise deal with the leases.
-    bool hints_present = !ctx.hints_.empty();
-
-    /// @todo: The draft-ietf-dhc-dhcpv6-stateful-issues added a new capability
-    /// of the server to to assign new PD leases in both Renew and Rebind.
-    /// There's allow_new_leases_in_renewals_ in the ClientContext6, but we
-    /// currently not use it in PD yet. This should be implemented as part
-    /// of the stateful-issues implementation effort. See ticket #3718.
 
     // Call Allocation Engine and attempt to renew leases. Number of things
     // may happen. Leases may be extended, revoked (if the lease is no longer
@@ -1973,44 +1938,25 @@ Dhcpv6Srv::extendIA_PD(const Pkt6Ptr& query,
     // already, inform the client that he can't have them.
     for (AllocEngine::HintContainer::const_iterator prefix = ctx.hints_.begin();
          prefix != ctx.hints_.end(); ++prefix) {
-        OptionPtr prefix_opt(new Option6IAPrefix(D6O_IAPREFIX, prefix->first,
-                                                 prefix->second, 0, 0));
-        ia_rsp->addOption(prefix_opt);
+        // Send the prefix with the zero lifetimes only if the prefix
+        // contains non-zero value. A zero value indicates that the hint was
+        // for the prefix length.
+        if (!prefix->first.isV6Zero()) {
+            OptionPtr prefix_opt(new Option6IAPrefix(D6O_IAPREFIX, prefix->first,
+                                                     prefix->second, 0, 0));
+            ia_rsp->addOption(prefix_opt);
+        }
     }
 
     // All is left is to insert the status code.
     if (leases.empty()) {
-        if (query->getType() == DHCPV6_RENEW) {
 
-            // We did not assign anything. If client has sent something, then
-            // the status code is NoBinding, if he sent an empty IA_NA, then it's
-            // NoAddrsAvailable
-            if (hints_present) {
-                // Insert status code NoBinding to indicate that the lease does not
-                // exist for this client.
-                ia_rsp->addOption(createStatusCode(*query, *ia_rsp,
-                                                   STATUS_NoBinding,
-                                                   "Sorry, no known PD leases for"
-                                                   " this duid/iaid/subnet."));
-            } else {
-                ia_rsp->addOption(createStatusCode(*query, *ia_rsp,
-                                                   STATUS_NoPrefixAvail,
-                                                   "Sorry, no prefixes could be"
-                                                   " assigned at this time."));
-            }
-        } else {
-            // Per RFC3633, section 12.2, if there is no binding and we are
-            // processing Rebind, the message has to be discarded (assuming that
-            // the server doesn't know if the prefix in the IA_PD option is
-            // appropriate for the client's link). The exception being thrown
-            // here should propagate to the main loop and cause the message to
-            // be discarded.
-            isc_throw(DHCPv6DiscardMessageError, "no binding found for the"
-                      " DUID=" << duid->toText() << ", IAID="
-                      << ia->getIAID() << ", subnet="
-                      << subnet->toText() << " when processing a Rebind"
-                      " message with IA_PD option");
-        }
+        // The server wasn't able allocate new lease and renew an exising
+        // lease. In that case, the server sends NoPrefixAvail per RFC7550.
+        ia_rsp->addOption(createStatusCode(*query, *ia_rsp,
+                                           STATUS_NoPrefixAvail,
+                                           "Sorry, no prefixes could be"
+                                           " assigned at this time."));
     }
 
     return (ia_rsp);

+ 2 - 0
src/bin/dhcp6/json_config_parser.cc

@@ -473,6 +473,7 @@ protected:
             }
         }
 
+        // Gather boolean parameters values.
         bool rapid_commit = boolean_values_->getOptionalParam("rapid-commit", false);
 
         std::ostringstream output;
@@ -482,6 +483,7 @@ protected:
                << ", valid-lifetime=" << valid
                << ", rapid-commit is " << (rapid_commit ? "enabled" : "disabled");
 
+
         LOG_INFO(dhcp6_logger, DHCP6_CONFIG_NEW_SUBNET).arg(output.str());
 
         // Create a new subnet.

+ 1 - 0
src/bin/dhcp6/tests/Makefile.am

@@ -83,6 +83,7 @@ dhcp6_unittests_SOURCES += marker_file.cc
 dhcp6_unittests_SOURCES += ctrl_dhcp6_srv_unittest.cc
 dhcp6_unittests_SOURCES += dhcp6_client.cc dhcp6_client.h
 dhcp6_unittests_SOURCES += rebind_unittest.cc
+dhcp6_unittests_SOURCES += renew_unittest.cc
 dhcp6_unittests_SOURCES += sarr_unittest.cc
 dhcp6_unittests_SOURCES += config_parser_unittest.cc
 dhcp6_unittests_SOURCES += confirm_unittest.cc

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

@@ -276,8 +276,8 @@ TEST_F(ConfirmTest, relayedClientNoSubnet) {
 
     // Set lifetimes to 0 so as the Confirm will ignore the specific address
     // and send an empty IA_NA.
-    client.config_.leases_[0].lease_.preferred_lft_ = 0;
-    client.config_.leases_[0].lease_.valid_lft_ = 0;
+    client.config_.leases_[0].preferred_lft_ = 0;
+    client.config_.leases_[0].valid_lft_ = 0;
     ASSERT_NO_THROW(client.doConfirm());
     EXPECT_FALSE(client.getContext().response_);
 

+ 234 - 86
src/bin/dhcp6/tests/dhcp6_client.cc

@@ -23,12 +23,57 @@
 #include <dhcpsrv/lease.h>
 #include <dhcp6/tests/dhcp6_client.h>
 #include <util/buffer.h>
+#include <boost/foreach.hpp>
 #include <boost/pointer_cast.hpp>
 #include <cstdlib>
 #include <time.h>
 
+using namespace isc::dhcp;
+using namespace isc::dhcp::test;
 using namespace isc::test;
 
+namespace {
+
+/// @brief Functor searching for the leases using a specified property.
+///
+/// @tparam BaseType Base type to which the property belongs: @c Lease or
+/// @c Lease6.
+/// @tparam PropertyType A type of the property, e.g. @c uint32_t for IAID.
+/// @tparam MemberPointer A pointer to the member, e.g. @c &Lease6::iaid_.
+template<typename BaseType, typename PropertyType,
+         PropertyType BaseType::*MemberPointer>
+struct getLeasesByPropertyFun {
+
+    /// @brief Returns leases matching the specified condition.
+    ///
+    /// @param config DHCP client configuration structure holding leases.
+    /// @param property A value of the lease property used to search the lease.
+    /// @param equals A flag which indicates if the operator should search for
+    /// the leases which property is equal to the value of @c property parameter
+    /// (if true), or unequal (if false).
+    /// @param [out] leases A vector in which the operator will store leases
+    /// found.
+    void operator()(const Dhcp6Client::Configuration& config,
+                    const PropertyType& property, const bool equals,
+                    std::vector<Lease6>& leases) {
+
+        // Iterate over the leases and match the property with a given lease
+        //field.
+        for (typename std::vector<Lease6>::const_iterator lease =
+                 config.leases_.begin(); lease != config.leases_.end();
+             ++lease) {
+            // Check if fulfils the condition.
+            if ((equals && ((*lease).*MemberPointer) == property) ||
+                (!equals && ((*lease).*MemberPointer) != property)) {
+                // Found the matching lease.
+                leases.push_back(*lease);
+            }
+        }
+    }
+};
+
+}; // end of anonymous namespace
+
 namespace isc {
 namespace dhcp {
 namespace test {
@@ -47,8 +92,11 @@ Dhcp6Client::Dhcp6Client() :
     use_oro_(false),
     use_client_id_(true),
     use_rapid_commit_(false),
+    address_hint_(),
     prefix_hint_(),
-    fqdn_() {
+    fqdn_(),
+    na_iaid_(1234),
+    pd_iaid_(5678) {
 }
 
 Dhcp6Client::Dhcp6Client(boost::shared_ptr<NakedDhcpv6Srv>& srv) :
@@ -65,8 +113,11 @@ Dhcp6Client::Dhcp6Client(boost::shared_ptr<NakedDhcpv6Srv>& srv) :
     use_oro_(false),
     use_client_id_(true),
     use_rapid_commit_(false),
+    address_hint_(),
     prefix_hint_(),
-    fqdn_() {
+    fqdn_(),
+    na_iaid_(1234),
+    pd_iaid_(5678) {
 }
 
 void
@@ -79,8 +130,6 @@ Dhcp6Client::applyRcvdConfiguration(const Pkt6Ptr& reply) {
     // Let's try to get a MAC
     HWAddrPtr hwaddr = reply->getMAC(HWAddr::HWADDR_SOURCE_ANY);
 
-    // Set the global status code to default: success and not received.
-    config_.resetGlobalStatusCode();
     for (Opts::const_iterator opt = opts.begin(); opt != opts.end(); ++opt) {
         Option6IAPtr ia = boost::dynamic_pointer_cast<Option6IA>(opt->second);
         if (!ia) {
@@ -93,31 +142,27 @@ Dhcp6Client::applyRcvdConfiguration(const Pkt6Ptr& reply) {
         for (Opts::const_iterator iter_ia_opt = ia_opts.begin();
              iter_ia_opt != ia_opts.end(); ++iter_ia_opt) {
             OptionPtr ia_opt = iter_ia_opt->second;
-            LeaseInfo lease_info;
+            Lease6 lease;
+            lease.type_ = (ia->getType() == D6O_IA_NA ? Lease::TYPE_NA : Lease::TYPE_PD);
+            lease.iaid_ = ia->getIAID();
+
             switch (ia_opt->getType()) {
             case D6O_IAADDR:
                 {
                     Option6IAAddrPtr iaaddr = boost::dynamic_pointer_cast<
                         Option6IAAddr>(ia_opt);
 
-                    if (!iaaddr) {
-                        // There is no address. This IA option may simply
-                        // contain a status code, so let's just reset the
-                        // lease and keep IAID around.
-                        lease_info.lease_ = Lease6();
-                        lease_info.lease_.type_ = Lease::TYPE_NA;
-                        lease_info.lease_.iaid_ = ia->getIAID();
-                        break;
+                    if (iaaddr) {
+                        lease = Lease6(Lease::TYPE_NA,
+                                       iaaddr->getAddress(),
+                                       duid_, ia->getIAID(),
+                                       iaaddr->getPreferred(),
+                                       iaaddr->getValid(),
+                                       ia->getT1(), ia->getT2(), 0,
+                                       hwaddr);
+                        lease.cltt_ = time(NULL);
+                        applyLease(lease);
                     }
-
-                    lease_info.lease_ = Lease6(Lease::TYPE_NA,
-                                               iaaddr->getAddress(),
-                                               duid_, ia->getIAID(),
-                                               iaaddr->getPreferred(),
-                                               iaaddr->getValid(),
-                                               ia->getT1(), ia->getT2(), 0,
-                                               hwaddr);
-                    lease_info.lease_.cltt_ = time(NULL);
                 }
                 break;
 
@@ -125,24 +170,19 @@ Dhcp6Client::applyRcvdConfiguration(const Pkt6Ptr& reply) {
                 {
                     Option6IAPrefixPtr iaprefix = boost::dynamic_pointer_cast<
                         Option6IAPrefix>(ia_opt);
-                    if (!iaprefix) {
-                        // There is no prefix. This IA option may simply
-                        // contain a status code, so let's just reset the
-                        // lease and keep IAID around.
-                        lease_info.lease_ = Lease6();
-                        lease_info.lease_.type_ = Lease::TYPE_PD;
-                        lease_info.lease_.iaid_ = ia->getIAID();
-                        break;
+
+                    if (iaprefix) {
+                        lease = Lease6(Lease::TYPE_PD,
+                                       iaprefix->getAddress(), duid_,
+                                       ia->getIAID(),
+                                       iaprefix->getPreferred(),
+                                       iaprefix->getValid(),
+                                       ia->getT1(), ia->getT2(), 0,
+                                       hwaddr,
+                                       iaprefix->getLength());
+                        lease.cltt_ = time(NULL);
+                        applyLease(lease);
                     }
-                    lease_info.lease_ = Lease6(Lease::TYPE_PD,
-                                               iaprefix->getAddress(), duid_,
-                                               ia->getIAID(),
-                                               iaprefix->getPreferred(),
-                                               iaprefix->getValid(),
-                                               ia->getT1(), ia->getT2(), 0,
-                                               hwaddr,
-                                               iaprefix->getLength());
-                    lease_info.lease_.cltt_ = time(NULL);
                 }
                 break;
 
@@ -152,8 +192,8 @@ Dhcp6Client::applyRcvdConfiguration(const Pkt6Ptr& reply) {
                     // code, assume the status code to be 0.
                     Option6StatusCodePtr status_code = boost::dynamic_pointer_cast<
                         Option6StatusCode>(ia->getOption(D6O_STATUS_CODE));
-                    lease_info.status_code_ =
-                        status_code ? status_code->getStatusCode() : 0;
+                    config_.status_codes_[ia->getIAID()] =
+                        (status_code ? status_code->getStatusCode() : 0);
                 }
                 break;
 
@@ -161,7 +201,6 @@ Dhcp6Client::applyRcvdConfiguration(const Pkt6Ptr& reply) {
                 ; // no-op
             }
 
-            applyLease(lease_info);
         }
     }
 
@@ -177,29 +216,25 @@ Dhcp6Client::applyRcvdConfiguration(const Pkt6Ptr& reply) {
 }
 
 void
-Dhcp6Client::applyLease(const LeaseInfo& lease_info) {
+Dhcp6Client::applyLease(const Lease6& lease) {
     // Go over existing leases and try to match the one that we have.
     for (size_t i = 0; i < config_.leases_.size(); ++i) {
-        Lease6 existing_lease = config_.leases_[i].lease_;
+        Lease6 existing_lease = config_.leases_[i];
         // If IAID is matching and there is an actual address assigned
         // replace the current lease. The default address is :: if the
         // server hasn't sent the IA option. In this case, there is no
         // lease assignment so we keep what we have.
-        if ((existing_lease.iaid_ == lease_info.lease_.iaid_)
-            && (existing_lease.type_ == lease_info.lease_.type_)
-            && (lease_info.lease_.addr_ != asiolink::IOAddress("::"))
-            && (existing_lease.addr_ == lease_info.lease_.addr_)) {
-            config_.leases_[i] = lease_info;
+        if ((existing_lease.iaid_ == lease.iaid_)
+            && (existing_lease.type_ == lease.type_)
+            && (lease.addr_ != asiolink::IOAddress("::"))
+            && (existing_lease.addr_ == lease.addr_)) {
+            config_.leases_[i] = lease;
             return;
-
-        } else if (lease_info.lease_.addr_ == asiolink::IOAddress("::")) {
-            config_.leases_[i].status_code_ = lease_info.status_code_;
-            return;
-
         }
     }
+
     // It is a new lease. Add it.
-    config_.leases_.push_back(lease_info);
+    config_.leases_.push_back(lease);
 }
 
 void
@@ -210,17 +245,73 @@ 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");
+        }
+        // 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 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_);
+
+    } else if (address_hint_ && (ia_type == D6O_IA_NA)) {
+        requested_ia->addOption(address_hint_);
+    }
+
+    query->addOption(requested_ia);
+}
+
+
+void
 Dhcp6Client::copyIAs(const Pkt6Ptr& source, const Pkt6Ptr& dest) {
     typedef OptionCollection Opts;
     // Copy IA_NAs.
     Opts opts = source->getOptions(D6O_IA_NA);
     for (Opts::const_iterator opt = opts.begin(); opt != opts.end(); ++opt) {
-        dest->addOption(opt->second);
+        // Only copy the entire IA_NA if there is at lease one IA Address option.
+        if (opt->second->getOption(D6O_IAADDR)) {
+            dest->addOption(opt->second);
+        }
     }
     // Copy IA_PDs.
     opts = source->getOptions(D6O_IA_PD);
     for (Opts::const_iterator opt = opts.begin(); opt != opts.end(); ++opt) {
-        dest->addOption(opt->second);
+        // Only copy the entire IA_PD if there is at least one IA Prefix option
+        // in it.
+        if (opt->second->getOption(D6O_IAPREFIX)) {
+            dest->addOption(opt->second);
+        }
     }
 }
 
@@ -232,6 +323,11 @@ Dhcp6Client::copyIAsFromLeases(const Pkt6Ptr& dest) const {
     for (std::set<uint32_t>::const_iterator iaid = iaids.begin();
          iaid != iaids.end(); ++iaid) {
         std::vector<Lease6> leases = getLeasesByIAID(*iaid);
+        // Only a valid lease should be included. Do not copy a
+        // lease which have been marked by the server as invalid.
+        if (leases[0].valid_lft_ == 0) {
+            continue;
+        }
         Option6IAPtr opt(new Option6IA(leases[0].type_ == Lease::TYPE_NA ?
                                        D6O_IA_NA : D6O_IA_PD, *iaid));
         opt->setT1(leases[0].t1_);
@@ -261,9 +357,7 @@ Dhcp6Client::copyIAsFromLeases(const Pkt6Ptr& dest) const {
 
 void
 Dhcp6Client::createLease(const Lease6& lease) {
-    LeaseInfo info;
-    info.lease_ = lease;
-    applyLease(info);
+    applyLease(lease);
 }
 
 Pkt6Ptr
@@ -298,17 +392,10 @@ Dhcp6Client::doSolicit() {
     if (forced_server_id_) {
         context_.query_->addOption(forced_server_id_);
     }
-    if (use_na_) {
-        context_.query_->addOption(Option6IAPtr(new Option6IA(D6O_IA_NA,
-                                                              1234)));
-    }
-    if (use_pd_) {
-        Option6IAPtr ia(new Option6IA(D6O_IA_PD, 5678));
-        if (prefix_hint_) {
-            ia->addOption(prefix_hint_);
-        }
-        context_.query_->addOption(ia);
-    }
+
+    // Append requested (empty) IAs.
+    appendRequestedIAs(context_.query_);
+
     if (use_rapid_commit_) {
         context_.query_->addOption(OptionPtr(new Option(Option::V6,
                                                         D6O_RAPID_COMMIT)));
@@ -323,6 +410,7 @@ Dhcp6Client::doSolicit() {
     // let's apply received configuration.
     if (use_rapid_commit_ && context_.response_ &&
         context_.response_->getType() == DHCPV6_REPLY) {
+        config_.clear();
         applyRcvdConfiguration(context_.response_);
     }
 }
@@ -336,6 +424,7 @@ Dhcp6Client::doRequest() {
         query->addOption(forced_server_id_);
     }
     copyIAs(context_.response_, query);
+    appendRequestedIAs(query);
 
     // Add Client FQDN if configured.
     appendFQDN();
@@ -348,6 +437,7 @@ Dhcp6Client::doRequest() {
 
     // Apply new configuration only if the server has responded.
     if (context_.response_) {
+        config_.clear();
         applyRcvdConfiguration(context_.response_);
     }
 }
@@ -359,15 +449,15 @@ 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 with iaid=1234.
+        // Insert IA_NA option.
         context_.query_->addOption(Option6IAPtr(new Option6IA(D6O_IA_NA,
-                                                              1234)));
+                                                              na_iaid_)));
     }
 
     // IA-PD is also not allowed. So it may be useful in testing, too.
     if (use_pd_) {
-        // Insert IA_PD option with iaid=5678
-        Option6IAPtr ia(new Option6IA(D6O_IA_PD, 5678));
+        // Insert IA_PD option.
+        Option6IAPtr ia(new Option6IA(D6O_IA_PD, pd_iaid_));
         if (prefix_hint_) {
             ia->addOption(prefix_hint_);
         }
@@ -384,6 +474,10 @@ Dhcp6Client::doRenew() {
     query->addOption(context_.response_->getOption(D6O_SERVERID));
     copyIAsFromLeases(query);
 
+    // During the Renew the client may request additional bindings per
+    // RFC7550.
+    appendRequestedIAs(query);
+
     // Add Client FQDN if configured.
     appendFQDN();
 
@@ -392,6 +486,7 @@ Dhcp6Client::doRenew() {
     context_.response_ = receiveOneMsg();
     // Apply configuration only if the server has responded.
     if (context_.response_) {
+        config_.clear();
         applyRcvdConfiguration(context_.response_);
     }
 }
@@ -401,6 +496,10 @@ Dhcp6Client::doRebind() {
     Pkt6Ptr query = createMsg(DHCPV6_REBIND);
     copyIAsFromLeases(query);
 
+    // During the Rebind the client may request additional bindings per
+    // RFC7550.
+    appendRequestedIAs(query);
+
     // Add Client FQDN if configured.
     appendFQDN();
 
@@ -409,6 +508,7 @@ Dhcp6Client::doRebind() {
     context_.response_ = receiveOneMsg();
     // Apply configuration only if the server has responded.
     if (context_.response_) {
+        config_.clear();
         applyRcvdConfiguration(context_.response_);
     }
 }
@@ -422,6 +522,7 @@ Dhcp6Client::doConfirm() {
     // Set the global status code to default: success and not received.
     config_.resetGlobalStatusCode();
     if (context_.response_) {
+        config_.resetGlobalStatusCode();
         applyRcvdConfiguration(context_.response_);
     }
 }
@@ -430,7 +531,7 @@ void
 Dhcp6Client::fastFwdTime(const uint32_t secs) {
     // Iterate over all leases and move their cltt backwards.
     for (size_t i = 0; i < config_.leases_.size(); ++i) {
-        config_.leases_[i].lease_.cltt_ -= secs;
+        config_.leases_[i].cltt_ -= secs;
     }
 }
 
@@ -466,10 +567,9 @@ Dhcp6Client::getClientId() const {
 std::set<uint32_t>
 Dhcp6Client::getIAIDs() const {
     std::set<uint32_t> iaids;
-    for (std::vector<LeaseInfo>::const_iterator lease_info =
-             config_.leases_.begin(); lease_info != config_.leases_.end();
-         ++lease_info) {
-        iaids.insert(lease_info->lease_.iaid_);
+    for (std::vector<Lease6>::const_iterator lease = config_.leases_.begin();
+         lease != config_.leases_.end(); ++lease) {
+        iaids.insert(lease->iaid_);
     }
     return (iaids);
 }
@@ -477,16 +577,56 @@ Dhcp6Client::getIAIDs() const {
 std::vector<Lease6>
 Dhcp6Client::getLeasesByIAID(const uint32_t iaid) const {
     std::vector<Lease6> leases;
-    for (std::vector<LeaseInfo>::const_iterator lease_info =
-             config_.leases_.begin(); lease_info != config_.leases_.end();
-         ++lease_info) {
-        if (lease_info->lease_.iaid_ == iaid) {
-            leases.push_back(lease_info->lease_);
+    getLeasesByProperty<Lease6, uint32_t, &Lease6::iaid_>(iaid, true, leases);
+    return (leases);
+}
+
+template<typename BaseType, typename PropertyType, PropertyType BaseType::*MemberPointer>
+void
+Dhcp6Client::getLeasesByProperty(const PropertyType& property, const bool equals,
+                                 std::vector<Lease6>& leases) const {
+    getLeasesByPropertyFun<BaseType, PropertyType, MemberPointer> fun;
+    fun(config_, property, equals, leases);
+}
+
+std::vector<Lease6>
+Dhcp6Client::getLeasesByType(const Lease6::Type& lease_type) const {
+    std::vector<Lease6> leases;
+    getLeasesByProperty<Lease6, Lease6::Type, &Lease6::type_>(lease_type, true, leases);
+    return (leases);
+}
+
+std::vector<Lease6>
+Dhcp6Client::getLeasesWithNonZeroLifetime() const {
+    std::vector<Lease6> leases;
+    getLeasesByProperty<Lease, uint32_t, &Lease::valid_lft_>(0, false, leases);
+    return (leases);
+}
+
+std::vector<Lease6>
+Dhcp6Client::getLeasesWithZeroLifetime() const {
+    std::vector<Lease6> leases;
+    getLeasesByProperty<Lease, uint32_t, &Lease::valid_lft_>(0, true, leases);
+    return (leases);
+}
+
+uint16_t
+Dhcp6Client::getStatusCode(const uint32_t iaid) const {
+    std::map<uint32_t, uint16_t>::const_iterator status_code =
+        config_.status_codes_.find(iaid);
+    if (status_code == config_.status_codes_.end()) {
+        if (!getLeasesByIAID(iaid).empty()) {
+            return (STATUS_Success);
         }
+
+    } else {
+        return (status_code->second);
     }
-    return (leases);
+
+    return (0xFFFF);
 }
 
+
 void
 Dhcp6Client::setDUID(const std::string& str) {
     DUID d = DUID::fromText(str);
@@ -549,6 +689,14 @@ Dhcp6Client::sendMsg(const Pkt6Ptr& msg) {
 
 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));
+}
+
+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),

+ 115 - 10
src/bin/dhcp6/tests/dhcp6_client.h

@@ -70,6 +70,15 @@ public:
         /// @brief Default constructor for the structure.
         LeaseInfo() :
             lease_(), status_code_(0) { }
+
+        /// @brief Constructor which sets the lease type.
+        ///
+        /// @param lease_type One of the D6O_IA_NA or D6O_IA_PD.
+        LeaseInfo(const uint16_t lease_type) :
+            lease_(), status_code_(0) {
+            lease_.type_ = lease_type == D6O_IA_NA ? Lease::TYPE_NA :
+                Lease::TYPE_PD;
+        }
     };
 
     /// @brief Holds the current client configuration obtained from the
@@ -81,7 +90,10 @@ public:
     /// server-id and client-id.
     struct Configuration {
         /// @brief List of received leases
-        std::vector<LeaseInfo> leases_;
+        std::vector<Lease6> leases_;
+
+        /// @brief A map of IAID, status code tuples.
+        std::map<uint32_t, uint16_t> status_codes_;
 
         /// @brief List of received options
         OptionCollection options_;
@@ -101,6 +113,7 @@ public:
         /// @brief Clears configuration.
         void clear() {
             leases_.clear();
+            status_codes_.clear();
             resetGlobalStatusCode();
         }
 
@@ -302,7 +315,7 @@ public:
     /// @param at Index of the lease held by the client.
     /// @return A lease at the specified index.
     Lease6 getLease(const size_t at) const {
-        return (config_.leases_[at].lease_);
+        return (config_.leases_[at]);
     }
 
     /// @brief Returns collection of leases for specified IAID.
@@ -312,13 +325,26 @@ public:
     /// @return Vector containing leases for the IAID.
     std::vector<Lease6> getLeasesByIAID(const uint32_t iaid) const;
 
+    /// @brief Returns collection of leases by type.
+    ///
+    /// @param type Lease type: D6O_IA_NA or D6O_IA_PD.
+    ///
+    /// @return Vector containing leases of the specified type.
+    std::vector<Lease6> getLeasesByType(const Lease::Type& lease_type) const;
+
+    /// @brief Returns leases with non-zero lifetimes.
+    std::vector<Lease6> getLeasesWithNonZeroLifetime() const;
+
+    /// @brief Returns leases with zero lifetimes.
+    std::vector<Lease6> getLeasesWithZeroLifetime() const;
+
     /// @brief Returns the value of the global status code for the last
     /// transaction.
     uint16_t getStatusCode() const {
         return (config_.status_code_);
     }
 
-    /// @brief Returns status code set by the server for the lease.
+    /// @brief Returns status code set by the server for the IAID.
     ///
     /// @warning This method doesn't check if the specified index is out of
     /// range. The caller is responsible for using a correct offset by
@@ -326,9 +352,7 @@ public:
     ///
     /// @param at Index of the lease held by the client.
     /// @return A status code for the lease at the specified index.
-    uint16_t getStatusCode(const size_t at) const {
-        return (config_.leases_[at].status_code_);
-    }
+    uint16_t getStatusCode(const uint32_t iaid) const;
 
     /// @brief Returns number of acquired leases.
     size_t getLeaseNum() const {
@@ -391,6 +415,14 @@ public:
         iface_name_ = iface_name;
     }
 
+    /// @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.
@@ -405,10 +437,32 @@ public:
     /// 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.
-    void useNA(const bool use = true) {
+    /// @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.
@@ -418,8 +472,10 @@ public:
     ///
     /// @param use Parameter which 'true' value indicates that client should
     /// request prefix assignment.
-    void usePD(const bool use = true) {
+    /// @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 Simulate sending messages through a relay.
@@ -527,14 +583,37 @@ private:
     /// each individual lease.
     ///
     /// @param lease_info Structure holding new lease information.
-    void applyLease(const LeaseInfo& lease_info);
+    void applyLease(const Lease6& lease);
 
-    /// @brief Includes CLient FQDN in the client's message.
+    /// @brief Includes Client FQDN in the client's message.
     ///
     /// This method checks if @c fqdn_ is specified and includes it in
     /// the client's message.
     void appendFQDN();
 
+    /// @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.
+    ///
+    /// @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.
@@ -573,6 +652,24 @@ private:
     /// @return Object encapsulating a DUID.
     DuidPtr generateDUID(DUID::DUIDType duid_type) const;
 
+    /// @brief Returns client's leases which match the specified condition.
+    ///
+    /// @param property A value of the lease property used to search the lease.
+    /// @param equals A flag which indicates if the operator should search
+    /// for the leases which property is equal to the value of @c property
+    /// parameter (if true), or unequal (if false).
+    /// @param [out] leases A vector in which the operator will store leases
+    /// found.
+    ///
+    /// @tparam BaseType Base type to which the property belongs: @c Lease or
+    /// @c Lease6.
+    /// @tparam PropertyType A type of the property, e.g. @c uint32_t for IAID.
+    /// @tparam MemberPointer A pointer to the member, e.g. @c &Lease6::iaid_.
+    template<typename BaseType, typename PropertyType,
+             PropertyType BaseType::*MemberPointer>
+    void getLeasesByProperty(const PropertyType& property, const bool equals,
+                             std::vector<Lease6>& leases) const;
+
     /// @brief Simulates reception of the message from the server.
     ///
     /// @return Received message.
@@ -616,6 +713,9 @@ private:
     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_;
 
@@ -630,6 +730,11 @@ 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_;
 };
 
 } // end of namespace isc::dhcp::test

+ 1 - 1
src/bin/dhcp6/tests/dhcp6_message_test.cc

@@ -81,7 +81,7 @@ Dhcpv6MessageTest::requestLease(const std::string& config,
     Lease6Ptr lease_server = checkLease(lease_client);
     ASSERT_TRUE(lease_server);
     // And that status code was OK.
-    ASSERT_EQ(STATUS_Success, client.getStatusCode(0));
+    ASSERT_EQ(STATUS_Success, client.getStatusCode(lease_client.iaid_));
 }
 
 }

+ 1 - 0
src/bin/dhcp6/tests/dhcp6_message_test.h

@@ -78,6 +78,7 @@ public:
     void requestLease(const std::string& config, const int subnets_num,
                       Dhcp6Client& client);
 
+
 protected:
 
     /// @brief Interface Manager's fake configuration control.

+ 0 - 17
src/bin/dhcp6/tests/dhcp6_srv_unittest.cc

@@ -939,23 +939,6 @@ TEST_F(Dhcpv6SrvTest, RenewSomeoneElesesLease) {
     testRenewSomeoneElsesLease(Lease::TYPE_NA, IOAddress("2001:db8::1"));
 }
 
-// This test verifies that incoming (invalid) RENEW with a prefix
-// can be handled properly.
-//
-// This test checks 3 scenarios:
-// 1. there is no such lease at all
-// 2. there is such a lease, but it is assigned to a different IAID
-// 3. there is such a lease, but it belongs to a different client
-//
-// expected:
-// - returned REPLY message has copy of client-id
-// - returned REPLY message has server-id
-// - returned REPLY message has IA_PD that includes STATUS-CODE
-// - No lease in LeaseMgr
-TEST_F(Dhcpv6SrvTest, pdRenewReject) {
-    testRenewReject(Lease::TYPE_PD, IOAddress("2001:db8:1:2::"));
-}
-
 // This test verifies that incoming (positive) RELEASE with address can be
 // handled properly, that a REPLY is generated, that the response has status
 // code and that the lease is indeed removed from the database.

+ 0 - 140
src/bin/dhcp6/tests/dhcp6_test_utils.cc

@@ -421,146 +421,6 @@ Dhcpv6SrvTest::testRenewSomeoneElsesLease(Lease::Type type, const IOAddress& add
 }
 
 void
-Dhcpv6SrvTest::testRenewReject(Lease::Type type, const IOAddress& addr) {
-
-    NakedDhcpv6Srv srv(0);
-
-    const uint32_t transid = 1234;
-    const uint32_t valid_iaid = 234;
-    const uint32_t bogus_iaid = 456;
-
-    uint32_t code;
-    uint8_t prefix_len;
-    if (type == Lease::TYPE_NA) {
-        code = D6O_IA_NA;
-        prefix_len = 128;
-    } else if (type == Lease::TYPE_PD) {
-        code = D6O_IA_PD;
-        prefix_len = pd_pool_->getLength();
-    } else {
-        isc_throw(BadValue, "Invalid lease type");
-    }
-
-    // Quick sanity check that the address we're about to use is ok
-    ASSERT_TRUE(subnet_->inPool(type, addr));
-
-    // GenerateClientId() also sets duid_
-    OptionPtr clientid = generateClientId();
-
-    // Check that the lease is NOT in the database
-    Lease6Ptr l = LeaseMgrFactory::instance().getLease6(type, addr);
-    ASSERT_FALSE(l);
-
-    // Let's create a RENEW
-    Pkt6Ptr req = createMessage(DHCPV6_RENEW, type, IOAddress(addr), prefix_len,
-                                bogus_iaid);
-    req->addOption(clientid);
-    req->addOption(srv.getServerID());
-
-    // Case 1: No lease known to server
-
-    // Pass it to the server and hope for a REPLY
-    Pkt6Ptr reply = srv.processRenew(req);
-
-    // Check if we get response at all
-    checkResponse(reply, DHCPV6_REPLY, transid);
-    OptionPtr tmp = reply->getOption(code);
-    ASSERT_TRUE(tmp);
-
-    // Check that IA_?? was returned and that there's proper status code
-    boost::shared_ptr<Option6IA> ia = boost::dynamic_pointer_cast<Option6IA>(tmp);
-    ASSERT_TRUE(ia);
-
-    if (type == Lease::TYPE_PD) {
-        // For PD, the check is easy. NoBinding and no prefixes
-        checkIA_NAStatusCode(ia, STATUS_NoBinding, subnet_->getT1(), subnet_->getT2());
-    } else {
-        // For IA, it's more involved, as the server will reject the address
-        // (and send it with 0 lifetimes), but will also assign a new address.
-
-        // First, check that the requested address is rejected.
-        bool found = false;
-
-        dhcp::OptionCollection options = ia->getOptions();
-        for (isc::dhcp::OptionCollection::iterator opt = options.begin();
-             opt != options.end(); ++opt) {
-            if (opt->second->getType() != D6O_IAADDR) {
-                continue;
-            }
-
-            dhcp::Option6IAAddrPtr opt_addr =
-                boost::dynamic_pointer_cast<isc::dhcp::Option6IAAddr>(opt->second);
-            ASSERT_TRUE(opt_addr);
-
-            if (opt_addr->getAddress() != addr) {
-                // There may be other addresses, e.g. the newly assigned one
-                continue;
-            }
-
-            found = true;
-            EXPECT_NE(0, opt_addr->getPreferred());
-            EXPECT_NE(0, opt_addr->getValid());
-        }
-
-        EXPECT_TRUE(found) << "Expected address " << addr.toText()
-                           << " with zero lifetimes not found.";
-    }
-
-    // Check that there is no lease added
-    l = LeaseMgrFactory::instance().getLease6(type, addr);
-    ASSERT_FALSE(l);
-
-    // CASE 2: Lease is known and belongs to this client, but to a different IAID
-
-    // Note that preferred, valid, T1 and T2 timers and CLTT are set to invalid
-    // value on purpose. They should be updated during RENEW.
-    Lease6Ptr lease(new Lease6(type, addr, duid_, valid_iaid,
-                               501, 502, 503, 504, subnet_->getID(),
-                               HWAddrPtr(), prefix_len));
-    lease->cltt_ = 123; // Let's use it as an indicator that the lease
-                        // was NOT updated.
-    ASSERT_TRUE(LeaseMgrFactory::instance().addLease(lease));
-
-    // Pass it to the server and hope for a REPLY
-    reply = srv.processRenew(req);
-    checkResponse(reply, DHCPV6_REPLY, transid);
-    tmp = reply->getOption(code);
-    ASSERT_TRUE(tmp);
-
-    // Check that IA_?? was returned and that there's proper status code
-    ia = boost::dynamic_pointer_cast<Option6IA>(tmp);
-    ASSERT_TRUE(ia);
-    checkIA_NAStatusCode(ia, STATUS_NoBinding, subnet_->getT1(), subnet_->getT2());
-
-    // There is a iaid mis-match, so server should respond that there is
-    // no such address to renew.
-
-    // CASE 3: Lease belongs to a client with different client-id
-    req->delOption(D6O_CLIENTID);
-    ia = boost::dynamic_pointer_cast<Option6IA>(req->getOption(code));
-    ia->setIAID(valid_iaid); // Now iaid in renew matches that in leasemgr
-    req->addOption(generateClientId(13)); // generate different DUID
-                                          // (with length 13)
-
-    reply = srv.processRenew(req);
-    checkResponse(reply, DHCPV6_REPLY, transid);
-    tmp = reply->getOption(code);
-    ASSERT_TRUE(tmp);
-
-    // Check that IA_?? was returned and that there's proper status code
-    ia = boost::dynamic_pointer_cast<Option6IA>(tmp);
-    ASSERT_TRUE(ia);
-    checkIA_NAStatusCode(ia, STATUS_NoBinding, subnet_->getT1(), subnet_->getT2());
-
-    lease = LeaseMgrFactory::instance().getLease6(type, addr);
-    ASSERT_TRUE(lease);
-    // Verify that the lease was not updated.
-    EXPECT_EQ(123, lease->cltt_);
-
-    EXPECT_TRUE(LeaseMgrFactory::instance().deleteLease(addr));
-}
-
-void
 Dhcpv6SrvTest::testReleaseBasic(Lease::Type type, const IOAddress& existing,
                                 const IOAddress& release_addr) {
     NakedDhcpv6Srv srv(0);

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

@@ -468,19 +468,6 @@ public:
     testRenewSomeoneElsesLease(isc::dhcp::Lease::Type type,
                                const asiolink::IOAddress& addr);
 
-    /// @brief Performs negative RENEW test
-    ///
-    /// See renewReject and pdRenewReject tests for detailed explanation.
-    /// In essence the test attempts to perform couple failed RENEW scenarios.
-    ///
-    /// This method does not throw, but uses gtest macros to signify failures.
-    ///
-    /// @param type type (TYPE_NA or TYPE_PD)
-    /// @param addr address being sent in RENEW
-    void
-    testRenewReject(isc::dhcp::Lease::Type type,
-                    const isc::asiolink::IOAddress& addr);
-
     /// @brief Performs basic (positive) RELEASE test
     ///
     /// See releaseBasic and pdReleaseBasic tests for detailed explanation.

+ 267 - 116
src/bin/dhcp6/tests/rebind_unittest.cc

@@ -32,7 +32,7 @@ namespace {
 ///
 /// - Configuration 0:
 ///   - only addresses (no prefixes)
-///   - 2 subnets with 2001:db8:1::/64 and 2001:db8:2::64
+///   - 2 subnets with 2001:db8:1::/64 and 2001:db8:2::/64
 ///   - 1 subnet for eth0 and 1 subnet for eth1
 ///
 /// - Configuration 1:
@@ -57,11 +57,17 @@ namespace {
 ///   - this specific configuration is used by tests which don't use relays
 ///
 /// - Configuration 5:
-///   - similar to Configuration 5 but with different subnets
+///   - similar to Configuration 4 but with different subnets
 ///   - 2 subnets: 2001:db8:3::/40 and 2001:db8:4::/40
 ///   - 2 prefix pools: 2001:db8:3::/72 and 2001:db8:4::/72
 ///   - delegated length /80
 ///   - this specific configuration is used by tests which don't use relays
+///
+/// - Configuration 6:
+///   - addresses and prefixes
+///   - address pool: 2001:db8:1::/64
+///   - prefix pool: 3000::/72
+///
 const char* REBIND_CONFIGS[] = {
 // Configuration 0
     "{ \"interfaces-config\": {"
@@ -205,6 +211,25 @@ const char* REBIND_CONFIGS[] = {
         " } ],"
         "\"valid-lifetime\": 4000 }",
 
+// Configuration 6
+    "{ \"interfaces-config\": {"
+        "  \"interfaces\": [ \"*\" ]"
+        "},"
+        "\"preferred-lifetime\": 3000,"
+        "\"rebind-timer\": 2000, "
+        "\"renew-timer\": 1000, "
+        "\"subnet6\": [ { "
+        "    \"pools\": [ { \"pool\": \"2001:db8:1::/64\" } ],"
+        "    \"pd-pools\": ["
+        "        { \"prefix\": \"3000::\", "
+        "          \"prefix-len\": 72, "
+        "          \"delegated-len\": 80"
+        "        } ],"
+        "    \"subnet\": \"2001:db8:1::/48\", "
+        "    \"interface-id\": \"\","
+        "    \"interface\": \"eth0\""
+        " } ],"
+        "\"valid-lifetime\": 4000 }"
 };
 
 /// @brief Test fixture class for testing Rebind.
@@ -246,8 +271,10 @@ TEST_F(RebindTest, directClient) {
     EXPECT_TRUE(lease_server2);
 }
 
-// Test that server doesn't extend the lease when the configuration has changed
-// such that the existing subnet is replaced with a different subnet.
+// Test that server allocates a lease from a new subnet when the server
+// is reconfigured such that the previous subnet is replaced with a
+// new subnet. The client should get the new lease and an old lease
+// with zero lifetimes in the Reply.
 TEST_F(RebindTest, directClientChangingSubnet) {
     Dhcp6Client client;
     // Configure client to request IA_NA.
@@ -265,29 +292,26 @@ TEST_F(RebindTest, directClientChangingSubnet) {
 
     ASSERT_NO_THROW(client.doRebind());
 
-    // We are expecting that the server didn't extend the lease because
-    // the address that client is using doesn't match the new subnet.
-    // But, the client still has an old lease.
-    ASSERT_EQ(1, client.getLeaseNum());
-    Lease6 lease_client2 = client.getLease(0);
+    // We are expecting that the server has allocated a lease from the new
+    // subnet and sent zero lifetimes for a previous lease.
+
+    std::vector<Lease6> old_leases = client.getLeasesWithZeroLifetime();
+    ASSERT_EQ(1, old_leases.size());
+    EXPECT_EQ(lease_client.addr_, old_leases[0].addr_);
 
-    // The current lease should be exactly the same as old lease,
-    // because server shouldn't have extended.
-    EXPECT_TRUE(lease_client.addr_ == lease_client2.addr_);
-    EXPECT_EQ(0, lease_client2.preferred_lft_);
-    EXPECT_EQ(0, lease_client2.valid_lft_);
+    std::vector<Lease6> new_leases = client.getLeasesWithNonZeroLifetime();
+    ASSERT_EQ(1, new_leases.size());
 
     // Make sure, that the lease that client has, is matching the lease
     // in the lease database.
-    Lease6Ptr lease_server2 = checkLease(lease_client2);
+    Lease6Ptr lease_server2 = checkLease(new_leases[0]);
     EXPECT_TRUE(lease_server2);
-    // Client should have received NoBinding status code.
-    EXPECT_EQ(STATUS_NoBinding, client.getStatusCode(0));
-
+    // Client should have received Success status code.
+    EXPECT_EQ(STATUS_Success, client.getStatusCode(1234));
 }
 
-// Check that the server doesn't extend the lease for the client when the
-// client sends IAID which doesn't belong to the lease that client has.
+// Check that the server allocates a new lease when the client sends IA_NA
+// with a new IAID.
 TEST_F(RebindTest, directClientChangingIAID) {
     Dhcp6Client client;
     // Configure client to request IA_NA.
@@ -298,23 +322,29 @@ TEST_F(RebindTest, directClientChangingIAID) {
     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.config_.leases_[0].lease_.iaid_;
-    // Try to Rebind. Note that client will use a different IAID (which
-    // is not matching IAID that server retains for the client). Server
-    // should not find the lease that client is trying to extend and
-    // should return NoBinding.
+    client.config_.leases_[0].iaid_ = 1235;
+    client.useNA(true, 1235);
+
+    // Try to Rebind. The server should allocate new lease for this IAID.
     ASSERT_NO_THROW(client.doRebind());
-    // The lease obtained in 4-way exchange should not change after the Rebind
-    // attempt.
-    Lease6Ptr lease_server2 = checkLease(lease_client);
-    EXPECT_TRUE(lease_server2);
-    // The Status code returned to the client, should be NoBinding.
-    EXPECT_EQ(STATUS_NoBinding, client.getStatusCode(0));
 
+    // The old lease should be returned with 0 lifetimes.
+    std::vector<Lease6> old_leases = client.getLeasesWithZeroLifetime();
+    ASSERT_EQ(1, old_leases.size());
+    EXPECT_EQ(lease_client.addr_, old_leases[0].addr_);
+
+    // The new lease should be allocated.
+    std::vector<Lease6> new_leases = client.getLeasesWithNonZeroLifetime();
+    ASSERT_EQ(1, new_leases.size());
+
+    Lease6Ptr lease_server2 = checkLease(new_leases[0]);
+    EXPECT_TRUE(lease_server2);
+    // The Status code returned to the client, should be Success.
+    EXPECT_EQ(STATUS_Success, client.getStatusCode(1235));
 }
 
-// Check that server sends NoBinding when the lease has been lost from
-// the database and client is trying to Rebind it.
+// Check that the server allocates a requested lease for the client when
+// this lease has been lost from the database.
 TEST_F(RebindTest, directClientLostLease) {
     Dhcp6Client client;
     // Configure client to request IA_NA.
@@ -326,11 +356,15 @@ TEST_F(RebindTest, directClientLostLease) {
     // The lease has been acquired. Now, let's explicitly remove it from the
     // lease database.
     LeaseMgrFactory::instance().deleteLease(lease_client.addr_);
-    // An attempt to Rebind should fail. The lease should not be found by
-    // the server and the server should return NoBinding status code.
+    // Send Rebind.
     ASSERT_NO_THROW(client.doRebind());
-    ASSERT_EQ(1, client.getLeaseNum());
-    EXPECT_EQ(STATUS_NoBinding, client.getStatusCode(0));
+
+    // The server should re-allocate this lease to the client.
+    std::vector<Lease6> new_leases = client.getLeasesWithNonZeroLifetime();
+    ASSERT_EQ(1, new_leases.size());
+    EXPECT_EQ(lease_client.addr_, new_leases[0].addr_);
+    // Status code should be Success.
+    EXPECT_EQ(STATUS_Success, client.getStatusCode(1234));
 }
 
 /// @todo Extend tests for direct client changing address.
@@ -392,18 +426,9 @@ TEST_F(RebindTest, relayedClientChangingSubnet) {
     ASSERT_NO_THROW(client.doRebind());
     // We are expecting that the server didn't extend the lease because
     // the address that client is using doesn't match the new subnet.
-    // But, the client still has an old lease.
-    ASSERT_EQ(1, client.getLeaseNum());
-    Lease6 lease_client2 = client.getLease(0);
-    // The current lease should be exactly the same as old lease,
-    // because server shouldn't have extended.
-    EXPECT_TRUE(lease_client == lease_client2);
-    // Make sure, that the lease that client has, is matching the lease
-    // in the lease database.
-    Lease6Ptr lease_server2 = checkLease(lease_client2);
-    EXPECT_TRUE(lease_server2);
+    ASSERT_EQ(0, client.getLeaseNum());
     // Client should have received NoBinding status code.
-    EXPECT_EQ(STATUS_NoBinding, client.getStatusCode(0));
+    EXPECT_EQ(STATUS_NoBinding, client.getStatusCode(1234));
 
 }
 
@@ -421,25 +446,32 @@ TEST_F(RebindTest, relayedClientChangingIAID) {
     ASSERT_NO_FATAL_FAILURE(requestLease(REBIND_CONFIGS[2], 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.config_.leases_[0].lease_.iaid_;
-    // Try to Rebind. Note that client will use a different IAID (which
-    // is not matching IAID that server retains for the client). Server
-    // should not find the lease that client is trying to extend and
-    // should return NoBinding.
+    client.config_.leases_[0].iaid_ = 1235;
+    client.useNA(true, 1235);
+
+    // Try to Rebind. The server should allocate new lease for this IAID.
     ASSERT_NO_THROW(client.doRebind());
-    // The lease obtained in 4-way exchange should not change after the Rebind
-    // attempt.
-    Lease6Ptr lease_server2 = checkLease(lease_client);
-    EXPECT_TRUE(lease_server2);
-    // The Status code returned to the client, should be NoBinding.
-    EXPECT_EQ(STATUS_NoBinding, client.getStatusCode(0));
 
+    // The old lease should be returned with 0 lifetimes.
+    std::vector<Lease6> old_leases = client.getLeasesWithZeroLifetime();
+    ASSERT_EQ(1, old_leases.size());
+    EXPECT_EQ(lease_client.addr_, old_leases[0].addr_);
+
+    // The new lease should be allocated.
+    std::vector<Lease6> new_leases = client.getLeasesWithNonZeroLifetime();
+    ASSERT_EQ(1, new_leases.size());
+
+    Lease6Ptr lease_server2 = checkLease(new_leases[0]);
+    EXPECT_TRUE(lease_server2);
+    // The Status code returned to the client, should be Success.
+    EXPECT_EQ(STATUS_Success, client.getStatusCode(1235));
 }
 
-// Check that the relayed client receives NoBinding when the lease that he
-// is Rebinding has been lost from the database.
+// Check that the server allocates a requested lease for the client when
+// this lease has been lost from the database.
 TEST_F(RebindTest, relayedClientLostLease) {
     Dhcp6Client client;
     // Configure client to request IA_NA.
@@ -455,11 +487,16 @@ TEST_F(RebindTest, relayedClientLostLease) {
     // The lease has been acquired. Now, let's explicitly remove it from the
     // lease database.
     LeaseMgrFactory::instance().deleteLease(lease_client.addr_);
-    // An attempt to Rebind should fail. The lease should not be found by
-    // the server and the server should return NoBinding status code.
+
+    // Send Rebind.
     ASSERT_NO_THROW(client.doRebind());
-    ASSERT_EQ(1, client.getLeaseNum());
-    EXPECT_EQ(STATUS_NoBinding, client.getStatusCode(0));
+
+    // The server should re-allocate this lease to the client.
+    std::vector<Lease6> new_leases = client.getLeasesWithNonZeroLifetime();
+    ASSERT_EQ(1, new_leases.size());
+    EXPECT_EQ(lease_client.addr_, new_leases[0].addr_);
+    // Status code should be Success.
+    EXPECT_EQ(STATUS_Success, client.getStatusCode(1234));
 }
 
 // Check that relayed client receives the IA with lifetimes of 0, when
@@ -475,7 +512,7 @@ TEST_F(RebindTest, relayedClientChangingAddress) {
     // Modify the address of the lease record that client stores. The server
     // should check that the address is invalid (hasn't been allocated for
     // the particular IAID).
-    client.config_.leases_[0].lease_.addr_ = IOAddress("3000::100");
+    client.config_.leases_[0].addr_ = IOAddress("3000::100");
     // Try to Rebind. The client will use correct IAID but will specify a
     // wrong address. The server will discover that the client has a binding
     // but the address will not match.
@@ -543,9 +580,10 @@ TEST_F(RebindTest, directClientPD) {
     EXPECT_TRUE(lease_server2);
 }
 
-// Check that the prefix lifetime is not extended for the client in case
-// the configuration has been changed such, that the subnet he is using
-// doesn't exist anymore.
+// Test that server allocates a lease from a new subnet when the server
+// is reconfigured such that the previous subnet is replaced with a
+// new subnet. The client should get the new lease and an old lease
+// with zero lifetimes in the Reply.
 TEST_F(RebindTest, directClientPDChangingSubnet) {
     Dhcp6Client client;
     // Configure client to request IA_PD.
@@ -558,31 +596,31 @@ TEST_F(RebindTest, directClientPDChangingSubnet) {
     // client's interface. Note that there will also be a new subnet
     // id assigned to the subnet on this interface.
     configure(REBIND_CONFIGS[5], *client.getServer());
-    // Try to rebind, using the address that the client had acquired using
+
+    // Try to rebind, using the prefix that the client had acquired using
     // previous server configuration.
-    ASSERT_NO_THROW(client.doRebind());
-    // Make sure that the server has discarded client's message. In such case,
-    // the message sent back to the client should be NULL.
-    EXPECT_FALSE(client.getContext().response_)
-        << "The server responded to the Rebind message, while it should have"
-        " discarded it because there is no binding for the client.";
-    // We are expecting that the server didn't extend the lease because
-    // the address that client is using doesn't match the new subnet.
-    // But, the client still has an old lease.
-    ASSERT_EQ(1, client.getLeaseNum());
-    Lease6 lease_client2 = client.getLease(0);
-    // The current lease should be exactly the same as old lease,
-    // because server shouldn't have extended.
-    EXPECT_TRUE(lease_client == lease_client2);
+    client.doRebind();
+
+    // We are expecting that the server has allocated a lease from the new
+    // subnet and sent zero lifetimes for a previous lease.
+
+    std::vector<Lease6> old_leases = client.getLeasesWithZeroLifetime();
+    ASSERT_EQ(1, old_leases.size());
+    EXPECT_EQ(lease_client.addr_, old_leases[0].addr_);
+
+    std::vector<Lease6> new_leases = client.getLeasesWithNonZeroLifetime();
+    ASSERT_EQ(1, new_leases.size());
+
     // Make sure, that the lease that client has, is matching the lease
     // in the lease database.
-    Lease6Ptr lease_server2 = checkLease(lease_client2);
+    Lease6Ptr lease_server2 = checkLease(new_leases[0]);
     EXPECT_TRUE(lease_server2);
+    // Client should have received Success status code.
+    EXPECT_EQ(STATUS_Success, client.getStatusCode(5678));
 }
 
-// Check that the prefix lifetime is not extended for the client when the
-// IAID used in the Rebind is not matching the one recorded by the server
-// for the particular client.
+// Check that the server allocates a new lease when the client sends IA_PD
+// with a new IAID.
 TEST_F(RebindTest, directClientPDChangingIAID) {
     Dhcp6Client client;
     // Configure client to request IA_PD.
@@ -591,24 +629,28 @@ TEST_F(RebindTest, directClientPDChangingIAID) {
     ASSERT_NO_FATAL_FAILURE(requestLease(REBIND_CONFIGS[4], 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.config_.leases_[0].lease_.iaid_;
-    // Try to Rebind. Note that client will use a different IAID (which
-    // is not matching IAID that server retains for the client). This is
-    // a condition described in RFC3633, section 12.2 as the server finds
-    // no binding for the client. It is an indication that some other
-    // server has probably allocated the lease for the client. Hence, our
-    // server should discard the message.
+    client.config_.leases_[0].iaid_ = 5679;
+    client.usePD(true, 5679);
+
+    // Try to Rebind. The server should allocate new lease for this IAID.
     ASSERT_NO_THROW(client.doRebind());
-    // Make sure that the server has discarded client's message. In such case,
-    // the message sent back to the client should be NULL.
-    EXPECT_FALSE(client.getContext().response_)
-        << "The server responded to the Rebind message, while it should have"
-        " discarded it because there is no binding for the client.";
-    // Check that server still has the same lease.
-    Lease6Ptr lease_server = checkLease(lease_client);
-    EXPECT_TRUE(lease_server);
+
+    // The old lease should be returned with 0 lifetimes.
+    std::vector<Lease6> old_leases = client.getLeasesWithZeroLifetime();
+    ASSERT_EQ(1, old_leases.size());
+    EXPECT_EQ(lease_client.addr_, old_leases[0].addr_);
+
+    // The new lease should be allocated.
+    std::vector<Lease6> new_leases = client.getLeasesWithNonZeroLifetime();
+    ASSERT_EQ(1, new_leases.size());
+
+    Lease6Ptr lease_server2 = checkLease(new_leases[0]);
+    EXPECT_TRUE(lease_server2);
+    // The Status code returned to the client, should be Success.
+    EXPECT_EQ(STATUS_Success, client.getStatusCode(5679));
 }
 
 // Check that the prefix lifetime is not extended for the client when the
@@ -624,9 +666,9 @@ TEST_F(RebindTest, directClientPDChangingPrefix) {
     // Modify the Prefix of the lease record that client stores. The server
     // should check that the prefix is invalid (hasn't been allocated for
     // the particular IAID).
-    ASSERT_NE(client.config_.leases_[0].lease_.addr_,
+    ASSERT_NE(client.config_.leases_[0].addr_,
               IOAddress("2001:db8:1:10::"));
-    client.config_.leases_[0].lease_.addr_ = IOAddress("2001:db8:1:10::");
+    client.config_.leases_[0].addr_ = IOAddress("2001:db8:1:10::");
     // Try to Rebind. The client will use correct IAID but will specify a
     // wrong prefix. The server will discover that the client has a binding
     // but the prefix will not match. According to the RFC3633, section 12.2.
@@ -645,21 +687,19 @@ TEST_F(RebindTest, directClientPDChangingPrefix) {
     // Client should get two entries. One with the invalid address he requested
     // with zeroed lifetimes and a second one with the actual prefix he has
     // with non-zero lifetimes.
-    Lease6 lease_client1 = client.getLease(0);
-    Lease6 lease_client2 = client.getLease(1);
 
-    // The lifetimes should be set to 0, as an explicit notification to the
-    // client to stop using invalid prefix.
-    EXPECT_EQ(0, lease_client1.valid_lft_);
-    EXPECT_EQ(0, lease_client1.preferred_lft_);
+    // Get the lease with 0 lifetimes.
+    std::vector<Lease6> invalid_leases = client.getLeasesWithZeroLifetime();
+    ASSERT_EQ(1, invalid_leases.size());
+    EXPECT_EQ(0, invalid_leases[0].valid_lft_);
+    EXPECT_EQ(0, invalid_leases[0].preferred_lft_);
 
-    // The lifetimes should be set to 0, as an explicit notification to the
-    // client to stop using invalid prefix.
-    EXPECT_NE(0, lease_client2.valid_lft_);
-    EXPECT_NE(0, lease_client2.preferred_lft_);
+    // Get the valid lease with non-zero lifetime.
+    std::vector<Lease6> valid_leases = client.getLeasesWithNonZeroLifetime();
+    ASSERT_EQ(1, valid_leases.size());
 
     // Check that server still has the same lease.
-    Lease6Ptr lease_server = checkLease(lease_client);
+    Lease6Ptr lease_server = checkLease(valid_leases[0]);
     ASSERT_TRUE(lease_server);
     // Make sure that the lease in the data base hasn't been added.
     EXPECT_NE(0, lease_server->valid_lft_);
@@ -732,4 +772,115 @@ TEST_F(RebindTest, relayedUnicast) {
     EXPECT_TRUE(lease_server2);
 }
 
+// This test verifies that the client can request the prefix delegation
+// while it is rebinding an address lease.
+TEST_F(RebindTest, requestPrefixInRebind) {
+    Dhcp6Client client;
+
+    // Configure client to request IA_NA and IA_PD.
+    client.useNA();
+    client.usePD();
+
+    // Configure the server with NA pools only.
+    ASSERT_NO_THROW(configure(REBIND_CONFIGS[0], *client.getServer()));
+
+    // Perform 4-way exchange.
+    ASSERT_NO_THROW(client.doSARR());
+
+    // Simulate aging of leases.
+    client.fastFwdTime(1000);
+
+    // Make sure that the client has acquired NA lease.
+    std::vector<Lease6> leases_client_na = client.getLeasesByType(Lease::TYPE_NA);
+    ASSERT_EQ(1, leases_client_na.size());
+
+    // The client should not acquire a PD lease.
+    std::vector<Lease6> leases_client_pd = client.getLeasesByType(Lease::TYPE_PD);
+    ASSERT_TRUE(leases_client_pd.empty());
+    ASSERT_EQ(STATUS_NoPrefixAvail, client.getStatusCode(5678));
+
+    // Send Rebind message to the server, including IA_NA and requesting IA_PD.
+    ASSERT_NO_THROW(client.doRebind());
+    ASSERT_TRUE(client.getContext().response_);
+    leases_client_pd = client.getLeasesByType(Lease::TYPE_PD);
+    ASSERT_TRUE(leases_client_pd.empty());
+    ASSERT_EQ(STATUS_NoPrefixAvail, client.getStatusCode(5678));
+
+    // Reconfigure the server to use both NA and PD pools.
+    configure(REBIND_CONFIGS[6], *client.getServer());
+
+    // Send Rebind message to the server, including IA_NA and requesting IA_PD.
+    ASSERT_NO_THROW(client.doRebind());
+
+    // Make sure that the client has acquired NA lease.
+    std::vector<Lease6> leases_client_na_rebound =
+        client.getLeasesByType(Lease::TYPE_NA);
+    ASSERT_EQ(1, leases_client_na_rebound.size());
+    EXPECT_EQ(STATUS_Success, client.getStatusCode(1234));
+
+    // The lease should have been rebound.
+    EXPECT_EQ(1000, leases_client_na_rebound[0].cltt_ - leases_client_na[0].cltt_);
+
+    // The client should now also acquire a PD lease.
+    leases_client_pd = client.getLeasesByType(Lease::TYPE_PD);
+    ASSERT_EQ(1, leases_client_pd.size());
+    EXPECT_EQ(STATUS_Success, client.getStatusCode(5678));
+}
+
+// This test verifies that the client can request the prefix delegation
+// while it is rebinding an address lease.
+TEST_F(RebindTest, requestAddressInRebind) {
+    Dhcp6Client client;
+
+    // Configure client to request IA_NA and IA_PD.
+    client.useNA();
+    client.usePD();
+
+    // Configure the server with PD pools only.
+    ASSERT_NO_THROW(configure(REBIND_CONFIGS[4], *client.getServer()));
+
+    // Perform 4-way exchange.
+    ASSERT_NO_THROW(client.doSARR());
+
+    // Simulate aging of leases.
+    client.fastFwdTime(1000);
+
+    // Make sure that the client has acquired PD lease.
+    std::vector<Lease6> leases_client_pd = client.getLeasesByType(Lease::TYPE_PD);
+    ASSERT_EQ(1, leases_client_pd.size());
+    EXPECT_EQ(STATUS_Success, client.getStatusCode(5678));
+
+    // The client should not acquire a NA lease.
+    std::vector<Lease6> leases_client_na =
+        client.getLeasesByType(Lease::TYPE_NA);
+    ASSERT_EQ(0, leases_client_na.size());
+    ASSERT_EQ(STATUS_NoAddrsAvail, client.getStatusCode(1234));
+
+    // Send Rebind message to the server, including IA_PD and requesting IA_NA.
+    // The server should return NoAddrsAvail status code in this case.
+    ASSERT_NO_THROW(client.doRebind());
+    leases_client_na = client.getLeasesByType(Lease::TYPE_NA);
+    ASSERT_EQ(0, leases_client_na.size());
+    ASSERT_EQ(STATUS_NoAddrsAvail, client.getStatusCode(1234));
+
+    // Reconfigure the server to use both NA and PD pools.
+    configure(REBIND_CONFIGS[6], *client.getServer());
+
+    // Send Rebind message to the server, including IA_PD and requesting IA_NA.
+    ASSERT_NO_THROW(client.doRebind());
+
+    // Make sure that the client has renewed PD lease.
+    std::vector<Lease6> leases_client_pd_renewed =
+        client.getLeasesByType(Lease::TYPE_PD);
+    ASSERT_EQ(1, leases_client_pd_renewed.size());
+    EXPECT_EQ(STATUS_Success, client.getStatusCode(5678));
+    EXPECT_GE(leases_client_pd_renewed[0].cltt_ - leases_client_pd[0].cltt_, 1000);
+
+    // The client should now also acquire a NA lease.
+    leases_client_na = client.getLeasesByType(Lease::TYPE_NA);
+    ASSERT_EQ(1, leases_client_na.size());
+    EXPECT_EQ(STATUS_Success, client.getStatusCode(1234));
+}
+
+
 } // end of anonymous namespace

+ 383 - 0
src/bin/dhcp6/tests/renew_unittest.cc

@@ -0,0 +1,383 @@
+// Copyright (C) 2015 Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#include <config.h>
+#include <asiolink/io_address.h>
+#include <cc/data.h>
+#include <dhcp/tests/iface_mgr_test_config.h>
+#include <dhcp6/json_config_parser.h>
+#include <dhcp6/tests/dhcp6_message_test.h>
+
+using namespace isc;
+using namespace isc::asiolink;
+using namespace isc::data;
+using namespace isc::dhcp;
+using namespace isc::dhcp::test;
+using namespace isc::test;
+
+namespace {
+
+/// @brief Set of JSON configurations used throughout the Renew tests.
+///
+/// - Configuration 0:
+///   - only addresses (no prefixes)
+///   - 1 subnet with 2001:db8:1::/64 pool
+///
+/// - Configuration 1:
+///   - only prefixes (no addresses)
+///   - prefix pool: 3000::/72
+///
+/// - Configuration 2:
+///   - addresses and prefixes
+///   - 1 subnet with one address pool and one prefix pool
+///   - address pool: 2001:db8:1::/64
+///   - prefix pool: 3000::/72
+///
+const char* RENEW_CONFIGS[] = {
+// Configuration 0
+    "{ \"interfaces-config\": {"
+        "  \"interfaces\": [ \"*\" ]"
+        "},"
+        "\"preferred-lifetime\": 3000,"
+        "\"rebind-timer\": 2000, "
+        "\"renew-timer\": 1000, "
+        "\"subnet6\": [ { "
+        "    \"pools\": [ { \"pool\": \"2001:db8:1::/64\" } ],"
+        "    \"subnet\": \"2001:db8:1::/48\", "
+        "    \"interface-id\": \"\","
+        "    \"interface\": \"eth0\""
+        " } ],"
+        "\"valid-lifetime\": 4000 }",
+
+// Configuration 1
+    "{ \"interfaces-config\": {"
+        "  \"interfaces\": [ \"*\" ]"
+        "},"
+        "\"preferred-lifetime\": 3000,"
+        "\"rebind-timer\": 2000, "
+        "\"renew-timer\": 1000, "
+        "\"subnet6\": [ { "
+        "    \"pd-pools\": ["
+        "        { \"prefix\": \"3000::\", "
+        "          \"prefix-len\": 72, "
+        "          \"delegated-len\": 80"
+        "        } ],"
+        "    \"subnet\": \"2001:db8:1::/48\", "
+        "    \"interface-id\": \"\","
+        "    \"interface\": \"eth0\""
+        " } ],"
+        "\"valid-lifetime\": 4000 }",
+
+// Configuration 2
+    "{ \"interfaces-config\": {"
+        "  \"interfaces\": [ \"*\" ]"
+        "},"
+        "\"preferred-lifetime\": 3000,"
+        "\"rebind-timer\": 2000, "
+        "\"renew-timer\": 1000, "
+        "\"subnet6\": [ { "
+        "    \"pools\": [ { \"pool\": \"2001:db8:1::/64\" } ],"
+        "    \"pd-pools\": ["
+        "        { \"prefix\": \"3000::\", "
+        "          \"prefix-len\": 72, "
+        "          \"delegated-len\": 80"
+        "        } ],"
+        "    \"subnet\": \"2001:db8:1::/48\", "
+        "    \"interface-id\": \"\","
+        "    \"interface\": \"eth0\""
+        " } ],"
+        "\"valid-lifetime\": 4000 }"
+};
+
+/// @brief Test fixture class for testing Renew.
+class RenewTest : public Dhcpv6MessageTest {
+public:
+
+    /// @brief Constructor.
+    ///
+    /// Sets up fake interfaces.
+    RenewTest()
+        : Dhcpv6MessageTest(), na_iaid_(1234), pd_iaid_(5678) {
+    }
+
+    /// @brief IAID used for IA_NA.
+    uint32_t na_iaid_;
+
+    /// @brief IAID used for IA_PD.
+    uint32_t pd_iaid_;
+
+};
+
+// This test verifies that the client can request the prefix delegation
+// while it is renewing an address lease.
+TEST_F(RenewTest, requestPrefixInRenew) {
+    Dhcp6Client client;
+
+    // Configure client to request IA_NA and IA_PD.
+    client.useNA(na_iaid_);
+    client.usePD(pd_iaid_);
+
+    // Configure the server with NA pools only.
+    ASSERT_NO_THROW(configure(RENEW_CONFIGS[0], *client.getServer()));
+
+    // Perform 4-way exchange.
+    ASSERT_NO_THROW(client.doSARR());
+
+    // Simulate aging of leases.
+    client.fastFwdTime(1000);
+
+    // Make sure that the client has acquired NA lease.
+    std::vector<Lease6> leases_client_na = client.getLeasesByType(Lease::TYPE_NA);
+    ASSERT_EQ(1, leases_client_na.size());
+    EXPECT_EQ(STATUS_Success, client.getStatusCode(na_iaid_));
+
+    // The client should not acquire a PD lease.
+    std::vector<Lease6> leases_client_pd = client.getLeasesByType(Lease::TYPE_PD);
+    ASSERT_TRUE(leases_client_pd.empty());
+    ASSERT_EQ(STATUS_NoPrefixAvail, client.getStatusCode(pd_iaid_));
+
+    // Send Renew message to the server, including IA_NA and requesting IA_PD.
+    ASSERT_NO_THROW(client.doRenew());
+    leases_client_pd = client.getLeasesByType(Lease::TYPE_PD);
+    ASSERT_TRUE(leases_client_pd.empty());
+    ASSERT_EQ(STATUS_NoPrefixAvail, client.getStatusCode(pd_iaid_));
+
+    std::vector<Lease6> leases_client_na_renewed =
+        client.getLeasesByType(Lease::TYPE_NA);
+    ASSERT_EQ(1, leases_client_na_renewed.size());
+    EXPECT_EQ(STATUS_Success, client.getStatusCode(na_iaid_));
+
+    // Reconfigure the server to use both NA and PD pools.
+    configure(RENEW_CONFIGS[2], *client.getServer());
+
+    // Send Renew message to the server, including IA_NA and requesting IA_PD.
+    ASSERT_NO_THROW(client.doRenew());
+
+    // Make sure that the client has acquired NA lease.
+    leases_client_na_renewed = client.getLeasesByType(Lease::TYPE_NA);
+    ASSERT_EQ(1, leases_client_na_renewed.size());
+    EXPECT_EQ(STATUS_Success, client.getStatusCode(na_iaid_));
+
+    // The lease should have been renewed.
+    EXPECT_EQ(1000, leases_client_na_renewed[0].cltt_ - leases_client_na[0].cltt_);
+
+    // The client should now also acquire a PD lease.
+    leases_client_pd = client.getLeasesByType(Lease::TYPE_PD);
+    ASSERT_EQ(1, leases_client_pd.size());
+    EXPECT_EQ(STATUS_Success, client.getStatusCode(pd_iaid_));
+}
+
+// This test verifies that the client can request a prefix delegation
+// with a hint, while it is renewing an address lease.
+TEST_F(RenewTest, requestPrefixInRenewUseHint) {
+    Dhcp6Client client;
+
+    // Configure client to request IA_NA and IA_PD.
+    client.useNA(na_iaid_);
+    client.usePD(pd_iaid_);
+
+    // Configure the server with NA pools only.
+    ASSERT_NO_THROW(configure(RENEW_CONFIGS[0], *client.getServer()));
+
+    // Perform 4-way exchange.
+    ASSERT_NO_THROW(client.doSARR());
+
+    // Simulate aging of leases.
+    client.fastFwdTime(1000);
+
+    // Make sure that the client has acquired NA lease.
+    std::vector<Lease6> leases_client_na = client.getLeasesByType(Lease::TYPE_NA);
+    ASSERT_EQ(1, leases_client_na.size());
+
+    // The client should not acquire a PD lease.
+    std::vector<Lease6> leases_client_pd = client.getLeasesByType(Lease::TYPE_PD);
+    ASSERT_TRUE(leases_client_pd.empty());
+    ASSERT_EQ(STATUS_NoPrefixAvail, client.getStatusCode(pd_iaid_));
+
+    // Send Renew message to the server, including IA_NA and requesting IA_PD.
+    ASSERT_NO_THROW(client.doRenew());
+    leases_client_pd = client.getLeasesByType(Lease::TYPE_PD);
+    ASSERT_TRUE(leases_client_pd.empty());
+    ASSERT_EQ(STATUS_NoPrefixAvail, client.getStatusCode(pd_iaid_));
+
+    std::vector<Lease6> leases_client_na_renewed =
+        client.getLeasesByType(Lease::TYPE_NA);
+    ASSERT_EQ(1, leases_client_na_renewed.size());
+    EXPECT_EQ(STATUS_Success, client.getStatusCode(na_iaid_));
+
+    // 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());
+
+    // Make sure that the client has acquired NA lease.
+    leases_client_na_renewed = client.getLeasesByType(Lease::TYPE_NA);
+    ASSERT_EQ(1, leases_client_na_renewed.size());
+    EXPECT_EQ(STATUS_Success, client.getStatusCode(na_iaid_));
+
+    leases_client_pd = client.getLeasesByType(Lease::TYPE_PD);
+    ASSERT_TRUE(leases_client_pd.empty());
+    ASSERT_EQ(STATUS_NoPrefixAvail, client.getStatusCode(pd_iaid_));
+
+    // 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());
+
+    // Make sure that the client has acquired NA lease.
+    leases_client_na_renewed = client.getLeasesByType(Lease::TYPE_NA);
+    ASSERT_EQ(1, leases_client_na_renewed.size());
+    EXPECT_EQ(STATUS_Success, client.getStatusCode(na_iaid_));
+
+    // The lease should have been renewed.
+    EXPECT_GE(leases_client_na_renewed[0].cltt_ - leases_client_na[0].cltt_, 1000);
+
+    // The client should now also acquire a PD lease.
+    leases_client_pd = client.getLeasesByType(Lease::TYPE_PD);
+    ASSERT_EQ(1, leases_client_pd.size());
+    EXPECT_EQ(STATUS_Success, client.getStatusCode(pd_iaid_));
+}
+
+// This test verifies that the client can request the prefix delegation
+// while it is renewing an address lease.
+TEST_F(RenewTest, requestAddressInRenew) {
+    Dhcp6Client client;
+
+    // Configure client to request IA_NA and IA_PD.
+    client.useNA(na_iaid_);
+    client.usePD(pd_iaid_);
+
+    // Configure the server with PD pools only.
+    ASSERT_NO_THROW(configure(RENEW_CONFIGS[1], *client.getServer()));
+
+    // Perform 4-way exchange.
+    ASSERT_NO_THROW(client.doSARR());
+
+    // Simulate aging of leases.
+    client.fastFwdTime(1000);
+
+    // Make sure that the client has acquired PD lease.
+    std::vector<Lease6> leases_client_pd = client.getLeasesByType(Lease::TYPE_PD);
+    ASSERT_EQ(1, leases_client_pd.size());
+    EXPECT_EQ(STATUS_Success, client.getStatusCode(pd_iaid_));
+
+    // The client should not acquire a NA lease.
+    std::vector<Lease6> leases_client_na =
+        client.getLeasesByType(Lease::TYPE_NA);
+    ASSERT_EQ(0, leases_client_na.size());
+    ASSERT_EQ(STATUS_NoAddrsAvail, client.getStatusCode(na_iaid_));
+
+    // Send Renew message to the server, including IA_PD and requesting IA_NA.
+    // The server should return NoAddrsAvail status code in this case.
+    ASSERT_NO_THROW(client.doRenew());
+    leases_client_na = client.getLeasesByType(Lease::TYPE_NA);
+    ASSERT_EQ(0, leases_client_na.size());
+    ASSERT_EQ(STATUS_NoAddrsAvail, client.getStatusCode(na_iaid_));
+
+    std::vector<Lease6> leases_client_pd_renewed =
+        client.getLeasesByType(Lease::TYPE_PD);
+    ASSERT_EQ(1, leases_client_pd_renewed.size());
+    EXPECT_EQ(STATUS_Success, client.getStatusCode(pd_iaid_));
+    EXPECT_GE(leases_client_pd_renewed[0].cltt_ - leases_client_pd[0].cltt_, 1000);
+
+    // Reconfigure the server to use both NA and PD pools.
+    configure(RENEW_CONFIGS[2], *client.getServer());
+
+    // Send Renew message to the server, including IA_PD and requesting IA_NA.
+    ASSERT_NO_THROW(client.doRenew());
+
+    // Make sure that the client has renewed PD lease.
+    leases_client_pd_renewed =  client.getLeasesByType(Lease::TYPE_PD);
+    ASSERT_EQ(1, leases_client_pd_renewed.size());
+    EXPECT_EQ(STATUS_Success, client.getStatusCode(pd_iaid_));
+    EXPECT_GE(leases_client_pd_renewed[0].cltt_ - leases_client_pd[0].cltt_, 1000);
+
+    // The client should now also acquire a NA lease.
+    leases_client_na = client.getLeasesByType(Lease::TYPE_NA);
+    ASSERT_EQ(1, leases_client_na.size());
+    EXPECT_EQ(STATUS_Success, client.getStatusCode(na_iaid_));
+}
+
+// This test verifies that the client can request address assignment
+// while it is renewing an address lease, with a hint.
+TEST_F(RenewTest, requestAddressInRenewHint) {
+    Dhcp6Client client;
+
+    // Configure client to request IA_NA and IA_PD.
+    client.useNA(na_iaid_);
+    client.usePD(pd_iaid_);
+
+    // Configure the server with PD pools only.
+    ASSERT_NO_THROW(configure(RENEW_CONFIGS[1], *client.getServer()));
+
+    // Perform 4-way exchange.
+    ASSERT_NO_THROW(client.doSARR());
+
+    // Simulate aging of leases.
+    client.fastFwdTime(1000);
+
+    // Make sure that the client has acquired PD lease.
+    std::vector<Lease6> leases_client_pd = client.getLeasesByType(Lease::TYPE_PD);
+    ASSERT_EQ(1, leases_client_pd.size());
+    EXPECT_EQ(STATUS_Success, client.getStatusCode(pd_iaid_));
+
+    // The client should not acquire a NA lease.
+    std::vector<Lease6> leases_client_na =
+        client.getLeasesByType(Lease::TYPE_NA);
+    ASSERT_EQ(0, leases_client_na.size());
+    ASSERT_EQ(STATUS_NoAddrsAvail, client.getStatusCode(na_iaid_));
+
+    client.useHint(0, 0, "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.
+    ASSERT_NO_THROW(client.doRenew());
+    leases_client_na = client.getLeasesByType(Lease::TYPE_NA);
+    // The server should return the hint with the zero lifetimes.
+    ASSERT_EQ(1, leases_client_na.size());
+    EXPECT_EQ(0, leases_client_na[0].preferred_lft_);
+    EXPECT_EQ(0, leases_client_na[0].valid_lft_);
+    ASSERT_EQ(STATUS_NoAddrsAvail, client.getStatusCode(na_iaid_));
+
+    std::vector<Lease6> leases_client_pd_renewed =
+        client.getLeasesByType(Lease::TYPE_PD);
+    ASSERT_EQ(1, leases_client_pd_renewed.size());
+    EXPECT_EQ(STATUS_Success, client.getStatusCode(pd_iaid_));
+    EXPECT_GE(leases_client_pd_renewed[0].cltt_ - leases_client_pd[0].cltt_, 1000);
+
+    // Reconfigure the server to use both NA and PD pools.
+    configure(RENEW_CONFIGS[2], *client.getServer());
+
+    // Send Renew message to the server, including IA_PD and requesting IA_NA.
+    ASSERT_NO_THROW(client.doRenew());
+
+    // Make sure that the client has renewed PD lease.
+    leases_client_pd_renewed = client.getLeasesByType(Lease::TYPE_PD);
+    ASSERT_EQ(1, leases_client_pd_renewed.size());
+    EXPECT_EQ(STATUS_Success, client.getStatusCode(pd_iaid_));
+    EXPECT_GE(leases_client_pd_renewed[0].cltt_ - leases_client_pd[0].cltt_, 1000);
+
+    // The client should now also acquire a NA lease.
+    leases_client_na = client.getLeasesByType(Lease::TYPE_NA);
+    ASSERT_EQ(1, leases_client_na.size());
+    EXPECT_EQ(STATUS_Success, client.getStatusCode(na_iaid_));
+}
+
+
+} // end of anonymous namespace

+ 3 - 4
src/lib/dhcpsrv/alloc_engine.cc

@@ -297,7 +297,7 @@ AllocEngine::ClientContext6::ClientContext6()
     : subnet_(), duid_(), iaid_(0), type_(Lease::TYPE_NA), hwaddr_(),
       hints_(), fwd_dns_update_(false), rev_dns_update_(false), hostname_(""),
       callout_handle_(), fake_allocation_(false), old_leases_(), host_(),
-      query_(), ia_rsp_(), allow_new_leases_in_renewals_(false) {
+      query_(), ia_rsp_() {
 }
 
 AllocEngine::ClientContext6::ClientContext6(const Subnet6Ptr& subnet, const DuidPtr& duid,
@@ -310,8 +310,7 @@ AllocEngine::ClientContext6::ClientContext6(const Subnet6Ptr& subnet, const Duid
     subnet_(subnet), duid_(duid), iaid_(iaid), type_(type), hwaddr_(),
     hints_(), fwd_dns_update_(fwd_dns), rev_dns_update_(rev_dns),
     hostname_(hostname), fake_allocation_(fake_allocation),
-    old_leases_(), host_(), query_(), ia_rsp_(),
-    allow_new_leases_in_renewals_(false) {
+    old_leases_(), host_(), query_(), ia_rsp_() {
 
     static asiolink::IOAddress any("::");
 
@@ -1121,7 +1120,7 @@ AllocEngine::renewLeases6(ClientContext6& ctx) {
         // Depending on the configuration, we may enable or disable granting
         // new leases during renewals. This is controlled with the
         // allow_new_leases_in_renewals_ field.
-        if (leases.empty() && ctx.allow_new_leases_in_renewals_) {
+        if (leases.empty()) {
 
             LOG_DEBUG(alloc_engine_logger, ALLOC_ENGINE_DBG_TRACE,
                       ALLOC_ENGINE_V6_EXTEND_ALLOC_UNRESERVED)

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

@@ -357,18 +357,6 @@ public:
         /// @brief A pointer to the IA_NA/IA_PD option to be sent in response
         Option6IAPtr ia_rsp_;
 
-
-        /// @brief Specifies whether new leases in Renew/Rebind are allowed
-        ///
-        /// This field controls what to do when renewing or rebinding client
-        /// does not have any leases. RFC3315 and the stateful-issues draft do
-        /// not specify it and it is left up to the server configuration policy.
-        /// False (the default) means that the client will not get any new
-        /// unreserved leases if his existing leases are no longer suitable.
-        /// True means that the allocation engine will do its best to assign
-        /// something.
-        bool allow_new_leases_in_renewals_;
-
         /// @brief Default constructor.
         ClientContext6();
 

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

@@ -683,6 +683,7 @@ private:
     /// It's default value is false, which indicates that the Rapid
     /// Commit is disabled for the subnet.
     bool rapid_commit_;
+
 };
 
 /// @brief A pointer to a Subnet6 object

+ 0 - 2
src/lib/dhcpsrv/tests/alloc_engine_utils.cc

@@ -233,7 +233,6 @@ AllocEngine6Test::simpleAlloc6Test(const Pool6Ptr& pool, const IOAddress& hint,
 Lease6Collection
 AllocEngine6Test::renewTest(AllocEngine& engine, const Pool6Ptr& pool,
                             AllocEngine::HintContainer& hints,
-                            bool allow_new_leases_in_renewal,
                             bool in_pool) {
 
     Lease::Type type = pool->getType();
@@ -243,7 +242,6 @@ AllocEngine6Test::renewTest(AllocEngine& engine, const Pool6Ptr& pool,
                                     type, false, false, "", false);
     ctx.hints_ = hints;
     ctx.query_.reset(new Pkt6(DHCPV6_RENEW, 123));
-    ctx.allow_new_leases_in_renewals_ = allow_new_leases_in_renewal;
     ctx.query_.reset(new Pkt6(DHCPV6_RENEW, 1234));
 
     findReservation(engine, ctx);

+ 0 - 3
src/lib/dhcpsrv/tests/alloc_engine_utils.h

@@ -246,13 +246,10 @@ public:
     /// @param engine a reference to Allocation Engine
     /// @param pool pool from which the lease will be allocated from
     /// @param hints address to be used as a hint
-    /// @param allow_new_leases_in_renewal - specifies how to set the
-    ///        allow_new_leases_in_renewal flag in ClientContext6
     /// @param in_pool specifies whether the lease is expected to be in pool
     /// @return allocated lease(s) (may be empty)
     Lease6Collection renewTest(AllocEngine& engine, const Pool6Ptr& pool,
                                AllocEngine::HintContainer& hints,
-                               bool allow_new_leases_in_renewal,
                                bool in_pool = true);
 
     /// @brief Checks if the address allocation with a hint that is in range,

+ 1 - 0
src/lib/stats/tests/.gitignore

@@ -0,0 +1 @@
+/libstats_unittests