Browse Source

[3036] Generate NameChangeRequests for Solicit, Request, Renew and Release.

Marcin Siodelski 11 years ago
parent
commit
87e43d5dfd

+ 5 - 0
src/bin/dhcp6/Makefile.am

@@ -54,6 +54,11 @@ b10_dhcp6_SOURCES += config_parser.cc config_parser.h
 b10_dhcp6_SOURCES += dhcp6_log.cc dhcp6_log.h
 b10_dhcp6_SOURCES += dhcp6_log.cc dhcp6_log.h
 b10_dhcp6_SOURCES += dhcp6_srv.cc dhcp6_srv.h
 b10_dhcp6_SOURCES += dhcp6_srv.cc dhcp6_srv.h
 
 
+# Temporarily compile this file here. It will be removed once libdhcp-ddns
+# is implemented which will include this file and other files required
+# by DHCPv6.
+b10_dhcp6_SOURCES += ../d2/ncr_msg.cc ../d2/ncr_msg.h
+
 nodist_b10_dhcp6_SOURCES = dhcp6_messages.h dhcp6_messages.cc
 nodist_b10_dhcp6_SOURCES = dhcp6_messages.h dhcp6_messages.cc
 EXTRA_DIST += dhcp6_messages.mes
 EXTRA_DIST += dhcp6_messages.mes
 
 

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

@@ -65,6 +65,18 @@ This informational message is printed every time the IPv6 DHCP server
 is started.  It indicates what database backend type is being to store
 is started.  It indicates what database backend type is being to store
 lease and other information.
 lease and other information.
 
 
+% 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
+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
+database has invalid format and can't be transformed to the canonical on-wire
+format.
+
 % DHCP6_LEASE_ADVERT lease %1 advertised (client duid=%2, iaid=%3)
 % DHCP6_LEASE_ADVERT lease %1 advertised (client duid=%2, iaid=%3)
 This debug message indicates that the server successfully advertised
 This debug message indicates that the server successfully advertised
 a lease. It is up to the client to choose one server out of the
 a lease. It is up to the client to choose one server out of the

+ 267 - 44
src/bin/dhcp6/dhcp6_srv.cc

@@ -15,6 +15,7 @@
 #include <config.h>
 #include <config.h>
 
 
 #include <asiolink/io_address.h>
 #include <asiolink/io_address.h>
+#include <d2/ncr_msg.h>
 #include <dhcp/dhcp6.h>
 #include <dhcp/dhcp6.h>
 #include <dhcp/duid.h>
 #include <dhcp/duid.h>
 #include <dhcp/iface_mgr.h>
 #include <dhcp/iface_mgr.h>
@@ -198,15 +199,15 @@ bool Dhcpv6Srv::run() {
                     break;
                     break;
 
 
                 case DHCPV6_REQUEST:
                 case DHCPV6_REQUEST:
-                    rsp = processRequest(query, ncr);
+                    rsp = processRequest(query);
                     break;
                     break;
 
 
                 case DHCPV6_RENEW:
                 case DHCPV6_RENEW:
-                    rsp = processRenew(query, ncr);
+                    rsp = processRenew(query);
                     break;
                     break;
 
 
                 case DHCPV6_REBIND:
                 case DHCPV6_REBIND:
-                    rsp = processRebind(query, ncr);
+                    rsp = processRebind(query);
                     break;
                     break;
 
 
                 case DHCPV6_CONFIRM:
                 case DHCPV6_CONFIRM:
@@ -214,7 +215,7 @@ bool Dhcpv6Srv::run() {
                     break;
                     break;
 
 
                 case DHCPV6_RELEASE:
                 case DHCPV6_RELEASE:
-                    rsp = processRelease(query, ncr);
+                    rsp = processRelease(query);
                     break;
                     break;
 
 
                 case DHCPV6_DECLINE:
                 case DHCPV6_DECLINE:
@@ -596,7 +597,8 @@ Dhcpv6Srv::selectSubnet(const Pkt6Ptr& question) {
 }
 }
 
 
 void
 void
-Dhcpv6Srv::assignLeases(const Pkt6Ptr& question, Pkt6Ptr& answer) {
+Dhcpv6Srv::assignLeases(const Pkt6Ptr& question, Pkt6Ptr& answer,
+                        const Option6ClientFqdnPtr& fqdn) {
 
 
     // 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.
@@ -651,7 +653,9 @@ Dhcpv6Srv::assignLeases(const Pkt6Ptr& question, Pkt6Ptr& answer) {
         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,
-                                   boost::dynamic_pointer_cast<Option6IA>(opt->second));
+                                               boost::dynamic_pointer_cast<
+                                               Option6IA>(opt->second),
+                                               fqdn);
             if (answer_opt) {
             if (answer_opt) {
                 answer->addOption(answer_opt);
                 answer->addOption(answer_opt);
             }
             }
@@ -663,15 +667,14 @@ Dhcpv6Srv::assignLeases(const Pkt6Ptr& question, Pkt6Ptr& answer) {
     }
     }
 }
 }
 
 
-void
+Option6ClientFqdnPtr
-Dhcpv6Srv::processClientFqdn(const Pkt6Ptr& question, Pkt6Ptr& answer,
+Dhcpv6Srv::processClientFqdn(const Pkt6Ptr& question) {
-                             NameChangeRequestPtr& ncr) {
     // 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;
+        return (fqdn);
     }
     }
 
 
     // Prepare the FQDN option which will be included in the response to
     // Prepare the FQDN option which will be included in the response to
@@ -688,9 +691,10 @@ Dhcpv6Srv::processClientFqdn(const Pkt6Ptr& question, Pkt6Ptr& answer,
     //    server neither respects delegation of updates nor it is configured
     //    server neither respects delegation of updates nor it is configured
     //    to send update on its own when client requested delegation.
     //    to send update on its own when client requested delegation.
     if (!FQDN_ENABLE_UPDATE ||
     if (!FQDN_ENABLE_UPDATE ||
-        (fqdn->getFlag(Option6ClientFqdn::FLAG_N) && !FQDN_OVERRIDE_NO_UPDATE) ||
+        (fqdn->getFlag(Option6ClientFqdn::FLAG_N) &&
-        (!fqdn->getFlag(Option6ClientFqdn::FLAG_S) && !FQDN_ALLOW_CLIENT_UPDATE &&
+         !FQDN_OVERRIDE_NO_UPDATE) ||
-         !FQDN_OVERRIDE_CLIENT_UPDATE)) {
+        (!fqdn->getFlag(Option6ClientFqdn::FLAG_S) &&
+         !FQDN_ALLOW_CLIENT_UPDATE && !FQDN_OVERRIDE_CLIENT_UPDATE)) {
         fqdn_resp->setFlag(Option6ClientFqdn::FLAG_N, true);
         fqdn_resp->setFlag(Option6ClientFqdn::FLAG_N, true);
 
 
     // Conditions when S flag is set to indicate that server will perform
     // Conditions when S flag is set to indicate that server will perform
@@ -734,6 +738,24 @@ Dhcpv6Srv::processClientFqdn(const Pkt6Ptr& question, Pkt6Ptr& answer,
 
 
     }
     }
 
 
+    // 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);
+}
+
+
+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
     // Server sends back the FQDN option to the client if client has requested
     // it using Option Request Option. However, server may be configured to
     // it using Option Request Option. However, server may be configured to
     // send the FQDN option in its response, regardless whether client requested
     // send the FQDN option in its response, regardless whether client requested
@@ -753,23 +775,137 @@ Dhcpv6Srv::processClientFqdn(const Pkt6Ptr& question, Pkt6Ptr& answer,
     }
     }
 
 
     if (include_fqdn) {
     if (include_fqdn) {
-        answer->addOption(fqdn_resp);
+        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) {
+        return;
+    }
+
+    // The response message instance is always required. For instance it
+    // holds the Client Identifier. It is a programming error if supplied
+    // message is NULL.
+    if (!answer) {
+        isc_throw(isc::Unexpected, "an instance of the object"
+                  << " encapsulating server's message must not be"
+                  << " NULL when creating DNS NameChangeRequest");
     }
     }
 
 
-    createNameChangeRequest(answer, fqdn_resp, ncr);
+    // Get the Client Id. It is mandatory and a function creating a response
+    // would have thrown an exception if it was missing. Thus throwning
+    // Unexpected if it is missing as it is a programming error.
+    OptionPtr opt_duid = answer->getOption(D6O_CLIENTID);
+    if (!opt_duid) {
+        isc_throw(isc::Unexpected,
+                  "client identifier is required when creating a new"
+                  " DNS NameChangeRequest");
+    }
+    DuidPtr duid = DuidPtr(new DUID(opt_duid->getData()));
+
+    // Get the FQDN in the on-wire format. It will be needed to compute
+    // DHCID.
+    OutputBuffer name_buf(1);
+    opt_fqdn->packDomainName(name_buf);
+    const uint8_t* name_data = static_cast<const uint8_t*>(name_buf.getData());
+    // @todo currently D2Dhcid accepts a vector holding FQDN.
+    // However, it will be faster if we used a pointer name_data.
+    std::vector<uint8_t> buf_vec(name_data, name_data + name_buf.getLength());
+    // Compute DHCID from Client Identifier and FQDN.
+    isc::d2::D2Dhcid dhcid(*duid, buf_vec);
+
+    // Get all IAs from the answer. For each IA, holding an address we will
+    // create a corresponding NameChangeRequest.
+    Option::OptionCollection answer_ias = answer->getOptions(D6O_IA_NA);
+    for (Option::OptionCollection::const_iterator answer_ia =
+             answer_ias.begin(); answer_ia != answer_ias.end(); ++answer_ia) {
+        Option6IAAddrPtr iaaddr = boost::static_pointer_cast<
+            Option6IAAddr>(answer_ia->second->getOption(D6O_IAADDR));
+        // We need an address to create a name-to-address mapping.
+        // If address is missing for any reason, go to the next IA.
+        if (!iaaddr) {
+            continue;
+        }
+        // Create new NameChangeRequest. Use the domain name from the FQDN.
+        // This is an FQDN included in the response to the client, so it
+        // holds a fully qualified domain-name already (not partial).
+        // Get the IP address from the lease. Also, use the S flag to determine
+        // if forward change should be performed. This flag will always be
+        // set if server has taken responsibility for the forward update.
+        NameChangeRequest ncr(isc::d2::CHG_ADD,
+                              opt_fqdn->getFlag(Option6ClientFqdn::FLAG_S),
+                              true, opt_fqdn->getDomainName(),
+                              iaaddr->getAddress().toText(),
+                              dhcid, 0, iaaddr->getValid());
+        // Add the request to the queue. This queue will be read elsewhere in
+        // the code and all requests from this queue will be sent to the
+        // D2 module.
+        name_change_reqs_.push(ncr);
+    }
 }
 }
 
 
 void
 void
