Browse Source

[master] Merge branch 'trac3295_2'

Conflicts:
	src/bin/dhcp6/dhcp6_srv.cc
Marcin Siodelski 11 years ago
parent
commit
aa1c94a541

+ 3 - 6
src/bin/dhcp4/dhcp4_srv.cc

@@ -852,17 +852,14 @@ Dhcpv4Srv::createNameChangeRequests(const Lease4Ptr& lease,
             //   removal request for non-existent hostname.
             //   removal request for non-existent hostname.
             // - A server has performed reverse, forward or both updates.
             // - A server has performed reverse, forward or both updates.
             // - FQDN data between the new and old lease do not match.
             // - FQDN data between the new and old lease do not match.
-            if  ((lease->hostname_ != old_lease->hostname_) ||
-                 (lease->fqdn_fwd_ != old_lease->fqdn_fwd_) ||
-                 (lease->fqdn_rev_ != old_lease->fqdn_rev_)) {
+            if (!lease->hasIdenticalFqdn(*old_lease)) {
                 queueNameChangeRequest(isc::dhcp_ddns::CHG_REMOVE,
                 queueNameChangeRequest(isc::dhcp_ddns::CHG_REMOVE,
                                        old_lease);
                                        old_lease);
 
 
             // If FQDN data from both leases match, there is no need to update.
             // If FQDN data from both leases match, there is no need to update.
-            } else if ((lease->hostname_ == old_lease->hostname_) &&
-                       (lease->fqdn_fwd_ == old_lease->fqdn_fwd_) &&
-                       (lease->fqdn_rev_ == old_lease->fqdn_rev_)) {
+            } else if (lease->hasIdenticalFqdn(*old_lease)) {
                 return;
                 return;
+
             }
             }
 
 
         }
         }

+ 6 - 7
src/bin/dhcp6/dhcp6_messages.mes

@@ -104,13 +104,7 @@ in its response to the client.
 This debug message is logged when server has found the DHCPv6 Client FQDN Option
 This debug message is logged when server has found the DHCPv6 Client FQDN Option
 sent by a client and started processing it.
 sent by a client and started processing it.
 
 
-% DHCP6_DDNS_REMOVE_EMPTY_HOSTNAME FQDN for the lease being deleted is empty: %1
-This error message is issued when a lease being deleted contains an indication
-that the DNS Update has been performed for it, but the FQDN is missing for this
-lease. This is an indication that someone may have messed up in the lease
-database.
-
-% DHCP6_DDNS_REMOVE_INVALID_HOSTNAME FQDN for the lease being deleted has invalid format: %1
+% DHCP6_DDNS_REMOVE_INVALID_HOSTNAME invalid FQDN: %1 for the lease: %2 when removing DNS bindings
 This error message is issued when a lease being deleted contains an indication
 This error message is issued when a lease being deleted contains an indication
 that the DNS Update has been performed for it, but the FQDN held in the lease
 that the DNS Update has been performed for it, but the FQDN held in the lease
 database has invalid format and can't be transformed to the canonical on-wire
 database has invalid format and can't be transformed to the canonical on-wire
@@ -237,6 +231,11 @@ but the lease does not contain any client identification. This is most
 likely due to a software error: please raise a bug report. As a temporary
 likely due to a software error: please raise a bug report. As a temporary
 workaround, manually remove the lease entry from the database.
 workaround, manually remove the lease entry from the database.
 
 
+% DHCP6_NAME_GEN_UPDATE_FAIL failed to update the lease using address %1, after generating FQDN for a client, reason: %2
+This message indicates the failure when trying to update the lease and/or
+options in the server's response with the hostname generated by the server
+from the acquired address.
+
 % DHCP6_NOT_RUNNING IPv6 DHCP server is not running
 % DHCP6_NOT_RUNNING IPv6 DHCP server is not running
 A warning message is issued when an attempt is made to shut down the
 A warning message is issued when an attempt is made to shut down the
 IPv6 DHCP server but it is not running.
 IPv6 DHCP server but it is not running.

+ 168 - 123
src/bin/dhcp6/dhcp6_srv.cc

@@ -104,9 +104,6 @@ namespace {
 // DHCPv6 Client FQDN Option sent by a client. They will be removed
 // DHCPv6 Client FQDN Option sent by a client. They will be removed
 // when DDNS parameters for DHCPv6 are implemented with the ticket #3034.
 // when DDNS parameters for DHCPv6 are implemented with the ticket #3034.
 
 
-// Should server always include the FQDN option in its response, regardless
-// if it has been requested in ORO (Disabled).
-const bool FQDN_ALWAYS_INCLUDE = false;
 // Enable AAAA RR update delegation to the client (Disabled).
 // Enable AAAA RR update delegation to the client (Disabled).
 const bool FQDN_ALLOW_CLIENT_UPDATE = false;
 const bool FQDN_ALLOW_CLIENT_UPDATE = false;
 // Globally enable updates (Enabled).
 // Globally enable updates (Enabled).
@@ -924,8 +921,7 @@ Dhcpv6Srv::selectSubnet(const Pkt6Ptr& question) {
 }
 }
 
 
 void
 void
