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_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
 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
 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)
 This debug message indicates that the server successfully advertised
 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 <asiolink/io_address.h>
+#include <d2/ncr_msg.h>
 #include <dhcp/dhcp6.h>
 #include <dhcp/duid.h>
 #include <dhcp/iface_mgr.h>
@@ -198,15 +199,15 @@ bool Dhcpv6Srv::run() {
                     break;
 
                 case DHCPV6_REQUEST:
-                    rsp = processRequest(query, ncr);
+                    rsp = processRequest(query);
                     break;
 
                 case DHCPV6_RENEW:
-                    rsp = processRenew(query, ncr);
+                    rsp = processRenew(query);
                     break;
 
                 case DHCPV6_REBIND:
-                    rsp = processRebind(query, ncr);
+                    rsp = processRebind(query);
                     break;
 
                 case DHCPV6_CONFIRM:
@@ -214,7 +215,7 @@ bool Dhcpv6Srv::run() {
                     break;
 
                 case DHCPV6_RELEASE:
-                    rsp = processRelease(query, ncr);
+                    rsp = processRelease(query);
                     break;
 
                 case DHCPV6_DECLINE:
@@ -596,7 +597,8 @@ Dhcpv6Srv::selectSubnet(const Pkt6Ptr& question) {
 }
 
 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
     // question (i.e. SOLICIT or REQUEST) message.
@@ -651,7 +653,9 @@ Dhcpv6Srv::assignLeases(const Pkt6Ptr& question, Pkt6Ptr& answer) {
         switch (opt->second->getType()) {
         case D6O_IA_NA: {
             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) {
                 answer->addOption(answer_opt);
             }
@@ -663,15 +667,14 @@ Dhcpv6Srv::assignLeases(const Pkt6Ptr& question, Pkt6Ptr& answer) {
     }
 }
 
-void
-Dhcpv6Srv::processClientFqdn(const Pkt6Ptr& question, Pkt6Ptr& answer,
-                             NameChangeRequestPtr& ncr) {
+Option6ClientFqdnPtr
+Dhcpv6Srv::processClientFqdn(const Pkt6Ptr& question) {
     // Get Client FQDN Option from the client's message. If this option hasn't
     // been included, do nothing.
     Option6ClientFqdnPtr fqdn = boost::dynamic_pointer_cast<
         Option6ClientFqdn>(question->getOption(D6O_CLIENT_FQDN));
     if (!fqdn) {
-        return;
+        return (fqdn);
     }
 
     // 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
     //    to send update on its own when client requested delegation.
     if (!FQDN_ENABLE_UPDATE ||
-        (fqdn->getFlag(Option6ClientFqdn::FLAG_N) && !FQDN_OVERRIDE_NO_UPDATE) ||
-        (!fqdn->getFlag(Option6ClientFqdn::FLAG_S) && !FQDN_ALLOW_CLIENT_UPDATE &&
-         !FQDN_OVERRIDE_CLIENT_UPDATE)) {
+        (fqdn->getFlag(Option6ClientFqdn::FLAG_N) &&
+         !FQDN_OVERRIDE_NO_UPDATE) ||
+        (!fqdn->getFlag(Option6ClientFqdn::FLAG_S) &&
+         !FQDN_ALLOW_CLIENT_UPDATE && !FQDN_OVERRIDE_CLIENT_UPDATE)) {
         fqdn_resp->setFlag(Option6ClientFqdn::FLAG_N, true);
 
     // 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
     // it using Option Request Option. However, server may be configured to
     // 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) {
-        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
-Dhcpv6Srv::createNameChangeRequest(const Pkt6Ptr&,
-                                   const Option6ClientFqdnPtr&,
-                                   isc::d2::NameChangeRequestPtr&) {
-    // @todo Create NameChangeRequest here.
+Dhcpv6Srv::createRemovalNameChangeRequest(const Lease6Ptr& lease) {
+    // If we haven't performed a DNS Update when lease was acquired,
+    // there is nothing to do here.
+    if (!lease->fqdn_fwd_ && !lease->fqdn_rev_) {
+        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
 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
     // 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,
@@ -813,12 +949,41 @@ Dhcpv6Srv::assignIA_NA(const Subnet6Ptr& subnet, const DuidPtr& duid,
         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
     // 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
     // be inserted into the LeaseMgr as well.
-    Lease6Ptr lease = alloc_engine_->allocateAddress6(subnet, duid, ia->getIAID(),
-                                                      hint, fake_allocation);
+    Lease6Ptr lease = alloc_engine_->allocateAddress6(subnet, duid,
+                                                      ia->getIAID(),
+                                                      hint,
+                                                      do_fwd, do_rev,
+                                                      hostname,
+                                                      fake_allocation);
 
     // Create IA_NA that we will put in the response.
     // 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,
         // but this is considered waste of bandwidth as absence of status
         // 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 {
         // Allocation engine did not allocate a lease. The engine logged
         // 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
 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) {
         // 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()));
@@ -902,11 +1084,43 @@ Dhcpv6Srv::renewIA_NA(const Subnet6Ptr& subnet, const DuidPtr& duid,
         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->valid_lft_ = subnet->getValid();
     lease->t1_ = subnet->getT1();
     lease->t2_ = subnet->getT2();
     lease->cltt_ = time(NULL);
+    lease->hostname_ = hostname;
+    lease->fqdn_fwd_ = do_fwd;
+    lease->fqdn_rev_ = do_rev;
 
     LeaseMgrFactory::instance().updateLease6(lease);
 
@@ -924,7 +1138,8 @@ Dhcpv6Srv::renewIA_NA(const Subnet6Ptr& subnet, const DuidPtr& duid,
 }
 
 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
     // RENEW message.
@@ -968,7 +1183,9 @@ Dhcpv6Srv::renewLeases(const Pkt6Ptr& renew, Pkt6Ptr& reply) {
         switch (opt->second->getType()) {
         case D6O_IA_NA: {
             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) {
                 reply->addOption(answer_opt);
             }
@@ -1144,6 +1361,11 @@ Dhcpv6Srv::releaseIA_NA(const DuidPtr& duid, Pkt6Ptr /* question */,
         ia_rsp->addOption(createStatusCode(STATUS_Success,
                           "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);
     }
 }
@@ -1159,17 +1381,18 @@ Dhcpv6Srv::processSolicit(const Pkt6Ptr& solicit) {
     appendDefaultOptions(solicit, advertise);
     appendRequestedOptions(solicit, advertise);
 
-    assignLeases(solicit, advertise);
-
-    NameChangeRequestPtr ncr;
-    processClientFqdn(solicit, advertise, ncr);
+    Option6ClientFqdnPtr fqdn = processClientFqdn(solicit);
+    assignLeases(solicit, advertise, fqdn);
+    appendClientFqdn(solicit, advertise, fqdn);
+    // 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);
 }
 
 Pkt6Ptr
-Dhcpv6Srv::processRequest(const Pkt6Ptr& request,
-                          NameChangeRequestPtr& ncr) {
+Dhcpv6Srv::processRequest(const Pkt6Ptr& request) {
 
     sanityCheck(request, MANDATORY, MANDATORY);
 
@@ -1179,16 +1402,16 @@ Dhcpv6Srv::processRequest(const Pkt6Ptr& request,
     appendDefaultOptions(request, reply);
     appendRequestedOptions(request, reply);
 
-    assignLeases(request, reply);
-
-    processClientFqdn(request, reply, ncr);
+    Option6ClientFqdnPtr fqdn = processClientFqdn(request);
+    assignLeases(request, reply, fqdn);
+    appendClientFqdn(request, reply, fqdn);
+    createNameChangeRequests(reply, fqdn);
 
     return (reply);
 }
 
 Pkt6Ptr
-Dhcpv6Srv::processRenew(const Pkt6Ptr& renew,
-                        NameChangeRequestPtr& ncr) {
+Dhcpv6Srv::processRenew(const Pkt6Ptr& renew) {
 
     sanityCheck(renew, MANDATORY, MANDATORY);
 
@@ -1198,16 +1421,17 @@ Dhcpv6Srv::processRenew(const Pkt6Ptr& renew,
     appendDefaultOptions(renew, reply);
     appendRequestedOptions(renew, reply);
 
-    processClientFqdn(renew, reply, ncr);
-
-    renewLeases(renew, reply);
+    Option6ClientFqdnPtr fqdn = processClientFqdn(renew);
+    renewLeases(renew, reply, fqdn);
+    appendClientFqdn(renew, reply, fqdn);
+    createNameChangeRequests(reply, fqdn);
 
     return reply;
 }
 
 Pkt6Ptr
-Dhcpv6Srv::processRebind(const Pkt6Ptr& rebind,
-                         NameChangeRequestPtr&) {
+Dhcpv6Srv::processRebind(const Pkt6Ptr& rebind) {
+
     /// @todo: Implement this
     Pkt6Ptr reply(new Pkt6(DHCPV6_REPLY, rebind->getTransid()));
     return reply;
@@ -1221,8 +1445,7 @@ Dhcpv6Srv::processConfirm(const Pkt6Ptr& confirm) {
 }
 
 Pkt6Ptr
-Dhcpv6Srv::processRelease(const Pkt6Ptr& release,
-                          NameChangeRequestPtr&) {
+Dhcpv6Srv::processRelease(const Pkt6Ptr& release) {
 
     sanityCheck(release, MANDATORY, MANDATORY);
 
@@ -1236,7 +1459,7 @@ Dhcpv6Srv::processRelease(const Pkt6Ptr& release,
     // @todo If client sent a release and we should remove outstanding
     // DNS records.
 
-    return reply;
+    return (reply);
 }
 
 Pkt6Ptr

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

@@ -29,6 +29,7 @@
 #include <boost/noncopyable.hpp>
 
 #include <iostream>
+#include <queue>
 
 namespace isc {
 namespace dhcp {
@@ -130,20 +131,17 @@ protected:
     /// @param request a message received from client
     ///
     /// @return REPLY message or NULL
-    Pkt6Ptr processRequest(const Pkt6Ptr& request,
-                           isc::d2::NameChangeRequestPtr& ncr);
+    Pkt6Ptr processRequest(const Pkt6Ptr& request);
 
     /// @brief Stub function that will handle incoming RENEW messages.
     ///
     /// @param renew message received from client
-    Pkt6Ptr processRenew(const Pkt6Ptr& renew,
-                         isc::d2::NameChangeRequestPtr& ncr);
+    Pkt6Ptr processRenew(const Pkt6Ptr& renew);
 
     /// @brief Stub function that will handle incoming REBIND messages.
     ///
     /// @param rebind message received from client
-    Pkt6Ptr processRebind(const Pkt6Ptr& rebind,
-                          isc::d2::NameChangeRequestPtr& ncr);
+    Pkt6Ptr processRebind(const Pkt6Ptr& rebind);
 
     /// @brief Stub function that will handle incoming CONFIRM messages.
     ///
@@ -153,8 +151,7 @@ protected:
     /// @brief Stub function that will handle incoming RELEASE messages.
     ///
     /// @param release message received from client
-    Pkt6Ptr processRelease(const Pkt6Ptr& release,
-                           isc::d2::NameChangeRequestPtr& ncr);
+    Pkt6Ptr processRelease(const Pkt6Ptr& release);
 
     /// @brief Stub function that will handle incoming DECLINE messages.
     ///
@@ -191,11 +188,14 @@ protected:
     /// @param duid client's duid
     /// @param question client's message (typically SOLICIT or 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)
     OptionPtr assignIA_NA(const isc::dhcp::Subnet6Ptr& subnet,
                           const isc::dhcp::DuidPtr& duid,
                           isc::dhcp::Pkt6Ptr question,
-                          boost::shared_ptr<Option6IA> ia);
+                          Option6IAPtr ia,
+                          const Option6ClientFqdnPtr& fqdn);
 
     /// @brief Renews specific IA_NA option
     ///
@@ -207,9 +207,11 @@ protected:
     /// @param duid client's duid
     /// @param question client's message
     /// @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)
     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
     ///
@@ -268,7 +270,10 @@ protected:
     ///
     /// @param question client's message (with requested IA_NA)
     /// @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.
     ///
@@ -288,24 +293,56 @@ protected:
     /// held in this function.
     ///
     /// @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
     /// response message sent to a client.
-    /// @param [out] ncr A @c isc::d2::NameChangeRequest object to be sent to
-    /// the DHCP-DDNS module as a result of the Client FQDN %Option processing.
-    void createNameChangeRequest(const Pkt6Ptr& answer,
-                                 const Option6ClientFqdnPtr& fqdn_answer,
-                                 isc::d2::NameChangeRequestPtr& ncr);
+    void createNameChangeRequests(const Pkt6Ptr& answer,
+                                  const Option6ClientFqdnPtr& fqdn_answer);
+
+    /// @brief Creates a @c isc::d2::NameChangeRequest which requests removal
+    /// 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
     ///
@@ -315,7 +352,10 @@ protected:
     /// as IA_NA/IAADDR to reply packet.
     /// @param renew client's message asking for renew
     /// @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
     ///
@@ -377,6 +417,12 @@ private:
     /// Indicates if shutdown is in progress. Setting it to true will
     /// initiate server shutdown procedure.
     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

+ 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 += ../ctrl_dhcp6_srv.cc
 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
 
 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::processRelease;
     using Dhcpv6Srv::processClientFqdn;
+    using Dhcpv6Srv::createNameChangeRequests;
+    using Dhcpv6Srv::createRemovalNameChangeRequest;
     using Dhcpv6Srv::createStatusCode;
     using Dhcpv6Srv::selectSubnet;
     using Dhcpv6Srv::sanityCheck;
     using Dhcpv6Srv::loadServerID;
     using Dhcpv6Srv::writeServerID;
+    using Dhcpv6Srv::name_change_reqs_;
 };
 
 static const char* DUID_FILE = "server-id-test.txt";
@@ -246,9 +249,6 @@ public:
 
     int rcode_;
     ConstElementPtr comment_;
-
-    // A NameChangeRequest used in many tests.
-    NameChangeRequestPtr ncr_;
 };
 
 // Provides suport for tests against a preconfigured subnet6
@@ -344,14 +344,31 @@ public:
 
 };
 
-class FqdnDhcpv6SrvTest : public NakedDhcpv6SrvTest {
+class FqdnDhcpv6SrvTest : public Dhcpv6SrvTest {
 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() {
     }
 
+    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,
                                 const uint8_t fqdn_flags,
                                 const std::string& fqdn_domain_name,
@@ -361,16 +378,23 @@ public:
                                 OptionPtr srvid = OptionPtr()) {
         Pkt6Ptr pkt = Pkt6Ptr(new Pkt6(msg_type, 1234));
         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();
         pkt->addOption(clientid);
         if (srvid && (msg_type != DHCPV6_SOLICIT)) {
             pkt->addOption(srvid);
         }
 
-        pkt->addOption(OptionPtr(new Option6ClientFqdn(fqdn_flags,
-                                                       fqdn_domain_name,
-                                                       fqdn_type)));
+        pkt->addOption(createClientFqdn(fqdn_flags, fqdn_domain_name,
+                                        fqdn_type));
 
         if (include_oro) {
             OptionUint16ArrayPtr oro(new OptionUint16Array(Option::V6,
@@ -382,12 +406,65 @@ public:
         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.
     Option6ClientFqdnPtr getClientFqdnOption(const Pkt6Ptr& pkt) {
         return (boost::dynamic_pointer_cast<Option6ClientFqdn>
                 (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,
                   const bool use_oro,
                   const uint8_t in_flags,
@@ -403,18 +480,8 @@ public:
                                                use_oro);
         ASSERT_TRUE(getClientFqdnOption(question));
 
-        Pkt6Ptr answer;
-        if (msg_type == DHCPV6_SOLICIT) {
-            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);
+        Option6ClientFqdnPtr answ_fqdn;
+        ASSERT_NO_THROW(answ_fqdn = srv.processClientFqdn(question));
         ASSERT_TRUE(answ_fqdn);
 
         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(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
@@ -481,7 +631,7 @@ TEST_F(NakedDhcpv6SrvTest, RequestNoSubnet) {
     req->addOption(srv.getServerID());
 
     // 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
     checkNakResponse (reply, DHCPV6_REPLY, 1234, STATUS_NoAddrsAvail);
@@ -516,7 +666,7 @@ TEST_F(NakedDhcpv6SrvTest, RenewNoSubnet) {
     req->addOption(srv.getServerID());
 
     // 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
     checkNakResponse (reply, DHCPV6_REPLY, 1234, STATUS_NoBinding);
@@ -551,7 +701,7 @@ TEST_F(NakedDhcpv6SrvTest, ReleaseNoSubnet) {
     req->addOption(srv.getServerID());
 
     // 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
     checkNakResponse (reply, DHCPV6_REPLY, 1234, STATUS_NoBinding);
@@ -1019,7 +1169,7 @@ TEST_F(Dhcpv6SrvTest, RequestBasic) {
     req->addOption(srv.getServerID());
 
     // 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
     checkResponse(reply, DHCPV6_REPLY, 1234);
@@ -1084,9 +1234,9 @@ TEST_F(Dhcpv6SrvTest, ManyRequests) {
     req3->addOption(srv.getServerID());
 
     // Pass it to the server and get an advertise
-    Pkt6Ptr reply1 = srv.processRequest(req1, ncr_);
-    Pkt6Ptr reply2 = srv.processRequest(req2, ncr_);
-    Pkt6Ptr reply3 = srv.processRequest(req3, ncr_);
+    Pkt6Ptr reply1 = srv.processRequest(req1);
+    Pkt6Ptr reply2 = srv.processRequest(req2);
+    Pkt6Ptr reply3 = srv.processRequest(req3);
 
     // check if we get response at all
     checkResponse(reply1, DHCPV6_REPLY, 1234);
@@ -1181,7 +1331,7 @@ TEST_F(Dhcpv6SrvTest, RenewBasic) {
     req->addOption(srv.getServerID());
 
     // 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
     checkResponse(reply, DHCPV6_REPLY, 1234);
@@ -1267,7 +1417,7 @@ TEST_F(Dhcpv6SrvTest, RenewReject) {
     // Case 1: No lease known to server
 
     // 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
     checkResponse(reply, DHCPV6_REPLY, transid);
@@ -1293,7 +1443,7 @@ TEST_F(Dhcpv6SrvTest, RenewReject) {
     ASSERT_TRUE(LeaseMgrFactory::instance().addLease(lease));
 
     // Pass it to the server and hope for a REPLY
-    reply = srv.processRenew(req, ncr_);
+    reply = srv.processRenew(req);
     checkResponse(reply, DHCPV6_REPLY, transid);
     tmp = reply->getOption(D6O_IA_NA);
     ASSERT_TRUE(tmp);
@@ -1312,7 +1462,7 @@ TEST_F(Dhcpv6SrvTest, RenewReject) {
     req->addOption(generateClientId(13)); // generate different DUID
                                           // (with length 13)
 
-    reply = srv.processRenew(req, ncr_);
+    reply = srv.processRenew(req);
     checkResponse(reply, DHCPV6_REPLY, transid);
     tmp = reply->getOption(D6O_IA_NA);
     ASSERT_TRUE(tmp);
@@ -1375,7 +1525,7 @@ TEST_F(Dhcpv6SrvTest, ReleaseBasic) {
     req->addOption(srv.getServerID());
 
     // 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
     checkResponse(reply, DHCPV6_REPLY, 1234);
@@ -1453,7 +1603,7 @@ TEST_F(Dhcpv6SrvTest, ReleaseReject) {
     SCOPED_TRACE("CASE 1: No lease known to server");
 
     // 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
     checkResponse(reply, DHCPV6_REPLY, transid);
@@ -1477,7 +1627,7 @@ TEST_F(Dhcpv6SrvTest, ReleaseReject) {
     ASSERT_TRUE(LeaseMgrFactory::instance().addLease(lease));
 
     // Pass it to the server and hope for a REPLY
-    reply = srv.processRelease(req, ncr_);
+    reply = srv.processRelease(req);
     checkResponse(reply, DHCPV6_REPLY, transid);
     tmp = reply->getOption(D6O_IA_NA);
     ASSERT_TRUE(tmp);
@@ -1500,7 +1650,7 @@ TEST_F(Dhcpv6SrvTest, ReleaseReject) {
     req->addOption(generateClientId(13)); // generate different DUID
                                           // (with length 13)
 
-    reply = srv.processRelease(req, ncr_);
+    reply = srv.processRelease(req);
     checkResponse(reply, DHCPV6_REPLY, transid);
     tmp = reply->getOption(D6O_IA_NA);
     ASSERT_TRUE(tmp);
@@ -1903,6 +2053,325 @@ TEST_F(FqdnDhcpv6SrvTest, clientAAAAUpdateNotAllowed) {
              "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
 /// 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
 // purpose with or without fee is hereby granted, provided that the above
@@ -16,12 +16,17 @@
 #define OPTION_IA_H
 
 #include <dhcp/option.h>
-
+#include <boost/shared_ptr.hpp>
 #include <stdint.h>
 
 namespace isc {
 namespace dhcp {
 
+class Option6IA;
+
+/// A pointer to the @c Option6IA object.
+typedef boost::shared_ptr<Option6IA> Option6IAPtr;
+
 class Option6IA: public Option {
 
 public:

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

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

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

@@ -168,6 +168,9 @@ AllocEngine::allocateAddress6(const Subnet6Ptr& subnet,
                               const DuidPtr& duid,
                               uint32_t iaid,
                               const IOAddress& hint,
+                              const bool fwd_dns_update,
+                              const bool rev_dns_update,
+                              const std::string& hostname,
                               bool fake_allocation /* = false */ ) {
 
     try {
@@ -201,7 +204,10 @@ AllocEngine::allocateAddress6(const Subnet6Ptr& subnet,
                 /// implemented
 
                 // 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
                 // the race condition. That means that the hint is lo longer usable and
@@ -212,7 +218,8 @@ AllocEngine::allocateAddress6(const Subnet6Ptr& subnet,
             } else {
                 if (existing->expired()) {
                     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
                 // free. Let's allocate it.
                 Lease6Ptr lease = createLease6(subnet, duid, iaid, candidate,
-                                              fake_allocation);
+                                               fwd_dns_update, rev_dns_update,
+                                               hostname, fake_allocation);
                 if (lease) {
                     return (lease);
                 }
@@ -257,7 +265,8 @@ AllocEngine::allocateAddress6(const Subnet6Ptr& subnet,
             } else {
                 if (existing->expired()) {
                     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 DuidPtr& duid,
                                          uint32_t iaid,
+                                         const bool fwd_dns_update,
+                                         const bool rev_dns_update,
+                                         const std::string& hostname,
                                          bool fake_allocation /*= false */ ) {
 
     if (!expired->expired()) {
@@ -454,9 +466,9 @@ Lease6Ptr AllocEngine::reuseExpiredLease(Lease6Ptr& expired,
     expired->cltt_ = time(NULL);
     expired->subnet_id_ = subnet->getID();
     expired->fixed_ = false;
-    expired->hostname_ = std::string("");
-    expired->fqdn_fwd_ = false;
-    expired->fqdn_rev_ = false;
+    expired->hostname_ = hostname;
+    expired->fqdn_fwd_ = fwd_dns_update;
+    expired->fqdn_rev_ = rev_dns_update;
 
     /// @todo: log here that the lease was reused (there's ticket #2524 for
     /// logging in libdhcpsrv)
@@ -517,12 +529,19 @@ Lease6Ptr AllocEngine::createLease6(const Subnet6Ptr& subnet,
                                     const DuidPtr& duid,
                                     uint32_t iaid,
                                     const IOAddress& addr,
+                                    const bool fwd_dns_update,
+                                    const bool rev_dns_update,
+                                    const std::string& hostname,
                                     bool fake_allocation /*= false */ ) {
 
     Lease6Ptr lease(new Lease6(Lease6::LEASE_IA_NA, addr, duid, iaid,
                                subnet->getPreferred(), subnet->getValid(),
                                subnet->getT1(), subnet->getT2(), subnet->getID()));
 
+    lease->fqdn_fwd_ = fwd_dns_update;
+    lease->fqdn_rev_ = rev_dns_update;
+    lease->hostname_ = hostname;
+
     if (!fake_allocation) {
         // That is a real (REQUEST) allocation
         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 iaid iaid field from the IA_NA container that client sent
     /// @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
     ///        an address for SOLICIT that is not really allocated (true)
     /// @return Allocated IPv6 lease (or NULL if allocation failed)
@@ -241,6 +246,9 @@ protected:
                      const DuidPtr& duid,
                      uint32_t iaid,
                      const isc::asiolink::IOAddress& hint,
+                     const bool fwd_dns_update,
+                     const bool rev_dns_update,
+                     const std::string& hostname,
                      bool fake_allocation);
 
     /// @brief Destructor. Used during DHCPv6 service shutdown.
@@ -275,13 +283,21 @@ private:
     /// @param subnet subnet the lease is allocated from
     /// @param duid client's DUID
     /// @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
     ///        an address for SOLICIT that is not really allocated (true)
     /// @return allocated lease (or NULL in the unlikely case of the lease just
     ///        becomed unavailable)
     Lease6Ptr createLease6(const Subnet6Ptr& subnet, const DuidPtr& duid,
                            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);
 
     /// @brief Reuses expired IPv4 lease
@@ -313,12 +329,20 @@ private:
     /// @param subnet subnet the lease is allocated from
     /// @param duid client's DUID
     /// @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
     ///        an address for SOLICIT that is not really allocated (true)
     /// @return refreshed lease
     /// @throw BadValue if trying to recycle lease that is still valid
     Lease6Ptr reuseExpiredLease(Lease6Ptr& expired, const Subnet6Ptr& subnet,
                                 const DuidPtr& duid, uint32_t iaid,
+                                const bool fwd_dns_update,
+                                const bool rev_dns_update,
+                                const std::string& hostname,
                                 bool fake_allocation = false);
 
     /// @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_TRUE(engine);
 
-    Lease6Ptr lease = engine->allocateAddress6(subnet_, duid_, iaid_, IOAddress("::"),
+    Lease6Ptr lease = engine->allocateAddress6(subnet_, duid_, iaid_,
+                                               IOAddress("::"), false,
+                                               false, "",
                                                false);
 
     // 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_TRUE(engine);
 
-    Lease6Ptr lease = engine->allocateAddress6(subnet_, duid_, iaid_, IOAddress("::"),
-                                               true);
+    Lease6Ptr lease = engine->allocateAddress6(subnet_, duid_, iaid_,
+                                               IOAddress("::"), false,
+                                               false, "", true);
 
     // Check that we got a lease
     ASSERT_TRUE(lease);
@@ -248,6 +251,7 @@ TEST_F(AllocEngine6Test, allocWithValidHint6) {
 
     Lease6Ptr lease = engine->allocateAddress6(subnet_, duid_, iaid_,
                                                IOAddress("2001:db8:1::15"),
+                                               false, false, "",
                                                false);
 
     // Check that we got a lease
@@ -286,6 +290,7 @@ TEST_F(AllocEngine6Test, allocWithUsedHint6) {
     // twice.
     Lease6Ptr lease = engine->allocateAddress6(subnet_, duid_, iaid_,
                                                IOAddress("2001:db8:1::1f"),
+                                               false, false, "",
                                                false);
     // Check that we got a lease
     ASSERT_TRUE(lease);
@@ -319,6 +324,7 @@ TEST_F(AllocEngine6Test, allocBogusHint6) {
     // with the normal allocation
     Lease6Ptr lease = engine->allocateAddress6(subnet_, duid_, iaid_,
                                                IOAddress("3000::abc"),
+                                               false, false, "",
                                                false);
     // Check that we got a lease
     ASSERT_TRUE(lease);
@@ -345,12 +351,14 @@ TEST_F(AllocEngine6Test, allocateAddress6Nulls) {
 
     // Allocations without subnet are not allowed
     Lease6Ptr lease = engine->allocateAddress6(Subnet6Ptr(), duid_, iaid_,
-                                               IOAddress("::"), false);
+                                               IOAddress("::"),
+                                               false, false, "", false);
     ASSERT_FALSE(lease);
 
     // Allocations without DUID are not allowed either
     lease = engine->allocateAddress6(subnet_, DuidPtr(), iaid_,
-                                     IOAddress("::"), false);
+                                     IOAddress("::"),
+                                     false, false, "", false);
     ASSERT_FALSE(lease);
 }
 
@@ -438,7 +446,9 @@ TEST_F(AllocEngine6Test, smallPool6) {
     subnet_->addPool(pool_);
     cfg_mgr.addSubnet6(subnet_);
 
-    Lease6Ptr lease = engine->allocateAddress6(subnet_, duid_, iaid_, IOAddress("::"),
+    Lease6Ptr lease = engine->allocateAddress6(subnet_, duid_, iaid_,
+                                               IOAddress("::"),
+                                               false, false, "",
                                                false);
 
     // 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
     // else, so the allocation should fail
     Lease6Ptr lease2 = engine->allocateAddress6(subnet_, duid_, iaid_,
-                                                IOAddress("::"), false);
+                                                IOAddress("::"),
+                                                false, false, "", false);
     EXPECT_FALSE(lease2);
 }
 
@@ -519,6 +530,7 @@ TEST_F(AllocEngine6Test, solicitReuseExpiredLease6) {
 
     // CASE 1: Asking for any address
     lease = engine->allocateAddress6(subnet_, duid_, iaid_, IOAddress("::"),
+                                     false, false, "",
                                      true);
     // Check that we got that single lease
     ASSERT_TRUE(lease);
@@ -528,7 +540,9 @@ TEST_F(AllocEngine6Test, solicitReuseExpiredLease6) {
     checkLease6(lease);
 
     // 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);
     // Check that we got that single lease
     ASSERT_TRUE(lease);
@@ -563,7 +577,8 @@ TEST_F(AllocEngine6Test, requestReuseExpiredLease6) {
 
     // A client comes along, asking specifically for this address
     lease = engine->allocateAddress6(subnet_, duid_, iaid_,
-                                     IOAddress(addr.toText()), false);
+                                     IOAddress(addr.toText()),
+                                     false, false, "", false);
 
     // Check that he got that single lease
     ASSERT_TRUE(lease);