-Dhcpv6Srv::createNameChangeRequest(const Pkt6Ptr&,
+Dhcpv6Srv::createRemovalNameChangeRequest(const Lease6Ptr& lease) {
-                                   const Option6ClientFqdnPtr&,
+    // If we haven't performed a DNS Update when lease was acquired,
-                                   isc::d2::NameChangeRequestPtr&) {
+    // there is nothing to do here.
-    // @todo Create NameChangeRequest here.
+    if (!lease->fqdn_fwd_ && !lease->fqdn_rev_) {
+        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());
+        return;
+    }
+
+    // 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
+    // has invalid format. Again, this should be only possible in case of
+    // manual intervention in the database.
+    std::vector<uint8_t> hostname_wire;
+    try {
+        OptionDataTypeUtil::writeFqdn(lease->hostname_, hostname_wire);
+    } catch (const Exception& ex) {
+        LOG_ERROR(dhcp6_logger, DHCP6_DDNS_REMOVE_INVALID_HOSTNAME);
+        return;
+    }
+
+    // DUID must have been checked already  by the caller of this function.
+    // Let's be on the safe side and make sure it is non-NULL and throw
+    // an exception if it is NULL.
+    if (!lease->duid_) {
+        isc_throw(isc::Unexpected, "DUID must be set when creating"
+                  << " NameChangeRequest for DNS records removal for "
+                  << lease->addr_.toText());
+
+    }
+    isc::d2::D2Dhcid dhcid(*lease->duid_, hostname_wire);
+
+    // Create a NameChangeRequest to remove the entry.
+    NameChangeRequest ncr(isc::d2::CHG_REMOVE,
+                          lease->fqdn_fwd_, lease->fqdn_rev_,
+                          lease->hostname_,
+                          lease->addr_.toText(),
+                          dhcid, 0, lease->valid_lft_);
+    name_change_reqs_.push(ncr);
 }
 }
 
 
 
 
 OptionPtr
 OptionPtr
 Dhcpv6Srv::assignIA_NA(const Subnet6Ptr& subnet, const DuidPtr& duid,
 Dhcpv6Srv::assignIA_NA(const Subnet6Ptr& subnet, const DuidPtr& duid,
-                       Pkt6Ptr question, boost::shared_ptr<Option6IA> ia) {
+                       Pkt6Ptr question, Option6IAPtr ia,
+                       const Option6ClientFqdnPtr& fqdn) {
     // 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,
@@ -813,12 +949,41 @@ Dhcpv6Srv::assignIA_NA(const Subnet6Ptr& subnet, const DuidPtr& duid,
         fake_allocation = true;
         fake_allocation = true;
     }
     }
 
 
+    // At this point, we have to make make some decisions with respect to the
+    // FQDN option that we have generated as a result of receiving client's
+    // FQDN. In particular, we have to get to know if the DNS update will be
+    // performed or not. It is possible that option is NULL, which is valid
+    // condition if client didn't request DNS updates and server didn't force
+    // the update.
+    bool do_fwd = false;
+    bool do_rev = false;
+    if (fqdn) {
+        // 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.
+        // Otherwise, we have to check N.
+        if (fqdn->getFlag(Option6ClientFqdn::FLAG_S)) {
+            do_fwd = true;
+            do_rev = true;
+        } else if (!fqdn->getFlag(Option6ClientFqdn::FLAG_N)) {
+            do_rev = true;
+        }
+    }
+    // Set hostname only in case any of the updates is being performed.
+    std::string hostname;
+    if (do_fwd || do_rev) {
+        hostname = fqdn->getDomainName();
+    }
+
     // Use allocation engine to pick a lease for this client. Allocation engine
     // Use allocation engine to pick a lease for this client. Allocation engine
     // 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.
-    Lease6Ptr lease = alloc_engine_->allocateAddress6(subnet, duid, ia->getIAID(),
+    Lease6Ptr lease = alloc_engine_->allocateAddress6(subnet, duid,
-                                                      hint, fake_allocation);
+                                                      ia->getIAID(),
+                                                      hint,
+                                                      do_fwd, do_rev,
+                                                      hostname,
+                                                      fake_allocation);
 
 
     // Create IA_NA that we will put in the response.
     // Create IA_NA that we will put in the response.
     // Do not use OptionDefinition to create option's instance so
     // Do not use OptionDefinition to create option's instance so
@@ -847,6 +1012,22 @@ Dhcpv6Srv::assignIA_NA(const Subnet6Ptr& subnet, const DuidPtr& duid,
         // It would be possible to insert status code=0(success) as well,
         // It would be possible to insert status code=0(success) as well,
         // 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.
+
+        // Allocation engine may have returned an existing lease. If so, we
+        // 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
+        // DNS records and update the lease with the new settings.
+        if ((lease->hostname_ != hostname) || (lease->fqdn_fwd_ != do_fwd) ||
+            (lease->fqdn_rev_ != do_rev)) {
+            // 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);
+        }
+
     } else {
     } else {
         // Allocation engine did not allocate a lease. The engine logged
         // Allocation engine did not allocate a lease. The engine logged
         // cause of that failure. The only thing left is to insert
         // cause of that failure. The only thing left is to insert
@@ -865,7 +1046,8 @@ Dhcpv6Srv::assignIA_NA(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,
-                      Pkt6Ptr /* question */, boost::shared_ptr<Option6IA> ia) {
+                      Pkt6Ptr /* question */, boost::shared_ptr<Option6IA> ia,
+                      const Option6ClientFqdnPtr& fqdn) {
     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()));
@@ -902,11 +1084,43 @@ Dhcpv6Srv::renewIA_NA(const Subnet6Ptr& subnet, const DuidPtr& duid,
         return (ia_rsp);
         return (ia_rsp);
     }
     }
 
 
+    // At this point, we have to make make some decisions with respect to the
+    // FQDN option that we have generated as a result of receiving client's
+    // FQDN. In particular, we have to get to know if the DNS update will be
+    // performed or not. It is possible that option is NULL, which is valid
+    // condition if client didn't request DNS updates and server didn't force
+    // the update.
+    bool do_fwd = false;
+    bool do_rev = false;
+    if (fqdn) {
+        if (fqdn->getFlag(Option6ClientFqdn::FLAG_S)) {
+            do_fwd = true;
+            do_rev = true;
+        } else if (!fqdn->getFlag(Option6ClientFqdn::FLAG_N)) {
+            do_rev = true;
+        }
+    }
+
+    std::string hostname;
+    if (do_fwd || do_rev) {
+        hostname = fqdn->getDomainName();
+    }
+
+    // If the new FQDN settings have changed for the lease, we need to
+    // delete any existing FQDN records for this lease.
+    if ((lease->hostname_ != hostname) || (lease->fqdn_fwd_ != do_fwd) ||
+        (lease->fqdn_rev_ != do_rev)) {
+        createRemovalNameChangeRequest(lease);
+    }
+
     lease->preferred_lft_ = subnet->getPreferred();
     lease->preferred_lft_ = subnet->getPreferred();
     lease->valid_lft_ = subnet->getValid();
     lease->valid_lft_ = subnet->getValid();
     lease->t1_ = subnet->getT1();
     lease->t1_ = subnet->getT1();
     lease->t2_ = subnet->getT2();
     lease->t2_ = subnet->getT2();
     lease->cltt_ = time(NULL);
     lease->cltt_ = time(NULL);
+    lease->hostname_ = hostname;
+    lease->fqdn_fwd_ = do_fwd;
+    lease->fqdn_rev_ = do_rev;
 
 
     LeaseMgrFactory::instance().updateLease6(lease);
     LeaseMgrFactory::instance().updateLease6(lease);
 
 
@@ -924,7 +1138,8 @@ Dhcpv6Srv::renewIA_NA(const Subnet6Ptr& subnet, const DuidPtr& duid,
 }
 }
 
 
 void
 void
-Dhcpv6Srv::renewLeases(const Pkt6Ptr& renew, Pkt6Ptr& reply) {
+Dhcpv6Srv::renewLeases(const Pkt6Ptr& renew, Pkt6Ptr& reply,
+                       const Option6ClientFqdnPtr& fqdn) {
 
 
     // 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.
@@ -968,7 +1183,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,
-                                   boost::dynamic_pointer_cast<Option6IA>(opt->second));
+                                              boost::dynamic_pointer_cast<
+                                              Option6IA>(opt->second),
+                                              fqdn);
             if (answer_opt) {
             if (answer_opt) {
                 reply->addOption(answer_opt);
                 reply->addOption(answer_opt);
             }
             }
@@ -1144,6 +1361,11 @@ Dhcpv6Srv::releaseIA_NA(const DuidPtr& duid, Pkt6Ptr /* question */,
         ia_rsp->addOption(createStatusCode(STATUS_Success,
         ia_rsp->addOption(createStatusCode(STATUS_Success,
                           "Lease released. Thank you, please come again."));
                           "Lease released. Thank you, please come again."));
 
 
+        // Check if a lease has flags indicating that the FQDN update has
+        // been performed. If so, create NameChangeRequest which removes
+        // the entries.
+        createRemovalNameChangeRequest(lease);
+
         return (ia_rsp);
         return (ia_rsp);
     }
     }
 }
 }
@@ -1159,17 +1381,18 @@ Dhcpv6Srv::processSolicit(const Pkt6Ptr& solicit) {
     appendDefaultOptions(solicit, advertise);
     appendDefaultOptions(solicit, advertise);
     appendRequestedOptions(solicit, advertise);
     appendRequestedOptions(solicit, advertise);
 
 
-    assignLeases(solicit, advertise);
+    Option6ClientFqdnPtr fqdn = processClientFqdn(solicit);
-
+    assignLeases(solicit, advertise, fqdn);
-    NameChangeRequestPtr ncr;
+    appendClientFqdn(solicit, advertise, fqdn);
-    processClientFqdn(solicit, advertise, ncr);
+    // Note, that we don't create NameChangeRequests here because we don't
+    // perform DNS Updates for Solicit. Client must send Request to update
+    // DNS.
 
 
     return (advertise);
     return (advertise);
 }
 }
 
 
 Pkt6Ptr
 Pkt6Ptr