-Dhcpv6Srv::assignLeases(const Pkt6Ptr& question, Pkt6Ptr& answer,
-                        const Option6ClientFqdnPtr& fqdn) {
+Dhcpv6Srv::assignLeases(const Pkt6Ptr& question, Pkt6Ptr& answer) {
 
 
     // We need to allocate addresses for all IA_NA options in the client's
     // We need to allocate addresses for all IA_NA options in the client's
     // question (i.e. SOLICIT or REQUEST) message.
     // question (i.e. SOLICIT or REQUEST) message.
@@ -978,10 +974,9 @@ Dhcpv6Srv::assignLeases(const Pkt6Ptr& question, Pkt6Ptr& answer,
          opt != question->options_.end(); ++opt) {
          opt != question->options_.end(); ++opt) {
         switch (opt->second->getType()) {
         switch (opt->second->getType()) {
         case D6O_IA_NA: {
         case D6O_IA_NA: {
-            OptionPtr answer_opt = assignIA_NA(subnet, duid, question,
+            OptionPtr answer_opt = assignIA_NA(subnet, duid, question, answer,
                                                boost::dynamic_pointer_cast<
                                                boost::dynamic_pointer_cast<
-                                               Option6IA>(opt->second),
-                                               fqdn);
+                                               Option6IA>(opt->second));
             if (answer_opt) {
             if (answer_opt) {
                 answer->addOption(answer_opt);
                 answer->addOption(answer_opt);
             }
             }
@@ -1001,14 +996,14 @@ Dhcpv6Srv::assignLeases(const Pkt6Ptr& question, Pkt6Ptr& answer,
     }
     }
 }
 }
 
 
-Option6ClientFqdnPtr
-Dhcpv6Srv::processClientFqdn(const Pkt6Ptr& question) {
+void
+Dhcpv6Srv::processClientFqdn(const Pkt6Ptr& question, const Pkt6Ptr& answer) {
     // Get Client FQDN Option from the client's message. If this option hasn't
     // Get Client FQDN Option from the client's message. If this option hasn't
     // been included, do nothing.
     // been included, do nothing.
     Option6ClientFqdnPtr fqdn = boost::dynamic_pointer_cast<
     Option6ClientFqdnPtr fqdn = boost::dynamic_pointer_cast<
         Option6ClientFqdn>(question->getOption(D6O_CLIENT_FQDN));
         Option6ClientFqdn>(question->getOption(D6O_CLIENT_FQDN));
     if (!fqdn) {
     if (!fqdn) {
-        return (fqdn);
+        return;
     }
     }
 
 
     LOG_DEBUG(dhcp6_logger, DBG_DHCP6_DETAIL,
     LOG_DEBUG(dhcp6_logger, DBG_DHCP6_DETAIL,
@@ -1059,13 +1054,14 @@ Dhcpv6Srv::processClientFqdn(const Pkt6Ptr& question) {
     // generate one.
     // generate one.
     if (fqdn->getDomainNameType() == Option6ClientFqdn::PARTIAL) {
     if (fqdn->getDomainNameType() == Option6ClientFqdn::PARTIAL) {
         std::ostringstream name;
         std::ostringstream name;
-        if (fqdn->getDomainName().empty()) {
-            name << FQDN_GENERATED_PARTIAL_NAME;
+        if (fqdn->getDomainName().empty() || FQDN_REPLACE_CLIENT_NAME) {
+            fqdn->setDomainName("", Option6ClientFqdn::PARTIAL);
+
         } else {
         } else {
             name << fqdn->getDomainName();
             name << fqdn->getDomainName();
+            name << "." << FQDN_PARTIAL_SUFFIX;
+            fqdn_resp->setDomainName(name.str(), Option6ClientFqdn::FULL);
         }
         }
-        name << "." << FQDN_PARTIAL_SUFFIX;
-        fqdn_resp->setDomainName(name.str(), Option6ClientFqdn::FULL);
 
 
     // Server may be configured to replace a name supplied by a client,
     // Server may be configured to replace a name supplied by a client,
     // even if client supplied fully qualified domain-name.
     // even if client supplied fully qualified domain-name.
@@ -1076,58 +1072,17 @@ Dhcpv6Srv::processClientFqdn(const Pkt6Ptr& question) {
 
 
     }
     }
 
 
-    // Return the FQDN option which can be included in the server's response.
-    // Note that it doesn't have to be included, if client didn't request
-    // it using ORO and server is not configured to always include it.
-    return (fqdn_resp);
+    // The FQDN has been processed successfully. Let's append it to the
+    // response to be sent to a client. Note that the Client FQDN option is
+    // always sent back to the client if Client FQDN was included in the
+    // client's message.
+    answer->addOption(fqdn_resp);
 }
 }
 
 
-
 void
 void
-Dhcpv6Srv::appendClientFqdn(const Pkt6Ptr& question,
-                            Pkt6Ptr& answer,
-                            const Option6ClientFqdnPtr& fqdn) {
-
-    // If FQDN is NULL, it means that client did not request DNS Update, plus
-    // server doesn't force updates.
-    if (!fqdn) {
-        return;
-    }
-
-    // Server sends back the FQDN option to the client if client has requested
-    // it using Option Request Option. However, server may be configured to
-    // send the FQDN option in its response, regardless whether client requested
-    // it or not.
-    bool include_fqdn = FQDN_ALWAYS_INCLUDE;
-    if (!include_fqdn) {
-        OptionUint16ArrayPtr oro = boost::dynamic_pointer_cast<
-            OptionUint16Array>(question->getOption(D6O_ORO));
-        if (oro) {
-            const std::vector<uint16_t>& values = oro->getValues();
-            for (int i = 0; i < values.size(); ++i) {
-                if (values[i] == D6O_CLIENT_FQDN) {
-                    include_fqdn = true;
-                }
-            }
-        }
-    }
-
-    if (include_fqdn) {
-        LOG_DEBUG(dhcp6_logger, DBG_DHCP6_DETAIL,
-                  DHCP6_DDNS_SEND_FQDN).arg(fqdn->toText());
-        answer->addOption(fqdn);
-    }
-
-}
-
-void
-Dhcpv6Srv::createNameChangeRequests(const Pkt6Ptr& answer,
-                                    const Option6ClientFqdnPtr& opt_fqdn) {
-
-    // It is likely that client haven't included the FQDN option in the message
-    // and server is not configured to always update DNS. In such cases,
-    // FQDN option will be NULL. This is valid state, so we simply return.
-    if (!opt_fqdn) {
+Dhcpv6Srv::createNameChangeRequests(const Pkt6Ptr& answer) {
+    // Don't create NameChangeRequests if DNS updates are disabled.
+    if (!FQDN_ENABLE_UPDATE) {
         return;
         return;
     }
     }
 
 
@@ -1140,6 +1095,14 @@ Dhcpv6Srv::createNameChangeRequests(const Pkt6Ptr& answer,
                   << " NULL when creating DNS NameChangeRequest");
                   << " NULL when creating DNS NameChangeRequest");
     }
     }
 
 
+    // It is likely that client haven't included the FQDN option. In such case,
+    // FQDN option will be NULL. This is valid state, so we simply return.
+    Option6ClientFqdnPtr opt_fqdn = boost::dynamic_pointer_cast<
+        Option6ClientFqdn>(answer->getOption(D6O_CLIENT_FQDN));
+    if (!opt_fqdn) {
+        return;
+    }
+
     // Get the Client Id. It is mandatory and a function creating a response
     // Get the Client Id. It is mandatory and a function creating a response
     // would have thrown an exception if it was missing. Thus throwning
     // would have thrown an exception if it was missing. Thus throwning
     // Unexpected if it is missing as it is a programming error.
     // Unexpected if it is missing as it is a programming error.
@@ -1167,8 +1130,8 @@ Dhcpv6Srv::createNameChangeRequests(const Pkt6Ptr& answer,
     OptionCollection answer_ias = answer->getOptions(D6O_IA_NA);
     OptionCollection answer_ias = answer->getOptions(D6O_IA_NA);
     for (OptionCollection::const_iterator answer_ia =
     for (OptionCollection::const_iterator answer_ia =
              answer_ias.begin(); answer_ia != answer_ias.end(); ++answer_ia) {
              answer_ias.begin(); answer_ia != answer_ias.end(); ++answer_ia) {
-        // @todo IA_NA may contain multiple addresses. We should process
-        // each address individually. Currently we get only one.
+        /// @todo IA_NA may contain multiple addresses. We should process
+        /// each address individually. Currently we get only one.
         Option6IAAddrPtr iaaddr = boost::static_pointer_cast<
         Option6IAAddrPtr iaaddr = boost::static_pointer_cast<
             Option6IAAddr>(answer_ia->second->getOption(D6O_IAADDR));
             Option6IAAddr>(answer_ia->second->getOption(D6O_IAADDR));
         // We need an address to create a name-to-address mapping.
         // We need an address to create a name-to-address mapping.
@@ -1194,31 +1157,33 @@ Dhcpv6Srv::createNameChangeRequests(const Pkt6Ptr& answer,
 
 
         LOG_DEBUG(dhcp6_logger, DBG_DHCP6_DETAIL,
         LOG_DEBUG(dhcp6_logger, DBG_DHCP6_DETAIL,
                   DHCP6_DDNS_CREATE_ADD_NAME_CHANGE_REQUEST).arg(ncr.toText());
                   DHCP6_DDNS_CREATE_ADD_NAME_CHANGE_REQUEST).arg(ncr.toText());
+
+        /// @todo Currently we create NCR with the first IPv6 address that
+        /// is carried in one of the IA_NAs. In the future, the NCR API should
+        /// be extended to map multiple IPv6 addresses to a single FQDN.
+        /// In such case, this return statement will be removed.
+        return;
     }
     }
 }
 }
 
 
 void
 void
 Dhcpv6Srv::createRemovalNameChangeRequest(const Lease6Ptr& lease) {
 Dhcpv6Srv::createRemovalNameChangeRequest(const Lease6Ptr& lease) {
-    // If we haven't performed a DNS Update when lease was acquired,
-    // there is nothing to do here.
-    if (!lease->fqdn_fwd_ && !lease->fqdn_rev_) {
+    // Don't create NameChangeRequests if DNS updates are disabled.
+    if (!FQDN_ENABLE_UPDATE) {
         return;
         return;
     }
     }
 
 
-    // When lease was added into a database the host name should have
-    // been added. The hostname can be empty if someone messed up in the
-    // lease data base and removed the hostname.
-    if (lease->hostname_.empty()) {
-        LOG_ERROR(dhcp6_logger, DHCP6_DDNS_REMOVE_EMPTY_HOSTNAME)
-            .arg(lease->addr_.toText());
+    // If we haven't performed a DNS Update when lease was acquired,
+    // there is nothing to do here.
+    if (!lease->fqdn_fwd_ && !lease->fqdn_rev_) {
         return;
         return;
     }
     }
 
 
     // If hostname is non-empty, try to convert it to wire format so as
     // If hostname is non-empty, try to convert it to wire format so as
     // DHCID can be computed from it. This may throw an exception if hostname
     // DHCID can be computed from it. This may throw an exception if hostname
-    // has invalid format. Again, this should be only possible in case of
-    // manual intervention in the database. Note that the last parameter
-    // passed to the writeFqdn function forces conversion of the FQDN
+    // has invalid format or is empty. Again, this should be only possible
+    // in case of manual intervention in the database. Note that the last
+    // parameter passed to the writeFqdn function forces conversion of the FQDN
     // to lower case. This is required by the RFC4701, section 3.5.
     // to lower case. This is required by the RFC4701, section 3.5.
     // The DHCID computation is further in this function.
     // The DHCID computation is further in this function.
     std::vector<uint8_t> hostname_wire;
     std::vector<uint8_t> hostname_wire;
@@ -1226,7 +1191,8 @@ Dhcpv6Srv::createRemovalNameChangeRequest(const Lease6Ptr& lease) {
         OptionDataTypeUtil::writeFqdn(lease->hostname_, hostname_wire, true);
         OptionDataTypeUtil::writeFqdn(lease->hostname_, hostname_wire, true);
     } catch (const Exception& ex) {
     } catch (const Exception& ex) {
         LOG_ERROR(dhcp6_logger, DHCP6_DDNS_REMOVE_INVALID_HOSTNAME)
         LOG_ERROR(dhcp6_logger, DHCP6_DDNS_REMOVE_INVALID_HOSTNAME)
-            .arg(lease->hostname_);
+            .arg(lease->hostname_.empty() ? "(empty)" : lease->hostname_)
+            .arg(lease->addr_.toText());
         return;
         return;
     }
     }
 
 
@@ -1267,14 +1233,14 @@ Dhcpv6Srv::sendNameChangeRequests() {
 
 
 OptionPtr
 OptionPtr
 Dhcpv6Srv::assignIA_NA(const Subnet6Ptr& subnet, const DuidPtr& duid,
 Dhcpv6Srv::assignIA_NA(const Subnet6Ptr& subnet, const DuidPtr& duid,
-                       const Pkt6Ptr& query, boost::shared_ptr<Option6IA> ia,
-                       const Option6ClientFqdnPtr& fqdn) {
+                       const Pkt6Ptr& query, const Pkt6Ptr& answer,
+                       boost::shared_ptr<Option6IA> ia) {
     // If there is no subnet selected for handling this IA_NA, the only thing to do left is
     // If there is no subnet selected for handling this IA_NA, the only thing to do left is
     // to say that we are sorry, but the user won't get an address. As a convenience, we
     // to say that we are sorry, but the user won't get an address. As a convenience, we
     // use a different status text to indicate that (compare to the same status code,
     // use a different status text to indicate that (compare to the same status code,
     // but different wording below)
     // but different wording below)
     if (!subnet) {
     if (!subnet) {
-        // Create empty IA_NA option with IAID matching the request.
+        // Creatasse empty IA_NA option with IAID matching the request.
         // Note that we don't use OptionDefinition class to create this option.
         // Note that we don't use OptionDefinition class to create this option.
         // This is because we prefer using a constructor of Option6IA that
         // This is because we prefer using a constructor of Option6IA that
         // initializes IAID. Otherwise we would have to use setIAID() after
         // initializes IAID. Otherwise we would have to use setIAID() after
@@ -1289,16 +1255,16 @@ Dhcpv6Srv::assignIA_NA(const Subnet6Ptr& subnet, const DuidPtr& duid,
     // Check if the client sent us a hint in his IA_NA. Clients may send an
     // Check if the client sent us a hint in his IA_NA. Clients may send an
     // address in their IA_NA options as a suggestion (e.g. the last address
     // address in their IA_NA options as a suggestion (e.g. the last address
     // they used before).
     // they used before).
-    boost::shared_ptr<Option6IAAddr> hintOpt = boost::dynamic_pointer_cast<Option6IAAddr>
-                                        (ia->getOption(D6O_IAADDR));
+    boost::shared_ptr<Option6IAAddr> hint_opt =
+        boost::dynamic_pointer_cast<Option6IAAddr>(ia->getOption(D6O_IAADDR));
     IOAddress hint("::");
     IOAddress hint("::");
-    if (hintOpt) {
-        hint = hintOpt->getAddress();
+    if (hint_opt) {
+        hint = hint_opt->getAddress();
     }
     }
 
 
     LOG_DEBUG(dhcp6_logger, DBG_DHCP6_DETAIL, DHCP6_PROCESS_IA_NA_REQUEST)
     LOG_DEBUG(dhcp6_logger, DBG_DHCP6_DETAIL, DHCP6_PROCESS_IA_NA_REQUEST)
-        .arg(duid?duid->toText():"(no-duid)").arg(ia->getIAID())
-        .arg(hintOpt?hint.toText():"(no hint)");
+        .arg(duid ? duid->toText() : "(no-duid)").arg(ia->getIAID())
+        .arg(hint_opt ? hint.toText() : "(no hint)");
 
 
     // "Fake" allocation is processing of SOLICIT message. We pretend to do an
     // "Fake" allocation is processing of SOLICIT message. We pretend to do an
     // allocation, but we do not put the lease in the database. That is ok,
     // allocation, but we do not put the lease in the database. That is ok,
@@ -1322,6 +1288,8 @@ Dhcpv6Srv::assignIA_NA(const Subnet6Ptr& subnet, const DuidPtr& duid,
     // the update.
     // the update.
     bool do_fwd = false;
     bool do_fwd = false;
     bool do_rev = false;
     bool do_rev = false;
+    Option6ClientFqdnPtr fqdn = boost::dynamic_pointer_cast<
+        Option6ClientFqdn>(answer->getOption(D6O_CLIENT_FQDN));
     if (fqdn) {
     if (fqdn) {
         // Flag S must not coexist with flag N being set to 1, so if S=1
         // Flag S must not coexist with flag N being set to 1, so if S=1
         // server takes responsibility for both reverse and forward updates.
         // server takes responsibility for both reverse and forward updates.
@@ -1343,13 +1311,15 @@ Dhcpv6Srv::assignIA_NA(const Subnet6Ptr& subnet, const DuidPtr& duid,
     // will try to honour the hint, but it is just a hint - some other address
     // will try to honour the hint, but it is just a hint - some other address
     // may be used instead. If fake_allocation is set to false, the lease will
     // may be used instead. If fake_allocation is set to false, the lease will
     // be inserted into the LeaseMgr as well.
     // be inserted into the LeaseMgr as well.
+    Lease6Collection old_leases;
     Lease6Collection leases = alloc_engine_->allocateLeases6(subnet, duid,
     Lease6Collection leases = alloc_engine_->allocateLeases6(subnet, duid,
                                                              ia->getIAID(),
                                                              ia->getIAID(),
                                                              hint, Lease::TYPE_NA,
                                                              hint, Lease::TYPE_NA,
                                                              do_fwd, do_rev,
                                                              do_fwd, do_rev,
                                                              hostname,
                                                              hostname,
                                                              fake_allocation,
                                                              fake_allocation,
-                                                             callout_handle);
+                                                             callout_handle,
+                                                             old_leases);
     /// @todo: Handle more than one lease
     /// @todo: Handle more than one lease
     Lease6Ptr lease;
     Lease6Ptr lease;
     if (!leases.empty()) {
     if (!leases.empty()) {
@@ -1384,26 +1354,25 @@ Dhcpv6Srv::assignIA_NA(const Subnet6Ptr& subnet, const DuidPtr& duid,
         // but this is considered waste of bandwidth as absence of status
         // but this is considered waste of bandwidth as absence of status
         // code is considered a success.
         // code is considered a success.
 
 
+        Lease6Ptr old_lease;
+        if (!old_leases.empty()) {
+            old_lease = *old_leases.begin();
+        }
         // Allocation engine may have returned an existing lease. If so, we
         // Allocation engine may have returned an existing lease. If so, we
         // have to check that the FQDN settings we provided are the same
         // have to check that the FQDN settings we provided are the same
         // that were set. If they aren't, we will have to remove existing
         // that were set. If they aren't, we will have to remove existing
         // DNS records and update the lease with the new settings.
         // DNS records and update the lease with the new settings.
-        if ((lease->hostname_ != hostname) || (lease->fqdn_fwd_ != do_fwd) ||
-            (lease->fqdn_rev_ != do_rev)) {
+        if (!fake_allocation && old_lease &&
+            !lease->hasIdenticalFqdn(*old_lease)) {
             LOG_DEBUG(dhcp6_logger, DBG_DHCP6_DETAIL,
             LOG_DEBUG(dhcp6_logger, DBG_DHCP6_DETAIL,
                       DHCP6_DDNS_LEASE_ASSIGN_FQDN_CHANGE)
                       DHCP6_DDNS_LEASE_ASSIGN_FQDN_CHANGE)
-                .arg(lease->toText())
+                .arg(old_lease->toText())
                 .arg(hostname)
                 .arg(hostname)
                 .arg(do_rev ? "true" : "false")
                 .arg(do_rev ? "true" : "false")
                 .arg(do_fwd ? "true" : "false");
                 .arg(do_fwd ? "true" : "false");
 
 
             // Schedule removal of the existing lease.
             // Schedule removal of the existing lease.
-            createRemovalNameChangeRequest(lease);
-            // Set the new lease properties and update.
-            lease->hostname_ = hostname;
-            lease->fqdn_fwd_ = do_fwd;
-            lease->fqdn_rev_ = do_rev;
-            LeaseMgrFactory::instance().updateLease6(lease);
+            createRemovalNameChangeRequest(old_lease);
         }
         }
 
 
     } else {
     } else {
@@ -1471,13 +1440,15 @@ Dhcpv6Srv::assignIA_PD(const Subnet6Ptr& subnet, const DuidPtr& duid,
     // will try to honour the hint, but it is just a hint - some other address
     // will try to honour the hint, but it is just a hint - some other address
     // may be used instead. If fake_allocation is set to false, the lease will
     // may be used instead. If fake_allocation is set to false, the lease will
     // be inserted into the LeaseMgr as well.
     // be inserted into the LeaseMgr as well.
+    Lease6Collection old_leases;
     Lease6Collection leases = alloc_engine_->allocateLeases6(subnet, duid,
     Lease6Collection leases = alloc_engine_->allocateLeases6(subnet, duid,
-                                                            ia->getIAID(),
-                                                            hint, Lease::TYPE_PD,
-                                                            false, false,
-                                                            string(),
-                                                            fake_allocation,
-                                                            callout_handle);
+                                                             ia->getIAID(),
+                                                             hint, Lease::TYPE_PD,
+                                                             false, false,
+                                                             string(),
+                                                             fake_allocation,
+                                                             callout_handle,
+                                                             old_leases);
 
 
     if (!leases.empty()) {
     if (!leases.empty()) {
 
 
@@ -1525,8 +1496,8 @@ Dhcpv6Srv::assignIA_PD(const Subnet6Ptr& subnet, const DuidPtr& duid,
 
 
 OptionPtr
 OptionPtr
 Dhcpv6Srv::renewIA_NA(const Subnet6Ptr& subnet, const DuidPtr& duid,
 Dhcpv6Srv::renewIA_NA(const Subnet6Ptr& subnet, const DuidPtr& duid,
-                      const Pkt6Ptr& query, boost::shared_ptr<Option6IA> ia,
-                      const Option6ClientFqdnPtr& fqdn) {
+                      const Pkt6Ptr& query, const Pkt6Ptr& answer,
+                      boost::shared_ptr<Option6IA> ia) {
     if (!subnet) {
     if (!subnet) {
         // There's no subnet select for this client. There's nothing to renew.
         // There's no subnet select for this client. There's nothing to renew.
         boost::shared_ptr<Option6IA> ia_rsp(new Option6IA(D6O_IA_NA, ia->getIAID()));
         boost::shared_ptr<Option6IA> ia_rsp(new Option6IA(D6O_IA_NA, ia->getIAID()));
@@ -1575,6 +1546,8 @@ Dhcpv6Srv::renewIA_NA(const Subnet6Ptr& subnet, const DuidPtr& duid,
     // the update.
     // the update.
     bool do_fwd = false;
     bool do_fwd = false;
     bool do_rev = false;
     bool do_rev = false;
+    Option6ClientFqdnPtr fqdn = boost::dynamic_pointer_cast<
+        Option6ClientFqdn>(answer->getOption(D6O_CLIENT_FQDN));
     if (fqdn) {
     if (fqdn) {
         if (fqdn->getFlag(Option6ClientFqdn::FLAG_S)) {
         if (fqdn->getFlag(Option6ClientFqdn::FLAG_S)) {
             do_fwd = true;
             do_fwd = true;
@@ -1767,8 +1740,7 @@ Dhcpv6Srv::renewIA_PD(const Subnet6Ptr& subnet, const DuidPtr& duid,
 }
 }
 
 
 void
 void
-Dhcpv6Srv::renewLeases(const Pkt6Ptr& renew, Pkt6Ptr& reply,
-                       const Option6ClientFqdnPtr& fqdn) {
+Dhcpv6Srv::renewLeases(const Pkt6Ptr& renew, Pkt6Ptr& reply) {
 
 
     // We need to renew addresses for all IA_NA options in the client's
     // We need to renew addresses for all IA_NA options in the client's
     // RENEW message.
     // RENEW message.
@@ -1812,10 +1784,9 @@ Dhcpv6Srv::renewLeases(const Pkt6Ptr& renew, Pkt6Ptr& reply,
         switch (opt->second->getType()) {
         switch (opt->second->getType()) {
 
 
         case D6O_IA_NA: {
         case D6O_IA_NA: {
-            OptionPtr answer_opt = renewIA_NA(subnet, duid, renew,
+            OptionPtr answer_opt = renewIA_NA(subnet, duid, renew, reply,
                                               boost::dynamic_pointer_cast<
                                               boost::dynamic_pointer_cast<
-                                              Option6IA>(opt->second),
-                                              fqdn);
+                                              Option6IA>(opt->second));
             if (answer_opt) {
             if (answer_opt) {
                 reply->addOption(answer_opt);
                 reply->addOption(answer_opt);
             }
             }
@@ -2212,13 +2183,14 @@ Dhcpv6Srv::processSolicit(const Pkt6Ptr& solicit) {
     appendRequestedOptions(solicit, advertise);
     appendRequestedOptions(solicit, advertise);
     appendRequestedVendorOptions(solicit, advertise);
     appendRequestedVendorOptions(solicit, advertise);
 
 
-    Option6ClientFqdnPtr fqdn = processClientFqdn(solicit);
-    assignLeases(solicit, advertise, fqdn);
-    appendClientFqdn(solicit, advertise, fqdn);
+    processClientFqdn(solicit, advertise);
+    assignLeases(solicit, advertise);
     // Note, that we don't create NameChangeRequests here because we don't
     // Note, that we don't create NameChangeRequests here because we don't
     // perform DNS Updates for Solicit. Client must send Request to update
     // perform DNS Updates for Solicit. Client must send Request to update
     // DNS.
     // DNS.
 
 
+    generateFqdn(advertise);
+
     return (advertise);
     return (advertise);
 }
 }
 
 
@@ -2234,10 +2206,10 @@ Dhcpv6Srv::processRequest(const Pkt6Ptr& request) {
     appendRequestedOptions(request, reply);
     appendRequestedOptions(request, reply);
     appendRequestedVendorOptions(request, reply);
     appendRequestedVendorOptions(request, reply);
 
 
-    Option6ClientFqdnPtr fqdn = processClientFqdn(request);
-    assignLeases(request, reply, fqdn);
-    appendClientFqdn(request, reply, fqdn);
-    createNameChangeRequests(reply, fqdn);
+    processClientFqdn(request, reply);
+    assignLeases(request, reply);
+    generateFqdn(reply);
+    createNameChangeRequests(reply);
 
 
     return (reply);
     return (reply);
 }
 }
@@ -2253,12 +2225,12 @@ Dhcpv6Srv::processRenew(const Pkt6Ptr& renew) {
     appendDefaultOptions(renew, reply);
     appendDefaultOptions(renew, reply);
     appendRequestedOptions(renew, reply);
     appendRequestedOptions(renew, reply);
 
 
-    Option6ClientFqdnPtr fqdn = processClientFqdn(renew);
-    renewLeases(renew, reply, fqdn);
-    appendClientFqdn(renew, reply, fqdn);
-    createNameChangeRequests(reply, fqdn);
+    processClientFqdn(renew, reply);
+    renewLeases(renew, reply);
+    generateFqdn(reply);
+    createNameChangeRequests(reply);
 
 
-    return reply;
+    return (reply);
 }
 }
 
 
 Pkt6Ptr
 Pkt6Ptr
@@ -2497,5 +2469,78 @@ void Dhcpv6Srv::classifyPacket(const Pkt6Ptr& pkt) {
     }
     }
 }
 }
 
 
+void
+Dhcpv6Srv::generateFqdn(const Pkt6Ptr& answer) {
+    if (!answer) {
+        isc_throw(isc::Unexpected, "an instance of the object encapsulating"
+                  " a message must not be NULL when generating FQDN");
+    }
+
+    // It is likely that client haven't included the FQDN option. In such case,
+    // FQDN option will be NULL. Also, there is nothing to do if the option
+    // is present and conveys the non-empty FQDN.
+    Option6ClientFqdnPtr fqdn = boost::dynamic_pointer_cast<
+        Option6ClientFqdn>(answer->getOption(D6O_CLIENT_FQDN));
+    if (!fqdn || !fqdn->getDomainName().empty()) {
+        return;
+    }
+
+    // Get the first IA_NA acquired for the client.
+    OptionPtr ia = answer->getOption(D6O_IA_NA);
+    if (!ia) {
+        return;
+    }
+
+    // If it has any IAAddr, use the first one to generate unique FQDN.
+    Option6IAAddrPtr iaaddr = boost::dynamic_pointer_cast<
+        Option6IAAddr>(ia->getOption(D6O_IAADDR));
+    if (!iaaddr) {
+        return;
+    }
+    // Get the IPv6 address acquired by the client.
+    IOAddress addr = iaaddr->getAddress();
+    std::string hostname = addr.toText();
+    // Colons may not be ok for FQDNs so let's replace them with hyphens.
+    std::replace(hostname.begin(), hostname.end(), ':', '-');
+    std::ostringstream stream;
+    // The final FQDN consists of the partial domain name and the suffix.
+    // For example, if the acquired address is 2001:db8:1::2, the generated
+    // FQDN may be:
+    //     host-2001-db8:1--2.example.com.
+    // where prefix 'host' should be configurable. The domain name suffix
+    // should also be configurable.
+    stream << "host-" << hostname << "." << FQDN_PARTIAL_SUFFIX << ".";
+    try {
+        // The lease has been acquired but the FQDN for this lease hasn't
+        // been updated in the lease database. We now have new FQDN
+        // generated, so the lease database has to be updated here.
+        // However, never update lease database for Advertise, just send
+        // our notion of client's FQDN in the Client FQDN option.
+        if (answer->getType() != DHCPV6_ADVERTISE) {
+            Lease6Ptr lease =
+                LeaseMgrFactory::instance().getLease6(Lease::TYPE_NA, addr);
+            if (lease) {
+                lease->hostname_ = stream.str();
+                LeaseMgrFactory::instance().updateLease6(lease);
+
+            } else {
+                isc_throw(isc::Unexpected, "there is no lease in the database "
+                          " for address " << addr << ", so as it is impossible"
+                          " to update FQDN data. This is a programmatic error"
+                          " as the given address is now being handed to the"
+                          " client");
+            }
+        }
+
+        // Set the generated FQDN in the Client FQDN option.
+        fqdn->setDomainName(stream.str(), Option6ClientFqdn::FULL);
+
+    } catch (const Exception& ex) {
+        LOG_ERROR(dhcp6_logger, DHCP6_NAME_GEN_UPDATE_FAIL)
+            .arg(hostname)
+            .arg(ex.what());
+    }
+}
+
 };
 };
 };
 };

+ 66 - 41
src/bin/dhcp6/dhcp6_srv.h

@@ -224,15 +224,17 @@ protected:
     /// @param subnet subnet the client is connected to
     /// @param subnet subnet the client is connected to
     /// @param duid client's duid
     /// @param duid client's duid
     /// @param query client's message (typically SOLICIT or REQUEST)
     /// @param query client's message (typically SOLICIT or REQUEST)
+    /// @param answer server's response to the client's message. This
+    /// message should contain Client FQDN option being sent by the server
+    /// to the client (if the client sent this option to the server).
     /// @param ia pointer to client's IA_NA option (client's request)
     /// @param ia pointer to client's IA_NA option (client's request)
-    /// @param fqdn A DHCPv6 Client FQDN %Option generated in a response to the
-    /// FQDN option sent by a client.
+    ///
     /// @return IA_NA option (server's response)
     /// @return IA_NA option (server's response)
     OptionPtr assignIA_NA(const isc::dhcp::Subnet6Ptr& subnet,
     OptionPtr assignIA_NA(const isc::dhcp::Subnet6Ptr& subnet,
                           const isc::dhcp::DuidPtr& duid,
                           const isc::dhcp::DuidPtr& duid,
                           const isc::dhcp::Pkt6Ptr& query,
                           const isc::dhcp::Pkt6Ptr& query,
-                          Option6IAPtr ia,
-                          const Option6ClientFqdnPtr& fqdn);
+                          const isc::dhcp::Pkt6Ptr& answer,
+                          Option6IAPtr ia);
 
 
     /// @brief Processes IA_PD option (and assigns prefixes if necessary).
     /// @brief Processes IA_PD option (and assigns prefixes if necessary).
     ///
     ///
@@ -260,12 +262,14 @@ protected:
     /// @param subnet subnet the sender belongs to
     /// @param subnet subnet the sender belongs to
     /// @param duid client's duid
     /// @param duid client's duid
     /// @param query client's message
     /// @param query client's message
+    /// @param answer server's response to the client's message. This
+    /// message should contain Client FQDN option being sent by the server
+    /// to the client (if the client sent this option to the server).
     /// @param ia IA_NA option that is being renewed
     /// @param ia IA_NA option that is being renewed
-    /// @param fqdn DHCPv6 Client FQDN Option included in the server's response
     /// @return IA_NA option (server's response)
     /// @return IA_NA option (server's response)
     OptionPtr renewIA_NA(const Subnet6Ptr& subnet, const DuidPtr& duid,
     OptionPtr renewIA_NA(const Subnet6Ptr& subnet, const DuidPtr& duid,
-                         const Pkt6Ptr& query, boost::shared_ptr<Option6IA> ia,
-                         const Option6ClientFqdnPtr& fqdn);
+                         const Pkt6Ptr& query, const Pkt6Ptr& answer,
+                         boost::shared_ptr<Option6IA> ia);
 
 
     /// @brief Renews specific IA_PD option
     /// @brief Renews specific IA_PD option
     ///
     ///
@@ -362,11 +366,10 @@ protected:
     /// @todo: Extend this method once TA and PD becomes supported
     /// @todo: Extend this method once TA and PD becomes supported
     ///
     ///
     /// @param question client's message (with requested IA_NA)
     /// @param question client's message (with requested IA_NA)
-    /// @param answer server's message (IA_NA options will be added here)
-    /// @param fqdn an FQDN option generated in a response to the client's
-    /// FQDN option.
-    void assignLeases(const Pkt6Ptr& question, Pkt6Ptr& answer,
-                      const Option6ClientFqdnPtr& fqdn);
+    /// @param answer server's message (IA_NA options will be added here).
+    /// This message should contain Client FQDN option being sent by the server
+    /// to the client (if the client sent this option to the server).
+    void assignLeases(const Pkt6Ptr& question, Pkt6Ptr& answer);
 
 
     /// @brief Processes Client FQDN Option.
     /// @brief Processes Client FQDN Option.
     ///
     ///
@@ -375,7 +378,7 @@ protected:
     /// Received option comprises flags field which controls what DNS updates
     /// Received option comprises flags field which controls what DNS updates
     /// server should do. Server may override client's preference based on
     /// server should do. Server may override client's preference based on
     /// the current configuration. Server indicates that it has overridden
     /// the current configuration. Server indicates that it has overridden
-    /// the preference by storing DHCPv6 Client Fqdn %Option with the
+    /// the preference by storing DHCPv6 Client FQDN option with the
     /// appropriate flags in the response to a client. This option is also
     /// appropriate flags in the response to a client. This option is also
     /// used to communicate the client's domain-name which should be sent
     /// used to communicate the client's domain-name which should be sent
     /// to the DNS in the update. Again, server may act upon the received
     /// to the DNS in the update. Again, server may act upon the received
@@ -386,25 +389,10 @@ protected:
     /// held in this function.
     /// held in this function.
     ///
     ///
     /// @param question Client's message.
     /// @param question Client's message.
-    ///
-    /// @return FQDN option produced in the response to the client's message.
-    Option6ClientFqdnPtr processClientFqdn(const Pkt6Ptr& question);
-
-    /// @brief Adds DHCPv6 Client FQDN %Option to the server response.
-    ///
-    /// This function will add the specified FQDN option into the server's
-    /// response when FQDN is not NULL and server is either configured to
-    /// always include the FQDN in the response or client requested it using
-    /// %Option Request %Option.
-    /// This function is exception safe.
-    ///
-    /// @param question A message received from the client.
-    /// @param [out] answer A server's response where FQDN option will be added.
-    /// @param fqdn A DHCPv6 Client FQDN %Option to be added to the server's
-    /// response to a client.
-    void appendClientFqdn(const Pkt6Ptr& question,
-                          Pkt6Ptr& answer,
-                          const Option6ClientFqdnPtr& fqdn);
+    /// @param answer Server's response to a client. If server generated
+    /// Client FQDN option for the client, this option is stored in this
+    /// object.
+    void processClientFqdn(const Pkt6Ptr& question, const Pkt6Ptr& answer);
 
 
     /// @brief Creates a number of @c isc::dhcp_ddns::NameChangeRequest objects
     /// @brief Creates a number of @c isc::dhcp_ddns::NameChangeRequest objects
     /// based on the DHCPv6 Client FQDN %Option.
     /// based on the DHCPv6 Client FQDN %Option.
@@ -420,11 +408,9 @@ protected:
     ///
     ///
     /// @todo Add support for multiple IAADDR options in the IA_NA.
     /// @todo Add support for multiple IAADDR options in the IA_NA.
     ///
     ///
-    /// @param answer A message beging sent to the Client.
-    /// @param fqdn_answer A DHCPv6 Client FQDN %Option which is included in the
-    /// response message sent to a client.
-    void createNameChangeRequests(const Pkt6Ptr& answer,
-                                  const Option6ClientFqdnPtr& fqdn_answer);
+    /// @param answer A message beging sent to the Client. If it holds the
+    /// Client FQDN option, this option is used to create NameChangeRequests.
+    void createNameChangeRequests(const Pkt6Ptr& answer);
 
 
     /// @brief Creates a @c isc::dhcp_ddns::NameChangeRequest which requests
     /// @brief Creates a @c isc::dhcp_ddns::NameChangeRequest which requests
     /// removal of DNS entries for a particular lease.
     /// removal of DNS entries for a particular lease.
@@ -460,10 +446,7 @@ protected:
     /// as IA_NA/IAADDR to reply packet.
     /// as IA_NA/IAADDR to reply packet.
     /// @param renew client's message asking for renew
     /// @param renew client's message asking for renew
     /// @param reply server's response
     /// @param reply server's response
-    /// @param fqdn A DHCPv6 Client FQDN %Option generated in the response to the
-    /// client's FQDN option.
-    void renewLeases(const Pkt6Ptr& renew, Pkt6Ptr& reply,
-                     const Option6ClientFqdnPtr& fqdn);
+    void renewLeases(const Pkt6Ptr& renew, Pkt6Ptr& reply);
 
 
     /// @brief Attempts to release received addresses
     /// @brief Attempts to release received addresses
     ///
     ///
@@ -564,6 +547,48 @@ private:
     /// @param errmsg An error message containing a cause of the failure.
     /// @param errmsg An error message containing a cause of the failure.
     static void ifaceMgrSocket6ErrorHandler(const std::string& errmsg);
     static void ifaceMgrSocket6ErrorHandler(const std::string& errmsg);
 
 
+    /// @brief Generate FQDN to be sent to a client if none exists.
+    ///
+    /// This function is meant to be called by the functions which process
+    /// client's messages. The function should be called after a function
+    /// which creates FQDN option for the client. This option must exist
+    /// in the answer message specified as an argument. It must also be
+    /// called after functions which assign leases for a client. The
+    /// IA options being a result of lease acquisition must be appended
+    /// to the message specified as a parameter.
+    ///
+    /// If the Client FQDN option being present in the message carries empty
+    /// hostname, this function will attempt to generate hostname from the
+    /// IPv6 address being acquired by the client. The IPv6 address is retrieved
+    /// from the IA_NA option carried in the specified message. If multiple
+    /// addresses are present in the particular IA_NA option or multiple IA_NA
+    /// options exist, the first address found is selected.
+    ///
+    /// The IPv6 address is converted to the hostname using the following
+    /// pattern:
+    /// @code
+    ///     prefix-converted-ip-address.domain-name-suffix.
+    /// @endcode
+    /// where:
+    /// - prefix is a configurable prefix string appended to all auto-generated
+    /// hostnames.
+    /// - converted-ip-address is created by replacing all colons from the IPv6
+    /// address with hyphens.
+    /// - domain-name-suffix is a suffix for a domain name that, together with
+    /// the other parts, constitute the fully qualified domain name.
+    ///
+    /// When hostname is successfully generated, it is either used to update
+    /// FQDN-related fields in a lease database or to update the Client FQDN
+    /// option being sent back to the client. The lease database update is
+    /// NOT performed if Advertise message is being processed.
+    ///
+    /// @param answer Message being sent to a client, which may hold IA_NA
+    /// and Client FQDN options to be used to generate name for a client.
+    ///
+    /// @throw isc::Unexpected if specified message is NULL. This is treated
+    /// as a programmatic error.
+    void generateFqdn(const Pkt6Ptr& answer);
+
     /// @brief Allocation Engine.
     /// @brief Allocation Engine.
     /// Pointer to the allocation engine that we are currently using
     /// Pointer to the allocation engine that we are currently using
     /// It must be a pointer, because we will support changing engines
     /// It must be a pointer, because we will support changing engines

+ 195 - 67
src/bin/dhcp6/tests/fqdn_unittest.cc

@@ -1,4 +1,4 @@
-// Copyright (C) 2013  Internet Systems Consortium, Inc. ("ISC")
+// Copyright (C) 2013-2014  Internet Systems Consortium, Inc. ("ISC")
 //
 //
 // Permission to use, copy, modify, and/or distribute this software for any
 // Permission to use, copy, modify, and/or distribute this software for any
 // purpose with or without fee is hereby granted, provided that the above
 // purpose with or without fee is hereby granted, provided that the above
@@ -227,9 +227,6 @@ public:
     /// This function verifies that the FQDN option returned is correct.
     /// This function verifies that the FQDN option returned is correct.
     ///
     ///
     /// @param msg_type A type of the client's message.
     /// @param msg_type A type of the client's message.
-    /// @param use_oro A boolean value which indicates whether the DHCPv6 ORO
-    /// option (requesting return of the FQDN option by the server) should be
-    /// included in the client's message (if true), or not included (if false).
     /// @param in_flags A value of flags field to be set for the FQDN carried
     /// @param in_flags A value of flags field to be set for the FQDN carried
     /// in the client's message.
     /// in the client's message.
     /// @param in_domain_name A domain name to be carried in the client's FQDN
     /// @param in_domain_name A domain name to be carried in the client's FQDN
@@ -240,7 +237,6 @@ public:
     /// @param exp_domain_name A domain name expected in the FQDN sent by a
     /// @param exp_domain_name A domain name expected in the FQDN sent by a
     /// server.
     /// server.
     void testFqdn(const uint16_t msg_type,
     void testFqdn(const uint16_t msg_type,
-                  const bool use_oro,
                   const uint8_t in_flags,
                   const uint8_t in_flags,
                   const std::string& in_domain_name,
                   const std::string& in_domain_name,
                   const Option6ClientFqdn::DomainNameType in_domain_type,
                   const Option6ClientFqdn::DomainNameType in_domain_type,
@@ -251,11 +247,14 @@ public:
                                            in_flags,
                                            in_flags,
                                            in_domain_name,
                                            in_domain_name,
                                            in_domain_type,
                                            in_domain_type,
-                                           use_oro);
+                                           true);
         ASSERT_TRUE(getClientFqdnOption(question));
         ASSERT_TRUE(getClientFqdnOption(question));
 
 
-        Option6ClientFqdnPtr answ_fqdn;
-        ASSERT_NO_THROW(answ_fqdn = srv.processClientFqdn(question));
+        Pkt6Ptr answer(new Pkt6(msg_type == DHCPV6_SOLICIT ? DHCPV6_ADVERTISE :
+                                DHCPV6_REPLY, question->getTransid()));
+        ASSERT_NO_THROW(srv.processClientFqdn(question, answer));
+        Option6ClientFqdnPtr answ_fqdn = boost::dynamic_pointer_cast<
+            Option6ClientFqdn>(answer->getOption(D6O_CLIENT_FQDN));
         ASSERT_TRUE(answ_fqdn);
         ASSERT_TRUE(answ_fqdn);
 
 
         const bool flag_n = (exp_flags & Option6ClientFqdn::FLAG_N) != 0;
         const bool flag_n = (exp_flags & Option6ClientFqdn::FLAG_N) != 0;
@@ -267,7 +266,19 @@ public:
         EXPECT_EQ(flag_o, answ_fqdn->getFlag(Option6ClientFqdn::FLAG_O));
         EXPECT_EQ(flag_o, answ_fqdn->getFlag(Option6ClientFqdn::FLAG_O));
 
 
         EXPECT_EQ(exp_domain_name, answ_fqdn->getDomainName());
         EXPECT_EQ(exp_domain_name, answ_fqdn->getDomainName());
-        EXPECT_EQ(Option6ClientFqdn::FULL, answ_fqdn->getDomainNameType());
+        // If server is configured to generate full FQDN for a client, and/or
+        // client sent empty FQDN the expected result of the processing by
+        // processClientFqdn is an empty, partial FQDN. This is an indication
+        // for the code which performs lease allocation that the FQDN has to
+        // be generated from the lease address.
+        if (exp_domain_name.empty()) {
+            EXPECT_EQ(Option6ClientFqdn::PARTIAL,
+                      answ_fqdn->getDomainNameType());
+
+        } else {
+            EXPECT_EQ(Option6ClientFqdn::FULL, answ_fqdn->getDomainNameType());
+
+        }
     }
     }
 
 
     /// @brief Tests that the client's message holding an FQDN is processed
     /// @brief Tests that the client's message holding an FQDN is processed
@@ -283,15 +294,19 @@ public:
     /// that the server doesn't respond with the FQDN.
     /// that the server doesn't respond with the FQDN.
     void testProcessMessage(const uint8_t msg_type,
     void testProcessMessage(const uint8_t msg_type,
                             const std::string& hostname,
                             const std::string& hostname,
+                            const std::string& exp_hostname,
                             NakedDhcpv6Srv& srv,
                             NakedDhcpv6Srv& srv,
                             const bool include_oro = true) {
                             const bool include_oro = true) {
         // Create a message of a specified type, add server id and
         // Create a message of a specified type, add server id and
         // FQDN option.
         // FQDN option.
         OptionPtr srvid = srv.getServerID();
         OptionPtr srvid = srv.getServerID();
+        // Set the appropriate FQDN type. It must be partial if hostname is
+        // empty.
+        Option6ClientFqdn::DomainNameType fqdn_type = (hostname.empty() ?
+            Option6ClientFqdn::PARTIAL : Option6ClientFqdn::FULL);
+
         Pkt6Ptr req = generateMessage(msg_type, Option6ClientFqdn::FLAG_S,
         Pkt6Ptr req = generateMessage(msg_type, Option6ClientFqdn::FLAG_S,
-                                      hostname,
-                                      Option6ClientFqdn::FULL,
-                                      include_oro, srvid);
+                                      hostname, fqdn_type, include_oro, srvid);
 
 
         // For different client's message types we have to invoke different
         // For different client's message types we have to invoke different
         // functions to generate response.
         // functions to generate response.
@@ -337,13 +352,15 @@ public:
             Lease6Ptr lease =
             Lease6Ptr lease =
                 checkLease(duid_, reply->getOption(D6O_IA_NA), addr);
                 checkLease(duid_, reply->getOption(D6O_IA_NA), addr);
             ASSERT_TRUE(lease);
             ASSERT_TRUE(lease);
+            EXPECT_EQ(exp_hostname, lease->hostname_);
         }
         }
 
 
-        if (include_oro) {
-            ASSERT_TRUE(reply->getOption(D6O_CLIENT_FQDN));
-        } else {
-            ASSERT_FALSE(reply->getOption(D6O_CLIENT_FQDN));
-        }
+        // The Client FQDN option should be always present in the server's
+        // response, regardless if requested using ORO or not.
+        Option6ClientFqdnPtr fqdn;
+        ASSERT_TRUE(fqdn = boost::dynamic_pointer_cast<
+                        Option6ClientFqdn>(reply->getOption(D6O_CLIENT_FQDN)));
+        EXPECT_EQ(exp_hostname, fqdn->getDomainName());
     }
     }
 
 
     /// @brief Verify that NameChangeRequest holds valid values.
     /// @brief Verify that NameChangeRequest holds valid values.
@@ -394,7 +411,7 @@ public:
 
 
 // Test server's response when client requests that server performs AAAA update.
 // Test server's response when client requests that server performs AAAA update.
 TEST_F(FqdnDhcpv6SrvTest, serverAAAAUpdate) {
 TEST_F(FqdnDhcpv6SrvTest, serverAAAAUpdate) {
-    testFqdn(DHCPV6_SOLICIT, true, Option6ClientFqdn::FLAG_S,
+    testFqdn(DHCPV6_SOLICIT, Option6ClientFqdn::FLAG_S,
              "myhost.example.com",
              "myhost.example.com",
              Option6ClientFqdn::FULL, Option6ClientFqdn::FLAG_S,
              Option6ClientFqdn::FULL, Option6ClientFqdn::FLAG_S,
              "myhost.example.com.");
              "myhost.example.com.");
@@ -403,7 +420,7 @@ TEST_F(FqdnDhcpv6SrvTest, serverAAAAUpdate) {
 // Test server's response when client provides partial domain-name and requests
 // Test server's response when client provides partial domain-name and requests
 // that server performs AAAA update.
 // that server performs AAAA update.
 TEST_F(FqdnDhcpv6SrvTest, serverAAAAUpdatePartialName) {
 TEST_F(FqdnDhcpv6SrvTest, serverAAAAUpdatePartialName) {
-    testFqdn(DHCPV6_SOLICIT, true, Option6ClientFqdn::FLAG_S, "myhost",
+    testFqdn(DHCPV6_SOLICIT, Option6ClientFqdn::FLAG_S, "myhost",
              Option6ClientFqdn::PARTIAL, Option6ClientFqdn::FLAG_S,
              Option6ClientFqdn::PARTIAL, Option6ClientFqdn::FLAG_S,
              "myhost.example.com.");
              "myhost.example.com.");
 }
 }
@@ -411,14 +428,13 @@ TEST_F(FqdnDhcpv6SrvTest, serverAAAAUpdatePartialName) {
 // Test server's response when client provides empty domain-name and requests
 // Test server's response when client provides empty domain-name and requests
 // that server performs AAAA update.
 // that server performs AAAA update.
 TEST_F(FqdnDhcpv6SrvTest, serverAAAAUpdateNoName) {
 TEST_F(FqdnDhcpv6SrvTest, serverAAAAUpdateNoName) {
-    testFqdn(DHCPV6_SOLICIT, true, Option6ClientFqdn::FLAG_S, "",
-             Option6ClientFqdn::PARTIAL, Option6ClientFqdn::FLAG_S,
-             "myhost.example.com.");
+    testFqdn(DHCPV6_SOLICIT, Option6ClientFqdn::FLAG_S, "",
+             Option6ClientFqdn::PARTIAL, Option6ClientFqdn::FLAG_S, "");
 }
 }
 
 
 // Test server's response when client requests no DNS update.
 // Test server's response when client requests no DNS update.
 TEST_F(FqdnDhcpv6SrvTest, noUpdate) {
 TEST_F(FqdnDhcpv6SrvTest, noUpdate) {
-    testFqdn(DHCPV6_SOLICIT, true, Option6ClientFqdn::FLAG_N,
+    testFqdn(DHCPV6_SOLICIT, Option6ClientFqdn::FLAG_N,
              "myhost.example.com",
              "myhost.example.com",
              Option6ClientFqdn::FULL, Option6ClientFqdn::FLAG_N,
              Option6ClientFqdn::FULL, Option6ClientFqdn::FLAG_N,
              "myhost.example.com.");
              "myhost.example.com.");
@@ -427,7 +443,7 @@ TEST_F(FqdnDhcpv6SrvTest, noUpdate) {
 // Test server's response when client requests that server delegates the AAAA
 // Test server's response when client requests that server delegates the AAAA
 // update to the client and this delegation is not allowed.
 // update to the client and this delegation is not allowed.
 TEST_F(FqdnDhcpv6SrvTest, clientAAAAUpdateNotAllowed) {
 TEST_F(FqdnDhcpv6SrvTest, clientAAAAUpdateNotAllowed) {
-    testFqdn(DHCPV6_SOLICIT, true, 0, "myhost.example.com.",
+    testFqdn(DHCPV6_SOLICIT, 0, "myhost.example.com.",
              Option6ClientFqdn::FULL,
              Option6ClientFqdn::FULL,
              Option6ClientFqdn::FLAG_S | Option6ClientFqdn::FLAG_O,
              Option6ClientFqdn::FLAG_S | Option6ClientFqdn::FLAG_O,
              "myhost.example.com.");
              "myhost.example.com.");
@@ -439,10 +455,8 @@ TEST_F(FqdnDhcpv6SrvTest, createNameChangeRequestsNoAnswer) {
     NakedDhcpv6Srv srv(0);
     NakedDhcpv6Srv srv(0);
 
 
     Pkt6Ptr answer;
     Pkt6Ptr answer;
-    Option6ClientFqdnPtr fqdn = createClientFqdn(Option6ClientFqdn::FLAG_S,
-                                                 "myhost.example.com",
-                                                 Option6ClientFqdn::FULL);
-    EXPECT_THROW(srv.createNameChangeRequests(answer, fqdn),
+
+    EXPECT_THROW(srv.createNameChangeRequests(answer),
                  isc::Unexpected);
                  isc::Unexpected);
 
 
 }
 }
@@ -456,22 +470,21 @@ TEST_F(FqdnDhcpv6SrvTest, createNameChangeRequestsNoDUID) {
     Option6ClientFqdnPtr fqdn = createClientFqdn(Option6ClientFqdn::FLAG_S,
     Option6ClientFqdnPtr fqdn = createClientFqdn(Option6ClientFqdn::FLAG_S,
                                                  "myhost.example.com",
                                                  "myhost.example.com",
                                                  Option6ClientFqdn::FULL);
                                                  Option6ClientFqdn::FULL);
+    answer->addOption(fqdn);
 
 
-    EXPECT_THROW(srv.createNameChangeRequests(answer, fqdn),
-                 isc::Unexpected);
+    EXPECT_THROW(srv.createNameChangeRequests(answer), isc::Unexpected);
 
 
 }
 }
 
 
-// Test no NameChangeRequests are added if FQDN option is NULL.
+// Test no NameChangeRequests if Client FQDN is not added to the server's
+// response.
 TEST_F(FqdnDhcpv6SrvTest, createNameChangeRequestsNoFQDN) {
 TEST_F(FqdnDhcpv6SrvTest, createNameChangeRequestsNoFQDN) {
     NakedDhcpv6Srv srv(0);
     NakedDhcpv6Srv srv(0);
 
 
     // Create Reply message with Client Id and Server id.
     // Create Reply message with Client Id and Server id.
     Pkt6Ptr answer = generateMessageWithIds(DHCPV6_REPLY, srv);
     Pkt6Ptr answer = generateMessageWithIds(DHCPV6_REPLY, srv);
 
 
-    // Pass NULL FQDN option. No NameChangeRequests should be created.
-    Option6ClientFqdnPtr fqdn;
-    ASSERT_NO_THROW(srv.createNameChangeRequests(answer, fqdn));
+    ASSERT_NO_THROW(srv.createNameChangeRequests(answer));
 
 
     // There should be no new NameChangeRequests.
     // There should be no new NameChangeRequests.
     EXPECT_TRUE(srv.name_change_reqs_.empty());
     EXPECT_TRUE(srv.name_change_reqs_.empty());
@@ -485,20 +498,21 @@ TEST_F(FqdnDhcpv6SrvTest, createNameChangeRequestsNoAddr) {
     // Create Reply message with Client Id and Server id.
     // Create Reply message with Client Id and Server id.
     Pkt6Ptr answer = generateMessageWithIds(DHCPV6_REPLY, srv);
     Pkt6Ptr answer = generateMessageWithIds(DHCPV6_REPLY, srv);
 
 
+    // Add Client FQDN option.
     Option6ClientFqdnPtr fqdn = createClientFqdn(Option6ClientFqdn::FLAG_S,
     Option6ClientFqdnPtr fqdn = createClientFqdn(Option6ClientFqdn::FLAG_S,
                                                  "myhost.example.com",
                                                  "myhost.example.com",
                                                  Option6ClientFqdn::FULL);
                                                  Option6ClientFqdn::FULL);
+    answer->addOption(fqdn);
 
 
-    ASSERT_NO_THROW(srv.createNameChangeRequests(answer, fqdn));
+    ASSERT_NO_THROW(srv.createNameChangeRequests(answer));
 
 
     // We didn't add any IAs, so there should be no NameChangeRequests in th
     // We didn't add any IAs, so there should be no NameChangeRequests in th
     // queue.
     // queue.
     ASSERT_TRUE(srv.name_change_reqs_.empty());
     ASSERT_TRUE(srv.name_change_reqs_.empty());
 }
 }
 
 
-// Test that a number of NameChangeRequests is created as a result of
-// processing the answer message which holds 3 IAs and when FQDN is
-// specified.
+// Test that exactly one NameChangeRequest is created as a result of processing
+// the answer message which holds 3 IAs and when FQDN is specified.
 TEST_F(FqdnDhcpv6SrvTest, createNameChangeRequests) {
 TEST_F(FqdnDhcpv6SrvTest, createNameChangeRequests) {
     NakedDhcpv6Srv srv(0);
     NakedDhcpv6Srv srv(0);
 
 
@@ -516,33 +530,19 @@ TEST_F(FqdnDhcpv6SrvTest, createNameChangeRequests) {
     Option6ClientFqdnPtr fqdn = createClientFqdn(Option6ClientFqdn::FLAG_S,
     Option6ClientFqdnPtr fqdn = createClientFqdn(Option6ClientFqdn::FLAG_S,
                                                  "MYHOST.EXAMPLE.COM",
                                                  "MYHOST.EXAMPLE.COM",
                                                  Option6ClientFqdn::FULL);
                                                  Option6ClientFqdn::FULL);
+    answer->addOption(fqdn);
 
 
-    // Create NameChangeRequests. Since we have added 3 IAs, it should
-    // result in generation of 3 distinct NameChangeRequests.
-    ASSERT_NO_THROW(srv.createNameChangeRequests(answer, fqdn));
-    ASSERT_EQ(3, srv.name_change_reqs_.size());
-
-    // Verify that NameChangeRequests are correct. Each call to the
-    // verifyNameChangeRequest will pop verified request from the queue.
+    // Create NameChangeRequest for the first allocated address.
+    ASSERT_NO_THROW(srv.createNameChangeRequests(answer));
+    ASSERT_EQ(1, srv.name_change_reqs_.size());
 
 
+    // Verify that NameChangeRequest is correct.
     verifyNameChangeRequest(srv, isc::dhcp_ddns::CHG_ADD, true, true,
     verifyNameChangeRequest(srv, isc::dhcp_ddns::CHG_ADD, true, true,
                             "2001:db8:1::1",
                             "2001:db8:1::1",
                             "000201415AA33D1187D148275136FA30300478"
                             "000201415AA33D1187D148275136FA30300478"
                             "FAAAA3EBD29826B5C907B2C9268A6F52",
                             "FAAAA3EBD29826B5C907B2C9268A6F52",
                             0, 500);
                             0, 500);
 
 
-    verifyNameChangeRequest(srv, isc::dhcp_ddns::CHG_ADD, true, true,
-                            "2001:db8:1::2",
-                            "000201415AA33D1187D148275136FA30300478"
-                            "FAAAA3EBD29826B5C907B2C9268A6F52",
-                            0, 500);
-
-    verifyNameChangeRequest(srv, isc::dhcp_ddns::CHG_ADD, true, true,
-                            "2001:db8:1::3",
-                            "000201415AA33D1187D148275136FA30300478"
-                            "FAAAA3EBD29826B5C907B2C9268A6F52",
-                            0, 500);
-
 }
 }
 
 
 // Test creation of the NameChangeRequest to remove both forward and reverse
 // Test creation of the NameChangeRequest to remove both forward and reverse
@@ -642,7 +642,8 @@ TEST_F(FqdnDhcpv6SrvTest, processSolicit) {
 
 
     // Create a Solicit message with FQDN option and generate server's
     // Create a Solicit message with FQDN option and generate server's
     // response using processSolicit function.
     // response using processSolicit function.
-    testProcessMessage(DHCPV6_SOLICIT, "myhost.example.com", srv);
+    testProcessMessage(DHCPV6_SOLICIT, "myhost.example.com",
+                       "myhost.example.com.", srv);
     EXPECT_TRUE(srv.name_change_reqs_.empty());
     EXPECT_TRUE(srv.name_change_reqs_.empty());
 }
 }
 
 
@@ -657,7 +658,8 @@ TEST_F(FqdnDhcpv6SrvTest, processTwoRequests) {
     // response using processRequest function. This will result in the
     // response using processRequest function. This will result in the
     // creation of a new lease and the appropriate NameChangeRequest
     // creation of a new lease and the appropriate NameChangeRequest
     // to add both reverse and forward mapping to DNS.
     // to add both reverse and forward mapping to DNS.
-    testProcessMessage(DHCPV6_REQUEST, "myhost.example.com", srv);
+    testProcessMessage(DHCPV6_REQUEST, "myhost.example.com",
+                       "myhost.example.com.", srv);
     ASSERT_EQ(1, srv.name_change_reqs_.size());
     ASSERT_EQ(1, srv.name_change_reqs_.size());
     verifyNameChangeRequest(srv, isc::dhcp_ddns::CHG_ADD, true, true,
     verifyNameChangeRequest(srv, isc::dhcp_ddns::CHG_ADD, true, true,
                             "2001:db8:1:1::dead:beef",
                             "2001:db8:1:1::dead:beef",
@@ -672,7 +674,8 @@ TEST_F(FqdnDhcpv6SrvTest, processTwoRequests) {
     // entries should be removed and the entries for the new domain-name
     // entries should be removed and the entries for the new domain-name
     // should be added. Therefore, we expect two NameChangeRequests. One to
     // should be added. Therefore, we expect two NameChangeRequests. One to
     // remove the existing entries, one to add new entries.
     // remove the existing entries, one to add new entries.
-    testProcessMessage(DHCPV6_REQUEST, "otherhost.example.com", srv);
+    testProcessMessage(DHCPV6_REQUEST, "otherhost.example.com",
+                       "otherhost.example.com.", srv);
     ASSERT_EQ(2, srv.name_change_reqs_.size());
     ASSERT_EQ(2, srv.name_change_reqs_.size());
     verifyNameChangeRequest(srv, isc::dhcp_ddns::CHG_REMOVE, true, true,
     verifyNameChangeRequest(srv, isc::dhcp_ddns::CHG_REMOVE, true, true,
                             "2001:db8:1:1::dead:beef",
                             "2001:db8:1:1::dead:beef",
@@ -687,6 +690,38 @@ TEST_F(FqdnDhcpv6SrvTest, processTwoRequests) {
 
 
 }
 }
 
 
+// Test that NameChangeRequest is not generated when Solicit message is sent.
+// The Solicit is here sent after a lease has been allocated for a client.
+// The Solicit conveys a different hostname which would trigger updates to
+// DNS if the Request was sent instead of Soicit. The code should differentiate
+// behavior depending whether Solicit or Request is sent.
+TEST_F(FqdnDhcpv6SrvTest, processRequestSolicit) {
+    NakedDhcpv6Srv srv(0);
+
+    // Create a Request message with FQDN option and generate server's
+    // response using processRequest function. This will result in the
+    // creation of a new lease and the appropriate NameChangeRequest
+    // to add both reverse and forward mapping to DNS.
+    testProcessMessage(DHCPV6_REQUEST, "myhost.example.com",
+                       "myhost.example.com.", srv);
+    ASSERT_EQ(1, srv.name_change_reqs_.size());
+    verifyNameChangeRequest(srv, isc::dhcp_ddns::CHG_ADD, true, true,
+                            "2001:db8:1:1::dead:beef",
+                            "000201415AA33D1187D148275136FA30300478"
+                            "FAAAA3EBD29826B5C907B2C9268A6F52",
+                            0, 4000);
+
+    // When the returning client sends Solicit the code should never generate
+    // NameChangeRequest and preserve existing DNS entries for the client.
+    // The NameChangeRequest should only be generated when a client sends
+    // Request or Renew.
+    testProcessMessage(DHCPV6_SOLICIT, "otherhost.example.com",
+                       "otherhost.example.com.", srv);
+    ASSERT_TRUE(srv.name_change_reqs_.empty());
+
+}
+
+
 // Test that client may send Request followed by the Renew, both holding
 // Test that client may send Request followed by the Renew, both holding
 // FQDN options, but each option holding different domain-name. The Renew
 // FQDN options, but each option holding different domain-name. The Renew
 // should result in generation of the two NameChangeRequests, one to remove
 // should result in generation of the two NameChangeRequests, one to remove
@@ -699,7 +734,8 @@ TEST_F(FqdnDhcpv6SrvTest, processRequestRenew) {
     // response using processRequest function. This will result in the
     // response using processRequest function. This will result in the
     // creation of a new lease and the appropriate NameChangeRequest
     // creation of a new lease and the appropriate NameChangeRequest
     // to add both reverse and forward mapping to DNS.
     // to add both reverse and forward mapping to DNS.
-    testProcessMessage(DHCPV6_REQUEST, "myhost.example.com", srv);
+    testProcessMessage(DHCPV6_REQUEST, "myhost.example.com",
+                       "myhost.example.com.", srv);
     ASSERT_EQ(1, srv.name_change_reqs_.size());
     ASSERT_EQ(1, srv.name_change_reqs_.size());
     verifyNameChangeRequest(srv, isc::dhcp_ddns::CHG_ADD, true, true,
     verifyNameChangeRequest(srv, isc::dhcp_ddns::CHG_ADD, true, true,
                             "2001:db8:1:1::dead:beef",
                             "2001:db8:1:1::dead:beef",
@@ -714,7 +750,8 @@ TEST_F(FqdnDhcpv6SrvTest, processRequestRenew) {
     // entries should be removed and the entries for the new domain-name
     // entries should be removed and the entries for the new domain-name
     // should be added. Therefore, we expect two NameChangeRequests. One to
     // should be added. Therefore, we expect two NameChangeRequests. One to
     // remove the existing entries, one to add new entries.
     // remove the existing entries, one to add new entries.
-    testProcessMessage(DHCPV6_RENEW, "otherhost.example.com", srv);
+    testProcessMessage(DHCPV6_RENEW, "otherhost.example.com",
+                       "otherhost.example.com.", srv);
     ASSERT_EQ(2, srv.name_change_reqs_.size());
     ASSERT_EQ(2, srv.name_change_reqs_.size());
     verifyNameChangeRequest(srv, isc::dhcp_ddns::CHG_REMOVE, true, true,
     verifyNameChangeRequest(srv, isc::dhcp_ddns::CHG_REMOVE, true, true,
                             "2001:db8:1:1::dead:beef",
                             "2001:db8:1:1::dead:beef",
@@ -736,7 +773,8 @@ TEST_F(FqdnDhcpv6SrvTest, processRequestRelease) {
     // response using processRequest function. This will result in the
     // response using processRequest function. This will result in the
     // creation of a new lease and the appropriate NameChangeRequest
     // creation of a new lease and the appropriate NameChangeRequest
     // to add both reverse and forward mapping to DNS.
     // to add both reverse and forward mapping to DNS.
-    testProcessMessage(DHCPV6_REQUEST, "myhost.example.com", srv);
+    testProcessMessage(DHCPV6_REQUEST, "myhost.example.com",
+                       "myhost.example.com.", srv);
     ASSERT_EQ(1, srv.name_change_reqs_.size());
     ASSERT_EQ(1, srv.name_change_reqs_.size());
     verifyNameChangeRequest(srv, isc::dhcp_ddns::CHG_ADD, true, true,
     verifyNameChangeRequest(srv, isc::dhcp_ddns::CHG_ADD, true, true,
                             "2001:db8:1:1::dead:beef",
                             "2001:db8:1:1::dead:beef",
@@ -748,7 +786,8 @@ TEST_F(FqdnDhcpv6SrvTest, processRequestRelease) {
     // removed and all existing DNS entries for this lease should be
     // removed and all existing DNS entries for this lease should be
     // also removed. Therefore, we expect that single NameChangeRequest to
     // also removed. Therefore, we expect that single NameChangeRequest to
     // remove DNS entries is generated.
     // remove DNS entries is generated.
-    testProcessMessage(DHCPV6_RELEASE, "otherhost.example.com", srv);
+    testProcessMessage(DHCPV6_RELEASE, "otherhost.example.com",
+                       "otherhost.example.com.", srv);
     ASSERT_EQ(1, srv.name_change_reqs_.size());
     ASSERT_EQ(1, srv.name_change_reqs_.size());
     verifyNameChangeRequest(srv, isc::dhcp_ddns::CHG_REMOVE, true, true,
     verifyNameChangeRequest(srv, isc::dhcp_ddns::CHG_REMOVE, true, true,
                             "2001:db8:1:1::dead:beef",
                             "2001:db8:1:1::dead:beef",
@@ -758,15 +797,16 @@ TEST_F(FqdnDhcpv6SrvTest, processRequestRelease) {
 
 
 }
 }
 
 
-// Checks that the server does not include DHCPv6 Client FQDN option in its
-// response when client doesn't include ORO option in the Request.
+// Checks that the server include DHCPv6 Client FQDN option in its
+// response even when client doesn't request this option using ORO.
 TEST_F(FqdnDhcpv6SrvTest, processRequestWithoutFqdn) {
 TEST_F(FqdnDhcpv6SrvTest, processRequestWithoutFqdn) {
     NakedDhcpv6Srv srv(0);
     NakedDhcpv6Srv srv(0);
 
 
     // The last parameter disables use of the ORO to request FQDN option
     // The last parameter disables use of the ORO to request FQDN option
     // In this case, we expect that the FQDN option will not be included
     // In this case, we expect that the FQDN option will not be included
     // in the server's response. The testProcessMessage will check that.
     // in the server's response. The testProcessMessage will check that.
-    testProcessMessage(DHCPV6_REQUEST, "myhost.example.com", srv, false);
+    testProcessMessage(DHCPV6_REQUEST, "myhost.example.com",
+                       "myhost.example.com.", srv, false);
     ASSERT_EQ(1, srv.name_change_reqs_.size());
     ASSERT_EQ(1, srv.name_change_reqs_.size());
     verifyNameChangeRequest(srv, isc::dhcp_ddns::CHG_ADD, true, true,
     verifyNameChangeRequest(srv, isc::dhcp_ddns::CHG_ADD, true, true,
                             "2001:db8:1:1::dead:beef",
                             "2001:db8:1:1::dead:beef",
@@ -775,4 +815,92 @@ TEST_F(FqdnDhcpv6SrvTest, processRequestWithoutFqdn) {
                             0, 4000);
                             0, 4000);
 }
 }
 
 
+// Checks that FQDN is generated from an ip address, when client sends an empty
+// FQDN.
+TEST_F(FqdnDhcpv6SrvTest, processRequestEmptyFqdn) {
+    NakedDhcpv6Srv srv(0);
+
+    testProcessMessage(DHCPV6_REQUEST, "",
+                       "host-2001-db8-1-1--dead-beef.example.com.",
+                       srv, false);
+    ASSERT_EQ(1, srv.name_change_reqs_.size());
+    verifyNameChangeRequest(srv, isc::dhcp_ddns::CHG_ADD, true, true,
+                            "2001:db8:1:1::dead:beef",
+                            "0002018D6874B105A5C92DBBD6E4F6C80A93161"
+                            "BC03996F0CD0EB75800DEF997C29961",
+                            0, 4000);
+
+}
+
+// Checks that when the server reuses expired lease, the NameChangeRequest
+// is generated to remove the DNS mapping for the expired lease and second
+// NameChangeRequest to add a DNS mapping for a new lease.
+TEST_F(FqdnDhcpv6SrvTest, processRequestReuseExpiredLease) {
+    // This address will be used throughout the test.
+    IOAddress addr("2001:db8:1:1::dead:beef");
+    // We are going to configure a subnet with a pool that consists of
+    // exactly one address. This address will be handed out to the
+    // client, will get expired and then be reused.
+    CfgMgr::instance().deleteSubnets6();
+    subnet_ = Subnet6Ptr(new Subnet6(IOAddress("2001:db8:1:1::"), 56, 1, 2,
+                                     3, 4));
+    pool_ = Pool6Ptr(new Pool6(Lease::TYPE_NA, addr, addr));
+    subnet_->addPool(pool_);
+    CfgMgr::instance().addSubnet6(subnet_);
+
+    // Allocate a lease.
+    NakedDhcpv6Srv srv(0);
+    testProcessMessage(DHCPV6_REQUEST, "myhost.example.com",
+                       "myhost.example.com.", srv);
+    // Test that the appropriate NameChangeRequest has been generated.
+    ASSERT_EQ(1, srv.name_change_reqs_.size());
+    verifyNameChangeRequest(srv, isc::dhcp_ddns::CHG_ADD, true, true,
+                            "2001:db8:1:1::dead:beef",
+                            "000201415AA33D1187D148275136FA30300478"
+                            "FAAAA3EBD29826B5C907B2C9268A6F52",
+                            0, 4);
+    // Get the lease acquired and modify it. In particular, expire it.
+    Lease6Ptr lease =
+        LeaseMgrFactory::instance().getLease6(Lease::TYPE_NA, addr);
+    ASSERT_TRUE(lease);
+    // One of the following: IAID, DUID or subnet identifier has to be changed
+    // because otherwise the allocation engine will treat the lease as
+    // being renewed by the same client. If we at least change subnet identifier
+    // the lease will be treated as expired lease to be reused.
+    ++lease->subnet_id_;
+
+    // Move the cllt back in time and make sure that the lease got expired.
+    lease->cltt_ = time(NULL) - 10;
+    lease->valid_lft_ = 5;
+    ASSERT_TRUE(lease->expired());
+    // Change the hostname so as the name change request for removing existing
+    // DNS mapping is generated.
+    lease->hostname_ = "otherhost.example.com.";
+    // Update the lease in the lease database.
+    LeaseMgrFactory::instance().updateLease6(lease);
+
+    // Simulate another lease acquisition. Since, our pool consists of
+    // exactly one address and this address is used by the lease in the
+    // lease database, it is guaranteed that the allocation engine will
+    // reuse this lease.
+    testProcessMessage(DHCPV6_REQUEST, "myhost.example.com.",
+                       "myhost.example.com.", srv);
+    ASSERT_EQ(2, srv.name_change_reqs_.size());
+    // The first name change request generated, should remove a DNS
+    // mapping for an expired lease.
+    verifyNameChangeRequest(srv, isc::dhcp_ddns::CHG_REMOVE, true, true,
+                            "2001:db8:1:1::dead:beef",
+                            "000201D422AA463306223D269B6CB7AFE7AAD2"
+                            "65FCEA97F93623019B2E0D14E5323D5A",
+                            0, 5);
+    // The second name change request should add a DNS mapping for
+    // a new lease.
+    verifyNameChangeRequest(srv, isc::dhcp_ddns::CHG_ADD, true, true,
+                            "2001:db8:1:1::dead:beef",
+                            "000201415AA33D1187D148275136FA30300478"
+                            "FAAAA3EBD29826B5C907B2C9268A6F52",
+                            0, 4);
+
+}
+
 }   // end of anonymous namespace
 }   // end of anonymous namespace

+ 68 - 17
src/lib/dhcpsrv/alloc_engine.cc

@@ -1,4 +1,4 @@
-// Copyright (C) 2012-2013 Internet Systems Consortium, Inc. ("ISC")
+// Copyright (C) 2012-2014 Internet Systems Consortium, Inc. ("ISC")
 //
 //
 // Permission to use, copy, modify, and/or distribute this software for any
 // Permission to use, copy, modify, and/or distribute this software for any
 // purpose with or without fee is hereby granted, provided that the above
 // purpose with or without fee is hereby granted, provided that the above
@@ -294,11 +294,12 @@ AllocEngine::AllocEngine(AllocType engine_type, unsigned int attempts,
 
 
 Lease6Collection
 Lease6Collection
 AllocEngine::allocateLeases6(const Subnet6Ptr& subnet, const DuidPtr& duid,
 AllocEngine::allocateLeases6(const Subnet6Ptr& subnet, const DuidPtr& duid,
-                             uint32_t iaid, const IOAddress& hint,
+                             const uint32_t iaid, const IOAddress& hint,
                              Lease::Type type, const bool fwd_dns_update,
                              Lease::Type type, const bool fwd_dns_update,
                              const bool rev_dns_update,
                              const bool rev_dns_update,
                              const std::string& hostname, bool fake_allocation,
                              const std::string& hostname, bool fake_allocation,
-                             const isc::hooks::CalloutHandlePtr& callout_handle) {
+                             const isc::hooks::CalloutHandlePtr& callout_handle,
+                             Lease6Collection& old_leases) {
 
 
     try {
     try {
         AllocatorPtr allocator = getAllocator(type);
         AllocatorPtr allocator = getAllocator(type);
@@ -316,37 +317,49 @@ AllocEngine::allocateLeases6(const Subnet6Ptr& subnet, const DuidPtr& duid,
             isc_throw(InvalidOperation, "DUID is mandatory for allocation");
             isc_throw(InvalidOperation, "DUID is mandatory for allocation");
         }
         }
 
 
-        // check if there's existing lease for that subnet/duid/iaid combination.
+        // Check if there's existing lease for that subnet/duid/iaid
+        // combination.
         /// @todo: Make this generic (cover temp. addrs and prefixes)
         /// @todo: Make this generic (cover temp. addrs and prefixes)
         Lease6Collection existing = LeaseMgrFactory::instance().getLeases6(type,
         Lease6Collection existing = LeaseMgrFactory::instance().getLeases6(type,
                                     *duid, iaid, subnet->getID());
                                     *duid, iaid, subnet->getID());
 
 
+        // There is at least one lease for this client. We will return these
+        // leases for the client, but we may need to update FQDN information.
         if (!existing.empty()) {
         if (!existing.empty()) {
-            // we have at least one lease already. This is a returning client,
-            // probably after his reboot.
-            return (existing);
+            // Return old leases so the server can see what has changed.
+            old_leases = existing;
+            return (updateFqdnData(existing, fwd_dns_update, rev_dns_update,
+                                   hostname, fake_allocation));
         }
         }
 
 
         // check if the hint is in pool and is available
         // check if the hint is in pool and is available
         // This is equivalent of subnet->inPool(hint), but returns the pool
         // This is equivalent of subnet->inPool(hint), but returns the pool
-        Pool6Ptr pool = boost::dynamic_pointer_cast<Pool6>(subnet->getPool(type, hint, false));
+        Pool6Ptr pool = boost::dynamic_pointer_cast<
+            Pool6>(subnet->getPool(type, hint, false));
 
 
         if (pool) {
         if (pool) {
             /// @todo: We support only one hint for now
             /// @todo: We support only one hint for now
             Lease6Ptr lease = LeaseMgrFactory::instance().getLease6(type, hint);
             Lease6Ptr lease = LeaseMgrFactory::instance().getLease6(type, hint);
             if (!lease) {
             if (!lease) {
-                /// @todo: check if the hint is reserved once we have host support
-                /// implemented
-
-                // the hint is valid and not currently used, let's create a lease for it
-                lease = createLease6(subnet, duid, iaid, hint, pool->getLength(),
-                                     type, fwd_dns_update, rev_dns_update,
+                /// @todo: check if the hint is reserved once we have host
+                /// support implemented
+
+                // The hint is valid and not currently used, let's create a
+                // lease for it
+                lease = createLease6(subnet, duid, iaid, hint,
+                                     pool->getLength(), type,
+                                     fwd_dns_update, rev_dns_update,
                                      hostname, callout_handle, fake_allocation);
                                      hostname, callout_handle, fake_allocation);
 
 
-                // It can happen that the lease allocation failed (we could have lost
-                // the race condition. That means that the hint is lo longer usable and
-                // we need to continue the regular allocation path.
+                // It can happen that the lease allocation failed (we could
+                // have lost the race condition. That means that the hint is
+                // lo longer usable and we need to continue the regular
+                // allocation path.
                 if (lease) {
                 if (lease) {
+                    // We are allocating a new lease (not renewing). So, the
+                    // old lease should be NULL.
+                    old_leases.push_back(Lease6Ptr());
+
                     /// @todo: We support only one lease per ia for now
                     /// @todo: We support only one lease per ia for now
                     Lease6Collection collection;
                     Lease6Collection collection;
                     collection.push_back(lease);
                     collection.push_back(lease);
@@ -354,6 +367,11 @@ AllocEngine::allocateLeases6(const Subnet6Ptr& subnet, const DuidPtr& duid,
                 }
                 }
             } else {
             } else {
                 if (lease->expired()) {
                 if (lease->expired()) {
+                    // Copy an existing, expired lease so as it can be returned
+                    // to the caller.
+                    Lease6Ptr old_lease(new Lease6(*lease));
+                    old_leases.push_back(old_lease);
+
                     /// We found a lease and it is expired, so we can reuse it
                     /// We found a lease and it is expired, so we can reuse it
                     lease = reuseExpiredLease(lease, subnet, duid, iaid,
                     lease = reuseExpiredLease(lease, subnet, duid, iaid,
                                               pool->getLength(),
                                               pool->getLength(),
@@ -414,6 +432,10 @@ AllocEngine::allocateLeases6(const Subnet6Ptr& subnet, const DuidPtr& duid,
                                                rev_dns_update, hostname,
                                                rev_dns_update, hostname,
                                                callout_handle, fake_allocation);
                                                callout_handle, fake_allocation);
                 if (lease) {
                 if (lease) {
+                    // We are allocating a new lease (not renewing). So, the
+                    // old lease should be NULL.
+                    old_leases.push_back(Lease6Ptr());
+
                     Lease6Collection collection;
                     Lease6Collection collection;
                     collection.push_back(lease);
                     collection.push_back(lease);
                     return (collection);
                     return (collection);
@@ -424,6 +446,11 @@ AllocEngine::allocateLeases6(const Subnet6Ptr& subnet, const DuidPtr& duid,
                 // allocation attempts.
                 // allocation attempts.
             } else {
             } else {
                 if (existing->expired()) {
                 if (existing->expired()) {
+                    // Copy an existing, expired lease so as it can be returned
+                    // to the caller.
+                    Lease6Ptr old_lease(new Lease6(*existing));
+                    old_leases.push_back(old_lease);
+
                     existing = reuseExpiredLease(existing, subnet, duid, iaid,
                     existing = reuseExpiredLease(existing, subnet, duid, iaid,
                                                  prefix_len, fwd_dns_update,
                                                  prefix_len, fwd_dns_update,
                                                  rev_dns_update, hostname,
                                                  rev_dns_update, hostname,
@@ -1037,6 +1064,30 @@ Lease4Ptr AllocEngine::createLease4(const SubnetPtr& subnet,
     }
     }
 }
 }
 
 
+Lease6Collection
+AllocEngine::updateFqdnData(const Lease6Collection& leases,
+                            const bool fwd_dns_update,
+                            const bool rev_dns_update,
+                            const std::string& hostname,
+                            const bool fake_allocation) {
+    Lease6Collection updated_leases;
+    for (Lease6Collection::const_iterator lease_it = leases.begin();
+         lease_it != leases.end(); ++lease_it) {
+        Lease6Ptr lease(new Lease6(**lease_it));
+        lease->fqdn_fwd_ = fwd_dns_update;
+        lease->fqdn_rev_ = rev_dns_update;
+        lease->hostname_ = hostname;
+        if (!fake_allocation &&
+            ((lease->fqdn_fwd_ != (*lease_it)->fqdn_fwd_) ||
+             (lease->fqdn_rev_ != (*lease_it)->fqdn_rev_) ||
+             (lease->hostname_ != (*lease_it)->hostname_))) {
+            LeaseMgrFactory::instance().updateLease6(lease);
+        }
+        updated_leases.push_back(lease);
+    }
+    return (updated_leases);
+}
+
 AllocEngine::AllocatorPtr AllocEngine::getAllocator(Lease::Type type) {
 AllocEngine::AllocatorPtr AllocEngine::getAllocator(Lease::Type type) {
     std::map<Lease::Type, AllocatorPtr>::const_iterator alloc = allocators_.find(type);
     std::map<Lease::Type, AllocatorPtr>::const_iterator alloc = allocators_.find(type);
 
 

+ 32 - 3
src/lib/dhcpsrv/alloc_engine.h

@@ -1,4 +1,4 @@
-// Copyright (C) 2012-2013 Internet Systems Consortium, Inc. ("ISC")
+// Copyright (C) 2012-2014 Internet Systems Consortium, Inc. ("ISC")
 //
 //
 // Permission to use, copy, modify, and/or distribute this software for any
 // Permission to use, copy, modify, and/or distribute this software for any
 // purpose with or without fee is hereby granted, provided that the above
 // purpose with or without fee is hereby granted, provided that the above
@@ -338,14 +338,21 @@ protected:
     ///        an address for SOLICIT that is not really allocated (true)
     ///        an address for SOLICIT that is not really allocated (true)
     /// @param callout_handle a callout handle (used in hooks). A lease callouts
     /// @param callout_handle a callout handle (used in hooks). A lease callouts
     ///        will be executed if this parameter is passed.
     ///        will be executed if this parameter is passed.
+    /// @param [out] old_leases Collection to which this function will append
+    ///        old leases. Leases are stored in the same order as in the
+    ///        collection of new leases, being returned. For newly allocated
+    ///        leases (not renewed) the NULL pointers are stored in this
+    ///        collection as old leases.
     ///
     ///
     /// @return Allocated IPv6 leases (may be empty if allocation failed)
     /// @return Allocated IPv6 leases (may be empty if allocation failed)
     Lease6Collection
     Lease6Collection
-    allocateLeases6(const Subnet6Ptr& subnet, const DuidPtr& duid, uint32_t iaid,
+    allocateLeases6(const Subnet6Ptr& subnet, const DuidPtr& duid,
+                    const uint32_t iaid,
                     const isc::asiolink::IOAddress& hint, Lease::Type type,
                     const isc::asiolink::IOAddress& hint, Lease::Type type,
                     const bool fwd_dns_update, const bool rev_dns_update,
                     const bool fwd_dns_update, const bool rev_dns_update,
                     const std::string& hostname, bool fake_allocation,
                     const std::string& hostname, bool fake_allocation,
-                    const isc::hooks::CalloutHandlePtr& callout_handle);
+                    const isc::hooks::CalloutHandlePtr& callout_handle,
+                    Lease6Collection& old_leases);
 
 
     /// @brief returns allocator for a given pool type
     /// @brief returns allocator for a given pool type
     /// @param type type of pool (V4, IA, TA or PD)
     /// @param type type of pool (V4, IA, TA or PD)
@@ -489,6 +496,28 @@ private:
                                 const isc::hooks::CalloutHandlePtr& callout_handle,
                                 const isc::hooks::CalloutHandlePtr& callout_handle,
                                 bool fake_allocation = false);
                                 bool fake_allocation = false);
 
 
+    /// @brief Updates FQDN data for a collection of leases.
+    ///
+    /// @param leases Collection of leases for which FQDN data should be
+    /// updated.
+    /// @param fwd_dns_update Boolean value which indicates whether forward FQDN
+    /// update was performed for each lease (true) or not (false).
+    /// @param rev_dns_update Boolean value which indicates whether reverse FQDN
+    /// update was performed for each lease (true) or not (false).
+    /// @param hostname Client hostname associated with a lease.
+    /// @param fake_allocation Boolean value which indicates that it is a real
+    /// lease allocation, e.g. Request message is processed (false), or address
+    /// is just being picked as a result of processing Solicit (true). In the
+    /// latter case, the FQDN data should not be updated in the lease database.
+    ///
+    /// @return Collection of leases with updated FQDN data. Note that returned
+    /// collection holds updated FQDN data even for fake allocation.
+    Lease6Collection updateFqdnData(const Lease6Collection& leases,
+                                    const bool fwd_dns_update,
+                                    const bool rev_dns_update,
+                                    const std::string& hostname,
+                                    const bool fake_allocation);
+
     /// @brief a pointer to currently used allocator
     /// @brief a pointer to currently used allocator
     ///
     ///
     /// For IPv4, there will be only one allocator: TYPE_V4
     /// For IPv4, there will be only one allocator: TYPE_V4

+ 8 - 1
src/lib/dhcpsrv/lease.cc

@@ -1,4 +1,4 @@
-// Copyright (C) 2012-2013 Internet Systems Consortium, Inc. ("ISC")
+// Copyright (C) 2012-2014 Internet Systems Consortium, Inc. ("ISC")
 //
 //
 // Permission to use, copy, modify, and/or distribute this software for any
 // Permission to use, copy, modify, and/or distribute this software for any
 // purpose with or without fee is hereby granted, provided that the above
 // purpose with or without fee is hereby granted, provided that the above
@@ -56,6 +56,13 @@ bool Lease::expired() const {
     return (expire_time < time(NULL));
     return (expire_time < time(NULL));
 }
 }
 
 
+bool
+Lease::hasIdenticalFqdn(const Lease& other) const {
+    return (hostname_ == other.hostname_ &&
+            fqdn_fwd_ == other.fqdn_fwd_ &&
+            fqdn_rev_ == other.fqdn_rev_);
+}
+
 Lease4::Lease4(const Lease4& other)
 Lease4::Lease4(const Lease4& other)
     : Lease(other.addr_, other.t1_, other.t2_, other.valid_lft_,
     : Lease(other.addr_, other.t1_, other.t2_, other.valid_lft_,
             other.subnet_id_, other.cltt_, other.fqdn_fwd_,
             other.subnet_id_, other.cltt_, other.fqdn_fwd_,

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

@@ -1,4 +1,4 @@
-// Copyright (C) 2013 Internet Systems Consortium, Inc. ("ISC")
+// Copyright (C) 2013-2014 Internet Systems Consortium, Inc. ("ISC")
 //
 //
 // Permission to use, copy, modify, and/or distribute this software for any
 // Permission to use, copy, modify, and/or distribute this software for any
 // purpose with or without fee is hereby granted, provided that the above
 // purpose with or without fee is hereby granted, provided that the above
@@ -141,6 +141,14 @@ struct Lease {
     /// @return true if the lease is expired
     /// @return true if the lease is expired
     bool expired() const;
     bool expired() const;
 
 
+    /// @brief Returns true if the other lease has equal FQDN data.
+    ///
+    /// @param other Lease which FQDN data is to be compared with our lease.
+    ///
+    /// @return Boolean value which indicates whether FQDN data of the other
+    /// lease is equal to the FQDN data of our lease (true) or not (false).
+    bool hasIdenticalFqdn(const Lease& other) const;
+
 };
 };
 
 
 /// @brief Structure that holds a lease for IPv4 address
 /// @brief Structure that holds a lease for IPv4 address

+ 100 - 38
src/lib/dhcpsrv/tests/alloc_engine_unittest.cc

@@ -94,22 +94,56 @@ public:
         duid_ = DuidPtr(new DUID(vector<uint8_t>(8, 0x42)));
         duid_ = DuidPtr(new DUID(vector<uint8_t>(8, 0x42)));
         iaid_ = 42;
         iaid_ = 42;
 
 
-        // instantiate cfg_mgr
+        // Initialize a subnet and short address pool.
+        initSubnet(IOAddress("2001:db8:1::"),
+                   IOAddress("2001:db8:1::10"),
+                   IOAddress("2001:db8:1::20"));
+
+        initFqdn("", false, false);
+
+        factory_.create("type=memfile");
+    }
+
+    /// @brief Configures a subnet and adds one pool to it.
+    ///
+    /// This function removes existing v6 subnets before configuring
+    /// a new one.
+    ///
+    /// @param subnet Address of a subnet to be configured.
+    /// @param pool_start First address in the address pool.
+    /// @param pool_end Last address in the address pool.
+    void initSubnet(const IOAddress& subnet, const IOAddress& pool_start,
+                    const IOAddress& pool_end) {
         CfgMgr& cfg_mgr = CfgMgr::instance();
         CfgMgr& cfg_mgr = CfgMgr::instance();
+        cfg_mgr.deleteSubnets6();
+
+        subnet_ = Subnet6Ptr(new Subnet6(subnet, 56, 1, 2, 3, 4));
+        pool_ = Pool6Ptr(new Pool6(Lease::TYPE_NA, pool_start, pool_end));
 
 
-        // Configure normal address pool
-        subnet_ = Subnet6Ptr(new Subnet6(IOAddress("2001:db8:1::"), 56, 1, 2, 3, 4));
-        pool_ = Pool6Ptr(new Pool6(Lease::TYPE_NA, IOAddress("2001:db8:1::10"),
-                                   IOAddress("2001:db8:1::20")));
         subnet_->addPool(pool_);
         subnet_->addPool(pool_);
 
 
-        // Configure PD pool
-        pd_pool_ = Pool6Ptr(new Pool6(Lease::TYPE_PD, IOAddress("2001:db8:1::"), 56, 64));
+        pd_pool_ = Pool6Ptr(new Pool6(Lease::TYPE_PD, subnet, 56, 64));
         subnet_->addPool(pd_pool_);
         subnet_->addPool(pd_pool_);
 
 
         cfg_mgr.addSubnet6(subnet_);
         cfg_mgr.addSubnet6(subnet_);
 
 
-        factory_.create("type=memfile");
+    }
+
+    /// @brief Initializes FQDN data for a test.
+    ///
+    /// The initialized values are used by the test fixture class members to
+    /// verify the correctness of a lease.
+    ///
+    /// @param hostname Hostname to be assigned to a lease.
+    /// @param fqdn_fwd Indicates whether or not to perform forward DNS update
+    /// for a lease.
+    /// @param fqdn_fwd Indicates whether or not to perform reverse DNS update
+    /// for a lease.
+    void initFqdn(const std::string& hostname, const bool fqdn_fwd,
+                  const bool fqdn_rev) {
+        hostname_ = hostname;
+        fqdn_fwd_ = fqdn_fwd;
+        fqdn_rev_ = fqdn_rev;
     }
     }
 
 
     /// @brief attempts to convert leases collection to a single lease
     /// @brief attempts to convert leases collection to a single lease
@@ -151,8 +185,9 @@ public:
         EXPECT_EQ(subnet_->getT1(), lease->t1_);
         EXPECT_EQ(subnet_->getT1(), lease->t1_);
         EXPECT_EQ(subnet_->getT2(), lease->t2_);
         EXPECT_EQ(subnet_->getT2(), lease->t2_);
         EXPECT_EQ(exp_pd_len, lease->prefixlen_);
         EXPECT_EQ(exp_pd_len, lease->prefixlen_);
-        EXPECT_TRUE(false == lease->fqdn_fwd_);
-        EXPECT_TRUE(false == lease->fqdn_rev_);
+        EXPECT_EQ(fqdn_fwd_, lease->fqdn_fwd_);
+        EXPECT_EQ(fqdn_rev_, lease->fqdn_rev_);
+        EXPECT_EQ(hostname_, lease->hostname_);
         EXPECT_TRUE(*lease->duid_ == *duid_);
         EXPECT_TRUE(*lease->duid_ == *duid_);
         /// @todo: check cltt
         /// @todo: check cltt
     }
     }
@@ -211,7 +246,7 @@ public:
         Lease6Ptr lease;
         Lease6Ptr lease;
         EXPECT_NO_THROW(lease = expectOneLease(engine->allocateLeases6(subnet_,
         EXPECT_NO_THROW(lease = expectOneLease(engine->allocateLeases6(subnet_,
                         duid_, iaid_, hint, type, false, false,
                         duid_, iaid_, hint, type, false, false,
-                        "", fake, CalloutHandlePtr())));
+                        "", fake, CalloutHandlePtr(), old_leases_)));
 
 
         // Check that we got a lease
         // Check that we got a lease
         EXPECT_TRUE(lease);
         EXPECT_TRUE(lease);
@@ -275,7 +310,7 @@ public:
         Lease6Ptr lease;
         Lease6Ptr lease;
         EXPECT_NO_THROW(lease = expectOneLease(engine->allocateLeases6(subnet_,
         EXPECT_NO_THROW(lease = expectOneLease(engine->allocateLeases6(subnet_,
                         duid_, iaid_, requested, type, false, false, "", false,
                         duid_, iaid_, requested, type, false, false, "", false,
-                        CalloutHandlePtr())));
+                        CalloutHandlePtr(), old_leases_)));
 
 
         // Check that we got a lease
         // Check that we got a lease
         ASSERT_TRUE(lease);
         ASSERT_TRUE(lease);
@@ -319,7 +354,7 @@ public:
         Lease6Ptr lease;
         Lease6Ptr lease;
         EXPECT_NO_THROW(lease = expectOneLease(engine->allocateLeases6(subnet_,
         EXPECT_NO_THROW(lease = expectOneLease(engine->allocateLeases6(subnet_,
                         duid_, iaid_, hint, type, false,
                         duid_, iaid_, hint, type, false,
-                        false, "", false, CalloutHandlePtr())));
+                        false, "", false, CalloutHandlePtr(), old_leases_)));
 
 
         // Check that we got a lease
         // Check that we got a lease
         ASSERT_TRUE(lease);
         ASSERT_TRUE(lease);
@@ -353,7 +388,14 @@ public:
     Subnet6Ptr subnet_;       ///< subnet6 (used in tests)
     Subnet6Ptr subnet_;       ///< subnet6 (used in tests)
     Pool6Ptr pool_;           ///< NA pool belonging to subnet_
     Pool6Ptr pool_;           ///< NA pool belonging to subnet_
     Pool6Ptr pd_pool_;        ///< PD pool belonging to subnet_
     Pool6Ptr pd_pool_;        ///< PD pool belonging to subnet_
+    std::string hostname_;    ///< Hostname
+    bool fqdn_fwd_;           ///< Perform forward update for a lease.
+    bool fqdn_rev_;           ///< Perform reverse update for a lease.
     LeaseMgrFactory factory_; ///< pointer to LeaseMgr factory
     LeaseMgrFactory factory_; ///< pointer to LeaseMgr factory
+
+    /// @brief Collection of leases being replaced by newly allocated or renewed
+    /// leases.
+    Lease6Collection old_leases_;
 };
 };
 
 
 /// @brief Used in Allocation Engine tests for IPv4
 /// @brief Used in Allocation Engine tests for IPv4
@@ -521,13 +563,13 @@ TEST_F(AllocEngine6Test, allocateAddress6Nulls) {
     Lease6Ptr lease;
     Lease6Ptr lease;
     EXPECT_NO_THROW(lease = expectOneLease(engine->allocateLeases6(
     EXPECT_NO_THROW(lease = expectOneLease(engine->allocateLeases6(
                     Subnet6Ptr(), duid_, iaid_, IOAddress("::"), Lease::TYPE_NA,
                     Subnet6Ptr(), duid_, iaid_, IOAddress("::"), Lease::TYPE_NA,
-                    false, false, "", false, CalloutHandlePtr())));
+                    false, false, "", false, CalloutHandlePtr(), old_leases_)));
     ASSERT_FALSE(lease);
     ASSERT_FALSE(lease);
 
 
     // Allocations without DUID are not allowed either
     // Allocations without DUID are not allowed either
     EXPECT_NO_THROW(lease = expectOneLease(engine->allocateLeases6(subnet_,
     EXPECT_NO_THROW(lease = expectOneLease(engine->allocateLeases6(subnet_,
                     DuidPtr(), iaid_, IOAddress("::"), Lease::TYPE_NA, false,
                     DuidPtr(), iaid_, IOAddress("::"), Lease::TYPE_NA, false,
-                    false, "", false, CalloutHandlePtr())));
+                    false, "", false, CalloutHandlePtr(), old_leases_)));
     ASSERT_FALSE(lease);
     ASSERT_FALSE(lease);
 }
 }
 
 
@@ -765,19 +807,18 @@ TEST_F(AllocEngine6Test, smallPool6) {
     ASSERT_TRUE(engine);
     ASSERT_TRUE(engine);
 
 
     IOAddress addr("2001:db8:1::ad");
     IOAddress addr("2001:db8:1::ad");
-    CfgMgr& cfg_mgr = CfgMgr::instance();
-    cfg_mgr.deleteSubnets6(); // Get rid of the default test configuration
 
 
-    // Create configuration similar to other tests, but with a single address pool
-    subnet_ = Subnet6Ptr(new Subnet6(IOAddress("2001:db8:1::"), 56, 1, 2, 3, 4));
-    pool_ = Pool6Ptr(new Pool6(Lease::TYPE_NA, addr, addr)); // just a single address
-    subnet_->addPool(pool_);
-    cfg_mgr.addSubnet6(subnet_);
+    // Create a subnet with a pool that has one address.
+    initSubnet(IOAddress("2001:db8:1::"), addr, addr);
+
+    // Initialize FQDN for a lease.
+    initFqdn("myhost.example.com", true, true);
 
 
     Lease6Ptr lease;
     Lease6Ptr lease;
     EXPECT_NO_THROW(lease = expectOneLease(engine->allocateLeases6(subnet_,
     EXPECT_NO_THROW(lease = expectOneLease(engine->allocateLeases6(subnet_,
-                    duid_, iaid_, IOAddress("::"), Lease::TYPE_NA, false, false,
-                    "", false, CalloutHandlePtr())));
+                    duid_, iaid_, IOAddress("::"), Lease::TYPE_NA, fqdn_fwd_,
+                    fqdn_rev_, hostname_, false, CalloutHandlePtr(),
+                    old_leases_)));
 
 
     // Check that we got that single lease
     // Check that we got that single lease
     ASSERT_TRUE(lease);
     ASSERT_TRUE(lease);
@@ -794,6 +835,11 @@ TEST_F(AllocEngine6Test, smallPool6) {
 
 
     // Now check that the lease in LeaseMgr has the same parameters
     // Now check that the lease in LeaseMgr has the same parameters
     detailCompareLease(lease, from_mgr);
     detailCompareLease(lease, from_mgr);
+
+    // This is a new lease allocation. The old lease corresponding to a newly
+    // allocated lease should be NULL.
+    ASSERT_EQ(1, old_leases_.size());
+    EXPECT_FALSE(old_leases_[0]);
 }
 }
 
 
 // This test checks if all addresses in a pool are currently used, the attempt
 // This test checks if all addresses in a pool are currently used, the attempt
@@ -826,8 +872,9 @@ TEST_F(AllocEngine6Test, outOfAddresses6) {
     Lease6Ptr lease2;
     Lease6Ptr lease2;
     EXPECT_NO_THROW(lease2 = expectOneLease(engine->allocateLeases6(subnet_,
     EXPECT_NO_THROW(lease2 = expectOneLease(engine->allocateLeases6(subnet_,
                     duid_, iaid_, IOAddress("::"), Lease::TYPE_NA, false, false,
                     duid_, iaid_, IOAddress("::"), Lease::TYPE_NA, false, false,
-                    "", false, CalloutHandlePtr())));
+                    "", false, CalloutHandlePtr(), old_leases_)));
     EXPECT_FALSE(lease2);
     EXPECT_FALSE(lease2);
+
 }
 }
 
 
 // This test checks if an expired lease can be reused in SOLICIT (fake allocation)
 // This test checks if an expired lease can be reused in SOLICIT (fake allocation)
@@ -837,14 +884,12 @@ TEST_F(AllocEngine6Test, solicitReuseExpiredLease6) {
     ASSERT_TRUE(engine);
     ASSERT_TRUE(engine);
 
 
     IOAddress addr("2001:db8:1::ad");
     IOAddress addr("2001:db8:1::ad");
-    CfgMgr& cfg_mgr = CfgMgr::instance();
-    cfg_mgr.deleteSubnets6(); // Get rid of the default test configuration
 
 
-    // Create configuration similar to other tests, but with a single address pool
-    subnet_ = Subnet6Ptr(new Subnet6(IOAddress("2001:db8:1::"), 56, 1, 2, 3, 4));
-    pool_ = Pool6Ptr(new Pool6(Lease::TYPE_NA, addr, addr)); // just a single address
-    subnet_->addPool(pool_);
-    cfg_mgr.addSubnet6(subnet_);
+    // Create one subnet with a pool holding one address.
+    initSubnet(IOAddress("2001:db8:1::"), addr, addr);
+
+    // Initialize FQDN data for the lease.
+    initFqdn("myhost.example.com", true, true);
 
 
     // Just a different duid
     // Just a different duid
     DuidPtr other_duid = DuidPtr(new DUID(vector<uint8_t>(12, 0xff)));
     DuidPtr other_duid = DuidPtr(new DUID(vector<uint8_t>(12, 0xff)));
@@ -860,8 +905,9 @@ TEST_F(AllocEngine6Test, solicitReuseExpiredLease6) {
 
 
     // CASE 1: Asking for any address
     // CASE 1: Asking for any address
     EXPECT_NO_THROW(lease = expectOneLease(engine->allocateLeases6(subnet_,
     EXPECT_NO_THROW(lease = expectOneLease(engine->allocateLeases6(subnet_,
-                    duid_, iaid_, IOAddress("::"), Lease::TYPE_NA, false, false, "", true,
-                    CalloutHandlePtr())));
+                    duid_, iaid_, IOAddress("::"), Lease::TYPE_NA, fqdn_fwd_,
+                    fqdn_rev_, hostname_, true, CalloutHandlePtr(),
+                    old_leases_)));
     // Check that we got that single lease
     // Check that we got that single lease
     ASSERT_TRUE(lease);
     ASSERT_TRUE(lease);
     EXPECT_EQ(addr, lease->addr_);
     EXPECT_EQ(addr, lease->addr_);
@@ -872,7 +918,7 @@ TEST_F(AllocEngine6Test, solicitReuseExpiredLease6) {
     // CASE 2: Asking specifically for this address
     // CASE 2: Asking specifically for this address
     EXPECT_NO_THROW(lease = expectOneLease(engine->allocateLeases6(subnet_,
     EXPECT_NO_THROW(lease = expectOneLease(engine->allocateLeases6(subnet_,
                     duid_, iaid_, addr, Lease::TYPE_NA, false, false, "",
                     duid_, iaid_, addr, Lease::TYPE_NA, false, false, "",
-                    true, CalloutHandlePtr())));
+                    true, CalloutHandlePtr(), old_leases_)));
 
 
     // Check that we got that single lease
     // Check that we got that single lease
     ASSERT_TRUE(lease);
     ASSERT_TRUE(lease);
@@ -903,16 +949,32 @@ TEST_F(AllocEngine6Test, requestReuseExpiredLease6) {
                                501, 502, 503, 504, other_subnetid, 0));
                                501, 502, 503, 504, other_subnetid, 0));
     lease->cltt_ = time(NULL) - 500; // Allocated 500 seconds ago
     lease->cltt_ = time(NULL) - 500; // Allocated 500 seconds ago
     lease->valid_lft_ = 495; // Lease was valid for 495 seconds
     lease->valid_lft_ = 495; // Lease was valid for 495 seconds
+    lease->fqdn_fwd_ = true;
+    lease->fqdn_rev_ = true;
+    lease->hostname_ = "myhost.example.com.";
     ASSERT_TRUE(LeaseMgrFactory::instance().addLease(lease));
     ASSERT_TRUE(LeaseMgrFactory::instance().addLease(lease));
 
 
     // A client comes along, asking specifically for this address
     // A client comes along, asking specifically for this address
     EXPECT_NO_THROW(lease = expectOneLease(engine->allocateLeases6(subnet_,
     EXPECT_NO_THROW(lease = expectOneLease(engine->allocateLeases6(subnet_,
                     duid_, iaid_, addr, Lease::TYPE_NA, false, false, "",
                     duid_, iaid_, addr, Lease::TYPE_NA, false, false, "",
-                    false, CalloutHandlePtr())));
+                    false, CalloutHandlePtr(), old_leases_)));
 
 
     // Check that he got that single lease
     // Check that he got that single lease
     ASSERT_TRUE(lease);
     ASSERT_TRUE(lease);
     EXPECT_EQ(addr, lease->addr_);
     EXPECT_EQ(addr, lease->addr_);
+    // This reactivated lease should have updated FQDN data.
+    EXPECT_TRUE(lease->hostname_.empty());
+    EXPECT_FALSE(lease->fqdn_fwd_);
+    EXPECT_FALSE(lease->fqdn_rev_);
+
+    // Check that the old lease has been returned.
+    Lease6Ptr old_lease = expectOneLease(old_leases_);
+    // It should at least have the same IPv6 address.
+    EXPECT_EQ(lease->addr_, old_lease->addr_);
+    // Check that it carries not updated FQDN data.
+    EXPECT_EQ("myhost.example.com.", old_lease->hostname_);
+    EXPECT_TRUE(old_lease->fqdn_fwd_);
+    EXPECT_TRUE(old_lease->fqdn_rev_);
 
 
     // Check that the lease is indeed updated in LeaseMgr
     // Check that the lease is indeed updated in LeaseMgr
     Lease6Ptr from_mgr = LeaseMgrFactory::instance().getLease6(Lease::TYPE_NA,
     Lease6Ptr from_mgr = LeaseMgrFactory::instance().getLease6(Lease::TYPE_NA,
@@ -1623,7 +1685,7 @@ TEST_F(HookAllocEngine6Test, lease6_select) {
     Lease6Ptr lease;
     Lease6Ptr lease;
     EXPECT_NO_THROW(lease = expectOneLease(engine->allocateLeases6(subnet_,
     EXPECT_NO_THROW(lease = expectOneLease(engine->allocateLeases6(subnet_,
                     duid_, iaid_, IOAddress("::"), Lease::TYPE_NA, false, false,
                     duid_, iaid_, IOAddress("::"), Lease::TYPE_NA, false, false,
-                    "", false, callout_handle)));
+                    "", false, callout_handle, old_leases_)));
     // Check that we got a lease
     // Check that we got a lease
     ASSERT_TRUE(lease);
     ASSERT_TRUE(lease);
 
 
@@ -1694,7 +1756,7 @@ TEST_F(HookAllocEngine6Test, change_lease6_select) {
     Lease6Ptr lease;
     Lease6Ptr lease;
     EXPECT_NO_THROW(lease = expectOneLease(engine->allocateLeases6(subnet_,
     EXPECT_NO_THROW(lease = expectOneLease(engine->allocateLeases6(subnet_,
                     duid_, iaid_, IOAddress("::"), Lease::TYPE_NA, false, false,
                     duid_, iaid_, IOAddress("::"), Lease::TYPE_NA, false, false,
-                    "", false, callout_handle)));
+                    "", false, callout_handle, old_leases_)));
     // Check that we got a lease
     // Check that we got a lease
     ASSERT_TRUE(lease);
     ASSERT_TRUE(lease);
 
 

+ 1 - 622
src/lib/dhcpsrv/tests/lease_mgr_unittest.cc

@@ -1,4 +1,4 @@
-// Copyright (C) 2012-2013 Internet Systems Consortium, Inc. ("ISC")
+// Copyright (C) 2012-2014 Internet Systems Consortium, Inc. ("ISC")
 //
 //
 // Permission to use, copy, modify, and/or distribute this software for any
 // Permission to use, copy, modify, and/or distribute this software for any
 // purpose with or without fee is hereby granted, provided that the above
 // purpose with or without fee is hereby granted, provided that the above
@@ -251,17 +251,6 @@ public:
 
 
 namespace {
 namespace {
 
 
-/// Hardware address used by different tests.
-const uint8_t HWADDR[] = {0x08, 0x00, 0x2b, 0x02, 0x3f, 0x4e};
-/// Client id used by different tests.
-const uint8_t CLIENTID[] = {0x17, 0x34, 0xe2, 0xff, 0x09, 0x92, 0x54};
-/// Valid lifetime value used by different tests.
-const uint32_t VALID_LIFETIME = 500;
-/// Subnet ID used by different tests.
-const uint32_t SUBNET_ID = 42;
-/// IAID value used by different tests.
-const uint32_t IAID = 7;
-
 /// @brief getParameter test
 /// @brief getParameter test
 ///
 ///
 /// This test checks if the LeaseMgr can be instantiated and that it
 /// This test checks if the LeaseMgr can be instantiated and that it
@@ -320,616 +309,6 @@ TEST_F(LeaseMgrTest, getLease6) {
 // are purely virtual, so we would only call ConcreteLeaseMgr methods.
 // are purely virtual, so we would only call ConcreteLeaseMgr methods.
 // Those methods are just stubs that do not return anything.
 // Those methods are just stubs that do not return anything.
 
 
-/// @brief Lease4 Constructor Test
-///
-/// Lease4 is also defined in lease_mgr.h, so is tested in this file as well.
-// This test checks if the Lease4 structure can be instantiated correctly
-TEST(Lease4, constructor) {
-
-    // Random values for the tests
-    const uint8_t HWADDR[] = {0x08, 0x00, 0x2b, 0x02, 0x3f, 0x4e};
-    std::vector<uint8_t> hwaddr(HWADDR, HWADDR + sizeof(HWADDR));
-
-    const uint8_t CLIENTID[] = {0x17, 0x34, 0xe2, 0xff, 0x09, 0x92, 0x54};
-    std::vector<uint8_t> clientid_vec(CLIENTID, CLIENTID + sizeof(CLIENTID));
-    ClientId clientid(clientid_vec);
-
-    // ...and a time
-    const time_t current_time = time(NULL);
-
-    // Other random constants.
-    const uint32_t SUBNET_ID = 42;
-    const uint32_t VALID_LIFETIME = 500;
-
-    // We want to check that various addresses work, so let's iterate over
-    // these.
-    const uint32_t ADDRESS[] = {
-        0x00000000, 0x01020304, 0x7fffffff, 0x80000000, 0x80000001, 0xffffffff
-    };
-
-    for (int i = 0; i < sizeof(ADDRESS) / sizeof(ADDRESS[0]); ++i) {
-
-        // Create the lease
-        Lease4 lease(ADDRESS[i], HWADDR, sizeof(HWADDR),
-                     CLIENTID, sizeof(CLIENTID), VALID_LIFETIME, 0, 0,
-                     current_time, SUBNET_ID, true, true,
-                     "hostname.example.com.");
-
-        EXPECT_EQ(ADDRESS[i], static_cast<uint32_t>(lease.addr_));
-        EXPECT_EQ(0, lease.ext_);
-        EXPECT_TRUE(hwaddr == lease.hwaddr_);
-        EXPECT_TRUE(clientid == *lease.client_id_);
-        EXPECT_EQ(0, lease.t1_);
-        EXPECT_EQ(0, lease.t2_);
-        EXPECT_EQ(VALID_LIFETIME, lease.valid_lft_);
-        EXPECT_EQ(current_time, lease.cltt_);
-        EXPECT_EQ(SUBNET_ID, lease.subnet_id_);
-        EXPECT_FALSE(lease.fixed_);
-        EXPECT_EQ("hostname.example.com.", lease.hostname_);
-        EXPECT_TRUE(lease.fqdn_fwd_);
-        EXPECT_TRUE(lease.fqdn_rev_);
-        EXPECT_TRUE(lease.comments_.empty());
-    }
-}
-
-// This test verfies that copy constructor copies Lease4 fields correctly.
-TEST(Lease4, copyConstructor) {
-
-    // Random values for the tests
-    const uint8_t HWADDR[] = {0x08, 0x00, 0x2b, 0x02, 0x3f, 0x4e};
-    std::vector<uint8_t> hwaddr(HWADDR, HWADDR + sizeof(HWADDR));
-
-    const uint8_t CLIENTID[] = {0x17, 0x34, 0xe2, 0xff, 0x09, 0x92, 0x54};
-    std::vector<uint8_t> clientid_vec(CLIENTID, CLIENTID + sizeof(CLIENTID));
-    ClientId clientid(clientid_vec);
-
-    // ...and a time
-    const time_t current_time = time(NULL);
-
-    // Other random constants.
-    const uint32_t SUBNET_ID = 42;
-    const uint32_t VALID_LIFETIME = 500;
-
-    // Create the lease
-    Lease4 lease(0xffffffff, HWADDR, sizeof(HWADDR),
-                 CLIENTID, sizeof(CLIENTID), VALID_LIFETIME, 0, 0, current_time,
-                 SUBNET_ID);
-
-    // Use copy constructor to copy the lease.
-    Lease4 copied_lease(lease);
-
-    // Both leases should be now equal. When doing this check we assume that
-    // the equality operator works correctly.
-    EXPECT_TRUE(lease == copied_lease);
-    // Client IDs are equal, but they should be in two distinct pointers.
-    EXPECT_FALSE(lease.client_id_ == copied_lease.client_id_);
-}
-
-// This test verfies that the assignment operator copies all Lease4 fields
-// correctly.
-TEST(Lease4, operatorAssign) {
-
-    // Random values for the tests
-    const uint8_t HWADDR[] = {0x08, 0x00, 0x2b, 0x02, 0x3f, 0x4e};
-    std::vector<uint8_t> hwaddr(HWADDR, HWADDR + sizeof(HWADDR));
-
-    const uint8_t CLIENTID[] = {0x17, 0x34, 0xe2, 0xff, 0x09, 0x92, 0x54};
-    std::vector<uint8_t> clientid_vec(CLIENTID, CLIENTID + sizeof(CLIENTID));
-    ClientId clientid(clientid_vec);
-
-    // ...and a time
-    const time_t current_time = time(NULL);
-
-    // Other random constants.
-    const uint32_t SUBNET_ID = 42;
-    const uint32_t VALID_LIFETIME = 500;
-
-    // Create the lease
-    Lease4 lease(0xffffffff, HWADDR, sizeof(HWADDR),
-                 CLIENTID, sizeof(CLIENTID), VALID_LIFETIME, 0, 0, current_time,
-                 SUBNET_ID);
-
-    // Use assignment operator to assign the lease.
-    Lease4 copied_lease = lease;
-
-    // Both leases should be now equal. When doing this check we assume that
-    // the equality operator works correctly.
-    EXPECT_TRUE(lease == copied_lease);
-    // Client IDs are equal, but they should be in two distinct pointers.
-    EXPECT_FALSE(lease.client_id_ == copied_lease.client_id_);
-}
-
-// This test verifies that the matches() returns true if two leases differ
-// by values other than address, HW address, Client ID and ext_.
-TEST(Lease4, matches) {
-    // Create two leases which share the same address, HW address, client id
-    // and ext_ value.
-    const time_t current_time = time(NULL);
-    Lease4 lease1(IOAddress("192.0.2.3"), HWADDR, sizeof(HWADDR), CLIENTID,
-                  sizeof(CLIENTID), VALID_LIFETIME, current_time, 0, 0,
-                  SUBNET_ID);
-    lease1.hostname_ = "lease1.example.com.";
-    lease1.fqdn_fwd_ = true;
-    lease1.fqdn_rev_ = true;
-    Lease4 lease2(IOAddress("192.0.2.3"), HWADDR, sizeof(HWADDR), CLIENTID,
-                  sizeof(CLIENTID), VALID_LIFETIME + 10, current_time - 10,
-                  100, 200, SUBNET_ID);
-    lease2.hostname_ = "lease2.example.com.";
-    lease2.fqdn_fwd_ = false;
-    lease2.fqdn_rev_ = true;
-
-    // Leases should match.
-    EXPECT_TRUE(lease1.matches(lease2));
-    EXPECT_TRUE(lease2.matches(lease1));
-
-    // Change address, leases should not match anymore.
-    lease1.addr_ = IOAddress("192.0.2.4");
-    EXPECT_FALSE(lease1.matches(lease2));
-    lease1.addr_ = lease2.addr_;
-
-    // Change HW address, leases should not match.
-    lease1.hwaddr_[1] += 1;
-    EXPECT_FALSE(lease1.matches(lease2));
-    lease1.hwaddr_ = lease2.hwaddr_;
-
-    // Chanage client id, leases should not match.
-    std::vector<uint8_t> client_id = lease1.client_id_->getClientId();
-    client_id[1] += 1;
-    lease1.client_id_.reset(new ClientId(client_id));
-    EXPECT_FALSE(lease1.matches(lease2));
-    lease1.client_id_ = lease2.client_id_;
-
-    // Change ext_, leases should not match.
-    lease1.ext_ += 1;
-    EXPECT_FALSE(lease1.matches(lease2));
-    lease1.ext_ = lease2.ext_;
-}
-
-/// @brief Lease4 Equality Test
-///
-/// Checks that the operator==() correctly compares two leases for equality.
-/// As operator!=() is also defined for this class, every check on operator==()
-/// is followed by the reverse check on operator!=().
-TEST(Lease4, operatorEquals) {
-
-    // Random values for the tests
-    const uint32_t ADDRESS = 0x01020304;
-    const uint8_t HWADDR[] = {0x08, 0x00, 0x2b, 0x02, 0x3f, 0x4e};
-    std::vector<uint8_t> hwaddr(HWADDR, HWADDR + sizeof(HWADDR));
-    const uint8_t CLIENTID[] = {0x17, 0x34, 0xe2, 0xff, 0x09, 0x92, 0x54};
-    std::vector<uint8_t> clientid_vec(CLIENTID, CLIENTID + sizeof(CLIENTID));
-    ClientId clientid(clientid_vec);
-    const time_t current_time = time(NULL);
-    const uint32_t SUBNET_ID = 42;
-    const uint32_t VALID_LIFETIME = 500;
-
-    // Check when the leases are equal.
-    Lease4 lease1(ADDRESS, HWADDR, sizeof(HWADDR),
-                  CLIENTID, sizeof(CLIENTID), VALID_LIFETIME, current_time, 0,
-                  0, SUBNET_ID);
-    Lease4 lease2(ADDRESS, HWADDR, sizeof(HWADDR),
-                  CLIENTID, sizeof(CLIENTID), VALID_LIFETIME, current_time, 0, 0,
-                  SUBNET_ID);
-    EXPECT_TRUE(lease1 == lease2);
-    EXPECT_FALSE(lease1 != lease2);
-
-    // Now vary individual fields in a lease and check that the leases compare
-    // not equal in every case.
-    lease1.addr_ = IOAddress(ADDRESS + 1);
-    EXPECT_FALSE(lease1 == lease2);
-    EXPECT_TRUE(lease1 != lease2);
-    lease1.addr_ = lease2.addr_;
-    EXPECT_TRUE(lease1 == lease2);  // Check that the reversion has made the
-    EXPECT_FALSE(lease1 != lease2); // ... leases equal
-
-    ++lease1.ext_;
-    EXPECT_FALSE(lease1 == lease2);
-    EXPECT_TRUE(lease1 != lease2);
-    lease1.ext_ = lease2.ext_;
-    EXPECT_TRUE(lease1 == lease2);  // Check that the reversion has made the
-    EXPECT_FALSE(lease1 != lease2); // ... leases equal
-
-    ++lease1.hwaddr_[0];
-    EXPECT_FALSE(lease1 == lease2);
-    EXPECT_TRUE(lease1 != lease2);
-    lease1.hwaddr_ = lease2.hwaddr_;
-    EXPECT_TRUE(lease1 == lease2);  // Check that the reversion has made the
-    EXPECT_FALSE(lease1 != lease2); // ... leases equal
-
-    ++clientid_vec[0];
-    lease1.client_id_.reset(new ClientId(clientid_vec));
-    EXPECT_FALSE(lease1 == lease2);
-    EXPECT_TRUE(lease1 != lease2);
-    --clientid_vec[0];
-    lease1.client_id_.reset(new ClientId(clientid_vec));
-    EXPECT_TRUE(lease1 == lease2);  // Check that the reversion has made the
-    EXPECT_FALSE(lease1 != lease2); // ... leases equal
-
-    ++lease1.t1_;
-    EXPECT_FALSE(lease1 == lease2);
-    EXPECT_TRUE(lease1 != lease2);
-    lease1.t1_ = lease2.t1_;
-    EXPECT_TRUE(lease1 == lease2);  // Check that the reversion has made the
-    EXPECT_FALSE(lease1 != lease2); // ... leases equal
-
-    ++lease1.t2_;
-    EXPECT_FALSE(lease1 == lease2);
-    EXPECT_TRUE(lease1 != lease2);
-    lease1.t2_ = lease2.t2_;
-    EXPECT_TRUE(lease1 == lease2);  // Check that the reversion has made the
-    EXPECT_FALSE(lease1 != lease2); // ... leases equal
-
-    ++lease1.valid_lft_;
-    EXPECT_FALSE(lease1 == lease2);
-    EXPECT_TRUE(lease1 != lease2);
-    lease1.valid_lft_ = lease2.valid_lft_;
-    EXPECT_TRUE(lease1 == lease2);  // Check that the reversion has made the
-    EXPECT_FALSE(lease1 != lease2); // ... leases equal
-
-    ++lease1.cltt_;
-    EXPECT_FALSE(lease1 == lease2);
-    EXPECT_TRUE(lease1 != lease2);
-    lease1.cltt_ = lease2.cltt_;
-    EXPECT_TRUE(lease1 == lease2);  // Check that the reversion has made the
-    EXPECT_FALSE(lease1 != lease2); // ... leases equal
-
-    ++lease1.subnet_id_;
-    EXPECT_FALSE(lease1 == lease2);
-    EXPECT_TRUE(lease1 != lease2);
-    lease1.subnet_id_ = lease2.subnet_id_;
-    EXPECT_TRUE(lease1 == lease2);  // Check that the reversion has made the
-    EXPECT_FALSE(lease1 != lease2); // ... leases equal
-
-    lease1.fixed_ = !lease1.fixed_;
-    EXPECT_FALSE(lease1 == lease2);
-    EXPECT_TRUE(lease1 != lease2);
-    lease1.fixed_ = lease2.fixed_;
-    EXPECT_TRUE(lease1 == lease2);  // Check that the reversion has made the
-    EXPECT_FALSE(lease1 != lease2); // ... leases equal
-
-    lease1.hostname_ += string("Something random");
-    EXPECT_FALSE(lease1 == lease2);
-    EXPECT_TRUE(lease1 != lease2);
-    lease1.hostname_ = lease2.hostname_;
-    EXPECT_TRUE(lease1 == lease2);  // Check that the reversion has made the
-    EXPECT_FALSE(lease1 != lease2); // ... leases equal
-
-    lease1.fqdn_fwd_ = !lease1.fqdn_fwd_;
-    EXPECT_FALSE(lease1 == lease2);
-    EXPECT_TRUE(lease1 != lease2);
-    lease1.fqdn_fwd_ = lease2.fqdn_fwd_;
-    EXPECT_TRUE(lease1 == lease2);  // Check that the reversion has made the
-    EXPECT_FALSE(lease1 != lease2); // ... leases equal
-
-    lease1.fqdn_rev_ = !lease1.fqdn_rev_;
-    EXPECT_FALSE(lease1 == lease2);
-    EXPECT_TRUE(lease1 != lease2);
-    lease1.fqdn_rev_ = lease2.fqdn_rev_;
-    EXPECT_TRUE(lease1 == lease2);  // Check that the reversion has made the
-    EXPECT_FALSE(lease1 != lease2); // ... leases equal
-
-    lease1.comments_ += string("Something random");
-    EXPECT_FALSE(lease1 == lease2);
-    EXPECT_TRUE(lease1 != lease2);
-    lease1.comments_ = lease2.comments_;
-    EXPECT_TRUE(lease1 == lease2);  // Check that the reversion has made the
-    EXPECT_FALSE(lease1 != lease2); // ... leases equal
-}
-
-
-
-// Lease6 is also defined in lease_mgr.h, so is tested in this file as well.
-// This test checks if the Lease6 structure can be instantiated correctly
-TEST(Lease6, Lease6ConstructorDefault) {
-
-    // check a variety of addresses with different bits set.
-    const char* ADDRESS[] = {
-        "::", "::1", "2001:db8:1::456",
-        "7fff:ffff:ffff:ffff:ffff:ffff:ffff:ffff",
-        "8000::", "8000::1",
-        "ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff"
-    };
-
-    // Other values
-    uint8_t llt[] = {0, 1, 2, 3, 4, 5, 6, 0xa, 0xb, 0xc, 0xd, 0xe, 0xf};
-    DuidPtr duid(new DUID(llt, sizeof(llt)));
-    uint32_t iaid = 7;      // Just a number
-    SubnetID subnet_id = 8; // Just another number
-
-    for (int i = 0; i < sizeof(ADDRESS) / sizeof(ADDRESS[0]); ++i) {
-        IOAddress addr(ADDRESS[i]);
-        Lease6Ptr lease(new Lease6(Lease::TYPE_NA, addr,
-                               duid, iaid, 100, 200, 50, 80,
-                               subnet_id));
-
-        EXPECT_TRUE(lease->addr_ == addr);
-        EXPECT_TRUE(*lease->duid_ == *duid);
-        EXPECT_TRUE(lease->iaid_ == iaid);
-        EXPECT_TRUE(lease->subnet_id_ == subnet_id);
-        EXPECT_TRUE(lease->type_ == Lease::TYPE_NA);
-        EXPECT_TRUE(lease->preferred_lft_ == 100);
-        EXPECT_TRUE(lease->valid_lft_ == 200);
-        EXPECT_TRUE(lease->t1_ == 50);
-        EXPECT_TRUE(lease->t2_ == 80);
-        EXPECT_FALSE(lease->fqdn_fwd_);
-        EXPECT_FALSE(lease->fqdn_rev_);
-        EXPECT_TRUE(lease->hostname_.empty());
-
-    }
-
-    // Lease6 must be instantiated with a DUID, not with NULL pointer
-    IOAddress addr(ADDRESS[0]);
-    Lease6Ptr lease2;
-    EXPECT_THROW(lease2.reset(new Lease6(Lease::TYPE_NA, addr,
-                                         DuidPtr(), iaid, 100, 200, 50, 80,
-                                         subnet_id)), InvalidOperation);
-}
-
-// This test verifies that the Lease6 constructor which accepts FQDN data,
-// sets the data correctly for the lease.
-TEST(Lease6, Lease6ConstructorWithFQDN) {
-
-    // check a variety of addresses with different bits set.
-    const char* ADDRESS[] = {
-        "::", "::1", "2001:db8:1::456",
-        "7fff:ffff:ffff:ffff:ffff:ffff:ffff:ffff",
-        "8000::", "8000::1",
-        "ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff"
-    };
-
-    // Other values
-    uint8_t llt[] = {0, 1, 2, 3, 4, 5, 6, 0xa, 0xb, 0xc, 0xd, 0xe, 0xf};
-    DuidPtr duid(new DUID(llt, sizeof(llt)));
-    uint32_t iaid = 7;      // Just a number
-    SubnetID subnet_id = 8; // Just another number
-
-    for (int i = 0; i < sizeof(ADDRESS) / sizeof(ADDRESS[0]); ++i) {
-        IOAddress addr(ADDRESS[i]);
-        Lease6Ptr lease(new Lease6(Lease::TYPE_NA, addr,
-                               duid, iaid, 100, 200, 50, 80, subnet_id,
-                                   true, true, "host.example.com."));
-
-        EXPECT_TRUE(lease->addr_ == addr);
-        EXPECT_TRUE(*lease->duid_ == *duid);
-        EXPECT_TRUE(lease->iaid_ == iaid);
-        EXPECT_TRUE(lease->subnet_id_ == subnet_id);
-        EXPECT_TRUE(lease->type_ == Lease::TYPE_NA);
-        EXPECT_TRUE(lease->preferred_lft_ == 100);
-        EXPECT_TRUE(lease->valid_lft_ == 200);
-        EXPECT_TRUE(lease->t1_ == 50);
-        EXPECT_TRUE(lease->t2_ == 80);
-        EXPECT_TRUE(lease->fqdn_fwd_);
-        EXPECT_TRUE(lease->fqdn_rev_);
-        EXPECT_EQ("host.example.com.", lease->hostname_);
-    }
 
 
-    // Lease6 must be instantiated with a DUID, not with NULL pointer
-    IOAddress addr(ADDRESS[0]);
-    Lease6Ptr lease2;
-    EXPECT_THROW(lease2.reset(new Lease6(Lease::TYPE_NA, addr,
-                                         DuidPtr(), iaid, 100, 200, 50, 80,
-                                         subnet_id)), InvalidOperation);
-}
-
-// This test verifies that the matches() function returns true if two leases
-// differ by values other than address, type, prefix length, IAID and DUID.
-TEST(Lease6, matches) {
-
-    // Create two matching leases.
-    uint8_t llt[] = {0, 1, 2, 3, 4, 5, 6, 0xa, 0xb, 0xc, 0xd, 0xe, 0xf};
-    DuidPtr duid(new DUID(llt, sizeof(llt)));
-
-    Lease6 lease1(Lease6::TYPE_NA, IOAddress("2001:db8:1::1"), duid,
-                  IAID, 100, 200, 50, 80,
-                  SUBNET_ID);
-    lease1.hostname_ = "lease1.example.com.";
-    lease1.fqdn_fwd_ = true;
-    lease1.fqdn_rev_ = true;
-    Lease6 lease2(Lease6::TYPE_NA, IOAddress("2001:db8:1::1"), duid,
-                  IAID, 200, 300, 90, 70,
-                  SUBNET_ID);
-    lease2.hostname_ = "lease1.example.com.";
-    lease2.fqdn_fwd_ = false;
-    lease2.fqdn_rev_ = true;
-
-    EXPECT_TRUE(lease1.matches(lease2));
-
-    // Modify each value used to match both leases, and make sure that
-    // leases don't match.
-
-    // Modify address.
-    lease1.addr_ = IOAddress("2001:db8:1::2");
-    EXPECT_FALSE(lease1.matches(lease2));
-    lease1.addr_ = lease2.addr_;
-
-    // Modify lease type.
-    lease1.type_ = Lease6::TYPE_TA;
-    EXPECT_FALSE(lease1.matches(lease2));
-    lease1.type_ = lease2.type_;
-
-    // Modify prefix length.
-    lease1.prefixlen_ += 1;
-    EXPECT_FALSE(lease1.matches(lease2));
-    lease1.prefixlen_ = lease2.prefixlen_;
-
-    // Modify IAID.
-    lease1.iaid_ += 1;
-    EXPECT_FALSE(lease1.matches(lease2));
-    lease1.iaid_ = lease2.iaid_;
-
-    // Modify DUID.
-    llt[1] += 1;
-    duid.reset(new DUID(llt, sizeof(llt)));
-    lease1.duid_ = duid;
-    EXPECT_FALSE(lease1.matches(lease2));
-    lease1.duid_ = lease2.duid_;
-}
-
-/// @brief Lease6 Equality Test
-///
-/// Checks that the operator==() correctly compares two leases for equality.
-/// As operator!=() is also defined for this class, every check on operator==()
-/// is followed by the reverse check on operator!=().
-TEST(Lease6, OperatorEquals) {
-
-    // check a variety of addresses with different bits set.
-    const IOAddress addr("2001:db8:1::456");
-    uint8_t duid_array[] = {0, 1, 2, 3, 4, 5, 6, 0xa, 0xb, 0xc, 0xd, 0xe, 0xf};
-    DuidPtr duid(new DUID(duid_array, sizeof(duid_array)));
-    uint32_t iaid = 7; // just a number
-    SubnetID subnet_id = 8; // just another number
-
-    // Check for equality.
-    Lease6 lease1(Lease::TYPE_NA, addr, duid, iaid, 100, 200, 50, 80,
-                               subnet_id);
-    Lease6 lease2(Lease::TYPE_NA, addr, duid, iaid, 100, 200, 50, 80,
-                               subnet_id);
-
-    // cltt_ constructs with time(NULL), make sure they are always equal
-    lease1.cltt_ = lease2.cltt_;
-
-    EXPECT_TRUE(lease1 == lease2);
-    EXPECT_FALSE(lease1 != lease2);
-
-    // Go through and alter all the fields one by one
-
-    lease1.addr_ = IOAddress("::1");
-    EXPECT_FALSE(lease1 == lease2);
-    EXPECT_TRUE(lease1 != lease2);
-    lease1.addr_ = lease2.addr_;
-    EXPECT_TRUE(lease1 == lease2);  // Check that the reversion has made the
-    EXPECT_FALSE(lease1 != lease2); // ... leases equal
-
-    lease1.type_ = Lease::TYPE_PD;
-    EXPECT_FALSE(lease1 == lease2);
-    EXPECT_TRUE(lease1 != lease2);
-    lease1.type_ = lease2.type_;
-    EXPECT_TRUE(lease1 == lease2);  // Check that the reversion has made the
-    EXPECT_FALSE(lease1 != lease2); // ... leases equal
-
-    ++lease1.prefixlen_;
-    EXPECT_FALSE(lease1 == lease2);
-    EXPECT_TRUE(lease1 != lease2);
-    lease1.prefixlen_ = lease2.prefixlen_;
-    EXPECT_TRUE(lease1 == lease2);  // Check that the reversion has made the
-    EXPECT_FALSE(lease1 != lease2); // ... leases equal
-
-    ++lease1.iaid_;
-    EXPECT_FALSE(lease1 == lease2);
-    EXPECT_TRUE(lease1 != lease2);
-    lease1.iaid_ = lease2.iaid_;
-    EXPECT_TRUE(lease1 == lease2);  // Check that the reversion has made the
-    EXPECT_FALSE(lease1 != lease2); // ... leases equal
-
-    ++duid_array[0];
-    lease1.duid_.reset(new DUID(duid_array, sizeof(duid_array)));
-    EXPECT_FALSE(lease1 == lease2);
-    EXPECT_TRUE(lease1 != lease2);
-    --duid_array[0];
-    lease1.duid_.reset(new DUID(duid_array, sizeof(duid_array)));
-    EXPECT_TRUE(lease1 == lease2);  // Check that the reversion has made the
-    EXPECT_FALSE(lease1 != lease2); // ... leases equal
-
-    ++lease1.preferred_lft_;
-    EXPECT_FALSE(lease1 == lease2);
-    EXPECT_TRUE(lease1 != lease2);
-    lease1.preferred_lft_ = lease2.preferred_lft_;
-    EXPECT_TRUE(lease1 == lease2);  // Check that the reversion has made the
-    EXPECT_FALSE(lease1 != lease2); // ... leases equal
-
-    ++lease1.valid_lft_;
-    EXPECT_FALSE(lease1 == lease2);
-    EXPECT_TRUE(lease1 != lease2);
-    lease1.valid_lft_ = lease2.valid_lft_;
-    EXPECT_TRUE(lease1 == lease2);  // Check that the reversion has made the
-    EXPECT_FALSE(lease1 != lease2); // ... leases equal
-
-    ++lease1.t1_;
-    EXPECT_FALSE(lease1 == lease2);
-    EXPECT_TRUE(lease1 != lease2);
-    lease1.t1_ = lease2.t1_;
-    EXPECT_TRUE(lease1 == lease2);  // Check that the reversion has made the
-    EXPECT_FALSE(lease1 != lease2); // ... leases equal
-
-    ++lease1.t2_;
-    EXPECT_FALSE(lease1 == lease2);
-    EXPECT_TRUE(lease1 != lease2);
-    lease1.t2_ = lease2.t2_;
-    EXPECT_TRUE(lease1 == lease2);  // Check that the reversion has made the
-    EXPECT_FALSE(lease1 != lease2); // ... leases equal
-
-    ++lease1.cltt_;
-    EXPECT_FALSE(lease1 == lease2);
-    EXPECT_TRUE(lease1 != lease2);
-    lease1.cltt_ = lease2.cltt_;
-    EXPECT_TRUE(lease1 == lease2);  // Check that the reversion has made the
-    EXPECT_FALSE(lease1 != lease2); // ... leases equal
-
-    ++lease1.subnet_id_;
-    EXPECT_FALSE(lease1 == lease2);
-    EXPECT_TRUE(lease1 != lease2);
-    lease1.subnet_id_ = lease2.subnet_id_;
-    EXPECT_TRUE(lease1 == lease2);  // Check that the reversion has made the
-    EXPECT_FALSE(lease1 != lease2); // ... leases equal
-
-    lease1.fixed_ = !lease1.fixed_;
-    EXPECT_FALSE(lease1 == lease2);
-    EXPECT_TRUE(lease1 != lease2);
-    lease1.fixed_ = lease2.fixed_;
-    EXPECT_TRUE(lease1 == lease2);  // Check that the reversion has made the
-    EXPECT_FALSE(lease1 != lease2); // ... leases equal
-
-    lease1.hostname_ += string("Something random");
-    EXPECT_FALSE(lease1 == lease2);
-    EXPECT_TRUE(lease1 != lease2);
-    lease1.hostname_ = lease2.hostname_;
-    EXPECT_TRUE(lease1 == lease2);  // Check that the reversion has made the
-    EXPECT_FALSE(lease1 != lease2); // ... leases equal
-
-    lease1.fqdn_fwd_ = !lease1.fqdn_fwd_;
-    EXPECT_FALSE(lease1 == lease2);
-    EXPECT_TRUE(lease1 != lease2);
-    lease1.fqdn_fwd_ = lease2.fqdn_fwd_;
-    EXPECT_TRUE(lease1 == lease2);  // Check that the reversion has made the
-    EXPECT_FALSE(lease1 != lease2); // ... leases equal
-
-    lease1.fqdn_rev_ = !lease1.fqdn_rev_;
-    EXPECT_FALSE(lease1 == lease2);
-    EXPECT_TRUE(lease1 != lease2);
-    lease1.fqdn_rev_ = lease2.fqdn_rev_;
-    EXPECT_TRUE(lease1 == lease2);  // Check that the reversion has made the
-    EXPECT_FALSE(lease1 != lease2); // ... leases equal
-
-    lease1.comments_ += string("Something random");
-    EXPECT_FALSE(lease1 == lease2);
-    EXPECT_TRUE(lease1 != lease2);
-    lease1.comments_ = lease2.comments_;
-    EXPECT_TRUE(lease1 == lease2);  // Check that the reversion has made the
-    EXPECT_FALSE(lease1 != lease2); // ... leases equal
-}
-
-// Checks if lease expiration is calculated properly
-TEST(Lease6, Lease6Expired) {
-    const IOAddress addr("2001:db8:1::456");
-    const uint8_t duid_array[] = {0, 1, 2, 3, 4, 5, 6, 0xa, 0xb, 0xc, 0xd, 0xe, 0xf};
-    const DuidPtr duid(new DUID(duid_array, sizeof(duid_array)));
-    const uint32_t iaid = 7;        // Just a number
-    const SubnetID subnet_id = 8;   // Just another number
-    Lease6 lease(Lease::TYPE_NA, addr, duid, iaid, 100, 200, 50, 80,
-                               subnet_id);
-
-    // Case 1: a second before expiration
-    lease.cltt_ = time(NULL) - 100;
-    lease.valid_lft_ = 101;
-    EXPECT_FALSE(lease.expired());
-
-    // Case 2: the lease will expire after this second is concluded
-    lease.cltt_ = time(NULL) - 101;
-    EXPECT_FALSE(lease.expired());
-
-    // Case 3: the lease is expired
-    lease.cltt_ = time(NULL) - 102;
-    EXPECT_TRUE(lease.expired());
-}
 
 
 }; // end of anonymous namespace
 }; // end of anonymous namespace

+ 688 - 6
src/lib/dhcpsrv/tests/lease_unittest.cc

@@ -1,4 +1,4 @@
-// Copyright (C) 2013 Internet Systems Consortium, Inc. ("ISC")
+// Copyright (C) 2013-2014 Internet Systems Consortium, Inc. ("ISC")
 //
 //
 // Permission to use, copy, modify, and/or distribute this software for any
 // Permission to use, copy, modify, and/or distribute this software for any
 // purpose with or without fee is hereby granted, provided that the above
 // purpose with or without fee is hereby granted, provided that the above
@@ -13,23 +13,342 @@
 // PERFORMANCE OF THIS SOFTWARE.
 // PERFORMANCE OF THIS SOFTWARE.
 
 
 #include <config.h>
 #include <config.h>
+#include <asiolink/io_address.h>
 #include <dhcp/duid.h>
 #include <dhcp/duid.h>
 #include <dhcpsrv/lease.h>
 #include <dhcpsrv/lease.h>
 #include <gtest/gtest.h>
 #include <gtest/gtest.h>
 #include <vector>
 #include <vector>
 
 
 using namespace isc;
 using namespace isc;
+using namespace isc::asiolink;
 using namespace isc::dhcp;
 using namespace isc::dhcp;
 
 
 namespace {
 namespace {
 
 
-// @todo Currently this file contains tests for new functions which return DUID
-// or client identifier. Other tests for Lease objects must be implemented.
-// See http://bind10.isc.org/ticket/3240.
+/// Hardware address used by different tests.
+const uint8_t HWADDR[] = {0x08, 0x00, 0x2b, 0x02, 0x3f, 0x4e};
+/// Client id used by different tests.
+const uint8_t CLIENTID[] = {0x17, 0x34, 0xe2, 0xff, 0x09, 0x92, 0x54};
+/// Valid lifetime value used by different tests.
+const uint32_t VALID_LIFETIME = 500;
+/// Subnet ID used by different tests.
+const uint32_t SUBNET_ID = 42;
+/// IAID value used by different tests.
+const uint32_t IAID = 7;
+
+/// @brief Creates an instance of the lease with certain FQDN data.
+///
+/// @param hostname Hostname.
+/// @param fqdn_fwd Forward FQDN update setting for a created lease.
+/// @param fqdn_rev Reverse FQDN update setting for a created lease.
+///
+/// @return Instance of the created lease.
+Lease4 createLease4(const std::string& hostname, const bool fqdn_fwd,
+                    const bool fqdn_rev) {
+    Lease4 lease;
+    lease.hostname_ = hostname;
+    lease.fqdn_fwd_ = fqdn_fwd;
+    lease.fqdn_rev_ = fqdn_rev;
+    return (lease);
+}
+
+/// Lease4 is also defined in lease_mgr.h, so is tested in this file as well.
+// This test checks if the Lease4 structure can be instantiated correctly
+TEST(Lease4, constructor) {
+
+    // Random values for the tests
+    const uint8_t HWADDR[] = {0x08, 0x00, 0x2b, 0x02, 0x3f, 0x4e};
+    std::vector<uint8_t> hwaddr(HWADDR, HWADDR + sizeof(HWADDR));
+
+    const uint8_t CLIENTID[] = {0x17, 0x34, 0xe2, 0xff, 0x09, 0x92, 0x54};
+    std::vector<uint8_t> clientid_vec(CLIENTID, CLIENTID + sizeof(CLIENTID));
+    ClientId clientid(clientid_vec);
+
+    // ...and a time
+    const time_t current_time = time(NULL);
+
+    // Other random constants.
+    const uint32_t SUBNET_ID = 42;
+    const uint32_t VALID_LIFETIME = 500;
+
+    // We want to check that various addresses work, so let's iterate over
+    // these.
+    const uint32_t ADDRESS[] = {
+        0x00000000, 0x01020304, 0x7fffffff, 0x80000000, 0x80000001, 0xffffffff
+    };
+
+    for (int i = 0; i < sizeof(ADDRESS) / sizeof(ADDRESS[0]); ++i) {
+
+        // Create the lease
+        Lease4 lease(ADDRESS[i], HWADDR, sizeof(HWADDR),
+                     CLIENTID, sizeof(CLIENTID), VALID_LIFETIME, 0, 0,
+                     current_time, SUBNET_ID, true, true,
+                     "hostname.example.com.");
+
+        EXPECT_EQ(ADDRESS[i], static_cast<uint32_t>(lease.addr_));
+        EXPECT_EQ(0, lease.ext_);
+        EXPECT_TRUE(hwaddr == lease.hwaddr_);
+        EXPECT_TRUE(clientid == *lease.client_id_);
+        EXPECT_EQ(0, lease.t1_);
+        EXPECT_EQ(0, lease.t2_);
+        EXPECT_EQ(VALID_LIFETIME, lease.valid_lft_);
+        EXPECT_EQ(current_time, lease.cltt_);
+        EXPECT_EQ(SUBNET_ID, lease.subnet_id_);
+        EXPECT_FALSE(lease.fixed_);
+        EXPECT_EQ("hostname.example.com.", lease.hostname_);
+        EXPECT_TRUE(lease.fqdn_fwd_);
+        EXPECT_TRUE(lease.fqdn_rev_);
+        EXPECT_TRUE(lease.comments_.empty());
+    }
+}
+
+// This test verfies that copy constructor copies Lease4 fields correctly.
+TEST(Lease4, copyConstructor) {
+
+    // Random values for the tests
+    const uint8_t HWADDR[] = {0x08, 0x00, 0x2b, 0x02, 0x3f, 0x4e};
+    std::vector<uint8_t> hwaddr(HWADDR, HWADDR + sizeof(HWADDR));
+
+    const uint8_t CLIENTID[] = {0x17, 0x34, 0xe2, 0xff, 0x09, 0x92, 0x54};
+    std::vector<uint8_t> clientid_vec(CLIENTID, CLIENTID + sizeof(CLIENTID));
+    ClientId clientid(clientid_vec);
+
+    // ...and a time
+    const time_t current_time = time(NULL);
+
+    // Other random constants.
+    const uint32_t SUBNET_ID = 42;
+    const uint32_t VALID_LIFETIME = 500;
+
+    // Create the lease
+    Lease4 lease(0xffffffff, HWADDR, sizeof(HWADDR),
+                 CLIENTID, sizeof(CLIENTID), VALID_LIFETIME, 0, 0, current_time,
+                 SUBNET_ID);
+
+    // Use copy constructor to copy the lease.
+    Lease4 copied_lease(lease);
+
+    // Both leases should be now equal. When doing this check we assume that
+    // the equality operator works correctly.
+    EXPECT_TRUE(lease == copied_lease);
+    // Client IDs are equal, but they should be in two distinct pointers.
+    EXPECT_FALSE(lease.client_id_ == copied_lease.client_id_);
+}
+
+// This test verfies that the assignment operator copies all Lease4 fields
+// correctly.
+TEST(Lease4, operatorAssign) {
+
+    // Random values for the tests
+    const uint8_t HWADDR[] = {0x08, 0x00, 0x2b, 0x02, 0x3f, 0x4e};
+    std::vector<uint8_t> hwaddr(HWADDR, HWADDR + sizeof(HWADDR));
+
+    const uint8_t CLIENTID[] = {0x17, 0x34, 0xe2, 0xff, 0x09, 0x92, 0x54};
+    std::vector<uint8_t> clientid_vec(CLIENTID, CLIENTID + sizeof(CLIENTID));
+    ClientId clientid(clientid_vec);
+
+    // ...and a time
+    const time_t current_time = time(NULL);
+
+    // Other random constants.
+    const uint32_t SUBNET_ID = 42;
+    const uint32_t VALID_LIFETIME = 500;
+
+    // Create the lease
+    Lease4 lease(0xffffffff, HWADDR, sizeof(HWADDR),
+                 CLIENTID, sizeof(CLIENTID), VALID_LIFETIME, 0, 0, current_time,
+                 SUBNET_ID);
+
+    // Use assignment operator to assign the lease.
+    Lease4 copied_lease = lease;
+
+    // Both leases should be now equal. When doing this check we assume that
+    // the equality operator works correctly.
+    EXPECT_TRUE(lease == copied_lease);
+    // Client IDs are equal, but they should be in two distinct pointers.
+    EXPECT_FALSE(lease.client_id_ == copied_lease.client_id_);
+}
+
+// This test verifies that the matches() returns true if two leases differ
+// by values other than address, HW address, Client ID and ext_.
+TEST(Lease4, matches) {
+    // Create two leases which share the same address, HW address, client id
+    // and ext_ value.
+    const time_t current_time = time(NULL);
+    Lease4 lease1(IOAddress("192.0.2.3"), HWADDR, sizeof(HWADDR), CLIENTID,
+                  sizeof(CLIENTID), VALID_LIFETIME, current_time, 0, 0,
+                  SUBNET_ID);
+    lease1.hostname_ = "lease1.example.com.";
+    lease1.fqdn_fwd_ = true;
+    lease1.fqdn_rev_ = true;
+    Lease4 lease2(IOAddress("192.0.2.3"), HWADDR, sizeof(HWADDR), CLIENTID,
+                  sizeof(CLIENTID), VALID_LIFETIME + 10, current_time - 10,
+                  100, 200, SUBNET_ID);
+    lease2.hostname_ = "lease2.example.com.";
+    lease2.fqdn_fwd_ = false;
+    lease2.fqdn_rev_ = true;
+
+    // Leases should match.
+    EXPECT_TRUE(lease1.matches(lease2));
+    EXPECT_TRUE(lease2.matches(lease1));
+
+    // Change address, leases should not match anymore.
+    lease1.addr_ = IOAddress("192.0.2.4");
+    EXPECT_FALSE(lease1.matches(lease2));
+    lease1.addr_ = lease2.addr_;
+
+    // Change HW address, leases should not match.
+    lease1.hwaddr_[1] += 1;
+    EXPECT_FALSE(lease1.matches(lease2));
+    lease1.hwaddr_ = lease2.hwaddr_;
+
+    // Chanage client id, leases should not match.
+    std::vector<uint8_t> client_id = lease1.client_id_->getClientId();
+    client_id[1] += 1;
+    lease1.client_id_.reset(new ClientId(client_id));
+    EXPECT_FALSE(lease1.matches(lease2));
+    lease1.client_id_ = lease2.client_id_;
+
+    // Change ext_, leases should not match.
+    lease1.ext_ += 1;
+    EXPECT_FALSE(lease1.matches(lease2));
+    lease1.ext_ = lease2.ext_;
+}
+
+/// @brief Lease4 Equality Test
+///
+/// Checks that the operator==() correctly compares two leases for equality.
+/// As operator!=() is also defined for this class, every check on operator==()
+/// is followed by the reverse check on operator!=().
+TEST(Lease4, operatorEquals) {
+
+    // Random values for the tests
+    const uint32_t ADDRESS = 0x01020304;
+    const uint8_t HWADDR[] = {0x08, 0x00, 0x2b, 0x02, 0x3f, 0x4e};
+    std::vector<uint8_t> hwaddr(HWADDR, HWADDR + sizeof(HWADDR));
+    const uint8_t CLIENTID[] = {0x17, 0x34, 0xe2, 0xff, 0x09, 0x92, 0x54};
+    std::vector<uint8_t> clientid_vec(CLIENTID, CLIENTID + sizeof(CLIENTID));
+    ClientId clientid(clientid_vec);
+    const time_t current_time = time(NULL);
+    const uint32_t SUBNET_ID = 42;
+    const uint32_t VALID_LIFETIME = 500;
+
+    // Check when the leases are equal.
+    Lease4 lease1(ADDRESS, HWADDR, sizeof(HWADDR),
+                  CLIENTID, sizeof(CLIENTID), VALID_LIFETIME, current_time, 0,
+                  0, SUBNET_ID);
+    Lease4 lease2(ADDRESS, HWADDR, sizeof(HWADDR),
+                  CLIENTID, sizeof(CLIENTID), VALID_LIFETIME, current_time, 0, 0,
+                  SUBNET_ID);
+    EXPECT_TRUE(lease1 == lease2);
+    EXPECT_FALSE(lease1 != lease2);
+
+    // Now vary individual fields in a lease and check that the leases compare
+    // not equal in every case.
+    lease1.addr_ = IOAddress(ADDRESS + 1);
+    EXPECT_FALSE(lease1 == lease2);
+    EXPECT_TRUE(lease1 != lease2);
+    lease1.addr_ = lease2.addr_;
+    EXPECT_TRUE(lease1 == lease2);  // Check that the reversion has made the
+    EXPECT_FALSE(lease1 != lease2); // ... leases equal
+
+    ++lease1.ext_;
+    EXPECT_FALSE(lease1 == lease2);
+    EXPECT_TRUE(lease1 != lease2);
+    lease1.ext_ = lease2.ext_;
+    EXPECT_TRUE(lease1 == lease2);  // Check that the reversion has made the
+    EXPECT_FALSE(lease1 != lease2); // ... leases equal
+
+    ++lease1.hwaddr_[0];
+    EXPECT_FALSE(lease1 == lease2);
+    EXPECT_TRUE(lease1 != lease2);
+    lease1.hwaddr_ = lease2.hwaddr_;
+    EXPECT_TRUE(lease1 == lease2);  // Check that the reversion has made the
+    EXPECT_FALSE(lease1 != lease2); // ... leases equal
+
+    ++clientid_vec[0];
+    lease1.client_id_.reset(new ClientId(clientid_vec));
+    EXPECT_FALSE(lease1 == lease2);
+    EXPECT_TRUE(lease1 != lease2);
+    --clientid_vec[0];
+    lease1.client_id_.reset(new ClientId(clientid_vec));
+    EXPECT_TRUE(lease1 == lease2);  // Check that the reversion has made the
+    EXPECT_FALSE(lease1 != lease2); // ... leases equal
+
+    ++lease1.t1_;
+    EXPECT_FALSE(lease1 == lease2);
+    EXPECT_TRUE(lease1 != lease2);
+    lease1.t1_ = lease2.t1_;
+    EXPECT_TRUE(lease1 == lease2);  // Check that the reversion has made the
+    EXPECT_FALSE(lease1 != lease2); // ... leases equal
+
+    ++lease1.t2_;
+    EXPECT_FALSE(lease1 == lease2);
+    EXPECT_TRUE(lease1 != lease2);
+    lease1.t2_ = lease2.t2_;
+    EXPECT_TRUE(lease1 == lease2);  // Check that the reversion has made the
+    EXPECT_FALSE(lease1 != lease2); // ... leases equal
+
+    ++lease1.valid_lft_;
+    EXPECT_FALSE(lease1 == lease2);
+    EXPECT_TRUE(lease1 != lease2);
+    lease1.valid_lft_ = lease2.valid_lft_;
+    EXPECT_TRUE(lease1 == lease2);  // Check that the reversion has made the
+    EXPECT_FALSE(lease1 != lease2); // ... leases equal
+
+    ++lease1.cltt_;
+    EXPECT_FALSE(lease1 == lease2);
+    EXPECT_TRUE(lease1 != lease2);
+    lease1.cltt_ = lease2.cltt_;
+    EXPECT_TRUE(lease1 == lease2);  // Check that the reversion has made the
+    EXPECT_FALSE(lease1 != lease2); // ... leases equal
+
+    ++lease1.subnet_id_;
+    EXPECT_FALSE(lease1 == lease2);
+    EXPECT_TRUE(lease1 != lease2);
+    lease1.subnet_id_ = lease2.subnet_id_;
+    EXPECT_TRUE(lease1 == lease2);  // Check that the reversion has made the
+    EXPECT_FALSE(lease1 != lease2); // ... leases equal
+
+    lease1.fixed_ = !lease1.fixed_;
+    EXPECT_FALSE(lease1 == lease2);
+    EXPECT_TRUE(lease1 != lease2);
+    lease1.fixed_ = lease2.fixed_;
+    EXPECT_TRUE(lease1 == lease2);  // Check that the reversion has made the
+    EXPECT_FALSE(lease1 != lease2); // ... leases equal
+
+    lease1.hostname_ += std::string("Something random");
+    EXPECT_FALSE(lease1 == lease2);
+    EXPECT_TRUE(lease1 != lease2);
+    lease1.hostname_ = lease2.hostname_;
+    EXPECT_TRUE(lease1 == lease2);  // Check that the reversion has made the
+    EXPECT_FALSE(lease1 != lease2); // ... leases equal
+
+    lease1.fqdn_fwd_ = !lease1.fqdn_fwd_;
+    EXPECT_FALSE(lease1 == lease2);
+    EXPECT_TRUE(lease1 != lease2);
+    lease1.fqdn_fwd_ = lease2.fqdn_fwd_;
+    EXPECT_TRUE(lease1 == lease2);  // Check that the reversion has made the
+    EXPECT_FALSE(lease1 != lease2); // ... leases equal
+
+    lease1.fqdn_rev_ = !lease1.fqdn_rev_;
+    EXPECT_FALSE(lease1 == lease2);
+    EXPECT_TRUE(lease1 != lease2);
+    lease1.fqdn_rev_ = lease2.fqdn_rev_;
+    EXPECT_TRUE(lease1 == lease2);  // Check that the reversion has made the
+    EXPECT_FALSE(lease1 != lease2); // ... leases equal
+
+    lease1.comments_ += std::string("Something random");
+    EXPECT_FALSE(lease1 == lease2);
+    EXPECT_TRUE(lease1 != lease2);
+    lease1.comments_ = lease2.comments_;
+    EXPECT_TRUE(lease1 == lease2);  // Check that the reversion has made the
+    EXPECT_FALSE(lease1 != lease2); // ... leases equal
+}
 
 
 // Verify that the client id can be returned as a vector object and if client
 // Verify that the client id can be returned as a vector object and if client
 // id is NULL the empty vector is returned.
 // id is NULL the empty vector is returned.
-TEST(Lease4Test, getClientIdVector) {
+TEST(Lease4, getClientIdVector) {
     // Create a lease.
     // Create a lease.
     Lease4 lease;
     Lease4 lease;
     // By default, the lease should have client id set to NULL. If it doesn't,
     // By default, the lease should have client id set to NULL. If it doesn't,
@@ -47,9 +366,356 @@ TEST(Lease4Test, getClientIdVector) {
     EXPECT_TRUE(returned_vec == client_id_vec);
     EXPECT_TRUE(returned_vec == client_id_vec);
 }
 }
 
 
+// Verify the behavior of the function which checks FQDN data for equality.
+TEST(Lease4, hasIdenticalFqdn) {
+    Lease4 lease = createLease4("myhost.example.com.", true, true);
+    EXPECT_TRUE(lease.hasIdenticalFqdn(createLease4("myhost.example.com.",
+                                                     true, true)));
+    EXPECT_FALSE(lease.hasIdenticalFqdn(createLease4("other.example.com.",
+                                                     true, true)));
+    EXPECT_FALSE(lease.hasIdenticalFqdn(createLease4("myhost.example.com.",
+                                                     false, true)));
+    EXPECT_FALSE(lease.hasIdenticalFqdn(createLease4("myhost.example.com.",
+                                                     true, false)));
+    EXPECT_FALSE(lease.hasIdenticalFqdn(createLease4("myhost.example.com.",
+                                                     false, false)));
+    EXPECT_FALSE(lease.hasIdenticalFqdn(createLease4("other.example.com.",
+                                                     false, false)));
+}
+
+/// @brief Creates an instance of the lease with certain FQDN data.
+///
+/// @param hostname Hostname.
+/// @param fqdn_fwd Forward FQDN update setting for a created lease.
+/// @param fqdn_rev Reverse FQDN update setting for a created lease.
+///
+/// @return Instance of the created lease.
+Lease6 createLease6(const std::string& hostname, const bool fqdn_fwd,
+                    const bool fqdn_rev) {
+    Lease6 lease;
+    lease.hostname_ = hostname;
+    lease.fqdn_fwd_ = fqdn_fwd;
+    lease.fqdn_rev_ = fqdn_rev;
+    return (lease);
+}
+
+// Lease6 is also defined in lease_mgr.h, so is tested in this file as well.
+// This test checks if the Lease6 structure can be instantiated correctly
+TEST(Lease6, Lease6ConstructorDefault) {
+
+    // check a variety of addresses with different bits set.
+    const char* ADDRESS[] = {
+        "::", "::1", "2001:db8:1::456",
+        "7fff:ffff:ffff:ffff:ffff:ffff:ffff:ffff",
+        "8000::", "8000::1",
+        "ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff"
+    };
+
+    // Other values
+    uint8_t llt[] = {0, 1, 2, 3, 4, 5, 6, 0xa, 0xb, 0xc, 0xd, 0xe, 0xf};
+    DuidPtr duid(new DUID(llt, sizeof(llt)));
+    uint32_t iaid = 7;      // Just a number
+    SubnetID subnet_id = 8; // Just another number
+
+    for (int i = 0; i < sizeof(ADDRESS) / sizeof(ADDRESS[0]); ++i) {
+        IOAddress addr(ADDRESS[i]);
+        Lease6Ptr lease(new Lease6(Lease::TYPE_NA, addr,
+                               duid, iaid, 100, 200, 50, 80,
+                               subnet_id));
+
+        EXPECT_TRUE(lease->addr_ == addr);
+        EXPECT_TRUE(*lease->duid_ == *duid);
+        EXPECT_TRUE(lease->iaid_ == iaid);
+        EXPECT_TRUE(lease->subnet_id_ == subnet_id);
+        EXPECT_TRUE(lease->type_ == Lease::TYPE_NA);
+        EXPECT_TRUE(lease->preferred_lft_ == 100);
+        EXPECT_TRUE(lease->valid_lft_ == 200);
+        EXPECT_TRUE(lease->t1_ == 50);
+        EXPECT_TRUE(lease->t2_ == 80);
+        EXPECT_FALSE(lease->fqdn_fwd_);
+        EXPECT_FALSE(lease->fqdn_rev_);
+        EXPECT_TRUE(lease->hostname_.empty());
+
+    }
+
+    // Lease6 must be instantiated with a DUID, not with NULL pointer
+    IOAddress addr(ADDRESS[0]);
+    Lease6Ptr lease2;
+    EXPECT_THROW(lease2.reset(new Lease6(Lease::TYPE_NA, addr,
+                                         DuidPtr(), iaid, 100, 200, 50, 80,
+                                         subnet_id)), InvalidOperation);
+}
+
+// This test verifies that the Lease6 constructor which accepts FQDN data,
+// sets the data correctly for the lease.
+TEST(Lease6, Lease6ConstructorWithFQDN) {
+
+    // check a variety of addresses with different bits set.
+    const char* ADDRESS[] = {
+        "::", "::1", "2001:db8:1::456",
+        "7fff:ffff:ffff:ffff:ffff:ffff:ffff:ffff",
+        "8000::", "8000::1",
+        "ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff"
+    };
+
+    // Other values
+    uint8_t llt[] = {0, 1, 2, 3, 4, 5, 6, 0xa, 0xb, 0xc, 0xd, 0xe, 0xf};
+    DuidPtr duid(new DUID(llt, sizeof(llt)));
+    uint32_t iaid = 7;      // Just a number
+    SubnetID subnet_id = 8; // Just another number
+
+    for (int i = 0; i < sizeof(ADDRESS) / sizeof(ADDRESS[0]); ++i) {
+        IOAddress addr(ADDRESS[i]);
+        Lease6Ptr lease(new Lease6(Lease::TYPE_NA, addr,
+                               duid, iaid, 100, 200, 50, 80, subnet_id,
+                                   true, true, "host.example.com."));
+
+        EXPECT_TRUE(lease->addr_ == addr);
+        EXPECT_TRUE(*lease->duid_ == *duid);
+        EXPECT_TRUE(lease->iaid_ == iaid);
+        EXPECT_TRUE(lease->subnet_id_ == subnet_id);
+        EXPECT_TRUE(lease->type_ == Lease::TYPE_NA);
+        EXPECT_TRUE(lease->preferred_lft_ == 100);
+        EXPECT_TRUE(lease->valid_lft_ == 200);
+        EXPECT_TRUE(lease->t1_ == 50);
+        EXPECT_TRUE(lease->t2_ == 80);
+        EXPECT_TRUE(lease->fqdn_fwd_);
+        EXPECT_TRUE(lease->fqdn_rev_);
+        EXPECT_EQ("host.example.com.", lease->hostname_);
+    }
+
+    // Lease6 must be instantiated with a DUID, not with NULL pointer
+    IOAddress addr(ADDRESS[0]);
+    Lease6Ptr lease2;
+    EXPECT_THROW(lease2.reset(new Lease6(Lease::TYPE_NA, addr,
+                                         DuidPtr(), iaid, 100, 200, 50, 80,
+                                         subnet_id)), InvalidOperation);
+}
+
+// This test verifies that the matches() function returns true if two leases
+// differ by values other than address, type, prefix length, IAID and DUID.
+TEST(Lease6, matches) {
+
+    // Create two matching leases.
+    uint8_t llt[] = {0, 1, 2, 3, 4, 5, 6, 0xa, 0xb, 0xc, 0xd, 0xe, 0xf};
+    DuidPtr duid(new DUID(llt, sizeof(llt)));
+
+    Lease6 lease1(Lease6::TYPE_NA, IOAddress("2001:db8:1::1"), duid,
+                  IAID, 100, 200, 50, 80,
+                  SUBNET_ID);
+    lease1.hostname_ = "lease1.example.com.";
+    lease1.fqdn_fwd_ = true;
+    lease1.fqdn_rev_ = true;
+    Lease6 lease2(Lease6::TYPE_NA, IOAddress("2001:db8:1::1"), duid,
+                  IAID, 200, 300, 90, 70,
+                  SUBNET_ID);
+    lease2.hostname_ = "lease1.example.com.";
+    lease2.fqdn_fwd_ = false;
+    lease2.fqdn_rev_ = true;
+
+    EXPECT_TRUE(lease1.matches(lease2));
+
+    // Modify each value used to match both leases, and make sure that
+    // leases don't match.
+
+    // Modify address.
+    lease1.addr_ = IOAddress("2001:db8:1::2");
+    EXPECT_FALSE(lease1.matches(lease2));
+    lease1.addr_ = lease2.addr_;
+
+    // Modify lease type.
+    lease1.type_ = Lease6::TYPE_TA;
+    EXPECT_FALSE(lease1.matches(lease2));
+    lease1.type_ = lease2.type_;
+
+    // Modify prefix length.
+    lease1.prefixlen_ += 1;
+    EXPECT_FALSE(lease1.matches(lease2));
+    lease1.prefixlen_ = lease2.prefixlen_;
+
+    // Modify IAID.
+    lease1.iaid_ += 1;
+    EXPECT_FALSE(lease1.matches(lease2));
+    lease1.iaid_ = lease2.iaid_;
+
+    // Modify DUID.
+    llt[1] += 1;
+    duid.reset(new DUID(llt, sizeof(llt)));
+    lease1.duid_ = duid;
+    EXPECT_FALSE(lease1.matches(lease2));
+    lease1.duid_ = lease2.duid_;
+}
+
+/// @brief Lease6 Equality Test
+///
+/// Checks that the operator==() correctly compares two leases for equality.
+/// As operator!=() is also defined for this class, every check on operator==()
+/// is followed by the reverse check on operator!=().
+TEST(Lease6, OperatorEquals) {
+
+    // check a variety of addresses with different bits set.
+    const IOAddress addr("2001:db8:1::456");
+    uint8_t duid_array[] = {0, 1, 2, 3, 4, 5, 6, 0xa, 0xb, 0xc, 0xd, 0xe, 0xf};
+    DuidPtr duid(new DUID(duid_array, sizeof(duid_array)));
+    uint32_t iaid = 7; // just a number
+    SubnetID subnet_id = 8; // just another number
+
+    // Check for equality.
+    Lease6 lease1(Lease::TYPE_NA, addr, duid, iaid, 100, 200, 50, 80,
+                               subnet_id);
+    Lease6 lease2(Lease::TYPE_NA, addr, duid, iaid, 100, 200, 50, 80,
+                               subnet_id);
+
+    // cltt_ constructs with time(NULL), make sure they are always equal
+    lease1.cltt_ = lease2.cltt_;
+
+    EXPECT_TRUE(lease1 == lease2);
+    EXPECT_FALSE(lease1 != lease2);
+
+    // Go through and alter all the fields one by one
+
+    lease1.addr_ = IOAddress("::1");
+    EXPECT_FALSE(lease1 == lease2);
+    EXPECT_TRUE(lease1 != lease2);
+    lease1.addr_ = lease2.addr_;
+    EXPECT_TRUE(lease1 == lease2);  // Check that the reversion has made the
+    EXPECT_FALSE(lease1 != lease2); // ... leases equal
+
+    lease1.type_ = Lease::TYPE_PD;
+    EXPECT_FALSE(lease1 == lease2);
+    EXPECT_TRUE(lease1 != lease2);
+    lease1.type_ = lease2.type_;
+    EXPECT_TRUE(lease1 == lease2);  // Check that the reversion has made the
+    EXPECT_FALSE(lease1 != lease2); // ... leases equal
+
+    ++lease1.prefixlen_;
+    EXPECT_FALSE(lease1 == lease2);
+    EXPECT_TRUE(lease1 != lease2);
+    lease1.prefixlen_ = lease2.prefixlen_;
+    EXPECT_TRUE(lease1 == lease2);  // Check that the reversion has made the
+    EXPECT_FALSE(lease1 != lease2); // ... leases equal
+
+    ++lease1.iaid_;
+    EXPECT_FALSE(lease1 == lease2);
+    EXPECT_TRUE(lease1 != lease2);
+    lease1.iaid_ = lease2.iaid_;
+    EXPECT_TRUE(lease1 == lease2);  // Check that the reversion has made the
+    EXPECT_FALSE(lease1 != lease2); // ... leases equal
+
+    ++duid_array[0];
+    lease1.duid_.reset(new DUID(duid_array, sizeof(duid_array)));
+    EXPECT_FALSE(lease1 == lease2);
+    EXPECT_TRUE(lease1 != lease2);
+    --duid_array[0];
+    lease1.duid_.reset(new DUID(duid_array, sizeof(duid_array)));
+    EXPECT_TRUE(lease1 == lease2);  // Check that the reversion has made the
+    EXPECT_FALSE(lease1 != lease2); // ... leases equal
+
+    ++lease1.preferred_lft_;
+    EXPECT_FALSE(lease1 == lease2);
+    EXPECT_TRUE(lease1 != lease2);
+    lease1.preferred_lft_ = lease2.preferred_lft_;
+    EXPECT_TRUE(lease1 == lease2);  // Check that the reversion has made the
+    EXPECT_FALSE(lease1 != lease2); // ... leases equal
+
+    ++lease1.valid_lft_;
+    EXPECT_FALSE(lease1 == lease2);
+    EXPECT_TRUE(lease1 != lease2);
+    lease1.valid_lft_ = lease2.valid_lft_;
+    EXPECT_TRUE(lease1 == lease2);  // Check that the reversion has made the
+    EXPECT_FALSE(lease1 != lease2); // ... leases equal
+
+    ++lease1.t1_;
+    EXPECT_FALSE(lease1 == lease2);
+    EXPECT_TRUE(lease1 != lease2);
+    lease1.t1_ = lease2.t1_;
+    EXPECT_TRUE(lease1 == lease2);  // Check that the reversion has made the
+    EXPECT_FALSE(lease1 != lease2); // ... leases equal
+
+    ++lease1.t2_;
+    EXPECT_FALSE(lease1 == lease2);
+    EXPECT_TRUE(lease1 != lease2);
+    lease1.t2_ = lease2.t2_;
+    EXPECT_TRUE(lease1 == lease2);  // Check that the reversion has made the
+    EXPECT_FALSE(lease1 != lease2); // ... leases equal
+
+    ++lease1.cltt_;
+    EXPECT_FALSE(lease1 == lease2);
+    EXPECT_TRUE(lease1 != lease2);
+    lease1.cltt_ = lease2.cltt_;
+    EXPECT_TRUE(lease1 == lease2);  // Check that the reversion has made the
+    EXPECT_FALSE(lease1 != lease2); // ... leases equal
+
+    ++lease1.subnet_id_;
+    EXPECT_FALSE(lease1 == lease2);
+    EXPECT_TRUE(lease1 != lease2);
+    lease1.subnet_id_ = lease2.subnet_id_;
+    EXPECT_TRUE(lease1 == lease2);  // Check that the reversion has made the
+    EXPECT_FALSE(lease1 != lease2); // ... leases equal
+
+    lease1.fixed_ = !lease1.fixed_;
+    EXPECT_FALSE(lease1 == lease2);
+    EXPECT_TRUE(lease1 != lease2);
+    lease1.fixed_ = lease2.fixed_;
+    EXPECT_TRUE(lease1 == lease2);  // Check that the reversion has made the
+    EXPECT_FALSE(lease1 != lease2); // ... leases equal
+
+    lease1.hostname_ += std::string("Something random");
+    EXPECT_FALSE(lease1 == lease2);
+    EXPECT_TRUE(lease1 != lease2);
+    lease1.hostname_ = lease2.hostname_;
+    EXPECT_TRUE(lease1 == lease2);  // Check that the reversion has made the
+    EXPECT_FALSE(lease1 != lease2); // ... leases equal
+
+    lease1.fqdn_fwd_ = !lease1.fqdn_fwd_;
+    EXPECT_FALSE(lease1 == lease2);
+    EXPECT_TRUE(lease1 != lease2);
+    lease1.fqdn_fwd_ = lease2.fqdn_fwd_;
+    EXPECT_TRUE(lease1 == lease2);  // Check that the reversion has made the
+    EXPECT_FALSE(lease1 != lease2); // ... leases equal
+
+    lease1.fqdn_rev_ = !lease1.fqdn_rev_;
+    EXPECT_FALSE(lease1 == lease2);
+    EXPECT_TRUE(lease1 != lease2);
+    lease1.fqdn_rev_ = lease2.fqdn_rev_;
+    EXPECT_TRUE(lease1 == lease2);  // Check that the reversion has made the
+    EXPECT_FALSE(lease1 != lease2); // ... leases equal
+
+    lease1.comments_ += std::string("Something random");
+    EXPECT_FALSE(lease1 == lease2);
+    EXPECT_TRUE(lease1 != lease2);
+    lease1.comments_ = lease2.comments_;
+    EXPECT_TRUE(lease1 == lease2);  // Check that the reversion has made the
+    EXPECT_FALSE(lease1 != lease2); // ... leases equal
+}
+
+// Checks if lease expiration is calculated properly
+TEST(Lease6, Lease6Expired) {
+    const IOAddress addr("2001:db8:1::456");
+    const uint8_t duid_array[] = {0, 1, 2, 3, 4, 5, 6, 0xa, 0xb, 0xc, 0xd, 0xe, 0xf};
+    const DuidPtr duid(new DUID(duid_array, sizeof(duid_array)));
+    const uint32_t iaid = 7;        // Just a number
+    const SubnetID subnet_id = 8;   // Just another number
+    Lease6 lease(Lease::TYPE_NA, addr, duid, iaid, 100, 200, 50, 80,
+                               subnet_id);
+
+    // Case 1: a second before expiration
+    lease.cltt_ = time(NULL) - 100;
+    lease.valid_lft_ = 101;
+    EXPECT_FALSE(lease.expired());
+
+    // Case 2: the lease will expire after this second is concluded
+    lease.cltt_ = time(NULL) - 101;
+    EXPECT_FALSE(lease.expired());
+
+    // Case 3: the lease is expired
+    lease.cltt_ = time(NULL) - 102;
+    EXPECT_TRUE(lease.expired());
+}
+
 // Verify that the DUID can be returned as a vector object and if DUID is NULL
 // Verify that the DUID can be returned as a vector object and if DUID is NULL
 // the empty vector is returned.
 // the empty vector is returned.
-TEST(Lease6Test, getDuidVector) {
+TEST(Lease6, getDuidVector) {
     // Create a lease.
     // Create a lease.
     Lease6 lease;
     Lease6 lease;
     // By default, the lease should have client id set to NULL. If it doesn't,
     // By default, the lease should have client id set to NULL. If it doesn't,
@@ -67,5 +733,21 @@ TEST(Lease6Test, getDuidVector) {
     EXPECT_TRUE(returned_vec == duid_vec);
     EXPECT_TRUE(returned_vec == duid_vec);
 }
 }
 
 
+// Verify the behavior of the function which checks FQDN data for equality.
+TEST(Lease6, hasIdenticalFqdn) {
+    Lease6 lease = createLease6("myhost.example.com.", true, true);
+    EXPECT_TRUE(lease.hasIdenticalFqdn(createLease6("myhost.example.com.",
+                                                    true, true)));
+    EXPECT_FALSE(lease.hasIdenticalFqdn(createLease6("other.example.com.",
+                                                     true, true)));
+    EXPECT_FALSE(lease.hasIdenticalFqdn(createLease6("myhost.example.com.",
+                                                     false, true)));
+    EXPECT_FALSE(lease.hasIdenticalFqdn(createLease6("myhost.example.com.",
+                                                     true, false)));
+    EXPECT_FALSE(lease.hasIdenticalFqdn(createLease6("myhost.example.com.",
+                                                     false, false)));
+    EXPECT_FALSE(lease.hasIdenticalFqdn(createLease6("other.example.com.",
+                                                     false, false)));
+}
 
 
 }; // end of anonymous namespace
 }; // end of anonymous namespace