From: Tomek Mrugalski Date: Fri, 6 Mar 2015 19:35:16 +0000 (+0100) Subject: [3689] hostname from reservation is now applied, if available X-Git-Tag: kea-0.9.1~8^2~13 X-Git-Url: http://git.ipfire.org/gitweb.cgi?a=commitdiff_plain;h=7996ff0df528fc1c89f2cd088d7802c8cb18e09e;p=thirdparty%2Fkea.git [3689] hostname from reservation is now applied, if available --- diff --git a/src/bin/dhcp6/dhcp6_srv.cc b/src/bin/dhcp6/dhcp6_srv.cc index fe9f7099f5..1824d94581 100644 --- a/src/bin/dhcp6/dhcp6_srv.cc +++ b/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& 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(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(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); } diff --git a/src/bin/dhcp6/dhcp6_srv.h b/src/bin/dhcp6/dhcp6_srv.h index 2f294de5bd..3442f15ebc 100644 --- a/src/bin/dhcp6/dhcp6_srv.h +++ b/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 diff --git a/src/bin/dhcp6/tests/dhcp6_test_utils.h b/src/bin/dhcp6/tests/dhcp6_test_utils.h index 79839fc615..4896d5dde1 100644 --- a/src/bin/dhcp6/tests/dhcp6_test_utils.h +++ b/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 /// diff --git a/src/bin/dhcp6/tests/fqdn_unittest.cc b/src/bin/dhcp6/tests/fqdn_unittest.cc index c87f7a7577..3a263b0d33 100644 --- a/src/bin/dhcp6/tests/fqdn_unittest.cc +++ b/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 diff --git a/src/lib/dhcpsrv/alloc_engine.cc b/src/lib/dhcpsrv/alloc_engine.cc index e9b74aa96b..6cbb7ae43d 100644 --- a/src/lib/dhcpsrv/alloc_engine.cc +++ b/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.