-Dhcpv6Srv::processRequest(const Pkt6Ptr& request,
+Dhcpv6Srv::processRequest(const Pkt6Ptr& request) {
-                          NameChangeRequestPtr& ncr) {
 
 
     sanityCheck(request, MANDATORY, MANDATORY);
     sanityCheck(request, MANDATORY, MANDATORY);
 
 
@@ -1179,16 +1402,16 @@ Dhcpv6Srv::processRequest(const Pkt6Ptr& request,
     appendDefaultOptions(request, reply);
     appendDefaultOptions(request, reply);
     appendRequestedOptions(request, reply);
     appendRequestedOptions(request, reply);
 
 
-    assignLeases(request, reply);
+    Option6ClientFqdnPtr fqdn = processClientFqdn(request);
-
+    assignLeases(request, reply, fqdn);
-    processClientFqdn(request, reply, ncr);
+    appendClientFqdn(request, reply, fqdn);
+    createNameChangeRequests(reply, fqdn);
 
 
     return (reply);
     return (reply);
 }
 }
 
 
 Pkt6Ptr
 Pkt6Ptr
-Dhcpv6Srv::processRenew(const Pkt6Ptr& renew,
+Dhcpv6Srv::processRenew(const Pkt6Ptr& renew) {
-                        NameChangeRequestPtr& ncr) {
 
 
     sanityCheck(renew, MANDATORY, MANDATORY);
     sanityCheck(renew, MANDATORY, MANDATORY);
 
 
@@ -1198,16 +1421,17 @@ Dhcpv6Srv::processRenew(const Pkt6Ptr& renew,
     appendDefaultOptions(renew, reply);
     appendDefaultOptions(renew, reply);
     appendRequestedOptions(renew, reply);
     appendRequestedOptions(renew, reply);
 
 
-    processClientFqdn(renew, reply, ncr);
+    Option6ClientFqdnPtr fqdn = processClientFqdn(renew);
-
+    renewLeases(renew, reply, fqdn);
-    renewLeases(renew, reply);
+    appendClientFqdn(renew, reply, fqdn);
+    createNameChangeRequests(reply, fqdn);
 
 
     return reply;
     return reply;
 }
 }
 
 
 Pkt6Ptr
 Pkt6Ptr
-Dhcpv6Srv::processRebind(const Pkt6Ptr& rebind,
+Dhcpv6Srv::processRebind(const Pkt6Ptr& rebind) {
-                         NameChangeRequestPtr&) {
+
     /// @todo: Implement this
     /// @todo: Implement this
     Pkt6Ptr reply(new Pkt6(DHCPV6_REPLY, rebind->getTransid()));
     Pkt6Ptr reply(new Pkt6(DHCPV6_REPLY, rebind->getTransid()));
     return reply;
     return reply;
@@ -1221,8 +1445,7 @@ Dhcpv6Srv::processConfirm(const Pkt6Ptr& confirm) {
 }
 }
 
 
 Pkt6Ptr
 Pkt6Ptr
-Dhcpv6Srv::processRelease(const Pkt6Ptr& release,
+Dhcpv6Srv::processRelease(const Pkt6Ptr& release) {
-                          NameChangeRequestPtr&) {
 
 
     sanityCheck(release, MANDATORY, MANDATORY);
     sanityCheck(release, MANDATORY, MANDATORY);
 
 
@@ -1236,7 +1459,7 @@ Dhcpv6Srv::processRelease(const Pkt6Ptr& release,
     // @todo If client sent a release and we should remove outstanding
     // @todo If client sent a release and we should remove outstanding
     // DNS records.
     // DNS records.
 
 
-    return reply;
+    return (reply);
 }
 }
 
 
 Pkt6Ptr
 Pkt6Ptr

+ 73 - 27
src/bin/dhcp6/dhcp6_srv.h

@@ -29,6 +29,7 @@
 #include <boost/noncopyable.hpp>
 #include <boost/noncopyable.hpp>
 
 
 #include <iostream>
 #include <iostream>
+#include <queue>
 
 
 namespace isc {
 namespace isc {
 namespace dhcp {
 namespace dhcp {
@@ -130,20 +131,17 @@ protected:
     /// @param request a message received from client
     /// @param request a message received from client
     ///
     ///
     /// @return REPLY message or NULL
     /// @return REPLY message or NULL
-    Pkt6Ptr processRequest(const Pkt6Ptr& request,
+    Pkt6Ptr processRequest(const Pkt6Ptr& request);
-                           isc::d2::NameChangeRequestPtr& ncr);
 
 
     /// @brief Stub function that will handle incoming RENEW messages.
     /// @brief Stub function that will handle incoming RENEW messages.
     ///
     ///
     /// @param renew message received from client
     /// @param renew message received from client
-    Pkt6Ptr processRenew(const Pkt6Ptr& renew,
+    Pkt6Ptr processRenew(const Pkt6Ptr& renew);
-                         isc::d2::NameChangeRequestPtr& ncr);
 
 
     /// @brief Stub function that will handle incoming REBIND messages.
     /// @brief Stub function that will handle incoming REBIND messages.
     ///
     ///
     /// @param rebind message received from client
     /// @param rebind message received from client
-    Pkt6Ptr processRebind(const Pkt6Ptr& rebind,
+    Pkt6Ptr processRebind(const Pkt6Ptr& rebind);
-                          isc::d2::NameChangeRequestPtr& ncr);
 
 
     /// @brief Stub function that will handle incoming CONFIRM messages.
     /// @brief Stub function that will handle incoming CONFIRM messages.
     ///
     ///
@@ -153,8 +151,7 @@ protected:
     /// @brief Stub function that will handle incoming RELEASE messages.
     /// @brief Stub function that will handle incoming RELEASE messages.
     ///
     ///
     /// @param release message received from client
     /// @param release message received from client
-    Pkt6Ptr processRelease(const Pkt6Ptr& release,
+    Pkt6Ptr processRelease(const Pkt6Ptr& release);
-                           isc::d2::NameChangeRequestPtr& ncr);
 
 
     /// @brief Stub function that will handle incoming DECLINE messages.
     /// @brief Stub function that will handle incoming DECLINE messages.
     ///
     ///
@@ -191,11 +188,14 @@ protected:
     /// @param duid client's duid
     /// @param duid client's duid
     /// @param question client's message (typically SOLICIT or REQUEST)
     /// @param question client's message (typically SOLICIT or REQUEST)
     /// @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,
                           isc::dhcp::Pkt6Ptr question,
                           isc::dhcp::Pkt6Ptr question,
-                          boost::shared_ptr<Option6IA> ia);
+                          Option6IAPtr ia,
+                          const Option6ClientFqdnPtr& fqdn);
 
 
     /// @brief Renews specific IA_NA option
     /// @brief Renews specific IA_NA option
     ///
     ///
@@ -207,9 +207,11 @@ protected:
     /// @param duid client's duid
     /// @param duid client's duid
     /// @param question client's message
     /// @param question client's message
     /// @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,
-                         Pkt6Ptr question, boost::shared_ptr<Option6IA> ia);
+                         Pkt6Ptr question, boost::shared_ptr<Option6IA> ia,
+                         const Option6ClientFqdnPtr& fqdn);
 
 
     /// @brief Releases specific IA_NA option
     /// @brief Releases specific IA_NA option
     ///
     ///
@@ -268,7 +270,10 @@ protected:
     ///
     ///
     /// @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 answer server's message (IA_NA options will be added here)
-    void assignLeases(const Pkt6Ptr& question, Pkt6Ptr& answer);
+    /// @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);
 
 
     /// @brief Processes Client FQDN Option.
     /// @brief Processes Client FQDN Option.
     ///
     ///
@@ -288,24 +293,56 @@ protected:
     /// held in this function.
     /// held in this function.
     ///
     ///
     /// @param question Client's message.
     /// @param question Client's message.
-    /// @param answer Server's response to the client.
-    void processClientFqdn(const Pkt6Ptr& question, Pkt6Ptr& answer,
-                           d2::NameChangeRequestPtr& ncr);
-
-    /// @brief Creates a @c isc::d2::NameChangeRequest based on the DHCPv6
-    /// Client FQDN %Option stored in the response to the client.
-    ///
-    /// The @c isc:d2::NameChangeRequest class encapsulates the request from
-    /// the DHCPv6 server to the DHCP-DDNS module to perform DNS Update.
     ///
     ///
-    /// @param answer A response being sent to a client.
+    /// @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.
+    void appendClientFqdn(const Pkt6Ptr& question,
+                          Pkt6Ptr& answer,
+                          const Option6ClientFqdnPtr& fqdn);
+
+    /// @brief Creates a number of @c isc::d2::NameChangeRequest objects based
+    /// on the DHCPv6 Client FQDN %Option.
+    ///
+    /// The @c isc::d2::NameChangeRequest class encapsulates the request from
+    /// the DHCPv6 server to the DHCP-DDNS module to perform DNS Update. The
+    /// FQDN option carries response to the client about DNS updates that
+    /// server intents to perform for the DNS client. Based on this, the
+    /// function will create zero or more @c isc::d2::NameChangeRequest objects
+    /// and store them in the internal queue. Requests created by this function
+    /// are only adding or updating DNS records. In order to generate requests
+    /// for DNS records removal, use @c createRemovalNameChangeRequest.
+    ///
+    /// @param answer A message beging sent to the Client.
     /// @param fqdn_answer A DHCPv6 Client FQDN %Option which is included in the
     /// @param fqdn_answer A DHCPv6 Client FQDN %Option which is included in the
     /// response message sent to a client.
     /// response message sent to a client.
-    /// @param [out] ncr A @c isc::d2::NameChangeRequest object to be sent to
+    void createNameChangeRequests(const Pkt6Ptr& answer,
-    /// the DHCP-DDNS module as a result of the Client FQDN %Option processing.
+                                  const Option6ClientFqdnPtr& fqdn_answer);
-    void createNameChangeRequest(const Pkt6Ptr& answer,
+
-                                 const Option6ClientFqdnPtr& fqdn_answer,
+    /// @brief Creates a @c isc::d2::NameChangeRequest which requests removal
-                                 isc::d2::NameChangeRequestPtr& ncr);
+    /// of DNS entries for a particular lease.
+    ///
+    /// This function should be called upon removal of the lease from the lease
+    /// database, i.e, when client sent Release or Decline message. It will
+    /// create a single @isc::d2::NameChangeRequest which removes the existing
+    /// DNS records for the lease, which server is responsible for. Note that
+    /// this function will not remove the entries which server hadn't added.
+    /// This is the case, when client performs forward DNS update on its own.
+    ///
+    /// @param lease A lease for which the the removal of correponding DNS
+    /// records will be performed.
+    void createRemovalNameChangeRequest(const Lease6Ptr& lease);
 
 
     /// @brief Attempts to renew received addresses
     /// @brief Attempts to renew received addresses
     ///
     ///
@@ -315,7 +352,10 @@ 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
-    void renewLeases(const Pkt6Ptr& renew, Pkt6Ptr& reply);
+    /// @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);
 
 
     /// @brief Attempts to release received addresses
     /// @brief Attempts to release received addresses
     ///
     ///
@@ -377,6 +417,12 @@ private:
     /// Indicates if shutdown is in progress. Setting it to true will
     /// Indicates if shutdown is in progress. Setting it to true will
     /// initiate server shutdown procedure.
     /// initiate server shutdown procedure.
     volatile bool shutdown_;
     volatile bool shutdown_;
+
+protected:
+
+    /// Holds a list of @c isc::d2::NameChangeRequest objects, which
+    /// are waiting for sending to D2 module.
+    std::queue<isc::d2::NameChangeRequest> name_change_reqs_;
 };
 };
 
 
 }; // namespace isc::dhcp
 }; // namespace isc::dhcp

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

@@ -55,6 +55,11 @@ dhcp6_unittests_SOURCES += ../dhcp6_srv.h ../dhcp6_srv.cc
 dhcp6_unittests_SOURCES += ../dhcp6_log.h ../dhcp6_log.cc
 dhcp6_unittests_SOURCES += ../dhcp6_log.h ../dhcp6_log.cc
 dhcp6_unittests_SOURCES += ../ctrl_dhcp6_srv.cc
 dhcp6_unittests_SOURCES += ../ctrl_dhcp6_srv.cc
 dhcp6_unittests_SOURCES += ../config_parser.cc ../config_parser.h
 dhcp6_unittests_SOURCES += ../config_parser.cc ../config_parser.h
+
+# Temporarily compile this file here. It will be removed once libdhcp-ddns
+# is implemented which will include this file and other files required
+# by DHCPv6.
+dhcp6_unittests_SOURCES += ../../d2/ncr_msg.cc ../../d2/ncr_msg.h
 nodist_dhcp6_unittests_SOURCES = ../dhcp6_messages.h ../dhcp6_messages.cc
 nodist_dhcp6_unittests_SOURCES = ../dhcp6_messages.h ../dhcp6_messages.cc
 
 
 dhcp6_unittests_CPPFLAGS = $(AM_CPPFLAGS) $(GTEST_INCLUDES)
 dhcp6_unittests_CPPFLAGS = $(AM_CPPFLAGS) $(GTEST_INCLUDES)

+ 505 - 36
src/bin/dhcp6/tests/dhcp6_srv_unittest.cc

