Browse Source

[3689] hostname from reservation is now applied, if available

Tomek Mrugalski 10 years ago
parent
commit
7996ff0df5

+ 91 - 49
src/bin/dhcp6/dhcp6_srv.cc

@@ -224,6 +224,29 @@ Dhcpv6Srv::testUnicast(const Pkt6Ptr& pkt) const {
     return (true);
 }
 
+DuidPtr Dhcpv6Srv::extractClientId(const Pkt6Ptr& pkt) {
+    // Let's find client's DUID. Client is supposed to include its client-id
+    // option almost all the time (the only exception is an anonymous inf-request,
+    // but that is mostly a theoretical case). Our allocation engine needs DUID
+    // and will refuse to allocate anything to anonymous clients.
+    OptionPtr opt_duid = pkt->getOption(D6O_CLIENTID);
+    if (opt_duid) {
+        return (DuidPtr(new DUID(opt_duid->getData())));
+    } else {
+        return (DuidPtr());
+    }
+}
+
+AllocEngine::ClientContext6 Dhcpv6Srv::createContext(const Pkt6Ptr& pkt) {
+    AllocEngine::ClientContext6 ctx;
+    ctx.subnet_ = selectSubnet(pkt);
+    ctx.duid_ = extractClientId(pkt);
+    ctx.hwaddr_ = getMAC(pkt);
+    alloc_engine_->findReservation(ctx);
+
+    return (ctx);
+}
+
 bool Dhcpv6Srv::run() {
     while (!shutdown_) {
         // client's message and server's response
@@ -378,7 +401,8 @@ bool Dhcpv6Srv::run() {
         classifyPacket(query);
 
         try {
-                NameChangeRequestPtr ncr;
+            NameChangeRequestPtr ncr;
+
             switch (query->getType()) {
             case DHCPV6_SOLICIT:
                 rsp = processSolicit(query);
@@ -719,7 +743,8 @@ Dhcpv6Srv::appendDefaultOptions(const Pkt6Ptr&, Pkt6Ptr& answer) {
 }
 
 void
-Dhcpv6Srv::appendRequestedOptions(const Pkt6Ptr& question, Pkt6Ptr& answer) {
+Dhcpv6Srv::appendRequestedOptions(const Pkt6Ptr& question, Pkt6Ptr& answer,
+                                  const AllocEngine::ClientContext6 ctx) {
 
     // Client requests some options using ORO option. Try to
     // get this option from client's message.
@@ -737,17 +762,12 @@ Dhcpv6Srv::appendRequestedOptions(const Pkt6Ptr& question, Pkt6Ptr& answer) {
     ConstCfgOptionPtr global_opts = CfgMgr::instance().getCurrentCfg()->
         getCfgOption();
 
-    // Get the configured subnet suitable for the incoming packet.
-    // It may be NULL (if server is misconfigured or the client was rejected
-    // using client classes).
-    Subnet6Ptr subnet = selectSubnet(question);
-
     // Get the list of options that client requested.
     const std::vector<uint16_t>& requested_opts = option_oro->getValues();
     BOOST_FOREACH(uint16_t opt, requested_opts) {
         // If we found a subnet for this client, try subnet first.
-        if (subnet) {
-            OptionDescriptor desc = subnet->getCfgOption()->get("dhcp6", opt);
+        if (ctx.subnet_) {
+            OptionDescriptor desc = ctx.subnet_->getCfgOption()->get("dhcp6", opt);
             if (desc.option_) {
                 // Attempt to assign an option from subnet first.
                 answer->addOption(desc.option_);
@@ -947,15 +967,15 @@ Dhcpv6Srv::selectSubnet(const Pkt6Ptr& question) {
 }
 
 void
-Dhcpv6Srv::assignLeases(const Pkt6Ptr& question, Pkt6Ptr& answer) {
+Dhcpv6Srv::assignLeases(const Pkt6Ptr& question, Pkt6Ptr& answer,
+                        const AllocEngine::ClientContext6& ctx) {
 
     // We need to allocate addresses for all IA_NA options in the client's
     // question (i.e. SOLICIT or REQUEST) message.
     // @todo add support for IA_TA
 
     // We need to select a subnet the client is connected in.
-    Subnet6Ptr subnet = selectSubnet(question);
-    if (!subnet) {
+    if (!ctx.subnet_) {
         // This particular client is out of luck today. We do not have
         // information about the subnet he is connected to. This likely means
         // misconfiguration of the server (or some relays). We will continue to
@@ -970,22 +990,12 @@ Dhcpv6Srv::assignLeases(const Pkt6Ptr& question, Pkt6Ptr& answer) {
 
     } else {
         LOG_DEBUG(dhcp6_logger, DBG_DHCP6_DETAIL_DATA, DHCP6_SUBNET_SELECTED)
-            .arg(subnet->toText());
+            .arg(ctx.subnet_->toText());
     }
 
-    // @todo: We should implement Option6Duid some day, but we can do without it
-    // just fine for now
-
-    // Let's find client's DUID. Client is supposed to include its client-id
-    // option almost all the time (the only exception is an anonymous inf-request,
-    // but that is mostly a theoretical case). Our allocation engine needs DUID
-    // and will refuse to allocate anything to anonymous clients.
-    DuidPtr duid;
-    OptionPtr opt_duid = question->getOption(D6O_CLIENTID);
-    if (opt_duid) {
-        duid = DuidPtr(new DUID(opt_duid->getData()));
-    } else {
-        LOG_DEBUG(dhcp6_logger, DBG_DHCP6_BASIC, DHCP6_CLIENTID_MISSING);
+    if (!ctx.duid_) {
+        LOG_DEBUG(dhcp6_logger, DBG_DHCP6_BASIC, DHCP6_CLIENTID_MISSING)
+            .arg(question->getIface());
         // Let's drop the message. This client is not sane.
         isc_throw(RFCViolation, "Mandatory client-id is missing in received message");
     }
@@ -999,7 +1009,8 @@ Dhcpv6Srv::assignLeases(const Pkt6Ptr& question, Pkt6Ptr& answer) {
          opt != question->options_.end(); ++opt) {
         switch (opt->second->getType()) {
         case D6O_IA_NA: {
-            OptionPtr answer_opt = assignIA_NA(subnet, duid, question, answer,
+            OptionPtr answer_opt = assignIA_NA(ctx.subnet_, ctx.duid_, question,
+                                               answer,
                                                boost::dynamic_pointer_cast<
                                                Option6IA>(opt->second));
             if (answer_opt) {
@@ -1008,7 +1019,7 @@ Dhcpv6Srv::assignLeases(const Pkt6Ptr& question, Pkt6Ptr& answer) {
             break;
         }
         case D6O_IA_PD: {
-            OptionPtr answer_opt = assignIA_PD(subnet, duid, question,
+            OptionPtr answer_opt = assignIA_PD(ctx.subnet_, ctx.duid_, question,
                                                boost::dynamic_pointer_cast<
                                                Option6IA>(opt->second));
             if (answer_opt) {
@@ -1022,7 +1033,8 @@ Dhcpv6Srv::assignLeases(const Pkt6Ptr& question, Pkt6Ptr& answer) {
 }
 
 void
-Dhcpv6Srv::processClientFqdn(const Pkt6Ptr& question, const Pkt6Ptr& answer) {
+Dhcpv6Srv::processClientFqdn(const Pkt6Ptr& question, const Pkt6Ptr& answer,
+                             const AllocEngine::ClientContext6 ctx) {
     // Get Client FQDN Option from the client's message. If this option hasn't
     // been included, do nothing.
     Option6ClientFqdnPtr fqdn = boost::dynamic_pointer_cast<
@@ -1037,6 +1049,12 @@ Dhcpv6Srv::processClientFqdn(const Pkt6Ptr& question, const Pkt6Ptr& answer) {
     // response to a client.
     Option6ClientFqdnPtr fqdn_resp(new Option6ClientFqdn(*fqdn));
 
+    // If there's a reservation and it has a hostname specified, use it!
+    if (ctx.host_ && !ctx.host_->getHostname().empty()) {
+        /// @todo: We don't support partial domain names in HR yet.
+        fqdn_resp->setDomainName(ctx.host_->getHostname(), Option6ClientFqdn::FULL);
+    }
+
     // Set the server S, N, and O flags based on client's flags and
     // current configuration.
     D2ClientMgr& d2_mgr = CfgMgr::instance().getD2ClientMgr();
@@ -1920,7 +1938,8 @@ Dhcpv6Srv::extendLeases(const Pkt6Ptr& query, Pkt6Ptr& reply) {
 }
 
 void
-Dhcpv6Srv::releaseLeases(const Pkt6Ptr& release, Pkt6Ptr& reply) {
+Dhcpv6Srv::releaseLeases(const Pkt6Ptr& release, Pkt6Ptr& reply,
+                         AllocEngine::ClientContext6& ctx) {
 
     // We need to release addresses for all IA_NA options in the client's
     // RELEASE message.
@@ -1936,8 +1955,7 @@ Dhcpv6Srv::releaseLeases(const Pkt6Ptr& release, Pkt6Ptr& reply) {
     // option almost all the time (the only exception is an anonymous inf-request,
     // but that is mostly a theoretical case). Our allocation engine needs DUID
     // and will refuse to allocate anything to anonymous clients.
-    OptionPtr opt_duid = release->getOption(D6O_CLIENTID);
-    if (!opt_duid) {
+    if (!ctx.duid_) {
         // This should not happen. We have checked this before.
         // see sanityCheck() called from processRelease()
         LOG_WARN(dhcp6_logger, DHCP6_RELEASE_MISSING_CLIENTID)
@@ -1947,7 +1965,6 @@ Dhcpv6Srv::releaseLeases(const Pkt6Ptr& release, Pkt6Ptr& reply) {
                          "You did not include mandatory client-id"));
         return;
     }
-    DuidPtr duid(new DUID(opt_duid->getData()));
 
     // Let's set the status to be success by default. We can override it with
     // error status if needed. The important thing to understand here is that
@@ -1959,7 +1976,7 @@ Dhcpv6Srv::releaseLeases(const Pkt6Ptr& release, Pkt6Ptr& reply) {
          opt != release->options_.end(); ++opt) {
         switch (opt->second->getType()) {
         case D6O_IA_NA: {
-            OptionPtr answer_opt = releaseIA_NA(duid, release, general_status,
+            OptionPtr answer_opt = releaseIA_NA(ctx.duid_, release, general_status,
                                    boost::dynamic_pointer_cast<Option6IA>(opt->second));
             if (answer_opt) {
                 reply->addOption(answer_opt);
@@ -1967,7 +1984,7 @@ Dhcpv6Srv::releaseLeases(const Pkt6Ptr& release, Pkt6Ptr& reply) {
             break;
         }
         case D6O_IA_PD: {
-            OptionPtr answer_opt = releaseIA_PD(duid, release, general_status,
+            OptionPtr answer_opt = releaseIA_PD(ctx.duid_, release, general_status,
                                    boost::dynamic_pointer_cast<Option6IA>(opt->second));
             if (answer_opt) {
                 reply->addOption(answer_opt);
@@ -2281,20 +2298,25 @@ Dhcpv6Srv::releaseIA_PD(const DuidPtr& duid, const Pkt6Ptr& query,
     return (ia_rsp);
 }
 
+
+
 Pkt6Ptr
 Dhcpv6Srv::processSolicit(const Pkt6Ptr& solicit) {
 
     sanityCheck(solicit, MANDATORY, FORBIDDEN);
 
+    // Let's create a simplified client context here.
+    AllocEngine::ClientContext6 ctx = createContext(solicit);
+
     Pkt6Ptr advertise(new Pkt6(DHCPV6_ADVERTISE, solicit->getTransid()));
 
     copyClientOptions(solicit, advertise);
     appendDefaultOptions(solicit, advertise);
-    appendRequestedOptions(solicit, advertise);
+    appendRequestedOptions(solicit, advertise, ctx);
     appendRequestedVendorOptions(solicit, advertise);
 
-    processClientFqdn(solicit, advertise);
-    assignLeases(solicit, advertise);
+    processClientFqdn(solicit, advertise, ctx);
+    assignLeases(solicit, advertise, ctx);
     // Note, that we don't create NameChangeRequests here because we don't
     // perform DNS Updates for Solicit. Client must send Request to update
     // DNS.
@@ -2309,15 +2331,18 @@ Dhcpv6Srv::processRequest(const Pkt6Ptr& request) {
 
     sanityCheck(request, MANDATORY, MANDATORY);
 
+    // Let's create a simplified client context here.
+    AllocEngine::ClientContext6 ctx = createContext(request);
+
     Pkt6Ptr reply(new Pkt6(DHCPV6_REPLY, request->getTransid()));
 
     copyClientOptions(request, reply);
     appendDefaultOptions(request, reply);
-    appendRequestedOptions(request, reply);
+    appendRequestedOptions(request, reply, ctx);
     appendRequestedVendorOptions(request, reply);
 
-    processClientFqdn(request, reply);
-    assignLeases(request, reply);
+    processClientFqdn(request, reply, ctx);
+    assignLeases(request, reply, ctx);
     generateFqdn(reply);
     createNameChangeRequests(reply);
 
@@ -2329,13 +2354,16 @@ Dhcpv6Srv::processRenew(const Pkt6Ptr& renew) {
 
     sanityCheck(renew, MANDATORY, MANDATORY);
 
+    // Let's create a simplified client context here.
+    AllocEngine::ClientContext6 ctx = createContext(renew);
+
     Pkt6Ptr reply(new Pkt6(DHCPV6_REPLY, renew->getTransid()));
 
     copyClientOptions(renew, reply);
     appendDefaultOptions(renew, reply);
-    appendRequestedOptions(renew, reply);
+    appendRequestedOptions(renew, reply, ctx);
 
-    processClientFqdn(renew, reply);
+    processClientFqdn(renew, reply, ctx);
     extendLeases(renew, reply);
     generateFqdn(reply);
     createNameChangeRequests(reply);
@@ -2346,13 +2374,16 @@ Dhcpv6Srv::processRenew(const Pkt6Ptr& renew) {
 Pkt6Ptr
 Dhcpv6Srv::processRebind(const Pkt6Ptr& rebind) {
 
+    // Let's create a simplified client context here.
+    AllocEngine::ClientContext6 ctx = createContext(rebind);
+
     Pkt6Ptr reply(new Pkt6(DHCPV6_REPLY, rebind->getTransid()));
 
     copyClientOptions(rebind, reply);
     appendDefaultOptions(rebind, reply);
-    appendRequestedOptions(rebind, reply);
+    appendRequestedOptions(rebind, reply, ctx);
 
-    processClientFqdn(rebind, reply);
+    processClientFqdn(rebind, reply, ctx);
     extendLeases(rebind, reply);
     generateFqdn(reply);
     createNameChangeRequests(rebind);
@@ -2362,6 +2393,10 @@ Dhcpv6Srv::processRebind(const Pkt6Ptr& rebind) {
 
 Pkt6Ptr
 Dhcpv6Srv::processConfirm(const Pkt6Ptr& confirm) {
+
+    // Let's create a simplified client context here.
+    AllocEngine::ClientContext6 ctx = createContext(confirm);
+
     // Get IA_NAs from the Confirm. If there are none, the message is
     // invalid and must be discarded. There is nothing more to do.
     OptionCollection ias = confirm->getOptions(D6O_IA_NA);
@@ -2378,9 +2413,10 @@ Dhcpv6Srv::processConfirm(const Pkt6Ptr& confirm) {
     // are verified it means that the client has sent no IA_NA options
     // or no IAAddr options and that client's message has to be discarded.
     bool verified = false;
-    // Check if subnet can be selected for the message. If no subnet
+    // Check if subnet was selected for the message. If no subnet
     // has been selected, the client is not on link.
-    SubnetPtr subnet = selectSubnet(confirm);
+    SubnetPtr subnet = ctx.subnet_;
+
     // Regardless if the subnet has been selected or not, we will iterate
     // over the IA_NA options to check if they hold any addresses. If there
     // are no, the Confirm is discarded.
@@ -2444,12 +2480,15 @@ Dhcpv6Srv::processRelease(const Pkt6Ptr& release) {
 
     sanityCheck(release, MANDATORY, MANDATORY);
 
+    // Let's create a simplified client context here.
+    AllocEngine::ClientContext6 ctx = createContext(release);
+
     Pkt6Ptr reply(new Pkt6(DHCPV6_REPLY, release->getTransid()));
 
     copyClientOptions(release, reply);
     appendDefaultOptions(release, reply);
 
-    releaseLeases(release, reply);
+    releaseLeases(release, reply, ctx);
 
     // @todo If client sent a release and we should remove outstanding
     // DNS records.
@@ -2467,6 +2506,9 @@ Dhcpv6Srv::processDecline(const Pkt6Ptr& decline) {
 Pkt6Ptr
 Dhcpv6Srv::processInfRequest(const Pkt6Ptr& infRequest) {
 
+    // Let's create a simplified client context here.
+    AllocEngine::ClientContext6 ctx = createContext(infRequest);
+
     // Create a Reply packet, with the same trans-id as the client's.
     Pkt6Ptr reply(new Pkt6(DHCPV6_REPLY, infRequest->getTransid()));
 
@@ -2479,7 +2521,7 @@ Dhcpv6Srv::processInfRequest(const Pkt6Ptr& infRequest) {
     appendDefaultOptions(infRequest, reply);
 
     // Try to assign options that were requested by the client.
-    appendRequestedOptions(infRequest, reply);
+    appendRequestedOptions(infRequest, reply, ctx);
 
     return (reply);
 }

+ 31 - 11
src/bin/dhcp6/dhcp6_srv.h

@@ -416,7 +416,9 @@ protected:
     ///
     /// @param question client's message
     /// @param answer server's message (options will be added here)
-    void appendRequestedOptions(const Pkt6Ptr& question, Pkt6Ptr& answer);
+    /// @param ctx client context (contains subnet, duid and other parameters)
+    void appendRequestedOptions(const Pkt6Ptr& question, Pkt6Ptr& answer,
+                                const AllocEngine::ClientContext6 ctx);
 
     /// @brief Appends requested vendor options to server's answer.
     ///
@@ -429,15 +431,16 @@ protected:
 
     /// @brief Assigns leases.
     ///
-    /// It supports addresses (IA_NA) only. It does NOT support temporary
-    /// addresses (IA_TA) nor prefixes (IA_PD).
-    /// @todo: Extend this method once TA and PD becomes supported
+    /// It supports non-temporary addresses (IA_NA) and prefixes (IA_PD). It
+    /// does NOT support temporary addresses (IA_TA).
     ///
-    /// @param question client's message (with requested IA_NA)
-    /// @param answer server's message (IA_NA options will be added here).
-    /// This message should contain Client FQDN option being sent by the server
-    /// to the client (if the client sent this option to the server).
-    void assignLeases(const Pkt6Ptr& question, Pkt6Ptr& answer);
+    /// @param question client's message (with requested IA options)
+    /// @param answer server's message (IA options will be added here).
+    ///   This message should contain Client FQDN option being sent by the server
+    ///   to the client (if the client sent this option to the server).
+    /// @param ctx client context (contains subnet, duid and other parameters)
+    void assignLeases(const Pkt6Ptr& question, Pkt6Ptr& answer,
+                      const AllocEngine::ClientContext6& ctx);
 
     /// @brief Processes Client FQDN Option.
     ///
@@ -460,7 +463,9 @@ protected:
     /// @param answer Server's response to a client. If server generated
     /// Client FQDN option for the client, this option is stored in this
     /// object.
-    void processClientFqdn(const Pkt6Ptr& question, const Pkt6Ptr& answer);
+    /// @param ctx client context (includes subnet, client-id, hw-addr etc.)
+    void processClientFqdn(const Pkt6Ptr& question, const Pkt6Ptr& answer,
+                           const AllocEngine::ClientContext6 ctx);
 
     /// @brief Creates a number of @c isc::dhcp_ddns::NameChangeRequest objects
     /// based on the DHCPv6 Client FQDN %Option.
@@ -518,7 +523,9 @@ protected:
     /// to REPLY packet, just its IA_NA containers.
     /// @param release client's message asking to release
     /// @param reply server's response
-    void releaseLeases(const Pkt6Ptr& release, Pkt6Ptr& reply);
+    /// @param ctx client context (includes subnet, client-id, hw-addr etc.)
+    void releaseLeases(const Pkt6Ptr& release, Pkt6Ptr& reply,
+                       AllocEngine::ClientContext6& ctx);
 
     /// @brief Sets server-identifier.
     ///
@@ -606,6 +613,13 @@ protected:
     /// @return HWaddr pointer (or NULL if configured methods fail)
     static HWAddrPtr getMAC(const Pkt6Ptr& pkt);
 
+    /// @brief Creates client context for specified packet
+    ///
+    /// Creates context that includes subnet, client-id, hw address and
+    /// possibly other parameters.
+    /// @return client context
+    AllocEngine::ClientContext6 createContext(const Pkt6Ptr& pkt);
+
     /// @brief this is a prefix added to the contend of vendor-class option
     ///
     /// If incoming packet has a vendor class option, its content is
@@ -682,6 +696,12 @@ private:
                                const std::string& hostname,
                                bool do_fwd, bool do_rev);
 
+    /// @brief Utility method that extracts DUID from client-id option
+    ///
+    /// @param pkt the message that contains client-id option
+    /// @return extracted DUID (or NULL if client-id is missing)
+    DuidPtr extractClientId(const Pkt6Ptr& pkt);
+
     /// @brief Allocation Engine.
     /// Pointer to the allocation engine that we are currently using
     /// It must be a pointer, because we will support changing engines

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

@@ -113,6 +113,7 @@ public:
     using Dhcpv6Srv::shutdown_;
     using Dhcpv6Srv::name_change_reqs_;
     using Dhcpv6Srv::VENDOR_CLASS_PREFIX;
+    using Dhcpv6Srv::createContext;
 
     /// @brief packets we pretend to receive
     ///

+ 52 - 4
src/bin/dhcp6/tests/fqdn_unittest.cc

@@ -329,7 +329,8 @@ public:
         // Create three IAs, each having different address.
         addIA(1234, IOAddress("2001:db8:1::1"), answer);
 
-        ASSERT_NO_THROW(srv_->processClientFqdn(question, answer));
+        AllocEngine::ClientContext6 ctx;
+        ASSERT_NO_THROW(srv_->processClientFqdn(question, answer, ctx));
         Option6ClientFqdnPtr answ_fqdn = boost::dynamic_pointer_cast<
             Option6ClientFqdn>(answer->getOption(D6O_CLIENT_FQDN));
         ASSERT_TRUE(answ_fqdn);
@@ -393,6 +394,7 @@ public:
                             const std::string& exp_hostname,
                             const uint8_t client_flags =
                                 Option6ClientFqdn::FLAG_S,
+                            const IOAddress expected_address = IOAddress("2001:db8:1:1::dead:beef"),
                             const bool include_oro = true) {
         // Create a message of a specified type, add server id and
         // FQDN option.
@@ -440,7 +442,7 @@ public:
         ASSERT_TRUE(addr);
 
         // Check that we have got the address we requested.
-        checkIAAddr(addr, IOAddress("2001:db8:1:1::dead:beef"),
+        checkIAAddr(addr, expected_address,
                     Lease::TYPE_NA);
 
         if (msg_type != DHCPV6_SOLICIT) {
@@ -505,6 +507,40 @@ public:
         ASSERT_NO_THROW(d2_mgr_.runReadyIO());
     }
 
+    /// @brief Utility function that creates a host reservation (duid)
+    ///
+    /// @param add_to_host_mgr true if the reservation should be added
+    /// @param type specifies reservation type (NA or PD)
+    /// @param addr specifies reserved address
+    /// @param hostname specifies hostname to be used in reservation
+    /// @return created Host object.
+    HostPtr
+    createHost6(bool add_to_host_mgr, IPv6Resrv::Type type,
+                const asiolink::IOAddress& addr, const std::string& hostname) {
+        HostPtr host(new Host(&duid_->getDuid()[0], duid_->getDuid().size(),
+                              Host::IDENT_DUID, SubnetID(0), subnet_->getID(),
+                              asiolink::IOAddress("0.0.0.0"),
+                              hostname));
+
+        // Prefix length doesn't matter here, let's assume address is /128 and
+        // prefix is /64
+        IPv6Resrv resv(type, addr, type == IPv6Resrv::TYPE_NA? 128 : 64);
+        host->addReservation(resv);
+
+        if (add_to_host_mgr) {
+
+            // Let's add the host.
+            CfgMgr::instance().getStagingCfg()->getCfgHosts()->add(host);
+
+            // We also need to add existing subnet
+            CfgMgr::instance().getStagingCfg()->getCfgSubnets6()->add(subnet_);
+
+            // Commit this configuration.
+            CfgMgr::instance().commit();
+        }
+        return (host);
+    }
+
     // Holds a lease used by a test.
     Lease6Ptr lease_;
 
@@ -943,7 +979,8 @@ TEST_F(FqdnDhcpv6SrvTest, processRequestWithoutFqdn) {
     // In this case, we expect that the FQDN option will not be included
     // in the server's response. The testProcessMessage will check that.
     testProcessMessage(DHCPV6_REQUEST, "myhost.example.com",
-                       "myhost.example.com.", Option6ClientFqdn::FLAG_S, false);
+                       "myhost.example.com.", Option6ClientFqdn::FLAG_S,
+                       IOAddress("2001:db8:1:1::dead:beef"), false);
     ASSERT_EQ(1, d2_mgr_.getQueueSize());
     verifyNameChangeRequest(isc::dhcp_ddns::CHG_ADD, true, true,
                             "2001:db8:1:1::dead:beef",
@@ -957,7 +994,8 @@ TEST_F(FqdnDhcpv6SrvTest, processRequestWithoutFqdn) {
 TEST_F(FqdnDhcpv6SrvTest, processRequestEmptyFqdn) {
     testProcessMessage(DHCPV6_REQUEST, "",
                        "myhost-2001-db8-1-1--dead-beef.example.com.",
-                       Option6ClientFqdn::FLAG_S, false);
+                       Option6ClientFqdn::FLAG_S,
+                       IOAddress("2001:db8:1:1::dead:beef"), false);
     ASSERT_EQ(1, d2_mgr_.getQueueSize());
     verifyNameChangeRequest(isc::dhcp_ddns::CHG_ADD, true, true,
                             "2001:db8:1:1::dead:beef",
@@ -1050,5 +1088,15 @@ TEST_F(FqdnDhcpv6SrvTest, processClientDelegation) {
                             0, 4000);
 }
 
+TEST_F(FqdnDhcpv6SrvTest, hostnameReservation) {
+
+    createHost6(true, IPv6Resrv::TYPE_NA, IOAddress("2001:db8:1:1::babe"),
+                "alice.example.org.");
+
+    testProcessMessage(DHCPV6_REQUEST, "myhost.example.com",
+                       "alice.example.org.", 0, IOAddress("2001:db8:1:1::babe"));
+    ASSERT_EQ(1, d2_mgr_.getQueueSize());
+}
+
 
 }   // end of anonymous namespace

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

@@ -319,10 +319,8 @@ AllocEngine::ClientContext6::ClientContext6(const Subnet6Ptr& subnet, const Duid
 
 
 void AllocEngine::findReservation(ClientContext6& ctx) const {
-    if (!ctx.subnet_) {
-        isc_throw(InvalidOperation, "Subnet is required for IPv6 lease allocation");
-    } else if (!ctx.duid_) {
-        isc_throw(InvalidOperation, "DUID is mandatory for IPv6 lease allocation");
+    if (!ctx.subnet_ || !ctx.duid_) {
+        return;
     }
 
     // Check which host reservation mode is supported in this subnet.