@@ -79,11 +79,14 @@ public:
     using Dhcpv6Srv::processRenew;
     using Dhcpv6Srv::processRenew;
     using Dhcpv6Srv::processRelease;
     using Dhcpv6Srv::processRelease;
     using Dhcpv6Srv::processClientFqdn;
     using Dhcpv6Srv::processClientFqdn;
+    using Dhcpv6Srv::createNameChangeRequests;
+    using Dhcpv6Srv::createRemovalNameChangeRequest;
     using Dhcpv6Srv::createStatusCode;
     using Dhcpv6Srv::createStatusCode;
     using Dhcpv6Srv::selectSubnet;
     using Dhcpv6Srv::selectSubnet;
     using Dhcpv6Srv::sanityCheck;
     using Dhcpv6Srv::sanityCheck;
     using Dhcpv6Srv::loadServerID;
     using Dhcpv6Srv::loadServerID;
     using Dhcpv6Srv::writeServerID;
     using Dhcpv6Srv::writeServerID;
+    using Dhcpv6Srv::name_change_reqs_;
 };
 };
 
 
 static const char* DUID_FILE = "server-id-test.txt";
 static const char* DUID_FILE = "server-id-test.txt";
@@ -246,9 +249,6 @@ public:
 
 
     int rcode_;
     int rcode_;
     ConstElementPtr comment_;
     ConstElementPtr comment_;
-
-    // A NameChangeRequest used in many tests.
-    NameChangeRequestPtr ncr_;
 };
 };
 
 
 // Provides suport for tests against a preconfigured subnet6
 // Provides suport for tests against a preconfigured subnet6
@@ -344,14 +344,31 @@ public:
 
 
 };
 };
 
 
-class FqdnDhcpv6SrvTest : public NakedDhcpv6SrvTest {
+class FqdnDhcpv6SrvTest : public Dhcpv6SrvTest {
 public:
 public:
-    FqdnDhcpv6SrvTest() {
+    FqdnDhcpv6SrvTest()
+        : Dhcpv6SrvTest() {
+        // generateClientId assigns DUID to duid_.
+        generateClientId();
+        lease_.reset(new Lease6(Lease6::LEASE_IA_NA, IOAddress("2001:db8:1::1"),
+                                duid_, 1234, 501, 502, 503,
+                                504, 1, 0));
+
     }
     }
 
 
     virtual ~FqdnDhcpv6SrvTest() {
     virtual ~FqdnDhcpv6SrvTest() {
     }
     }
 
 
+    Option6ClientFqdnPtr
+    createClientFqdn(const uint8_t flags,
+                     const std::string& fqdn_name,
+                     const Option6ClientFqdn::DomainNameType fqdn_type) {
+        return (Option6ClientFqdnPtr(new Option6ClientFqdn(flags,
+                                                           fqdn_name,
+                                                           fqdn_type)));
+    }
+
+    // Create a message holding DHCPv6 Client FQDN Option.
     Pkt6Ptr generatePktWithFqdn(uint8_t msg_type,
     Pkt6Ptr generatePktWithFqdn(uint8_t msg_type,
                                 const uint8_t fqdn_flags,
                                 const uint8_t fqdn_flags,
                                 const std::string& fqdn_domain_name,
                                 const std::string& fqdn_domain_name,
@@ -361,16 +378,23 @@ public:
                                 OptionPtr srvid = OptionPtr()) {
                                 OptionPtr srvid = OptionPtr()) {
         Pkt6Ptr pkt = Pkt6Ptr(new Pkt6(msg_type, 1234));
         Pkt6Ptr pkt = Pkt6Ptr(new Pkt6(msg_type, 1234));
         pkt->setRemoteAddr(IOAddress("fe80::abcd"));
         pkt->setRemoteAddr(IOAddress("fe80::abcd"));
-        pkt->addOption(generateIA(234, 1500, 3000));
+        Option6IAPtr ia = generateIA(234, 1500, 3000);
+
+        if (msg_type != DHCPV6_REPLY) {
+            IOAddress hint("2001:db8:1:1::dead:beef");
+            OptionPtr hint_opt(new Option6IAAddr(D6O_IAADDR, hint, 300, 500));
+            ia->addOption(hint_opt);
+            pkt->addOption(ia);
+        }
+
         OptionPtr clientid = generateClientId();
         OptionPtr clientid = generateClientId();
         pkt->addOption(clientid);
         pkt->addOption(clientid);
         if (srvid && (msg_type != DHCPV6_SOLICIT)) {
         if (srvid && (msg_type != DHCPV6_SOLICIT)) {
             pkt->addOption(srvid);
             pkt->addOption(srvid);
         }
         }
 
 
-        pkt->addOption(OptionPtr(new Option6ClientFqdn(fqdn_flags,
+        pkt->addOption(createClientFqdn(fqdn_flags, fqdn_domain_name,
-                                                       fqdn_domain_name,
+                                        fqdn_type));
-                                                       fqdn_type)));
 
 
         if (include_oro) {
         if (include_oro) {
             OptionUint16ArrayPtr oro(new OptionUint16Array(Option::V6,
             OptionUint16ArrayPtr oro(new OptionUint16Array(Option::V6,
@@ -382,12 +406,65 @@ public:
         return (pkt);
         return (pkt);
     }
     }
 
 
+    // Creates instance of the DHCPv6 message with client id and server id.
+    Pkt6Ptr generateMessageWithIds(const uint8_t msg_type,
+                                   NakedDhcpv6Srv& srv) {
+        Pkt6Ptr pkt = Pkt6Ptr(new Pkt6(msg_type, 1234));
+        // Generate client-id.
+        OptionPtr opt_clientid = generateClientId();
+        pkt->addOption(opt_clientid);
+
+        if (msg_type != DHCPV6_SOLICIT) {
+            // Generate server-id.
+            pkt->addOption(srv.getServerID());
+        }
+
+        return (pkt);
+    }
+
     // Returns an instance of the option carrying FQDN.
     // Returns an instance of the option carrying FQDN.
     Option6ClientFqdnPtr getClientFqdnOption(const Pkt6Ptr& pkt) {
     Option6ClientFqdnPtr getClientFqdnOption(const Pkt6Ptr& pkt) {
         return (boost::dynamic_pointer_cast<Option6ClientFqdn>
         return (boost::dynamic_pointer_cast<Option6ClientFqdn>
                 (pkt->getOption(D6O_CLIENT_FQDN)));
                 (pkt->getOption(D6O_CLIENT_FQDN)));
     }
     }
 
 
+    // Adds IA option to the message. Option holds an address.
+    void addIA(const uint32_t iaid, const IOAddress& addr, Pkt6Ptr& pkt) {
+        Option6IAPtr opt_ia = generateIA(iaid, 1500, 3000);
+        Option6IAAddrPtr opt_iaaddr(new Option6IAAddr(D6O_IAADDR, addr,
+                                                      300, 500));
+        opt_ia->addOption(opt_iaaddr);
+        pkt->addOption(opt_ia);
+    }
+
+    // Adds IA option to the message. Option holds status code.
+    void addIA(const uint32_t iaid, const uint16_t status_code, Pkt6Ptr& pkt) {
+        Option6IAPtr opt_ia = generateIA(iaid, 1500, 3000);
+        addStatusCode(status_code, "", opt_ia);
+        pkt->addOption(opt_ia);
+    }
+
+    // Creates status code with the specified code and message.
+    OptionCustomPtr createStatusCode(const uint16_t code,
+                                     const std::string& msg) {
+        OptionDefinition def("status-code", D6O_STATUS_CODE, "record");
+        def.addRecordField("uint16");
+        def.addRecordField("string");
+        OptionCustomPtr opt_status(new OptionCustom(def, Option::V6));
+        opt_status->writeInteger(code);
+        if (!msg.empty()) {
+            opt_status->writeString(msg, 1);
+        }
+        return (opt_status);
+    }
+
+    // Adds Status Code option to the IA.
+    void addStatusCode(const uint16_t code, const std::string& msg,
+                       Option6IAPtr& opt_ia) {
+        opt_ia->addOption(createStatusCode(code, msg));
+    }
+
+    // Test processing of the DHCPv6 Client FQDN Option.
     void testFqdn(const uint16_t msg_type,
     void testFqdn(const uint16_t msg_type,
                   const bool use_oro,
                   const bool use_oro,
                   const uint8_t in_flags,
                   const uint8_t in_flags,
@@ -403,18 +480,8 @@ public:
                                                use_oro);
                                                use_oro);
         ASSERT_TRUE(getClientFqdnOption(question));
         ASSERT_TRUE(getClientFqdnOption(question));
 
 
-        Pkt6Ptr answer;
+        Option6ClientFqdnPtr answ_fqdn;
-        if (msg_type == DHCPV6_SOLICIT) {
+        ASSERT_NO_THROW(answ_fqdn = srv.processClientFqdn(question));
-            answer.reset(new Pkt6(DHCPV6_ADVERTISE, 1234));
-
-        } else {
-            answer.reset(new Pkt6(DHCPV6_REPLY, 1234));
-
-        }
-
-        ASSERT_NO_THROW(srv.processClientFqdn(question, answer, ncr_));
-
-        Option6ClientFqdnPtr answ_fqdn = getClientFqdnOption(answer);
         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 ?
@@ -431,6 +498,89 @@ public:
         EXPECT_EQ(exp_domain_name, answ_fqdn->getDomainName());
         EXPECT_EQ(exp_domain_name, answ_fqdn->getDomainName());
         EXPECT_EQ(Option6ClientFqdn::FULL, answ_fqdn->getDomainNameType());
         EXPECT_EQ(Option6ClientFqdn::FULL, answ_fqdn->getDomainNameType());
     }
     }
+
+    // Tests that the client message holding an FQDN is processed and the
+    // lease is acquired.
+    void testProcessMessage(const uint8_t msg_type,
+                            const std::string& hostname,
+                            NakedDhcpv6Srv& srv) {
+        // Create a message of a specified type, add server id and
+        // FQDN option.
+        OptionPtr srvid = srv.getServerID();
+        Pkt6Ptr req = generatePktWithFqdn(msg_type, FQDN_FLAG_S,
+                                          hostname,
+                                          Option6ClientFqdn::FULL,
+                                          true, srvid);
+
+        // For different client's message types we have to invoke different
+        // functions to generate response.
+        Pkt6Ptr reply;
+        if (msg_type == DHCPV6_SOLICIT) {
+            ASSERT_NO_THROW(reply = srv.processSolicit(req));
+
+        } else if (msg_type == DHCPV6_REQUEST) {
+            ASSERT_NO_THROW(reply = srv.processRequest(req));
+
+        } else if (msg_type == DHCPV6_RENEW) {
+            ASSERT_NO_THROW(reply = srv.processRequest(req));
+
+        } else if (msg_type == DHCPV6_RELEASE) {
+            // For Release no lease will be acquired so we have to leave
+            // function here.
+            ASSERT_NO_THROW(reply = srv.processRelease(req));
+            return;
+        } else {
+            // We are not interested in testing other message types.
+            return;
+        }
+
+        // For Solicit, we will get different message type obviously.
+        if (msg_type == DHCPV6_SOLICIT) {
+            checkResponse(reply, DHCPV6_ADVERTISE, 1234);
+
+        } else {
+            checkResponse(reply, DHCPV6_REPLY, 1234);
+        }
+
+        // Check verify that IA_NA is correct.
+        Option6IAAddrPtr addr =
+            checkIA_NA(reply, 234, subnet_->getT1(), subnet_->getT2());
+        ASSERT_TRUE(addr);
+
+        // Check that we have got the address we requested.
+        checkIAAddr(addr, IOAddress("2001:db8:1:1::dead:beef"),
+                    subnet_->getPreferred(),
+                    subnet_->getValid());
+
+        if (msg_type != DHCPV6_SOLICIT) {
+            // Check that the lease exists.
+            Lease6Ptr lease =
+                checkLease(duid_, reply->getOption(D6O_IA_NA), addr);
+            ASSERT_TRUE(lease);
+        }
+    }
+
+    // Verify that NameChangeRequest holds valid values.
+    void verifyNameChangeRequest(NakedDhcpv6Srv& srv,
+                                 const isc::d2::NameChangeType type,
+                                 const bool reverse, const bool forward,
+                                 const std::string& addr,
+                                 const std::string& dhcid,
+                                 const uint16_t expires,
+                                 const uint16_t len) {
+        NameChangeRequest ncr = srv.name_change_reqs_.front();
+        EXPECT_EQ(type, ncr.getChangeType());
+        EXPECT_EQ(forward, ncr.isForwardChange());
+        EXPECT_EQ(reverse, ncr.isReverseChange());
+        EXPECT_EQ(addr, ncr.getIpAddress());
+        EXPECT_EQ(dhcid, ncr.getDhcid().toStr());
+        EXPECT_EQ(expires, ncr.getLeaseExpiresOn());
+        EXPECT_EQ(len, ncr.getLeaseLength());
+        EXPECT_EQ(isc::d2::ST_NEW, ncr.getStatus());
+        srv.name_change_reqs_.pop();
+    }
+
+    Lease6Ptr lease_;
 };
 };
 
 
 // This test verifies that incoming SOLICIT can be handled properly when
 // This test verifies that incoming SOLICIT can be handled properly when
@@ -481,7 +631,7 @@ TEST_F(NakedDhcpv6SrvTest, RequestNoSubnet) {
     req->addOption(srv.getServerID());
     req->addOption(srv.getServerID());
 
 
     // Pass it to the server and hope for a REPLY
     // Pass it to the server and hope for a REPLY
-    Pkt6Ptr reply = srv.processRequest(req, ncr_);
+    Pkt6Ptr reply = srv.processRequest(req);
 
 
     // check that we get the right NAK
     // check that we get the right NAK
     checkNakResponse (reply, DHCPV6_REPLY, 1234, STATUS_NoAddrsAvail);
     checkNakResponse (reply, DHCPV6_REPLY, 1234, STATUS_NoAddrsAvail);
@@ -516,7 +666,7 @@ TEST_F(NakedDhcpv6SrvTest, RenewNoSubnet) {
     req->addOption(srv.getServerID());
     req->addOption(srv.getServerID());
 
 
     // Pass it to the server and hope for a REPLY
     // Pass it to the server and hope for a REPLY
-    Pkt6Ptr reply = srv.processRenew(req, ncr_);
+    Pkt6Ptr reply = srv.processRenew(req);
 
 
     // check that we get the right NAK
     // check that we get the right NAK
     checkNakResponse (reply, DHCPV6_REPLY, 1234, STATUS_NoBinding);
     checkNakResponse (reply, DHCPV6_REPLY, 1234, STATUS_NoBinding);
@@ -551,7 +701,7 @@ TEST_F(NakedDhcpv6SrvTest, ReleaseNoSubnet) {
     req->addOption(srv.getServerID());
     req->addOption(srv.getServerID());
 
 
     // Pass it to the server and hope for a REPLY
     // Pass it to the server and hope for a REPLY
-    Pkt6Ptr reply = srv.processRelease(req, ncr_);
+    Pkt6Ptr reply = srv.processRelease(req);
 
 
     // check that we get the right NAK
     // check that we get the right NAK
     checkNakResponse (reply, DHCPV6_REPLY, 1234, STATUS_NoBinding);
     checkNakResponse (reply, DHCPV6_REPLY, 1234, STATUS_NoBinding);
@@ -1019,7 +1169,7 @@ TEST_F(Dhcpv6SrvTest, RequestBasic) {
     req->addOption(srv.getServerID());
     req->addOption(srv.getServerID());
 
 
     // Pass it to the server and hope for a REPLY
     // Pass it to the server and hope for a REPLY
-    Pkt6Ptr reply = srv.processRequest(req, ncr_);
+    Pkt6Ptr reply = srv.processRequest(req);
 
 
     // check if we get response at all
     // check if we get response at all
     checkResponse(reply, DHCPV6_REPLY, 1234);
     checkResponse(reply, DHCPV6_REPLY, 1234);
@@ -1084,9 +1234,9 @@ TEST_F(Dhcpv6SrvTest, ManyRequests) {
     req3->addOption(srv.getServerID());
     req3->addOption(srv.getServerID());
 
 
     // Pass it to the server and get an advertise
     // Pass it to the server and get an advertise
-    Pkt6Ptr reply1 = srv.processRequest(req1, ncr_);
+    Pkt6Ptr reply1 = srv.processRequest(req1);
-    Pkt6Ptr reply2 = srv.processRequest(req2, ncr_);
+    Pkt6Ptr reply2 = srv.processRequest(req2);
-    Pkt6Ptr reply3 = srv.processRequest(req3, ncr_);
+    Pkt6Ptr reply3 = srv.processRequest(req3);
 
 
     // check if we get response at all
     // check if we get response at all
     checkResponse(reply1, DHCPV6_REPLY, 1234);
     checkResponse(reply1, DHCPV6_REPLY, 1234);
@@ -1181,7 +1331,7 @@ TEST_F(Dhcpv6SrvTest, RenewBasic) {
     req->addOption(srv.getServerID());
     req->addOption(srv.getServerID());
 
 
     // Pass it to the server and hope for a REPLY
     // Pass it to the server and hope for a REPLY
-    Pkt6Ptr reply = srv.processRenew(req, ncr_);
+    Pkt6Ptr reply = srv.processRenew(req);
 
 
     // Check if we get response at all
     // Check if we get response at all
     checkResponse(reply, DHCPV6_REPLY, 1234);
     checkResponse(reply, DHCPV6_REPLY, 1234);
@@ -1267,7 +1417,7 @@ TEST_F(Dhcpv6SrvTest, RenewReject) {
     // Case 1: No lease known to server
     // Case 1: No lease known to server
 
 
     // Pass it to the server and hope for a REPLY
     // Pass it to the server and hope for a REPLY
-    Pkt6Ptr reply = srv.processRenew(req, ncr_);
+    Pkt6Ptr reply = srv.processRenew(req);
 
 
     // Check if we get response at all
     // Check if we get response at all
     checkResponse(reply, DHCPV6_REPLY, transid);
     checkResponse(reply, DHCPV6_REPLY, transid);
@@ -1293,7 +1443,7 @@ TEST_F(Dhcpv6SrvTest, RenewReject) {
     ASSERT_TRUE(LeaseMgrFactory::instance().addLease(lease));
     ASSERT_TRUE(LeaseMgrFactory::instance().addLease(lease));
 
 
     // Pass it to the server and hope for a REPLY
     // Pass it to the server and hope for a REPLY
-    reply = srv.processRenew(req, ncr_);
+    reply = srv.processRenew(req);
     checkResponse(reply, DHCPV6_REPLY, transid);
     checkResponse(reply, DHCPV6_REPLY, transid);
     tmp = reply->getOption(D6O_IA_NA);
     tmp = reply->getOption(D6O_IA_NA);
     ASSERT_TRUE(tmp);
     ASSERT_TRUE(tmp);
@@ -1312,7 +1462,7 @@ TEST_F(Dhcpv6SrvTest, RenewReject) {
     req->addOption(generateClientId(13)); // generate different DUID
     req->addOption(generateClientId(13)); // generate different DUID
                                           // (with length 13)
                                           // (with length 13)
 
 
-    reply = srv.processRenew(req, ncr_);
+    reply = srv.processRenew(req);
     checkResponse(reply, DHCPV6_REPLY, transid);
     checkResponse(reply, DHCPV6_REPLY, transid);
     tmp = reply->getOption(D6O_IA_NA);
     tmp = reply->getOption(D6O_IA_NA);
     ASSERT_TRUE(tmp);
     ASSERT_TRUE(tmp);
@@ -1375,7 +1525,7 @@ TEST_F(Dhcpv6SrvTest, ReleaseBasic) {
     req->addOption(srv.getServerID());
     req->addOption(srv.getServerID());
 
 
     // Pass it to the server and hope for a REPLY
     // Pass it to the server and hope for a REPLY
-    Pkt6Ptr reply = srv.processRelease(req, ncr_);
+    Pkt6Ptr reply = srv.processRelease(req);
 
 
     // Check if we get response at all
     // Check if we get response at all
     checkResponse(reply, DHCPV6_REPLY, 1234);
     checkResponse(reply, DHCPV6_REPLY, 1234);
@@ -1453,7 +1603,7 @@ TEST_F(Dhcpv6SrvTest, ReleaseReject) {
     SCOPED_TRACE("CASE 1: No lease known to server");
     SCOPED_TRACE("CASE 1: No lease known to server");
 
 
     // Pass it to the server and hope for a REPLY
     // Pass it to the server and hope for a REPLY
-    Pkt6Ptr reply = srv.processRelease(req, ncr_);
+    Pkt6Ptr reply = srv.processRelease(req);
 
 
     // Check if we get response at all
     // Check if we get response at all
     checkResponse(reply, DHCPV6_REPLY, transid);
     checkResponse(reply, DHCPV6_REPLY, transid);
@@ -1477,7 +1627,7 @@ TEST_F(Dhcpv6SrvTest, ReleaseReject) {
     ASSERT_TRUE(LeaseMgrFactory::instance().addLease(lease));
     ASSERT_TRUE(LeaseMgrFactory::instance().addLease(lease));
 
 
     // Pass it to the server and hope for a REPLY
     // Pass it to the server and hope for a REPLY
-    reply = srv.processRelease(req, ncr_);
+    reply = srv.processRelease(req);
     checkResponse(reply, DHCPV6_REPLY, transid);
     checkResponse(reply, DHCPV6_REPLY, transid);
     tmp = reply->getOption(D6O_IA_NA);
     tmp = reply->getOption(D6O_IA_NA);
     ASSERT_TRUE(tmp);
     ASSERT_TRUE(tmp);
@@ -1500,7 +1650,7 @@ TEST_F(Dhcpv6SrvTest, ReleaseReject) {
     req->addOption(generateClientId(13)); // generate different DUID
     req->addOption(generateClientId(13)); // generate different DUID
                                           // (with length 13)
                                           // (with length 13)
 
 
-    reply = srv.processRelease(req, ncr_);
+    reply = srv.processRelease(req);
     checkResponse(reply, DHCPV6_REPLY, transid);
     checkResponse(reply, DHCPV6_REPLY, transid);
     tmp = reply->getOption(D6O_IA_NA);
     tmp = reply->getOption(D6O_IA_NA);
     ASSERT_TRUE(tmp);
     ASSERT_TRUE(tmp);
@@ -1903,6 +2053,325 @@ TEST_F(FqdnDhcpv6SrvTest, clientAAAAUpdateNotAllowed) {
              "myhost.example.com.");
              "myhost.example.com.");
 }
 }
 
 
+// Test that exception is thrown if supplied NULL answer packet when
+// creating NameChangeRequests.
+TEST_F(FqdnDhcpv6SrvTest, createNameChangeRequestsNoAnswer) {
+    NakedDhcpv6Srv srv(0);
+
+    Pkt6Ptr answer;
+    Option6ClientFqdnPtr fqdn = createClientFqdn(Option6ClientFqdn::FLAG_S,
+                                                 "myhost.example.com",
+                                                 Option6ClientFqdn::FULL);
+    EXPECT_THROW(srv.createNameChangeRequests(answer, fqdn),
+                 isc::Unexpected);
+
+}
+
+// Test that exception is thrown if supplied answer from the server
+// contains no DUID when creating NameChangeRequests.
+TEST_F(FqdnDhcpv6SrvTest, createNameChangeRequestsNoDUID) {
+    NakedDhcpv6Srv srv(0);
+
+    Pkt6Ptr answer = Pkt6Ptr(new Pkt6(DHCPV6_REPLY, 1234));
+    Option6ClientFqdnPtr fqdn = createClientFqdn(Option6ClientFqdn::FLAG_S,
+                                                 "myhost.example.com",
+                                                 Option6ClientFqdn::FULL);
+
+    EXPECT_THROW(srv.createNameChangeRequests(answer, fqdn),
+                 isc::Unexpected);
+
+}
+
+// Test no NameChangeRequests are added if FQDN option is NULL.
+TEST_F(FqdnDhcpv6SrvTest, createNameChangeRequestsNoFQDN) {
+    NakedDhcpv6Srv srv(0);
+
+    // Create Reply message with Client Id and Server id.
+    Pkt6Ptr answer = generateMessageWithIds(DHCPV6_REPLY, srv);
+
+    // Pass NULL FQDN option. No NameChangeRequests should be created.
+    Option6ClientFqdnPtr fqdn;
+    ASSERT_NO_THROW(srv.createNameChangeRequests(answer, fqdn));
+
+    // There should be no new NameChangeRequests.
+    EXPECT_TRUE(srv.name_change_reqs_.empty());
+}
+
+// Test that NameChangeRequests are not generated if an answer message
+// contains no addresses.
+TEST_F(FqdnDhcpv6SrvTest, createNameChangeRequestsNoAddr) {
+    NakedDhcpv6Srv srv(0);
+
+    // Create Reply message with Client Id and Server id.
+    Pkt6Ptr answer = generateMessageWithIds(DHCPV6_REPLY, srv);
+
+    Option6ClientFqdnPtr fqdn = createClientFqdn(Option6ClientFqdn::FLAG_S,
+                                                 "myhost.example.com",
+                                                 Option6ClientFqdn::FULL);
+
+    ASSERT_NO_THROW(srv.createNameChangeRequests(answer, fqdn));
+
+    // We didn't add any IAs, so there should be no NameChangeRequests in th
+    // queue.
+    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_F(FqdnDhcpv6SrvTest, createNameChangeRequests) {
+    NakedDhcpv6Srv srv(0);
+
+    // Create Reply message with Client Id and Server id.
+    Pkt6Ptr answer = generateMessageWithIds(DHCPV6_REPLY, srv);
+
+    // Create three IAs, each having different address.
+    addIA(1234, IOAddress("2001:db8:1::1"), answer);
+    addIA(2345, IOAddress("2001:db8:1::2"), answer);
+    addIA(3456, IOAddress("2001:db8:1::3"), answer);
+
+    Option6ClientFqdnPtr fqdn = createClientFqdn(Option6ClientFqdn::FLAG_S,
+                                                 "myhost.example.com",
+                                                 Option6ClientFqdn::FULL);
+
+    // 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.
+
+    verifyNameChangeRequest(srv, isc::d2::CHG_ADD, true, true, "2001:db8:1::1",
+                            "000201415AA33D1187D148275136FA30300478"
+                            "FAAAA3EBD29826B5C907B2C9268A6F52",
+                            0, 500);
+
+    verifyNameChangeRequest(srv, isc::d2::CHG_ADD, true, true, "2001:db8:1::2",
+                            "000201415AA33D1187D148275136FA30300478"
+                            "FAAAA3EBD29826B5C907B2C9268A6F52",
+                            0, 500);
+
+    verifyNameChangeRequest(srv, isc::d2::CHG_ADD, true, true, "2001:db8:1::3",
+                            "000201415AA33D1187D148275136FA30300478"
+                            "FAAAA3EBD29826B5C907B2C9268A6F52",
+                            0, 500);
+
+}
+
+// Test creation of the NameChangeRequest to remove both forward and reverse
+// mapping for the given lease.
+TEST_F(FqdnDhcpv6SrvTest, createRemovalNameChangeRequestFwdRev) {
+    NakedDhcpv6Srv srv(0);
+
+    lease_->fqdn_fwd_ = true;
+    lease_->fqdn_rev_ = true;
+    lease_->hostname_ = "myhost.example.com.";
+
+    ASSERT_NO_THROW(srv.createRemovalNameChangeRequest(lease_));
+
+    ASSERT_EQ(1, srv.name_change_reqs_.size());
+
+    verifyNameChangeRequest(srv, isc::d2::CHG_REMOVE, true, true,
+                            "2001:db8:1::1",
+                            "000201415AA33D1187D148275136FA30300478"
+                            "FAAAA3EBD29826B5C907B2C9268A6F52",
+                            0, 502);
+
+}
+
+// Test creation of the NameChangeRequest to remove reverse mapping for the
+// given lease.
+TEST_F(FqdnDhcpv6SrvTest, createRemovalNameChangeRequestRev) {
+    NakedDhcpv6Srv srv(0);
+
+    lease_->fqdn_fwd_ = false;
+    lease_->fqdn_rev_ = true;
+    lease_->hostname_ = "myhost.example.com.";
+
+    ASSERT_NO_THROW(srv.createRemovalNameChangeRequest(lease_));
+
+    ASSERT_EQ(1, srv.name_change_reqs_.size());
+
+    verifyNameChangeRequest(srv, isc::d2::CHG_REMOVE, true, false,
+                            "2001:db8:1::1",
+                            "000201415AA33D1187D148275136FA30300478"
+                            "FAAAA3EBD29826B5C907B2C9268A6F52",
+                            0, 502);
+
+}
+
+// Test that NameChangeRequest to remove DNS records is not generated when
+// neither forward nor reverse DNS update has been performed for a lease.
+TEST_F(FqdnDhcpv6SrvTest, createRemovalNameChangeRequestNoUpdate) {
+    NakedDhcpv6Srv srv(0);
+
+    lease_->fqdn_fwd_ = false;
+    lease_->fqdn_rev_ = false;
+
+    ASSERT_NO_THROW(srv.createRemovalNameChangeRequest(lease_));
+
+    EXPECT_TRUE(srv.name_change_reqs_.empty());
+
+}
+
+// Test that NameChangeRequest is not generated if the hostname hasn't been
+// specified for a lease for which forward and reverse mapping has been set.
+TEST_F(FqdnDhcpv6SrvTest, createRemovalNameChangeRequestNoHostname) {
+    NakedDhcpv6Srv srv(0);
+
+    lease_->fqdn_fwd_ = true;
+    lease_->fqdn_rev_ = true;
+    lease_->hostname_ = "";
+
+    ASSERT_NO_THROW(srv.createRemovalNameChangeRequest(lease_));
+
+    EXPECT_TRUE(srv.name_change_reqs_.empty());
+
+}
+
+// Test that NameChangeRequest is not generated if the invalid hostname has
+// been specified for a lease for which forward and reverse mapping has been
+// set.
+TEST_F(FqdnDhcpv6SrvTest, createRemovalNameChangeRequestWrongHostname) {
+    NakedDhcpv6Srv srv(0);
+
+    lease_->fqdn_fwd_ = true;
+    lease_->fqdn_rev_ = true;
+    lease_->hostname_ = "myhost..example.com.";
+
+    ASSERT_NO_THROW(srv.createRemovalNameChangeRequest(lease_));
+
+    EXPECT_TRUE(srv.name_change_reqs_.empty());
+
+}
+
+// Test that Advertise message generated in a response to the Solicit will
+// not result in generation if the NameChangeRequests.
+TEST_F(FqdnDhcpv6SrvTest, processSolicit) {
+    NakedDhcpv6Srv srv(0);
+
+    // Create a Solicit 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_SOLICIT, "myhost.example.com", srv);
+    EXPECT_TRUE(srv.name_change_reqs_.empty());
+}
+
+// Test that client may send two requests, each carrying FQDN option with
+// a different domain-name. Server should use existing lease for the second
+// request but modify the DNS entries for the lease according to the contents
+// of the FQDN sent in the second request.
+TEST_F(FqdnDhcpv6SrvTest, processTwoRequests) {
+    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", srv);
+    ASSERT_EQ(1, srv.name_change_reqs_.size());
+    verifyNameChangeRequest(srv, isc::d2::CHG_ADD, true, true,
+                            "2001:db8:1:1::dead:beef",
+                            "000201415AA33D1187D148275136FA30300478"
+                            "FAAAA3EBD29826B5C907B2C9268A6F52",
+                            0, 4000);
+
+    // Client may send another request message with a new domain-name. In this
+    // case the same lease will be returned. The existing DNS entry needs to
+    // be replaced with a new one. Server should determine that the different
+    // FQDN has been already added to the DNS. As a result, the old DNS
+    // entries should be removed and the entries for the new domain-name
+    // should be added. Therefore, we expect two NameChangeRequests. One to
+    // remove the existing entries, one to add new entries.
+    testProcessMessage(DHCPV6_REQUEST, "otherhost.example.com", srv);
+    ASSERT_EQ(2, srv.name_change_reqs_.size());
+    verifyNameChangeRequest(srv, isc::d2::CHG_REMOVE, true, true,
+                            "2001:db8:1:1::dead:beef",
+                            "000201415AA33D1187D148275136FA30300478"
+                            "FAAAA3EBD29826B5C907B2C9268A6F52",
+                            0, 4000);
+    verifyNameChangeRequest(srv, isc::d2::CHG_ADD, true, true,
+                            "2001:db8:1:1::dead:beef",
+                            "000201D422AA463306223D269B6CB7AFE7AAD265FC"
+                            "EA97F93623019B2E0D14E5323D5A",
+                            0, 4000);
+
+}
+
+// Test that client may send Request followed by the Renew, both holding
+// FQDN options, but each option holding different domain-name. The Renew
+// should result in generation of the two NameChangeRequests, one to remove
+// DNS entry added previously when Request was processed, another one to
+// add a new entry for the FQDN held in the Renew.
+TEST_F(FqdnDhcpv6SrvTest, processRequestRenew) {
+    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", srv);
+    ASSERT_EQ(1, srv.name_change_reqs_.size());
+    verifyNameChangeRequest(srv, isc::d2::CHG_ADD, true, true,
+                            "2001:db8:1:1::dead:beef",
+                            "000201415AA33D1187D148275136FA30300478"
+                            "FAAAA3EBD29826B5C907B2C9268A6F52",
+                            0, 4000);
+
+    // Client may send Renew message with a new domain-name. In this
+    // case the same lease will be returned. The existing DNS entry needs to
+    // be replaced with a new one. Server should determine that the different
+    // FQDN has been already added to the DNS. As a result, the old DNS
+    // entries should be removed and the entries for the new domain-name
+    // should be added. Therefore, we expect two NameChangeRequests. One to
+    // remove the existing entries, one to add new entries.
+    testProcessMessage(DHCPV6_RENEW, "otherhost.example.com", srv);
+    ASSERT_EQ(2, srv.name_change_reqs_.size());
+    verifyNameChangeRequest(srv, isc::d2::CHG_REMOVE, true, true,
+                            "2001:db8:1:1::dead:beef",
+                            "000201415AA33D1187D148275136FA30300478"
+                            "FAAAA3EBD29826B5C907B2C9268A6F52",
+                            0, 4000);
+    verifyNameChangeRequest(srv, isc::d2::CHG_ADD, true, true,
+                            "2001:db8:1:1::dead:beef",
+                            "000201D422AA463306223D269B6CB7AFE7AAD265FC"
+                            "EA97F93623019B2E0D14E5323D5A",
+                            0, 4000);
+
+}
+
+TEST_F(FqdnDhcpv6SrvTest, processRequestRelease) {
+    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", srv);
+    ASSERT_EQ(1, srv.name_change_reqs_.size());
+    verifyNameChangeRequest(srv, isc::d2::CHG_ADD, true, true,
+                            "2001:db8:1:1::dead:beef",
+                            "000201415AA33D1187D148275136FA30300478"
+                            "FAAAA3EBD29826B5C907B2C9268A6F52",
+                            0, 4000);
+
+    // Client may send Release message. In this case the lease should be
+    // removed and all existing DNS entries for this lease should be
+    // also removed. Therefore, we expect that single NameChangeRequest to
+    // remove DNS entries is generated.
+    testProcessMessage(DHCPV6_RELEASE, "otherhost.example.com", srv);
+    ASSERT_EQ(1, srv.name_change_reqs_.size());
+    verifyNameChangeRequest(srv, isc::d2::CHG_REMOVE, true, true,
+                            "2001:db8:1:1::dead:beef",
+                            "000201415AA33D1187D148275136FA30300478"
+                            "FAAAA3EBD29826B5C907B2C9268A6F52",
+                            0, 4000);
+
+}
+
+
 /// @todo: Add more negative tests for processX(), e.g. extend sanityCheck() test
 /// @todo: Add more negative tests for processX(), e.g. extend sanityCheck() test
 /// to call processX() methods.
 /// to call processX() methods.
 
 

+ 7 - 2
src/lib/dhcp/option6_ia.h

@@ -1,4 +1,4 @@
-// Copyright (C) 2011-2012 Internet Systems Consortium, Inc. ("ISC")
+// Copyright (C) 2011-2013 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
@@ -16,12 +16,17 @@
 #define OPTION_IA_H
 #define OPTION_IA_H
 
 
 #include <dhcp/option.h>
 #include <dhcp/option.h>
-
+#include <boost/shared_ptr.hpp>
 #include <stdint.h>
 #include <stdint.h>
 
 
 namespace isc {
 namespace isc {
 namespace dhcp {
 namespace dhcp {
 
 
+class Option6IA;
+
+/// A pointer to the @c Option6IA object.
+typedef boost::shared_ptr<Option6IA> Option6IAPtr;
+
 class Option6IA: public Option {
 class Option6IA: public Option {
 
 
 public:
 public:

+ 6 - 0
src/lib/dhcp/option6_iaaddr.h

@@ -17,10 +17,16 @@
 
 
 #include <asiolink/io_address.h>
 #include <asiolink/io_address.h>
 #include <dhcp/option.h>
 #include <dhcp/option.h>
+#include <boost/shared_ptr.hpp>
 
 
 namespace isc {
 namespace isc {
 namespace dhcp {
 namespace dhcp {
 
 
+class Option6IAAddr;
+
+/// A pointer to the @c isc::dhcp::Option6IAAddr object.
+typedef boost::shared_ptr<Option6IAAddr> Option6IAAddrPtr;
+
 class Option6IAAddr: public Option {
 class Option6IAAddr: public Option {
 
 
 public:
 public:

+ 26 - 7
src/lib/dhcpsrv/alloc_engine.cc

@@ -168,6 +168,9 @@ AllocEngine::allocateAddress6(const Subnet6Ptr& subnet,
                               const DuidPtr& duid,
                               const DuidPtr& duid,
                               uint32_t iaid,
                               uint32_t iaid,
                               const IOAddress& hint,
                               const IOAddress& hint,
+                              const bool fwd_dns_update,
+                              const bool rev_dns_update,
+                              const std::string& hostname,
                               bool fake_allocation /* = false */ ) {
                               bool fake_allocation /* = false */ ) {
 
 
     try {
     try {
@@ -201,7 +204,10 @@ AllocEngine::allocateAddress6(const Subnet6Ptr& subnet,
                 /// implemented
                 /// implemented
 
 
                 // the hint is valid and not currently used, let's create a lease for it
                 // the hint is valid and not currently used, let's create a lease for it
-                Lease6Ptr lease = createLease6(subnet, duid, iaid, hint, fake_allocation);
+                Lease6Ptr lease = createLease6(subnet, duid, iaid,
+                                               hint, fwd_dns_update,
+                                               rev_dns_update, hostname,
+                                               fake_allocation);
 
 
                 // It can happen that the lease allocation failed (we could have lost
                 // 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
                 // the race condition. That means that the hint is lo longer usable and
@@ -212,7 +218,8 @@ AllocEngine::allocateAddress6(const Subnet6Ptr& subnet,
             } else {
             } else {
                 if (existing->expired()) {
                 if (existing->expired()) {
                     return (reuseExpiredLease(existing, subnet, duid, iaid,
                     return (reuseExpiredLease(existing, subnet, duid, iaid,
-                                              fake_allocation));
+                                              fwd_dns_update, rev_dns_update,
+                                              hostname, fake_allocation));
                 }
                 }
 
 
             }
             }
@@ -246,7 +253,8 @@ AllocEngine::allocateAddress6(const Subnet6Ptr& subnet,
                 // there's no existing lease for selected candidate, so it is
                 // there's no existing lease for selected candidate, so it is
                 // free. Let's allocate it.
                 // free. Let's allocate it.
                 Lease6Ptr lease = createLease6(subnet, duid, iaid, candidate,
                 Lease6Ptr lease = createLease6(subnet, duid, iaid, candidate,
-                                              fake_allocation);
+                                               fwd_dns_update, rev_dns_update,
+                                               hostname, fake_allocation);
                 if (lease) {
                 if (lease) {
                     return (lease);
                     return (lease);
                 }
                 }
@@ -257,7 +265,8 @@ AllocEngine::allocateAddress6(const Subnet6Ptr& subnet,
             } else {
             } else {
                 if (existing->expired()) {
                 if (existing->expired()) {
                     return (reuseExpiredLease(existing, subnet, duid, iaid,
                     return (reuseExpiredLease(existing, subnet, duid, iaid,
-                                              fake_allocation));
+                                              fwd_dns_update, rev_dns_update,
+                                              hostname, fake_allocation));
                 }
                 }
             }
             }
 
 
@@ -438,6 +447,9 @@ Lease6Ptr AllocEngine::reuseExpiredLease(Lease6Ptr& expired,
                                          const Subnet6Ptr& subnet,
                                          const Subnet6Ptr& subnet,
                                          const DuidPtr& duid,
                                          const DuidPtr& duid,
                                          uint32_t iaid,
                                          uint32_t iaid,
+                                         const bool fwd_dns_update,
+                                         const bool rev_dns_update,
+                                         const std::string& hostname,
                                          bool fake_allocation /*= false */ ) {
                                          bool fake_allocation /*= false */ ) {
 
 
     if (!expired->expired()) {
     if (!expired->expired()) {
@@ -454,9 +466,9 @@ Lease6Ptr AllocEngine::reuseExpiredLease(Lease6Ptr& expired,
     expired->cltt_ = time(NULL);
     expired->cltt_ = time(NULL);
     expired->subnet_id_ = subnet->getID();
     expired->subnet_id_ = subnet->getID();
     expired->fixed_ = false;
     expired->fixed_ = false;
-    expired->hostname_ = std::string("");
+    expired->hostname_ = hostname;
-    expired->fqdn_fwd_ = false;
+    expired->fqdn_fwd_ = fwd_dns_update;
-    expired->fqdn_rev_ = false;
+    expired->fqdn_rev_ = rev_dns_update;
 
 
     /// @todo: log here that the lease was reused (there's ticket #2524 for
     /// @todo: log here that the lease was reused (there's ticket #2524 for
     /// logging in libdhcpsrv)
     /// logging in libdhcpsrv)
@@ -517,12 +529,19 @@ Lease6Ptr AllocEngine::createLease6(const Subnet6Ptr& subnet,
                                     const DuidPtr& duid,
                                     const DuidPtr& duid,
                                     uint32_t iaid,
                                     uint32_t iaid,
                                     const IOAddress& addr,
                                     const IOAddress& addr,
+                                    const bool fwd_dns_update,
+                                    const bool rev_dns_update,
+                                    const std::string& hostname,
                                     bool fake_allocation /*= false */ ) {
                                     bool fake_allocation /*= false */ ) {
 
 
     Lease6Ptr lease(new Lease6(Lease6::LEASE_IA_NA, addr, duid, iaid,
     Lease6Ptr lease(new Lease6(Lease6::LEASE_IA_NA, addr, duid, iaid,
                                subnet->getPreferred(), subnet->getValid(),
                                subnet->getPreferred(), subnet->getValid(),
                                subnet->getT1(), subnet->getT2(), subnet->getID()));
                                subnet->getT1(), subnet->getT2(), subnet->getID()));
 
 
+    lease->fqdn_fwd_ = fwd_dns_update;
+    lease->fqdn_rev_ = rev_dns_update;
+    lease->hostname_ = hostname;
+
     if (!fake_allocation) {
     if (!fake_allocation) {
         // That is a real (REQUEST) allocation
         // That is a real (REQUEST) allocation
         bool status = LeaseMgrFactory::instance().addLease(lease);
         bool status = LeaseMgrFactory::instance().addLease(lease);

+ 25 - 1
src/lib/dhcpsrv/alloc_engine.h

@@ -233,6 +233,11 @@ protected:
     /// @param duid Client's DUID
     /// @param duid Client's DUID
     /// @param iaid iaid field from the IA_NA container that client sent
     /// @param iaid iaid field from the IA_NA container that client sent
     /// @param hint a hint that the client provided
     /// @param hint a hint that the client provided
+    /// @param fwd_dns_update A boolean value which indicates that server takes
+    /// responisibility for the forward DNS Update for this lease (if true).
+    /// @param rev_dns_update A boolean value which indicates that server takes
+    /// responibility for the reverse DNS Update for this lease (if true).
+    /// @param hostname A fully qualified domain-name of the client.
     /// @param fake_allocation is this real i.e. REQUEST (false) or just picking
     /// @param fake_allocation is this real i.e. REQUEST (false) or just picking
     ///        an address for SOLICIT that is not really allocated (true)
     ///        an address for SOLICIT that is not really allocated (true)
     /// @return Allocated IPv6 lease (or NULL if allocation failed)
     /// @return Allocated IPv6 lease (or NULL if allocation failed)
@@ -241,6 +246,9 @@ protected:
                      const DuidPtr& duid,
                      const DuidPtr& duid,
                      uint32_t iaid,
                      uint32_t iaid,
                      const isc::asiolink::IOAddress& hint,
                      const isc::asiolink::IOAddress& hint,
+                     const bool fwd_dns_update,
+                     const bool rev_dns_update,
+                     const std::string& hostname,
                      bool fake_allocation);
                      bool fake_allocation);
 
 
     /// @brief Destructor. Used during DHCPv6 service shutdown.
     /// @brief Destructor. Used during DHCPv6 service shutdown.
@@ -275,13 +283,21 @@ private:
     /// @param subnet subnet the lease is allocated from
     /// @param subnet subnet the lease is allocated from
     /// @param duid client's DUID
     /// @param duid client's DUID
     /// @param iaid IAID from the IA_NA container the client sent to us
     /// @param iaid IAID from the IA_NA container the client sent to us
-    /// @param addr an address that was selected and is confirmed to be available
+    /// @param addr an address that was selected and is confirmed to be
+    /// available
+    /// @param fwd_dns_update A boolean value which indicates that server takes
+    /// responisibility for the forward DNS Update for this lease (if true).
+    /// @param rev_dns_update A boolean value which indicates that server takes
+    /// responibility for the reverse DNS Update for this lease (if true).
+    /// @param hostname A fully qualified domain-name of the client.
     /// @param fake_allocation is this real i.e. REQUEST (false) or just picking
     /// @param fake_allocation is this real i.e. REQUEST (false) or just picking
     ///        an address for SOLICIT that is not really allocated (true)
     ///        an address for SOLICIT that is not really allocated (true)
     /// @return allocated lease (or NULL in the unlikely case of the lease just
     /// @return allocated lease (or NULL in the unlikely case of the lease just
     ///        becomed unavailable)
     ///        becomed unavailable)
     Lease6Ptr createLease6(const Subnet6Ptr& subnet, const DuidPtr& duid,
     Lease6Ptr createLease6(const Subnet6Ptr& subnet, const DuidPtr& duid,
                            uint32_t iaid, const isc::asiolink::IOAddress& addr,
                            uint32_t iaid, const isc::asiolink::IOAddress& addr,
+                           const bool fwd_dns_update, const bool rev_dns_update,
+                           const std::string& hostname,
                            bool fake_allocation = false);
                            bool fake_allocation = false);
 
 
     /// @brief Reuses expired IPv4 lease
     /// @brief Reuses expired IPv4 lease
@@ -313,12 +329,20 @@ private:
     /// @param subnet subnet the lease is allocated from
     /// @param subnet subnet the lease is allocated from
     /// @param duid client's DUID
     /// @param duid client's DUID
     /// @param iaid IAID from the IA_NA container the client sent to us
     /// @param iaid IAID from the IA_NA container the client sent to us
+    /// @param fwd_dns_update A boolean value which indicates that server takes
+    /// responisibility for the forward DNS Update for this lease (if true).
+    /// @param rev_dns_update A boolean value which indicates that server takes
+    /// responibility for the reverse DNS Update for this lease (if true).
+    /// @param hostname A fully qualified domain-name of the client.
     /// @param fake_allocation is this real i.e. REQUEST (false) or just picking
     /// @param fake_allocation is this real i.e. REQUEST (false) or just picking
     ///        an address for SOLICIT that is not really allocated (true)
     ///        an address for SOLICIT that is not really allocated (true)
     /// @return refreshed lease
     /// @return refreshed lease
     /// @throw BadValue if trying to recycle lease that is still valid
     /// @throw BadValue if trying to recycle lease that is still valid
     Lease6Ptr reuseExpiredLease(Lease6Ptr& expired, const Subnet6Ptr& subnet,
     Lease6Ptr reuseExpiredLease(Lease6Ptr& expired, const Subnet6Ptr& subnet,
                                 const DuidPtr& duid, uint32_t iaid,
                                 const DuidPtr& duid, uint32_t iaid,
+                                const bool fwd_dns_update,
+                                const bool rev_dns_update,
+                                const std::string& hostname,
                                 bool fake_allocation = false);
                                 bool fake_allocation = false);
 
 
     /// @brief a pointer to currently used allocator
     /// @brief a pointer to currently used allocator

+ 24 - 9
src/lib/dhcpsrv/tests/alloc_engine_unittest.cc

@@ -202,7 +202,9 @@ TEST_F(AllocEngine6Test, simpleAlloc6) {
     ASSERT_NO_THROW(engine.reset(new AllocEngine(AllocEngine::ALLOC_ITERATIVE, 100)));
     ASSERT_NO_THROW(engine.reset(new AllocEngine(AllocEngine::ALLOC_ITERATIVE, 100)));
     ASSERT_TRUE(engine);
     ASSERT_TRUE(engine);
 
 
-    Lease6Ptr lease = engine->allocateAddress6(subnet_, duid_, iaid_, IOAddress("::"),
+    Lease6Ptr lease = engine->allocateAddress6(subnet_, duid_, iaid_,
+                                               IOAddress("::"), false,
+                                               false, "",
                                                false);
                                                false);
 
 
     // Check that we got a lease
     // Check that we got a lease
@@ -225,8 +227,9 @@ TEST_F(AllocEngine6Test, fakeAlloc6) {
     ASSERT_NO_THROW(engine.reset(new AllocEngine(AllocEngine::ALLOC_ITERATIVE, 100)));
     ASSERT_NO_THROW(engine.reset(new AllocEngine(AllocEngine::ALLOC_ITERATIVE, 100)));
     ASSERT_TRUE(engine);
     ASSERT_TRUE(engine);
 
 
-    Lease6Ptr lease = engine->allocateAddress6(subnet_, duid_, iaid_, IOAddress("::"),
+    Lease6Ptr lease = engine->allocateAddress6(subnet_, duid_, iaid_,
-                                               true);
+                                               IOAddress("::"), false,
+                                               false, "", true);
 
 
     // Check that we got a lease
     // Check that we got a lease
     ASSERT_TRUE(lease);
     ASSERT_TRUE(lease);
@@ -248,6 +251,7 @@ TEST_F(AllocEngine6Test, allocWithValidHint6) {
 
 
     Lease6Ptr lease = engine->allocateAddress6(subnet_, duid_, iaid_,
     Lease6Ptr lease = engine->allocateAddress6(subnet_, duid_, iaid_,
                                                IOAddress("2001:db8:1::15"),
                                                IOAddress("2001:db8:1::15"),
+                                               false, false, "",
                                                false);
                                                false);
 
 
     // Check that we got a lease
     // Check that we got a lease
@@ -286,6 +290,7 @@ TEST_F(AllocEngine6Test, allocWithUsedHint6) {
     // twice.
     // twice.
     Lease6Ptr lease = engine->allocateAddress6(subnet_, duid_, iaid_,
     Lease6Ptr lease = engine->allocateAddress6(subnet_, duid_, iaid_,
                                                IOAddress("2001:db8:1::1f"),
                                                IOAddress("2001:db8:1::1f"),
+                                               false, false, "",
                                                false);
                                                false);
     // Check that we got a lease
     // Check that we got a lease
     ASSERT_TRUE(lease);
     ASSERT_TRUE(lease);
@@ -319,6 +324,7 @@ TEST_F(AllocEngine6Test, allocBogusHint6) {
     // with the normal allocation
     // with the normal allocation
     Lease6Ptr lease = engine->allocateAddress6(subnet_, duid_, iaid_,
     Lease6Ptr lease = engine->allocateAddress6(subnet_, duid_, iaid_,
                                                IOAddress("3000::abc"),
                                                IOAddress("3000::abc"),
+                                               false, false, "",
                                                false);
                                                false);
     // Check that we got a lease
     // Check that we got a lease
     ASSERT_TRUE(lease);
     ASSERT_TRUE(lease);
@@ -345,12 +351,14 @@ TEST_F(AllocEngine6Test, allocateAddress6Nulls) {
 
 
     // Allocations without subnet are not allowed
     // Allocations without subnet are not allowed
     Lease6Ptr lease = engine->allocateAddress6(Subnet6Ptr(), duid_, iaid_,
     Lease6Ptr lease = engine->allocateAddress6(Subnet6Ptr(), duid_, iaid_,
-                                               IOAddress("::"), false);
+                                               IOAddress("::"),
+                                               false, false, "", false);
     ASSERT_FALSE(lease);
     ASSERT_FALSE(lease);
 
 
     // Allocations without DUID are not allowed either
     // Allocations without DUID are not allowed either
     lease = engine->allocateAddress6(subnet_, DuidPtr(), iaid_,
     lease = engine->allocateAddress6(subnet_, DuidPtr(), iaid_,
-                                     IOAddress("::"), false);
+                                     IOAddress("::"),
+                                     false, false, "", false);
     ASSERT_FALSE(lease);
     ASSERT_FALSE(lease);
 }
 }
 
 
@@ -438,7 +446,9 @@ TEST_F(AllocEngine6Test, smallPool6) {
     subnet_->addPool(pool_);
     subnet_->addPool(pool_);
     cfg_mgr.addSubnet6(subnet_);
     cfg_mgr.addSubnet6(subnet_);
 
 
-    Lease6Ptr lease = engine->allocateAddress6(subnet_, duid_, iaid_, IOAddress("::"),
+    Lease6Ptr lease = engine->allocateAddress6(subnet_, duid_, iaid_,
+                                               IOAddress("::"),
+                                               false, false, "",
                                                false);
                                                false);
 
 
     // Check that we got that single lease
     // Check that we got that single lease
@@ -485,7 +495,8 @@ TEST_F(AllocEngine6Test, outOfAddresses6) {
     // There is just a single address in the pool and allocated it to someone
     // There is just a single address in the pool and allocated it to someone
     // else, so the allocation should fail
     // else, so the allocation should fail
     Lease6Ptr lease2 = engine->allocateAddress6(subnet_, duid_, iaid_,
     Lease6Ptr lease2 = engine->allocateAddress6(subnet_, duid_, iaid_,
-                                                IOAddress("::"), false);
+                                                IOAddress("::"),
+                                                false, false, "", false);
     EXPECT_FALSE(lease2);
     EXPECT_FALSE(lease2);
 }
 }
 
 
@@ -519,6 +530,7 @@ TEST_F(AllocEngine6Test, solicitReuseExpiredLease6) {
 
 
     // CASE 1: Asking for any address
     // CASE 1: Asking for any address
     lease = engine->allocateAddress6(subnet_, duid_, iaid_, IOAddress("::"),
     lease = engine->allocateAddress6(subnet_, duid_, iaid_, IOAddress("::"),
+                                     false, false, "",
                                      true);
                                      true);
     // Check that we got that single lease
     // Check that we got that single lease
     ASSERT_TRUE(lease);
     ASSERT_TRUE(lease);
@@ -528,7 +540,9 @@ TEST_F(AllocEngine6Test, solicitReuseExpiredLease6) {
     checkLease6(lease);
     checkLease6(lease);
 
 
     // CASE 2: Asking specifically for this address
     // CASE 2: Asking specifically for this address
-    lease = engine->allocateAddress6(subnet_, duid_, iaid_, IOAddress(addr.toText()),
+    lease = engine->allocateAddress6(subnet_, duid_, iaid_,
+                                     IOAddress(addr.toText()),
+                                     false, false, "",
                                      true);
                                      true);
     // Check that we got that single lease
     // Check that we got that single lease
     ASSERT_TRUE(lease);
     ASSERT_TRUE(lease);
@@ -563,7 +577,8 @@ TEST_F(AllocEngine6Test, requestReuseExpiredLease6) {
 
 
     // A client comes along, asking specifically for this address
     // A client comes along, asking specifically for this address
     lease = engine->allocateAddress6(subnet_, duid_, iaid_,
     lease = engine->allocateAddress6(subnet_, duid_, iaid_,
-                                     IOAddress(addr.toText()), false);
+                                     IOAddress(addr.toText()),
+                                     false, false, "", false);
 
 
     // Check that he got that single lease
     // Check that he got that single lease
     ASSERT_TRUE(lease);
     ASSERT_TRUE(lease);