diff options
Diffstat (limited to 'src/bin/dhcp4/dhcp4_srv.cc')
-rw-r--r-- | src/bin/dhcp4/dhcp4_srv.cc | 179 |
1 files changed, 133 insertions, 46 deletions
diff --git a/src/bin/dhcp4/dhcp4_srv.cc b/src/bin/dhcp4/dhcp4_srv.cc index 483d12caf8..ea74b135ab 100644 --- a/src/bin/dhcp4/dhcp4_srv.cc +++ b/src/bin/dhcp4/dhcp4_srv.cc @@ -151,10 +151,11 @@ namespace dhcp { Dhcpv4Exchange::Dhcpv4Exchange(const AllocEnginePtr& alloc_engine, const Pkt4Ptr& query, + AllocEngine::ClientContext4Ptr& context, const Subnet4Ptr& subnet, bool& drop) : alloc_engine_(alloc_engine), query_(query), resp_(), - context_(new AllocEngine::ClientContext4()) { + context_(context) { if (!alloc_engine_) { isc_throw(BadValue, "alloc_engine value must not be NULL" @@ -165,31 +166,35 @@ Dhcpv4Exchange::Dhcpv4Exchange(const AllocEnginePtr& alloc_engine, isc_throw(BadValue, "query value must not be NULL when" " creating an instance of the Dhcpv4Exchange"); } + + // Reset the given context argument. + context.reset(); + // Create response message. initResponse(); // Select subnet for the query message. context_->subnet_ = subnet; - // Hardware address. - context_->hwaddr_ = query->getHWAddr(); - // Pointer to client's query. - context_->query_ = query; // If subnet found, retrieve client identifier which will be needed // for allocations and search for reservations associated with a // subnet/shared network. SharedNetwork4Ptr sn; - if (subnet) { + if (subnet && !context_->early_global_reservations_lookup_) { OptionPtr opt_clientid = query->getOption(DHO_DHCP_CLIENT_IDENTIFIER); if (opt_clientid) { context_->clientid_.reset(new ClientId(opt_clientid->getData())); } + } + if (subnet) { // Find static reservations if not disabled for our subnet. if (subnet->getReservationsInSubnet() || subnet->getReservationsGlobal()) { // Before we can check for static reservations, we need to prepare a set // of identifiers to be used for this. - setHostIdentifiers(); + if (!context_->early_global_reservations_lookup_) { + setHostIdentifiers(context_); + } // Check for static reservations. alloc_engine->findReservation(*context_); @@ -207,7 +212,8 @@ Dhcpv4Exchange::Dhcpv4Exchange(const AllocEnginePtr& alloc_engine, // affect selection of a pool within the selected subnet. auto global_host = context_->globalHost(); auto current_host = context_->currentHost(); - if ((global_host && !global_host->getClientClasses4().empty()) || + if ((!context_->early_global_reservations_lookup_ && + global_host && !global_host->getClientClasses4().empty()) || (!sn && current_host && !current_host->getClientClasses4().empty())) { // We have already evaluated client classes and some of them may // be in conflict with the reserved classes. Suppose there are @@ -223,19 +229,9 @@ Dhcpv4Exchange::Dhcpv4Exchange(const AllocEnginePtr& alloc_engine, // a result, the first_class set via the host reservation will // replace the second_class because the second_class will this // time evaluate to false as desired. - const ClientClassDictionaryPtr& dict = - CfgMgr::instance().getCurrentCfg()->getClientClassDictionary(); - const ClientClassDefListPtr& defs_ptr = dict->getClasses(); - for (auto def : *defs_ptr) { - // Only remove evaluated classes. Other classes can be - // assigned via hooks libraries and we should not remove - // them because there is no way they can be added back. - if (def->getMatchExpr()) { - context_->query_->classes_.erase(def->getName()); - } - } + removeDependentEvaluatedClasses(query); setReservedClientClasses(context_); - evaluateClasses(context_->query_, false); + evaluateClasses(query, false); } // Set KNOWN builtin class if something was found, UNKNOWN if not. @@ -392,7 +388,7 @@ Dhcpv4Exchange::copyDefaultOptions() { } void -Dhcpv4Exchange::setHostIdentifiers() { +Dhcpv4Exchange::setHostIdentifiers(AllocEngine::ClientContext4Ptr context) { const ConstCfgHostOperationsPtr cfg = CfgMgr::instance().getCurrentCfg()->getCfgHostOperations4(); @@ -402,23 +398,23 @@ Dhcpv4Exchange::setHostIdentifiers() { cfg->getIdentifierTypes()) { switch (id_type) { case Host::IDENT_HWADDR: - if (context_->hwaddr_ && !context_->hwaddr_->hwaddr_.empty()) { - context_->addHostIdentifier(id_type, context_->hwaddr_->hwaddr_); + if (context->hwaddr_ && !context->hwaddr_->hwaddr_.empty()) { + context->addHostIdentifier(id_type, context->hwaddr_->hwaddr_); } break; case Host::IDENT_DUID: - if (context_->clientid_) { - const std::vector<uint8_t>& vec = context_->clientid_->getDuid(); + if (context->clientid_) { + const std::vector<uint8_t>& vec = context->clientid_->getDuid(); if (!vec.empty()) { // Client identifier type = DUID? Client identifier holding a DUID // comprises Type (1 byte), IAID (4 bytes), followed by the actual // DUID. Thus, the minimal length is 6. if ((vec[0] == CLIENT_ID_OPTION_TYPE_DUID) && (vec.size() > 5)) { // Extract DUID, skip IAID. - context_->addHostIdentifier(id_type, - std::vector<uint8_t>(vec.begin() + 5, - vec.end())); + context->addHostIdentifier(id_type, + std::vector<uint8_t>(vec.begin() + 5, + vec.end())); } } } @@ -426,13 +422,13 @@ Dhcpv4Exchange::setHostIdentifiers() { case Host::IDENT_CIRCUIT_ID: { - OptionPtr rai = query_->getOption(DHO_DHCP_AGENT_OPTIONS); + OptionPtr rai = context->query_->getOption(DHO_DHCP_AGENT_OPTIONS); if (rai) { OptionPtr circuit_id_opt = rai->getOption(RAI_OPTION_AGENT_CIRCUIT_ID); if (circuit_id_opt) { const OptionBuffer& circuit_id_vec = circuit_id_opt->getData(); if (!circuit_id_vec.empty()) { - context_->addHostIdentifier(id_type, circuit_id_vec); + context->addHostIdentifier(id_type, circuit_id_vec); } } } @@ -440,10 +436,10 @@ Dhcpv4Exchange::setHostIdentifiers() { break; case Host::IDENT_CLIENT_ID: - if (context_->clientid_) { - const std::vector<uint8_t>& vec = context_->clientid_->getDuid(); + if (context->clientid_) { + const std::vector<uint8_t>& vec = context->clientid_->getDuid(); if (!vec.empty()) { - context_->addHostIdentifier(id_type, vec); + context->addHostIdentifier(id_type, vec); } } break; @@ -453,7 +449,7 @@ Dhcpv4Exchange::setHostIdentifiers() { break; } - CalloutHandlePtr callout_handle = getCalloutHandle(context_->query_); + CalloutHandlePtr callout_handle = getCalloutHandle(context->query_); Host::IdentifierType type = Host::IDENT_FLEX; std::vector<uint8_t> id; @@ -465,7 +461,7 @@ Dhcpv4Exchange::setHostIdentifiers() { ScopedCalloutHandleState callout_handle_state(callout_handle); // Pass incoming packet as argument - callout_handle->setArgument("query4", context_->query_); + callout_handle->setArgument("query4", context->query_); callout_handle->setArgument("id_type", type); callout_handle->setArgument("id_value", id); @@ -482,7 +478,7 @@ Dhcpv4Exchange::setHostIdentifiers() { LOG_DEBUG(packet4_logger, DBGLVL_TRACE_BASIC, DHCP4_FLEX_ID) .arg(Host::getIdentifierAsText(type, &id[0], id.size())); - context_->addHostIdentifier(type, id); + context->addHostIdentifier(type, id); } break; } @@ -493,6 +489,21 @@ Dhcpv4Exchange::setHostIdentifiers() { } void +Dhcpv4Exchange::removeDependentEvaluatedClasses(const Pkt4Ptr& query) { + const ClientClassDictionaryPtr& dict = + CfgMgr::instance().getCurrentCfg()->getClientClassDictionary(); + const ClientClassDefListPtr& defs_ptr = dict->getClasses(); + for (auto def : *defs_ptr) { + // Only remove evaluated classes. Other classes can be + // assigned via hooks libraries and we should not remove + // them because there is no way they can be added back. + if (def->getMatchExpr()) { + query->classes_.erase(def->getName()); + } + } +} + +void Dhcpv4Exchange::setReservedClientClasses(AllocEngine::ClientContext4Ptr context) { if (context->currentHost() && context->query_) { const ClientClasses& classes = context->currentHost()->getClientClasses4(); @@ -934,6 +945,79 @@ Dhcpv4Srv::sendPacket(const Pkt4Ptr& packet) { IfaceMgr::instance().send(packet); } +bool +Dhcpv4Srv::earlyGHRLookup(const Pkt4Ptr& query, + AllocEngine::ClientContext4Ptr ctx) { + // Pointer to client's query. + ctx->query_ = query; + + // Hardware address. + ctx->hwaddr_ = query->getHWAddr(); + + // Get the early-global-reservations-lookup flag value. + data::ConstElementPtr egrl = CfgMgr::instance().getCurrentCfg()-> + getConfiguredGlobal(CfgGlobals::EARLY_GLOBAL_RESERVATIONS_LOOKUP); + if (egrl) { + ctx->early_global_reservations_lookup_ = egrl->boolValue(); + } + + // Perform early global reservations lookup when wanted. + if (ctx->early_global_reservations_lookup_) { + // Retrieve retrieve client identifier. + OptionPtr opt_clientid = query->getOption(DHO_DHCP_CLIENT_IDENTIFIER); + if (opt_clientid) { + ctx->clientid_.reset(new ClientId(opt_clientid->getData())); + } + + // Get the host identifiers. + Dhcpv4Exchange::setHostIdentifiers(ctx); + + // Check for global host reservations. + ConstHostPtr global_host = alloc_engine_->findGlobalReservation(*ctx); + + if (global_host && !global_host->getClientClasses4().empty()) { + // Remove dependent evaluated classes. + Dhcpv4Exchange::removeDependentEvaluatedClasses(query); + + // Add classes from the global reservations. + const ClientClasses& classes = global_host->getClientClasses4(); + for (ClientClasses::const_iterator cclass = classes.cbegin(); + cclass != classes.cend(); ++cclass) { + query->addClass(*cclass); + } + + // Evaluate classes before KNOWN. + Dhcpv4Exchange::evaluateClasses(query, false); + } + + if (global_host) { + // Add the KNOWN class; + query->addClass("KNOWN"); + LOG_DEBUG(dhcp4_logger, DBG_DHCP4_BASIC, DHCP4_CLASS_ASSIGNED) + .arg(query->getLabel()) + .arg("KNOWN"); + + // Evaluate classes after KNOWN. + Dhcpv4Exchange::evaluateClasses(query, true); + + // Check the DROP special class. + if (query->inClass("DROP")) { + LOG_DEBUG(packet4_logger, DBGLVL_PKT_HANDLING, + DHCP4_PACKET_DROP_0014) + .arg(query->toText()); + isc::stats::StatsMgr::instance().addValue("pkt4-receive-drop", + static_cast<int64_t>(1)); + return (false); + } + + // Store the reservation. + ctx->hosts_[SUBNET_ID_GLOBAL] = global_host; + } + } + + return (true); +} + int Dhcpv4Srv::run() { #ifdef ENABLE_AFL @@ -1281,12 +1365,15 @@ Dhcpv4Srv::processDhcp4Query(Pkt4Ptr& query, Pkt4Ptr& rsp, } } - AllocEngine::ClientContext4Ptr ctx; + AllocEngine::ClientContext4Ptr ctx(new AllocEngine::ClientContext4()); + if (!earlyGHRLookup(query, ctx)) { + return; + } try { switch (query->getType()) { case DHCPDISCOVER: - rsp = processDiscover(query); + rsp = processDiscover(query, ctx); break; case DHCPREQUEST: @@ -1305,7 +1392,7 @@ Dhcpv4Srv::processDhcp4Query(Pkt4Ptr& query, Pkt4Ptr& rsp, break; case DHCPINFORM: - rsp = processInform(query); + rsp = processInform(query, ctx); break; default: @@ -1363,8 +1450,8 @@ Dhcpv4Srv::processDhcp4Query(Pkt4Ptr& query, Pkt4Ptr& rsp, if (allow_packet_park) { // Get the parking limit. Parsing should ensure the value is present. uint32_t parked_packet_limit = 0; - data::ConstElementPtr ppl = CfgMgr::instance(). - getCurrentCfg()->getConfiguredGlobal("parked-packet-limit"); + data::ConstElementPtr ppl = CfgMgr::instance().getCurrentCfg()-> + getConfiguredGlobal(CfgGlobals::PARKED_PACKET_LIMIT); if (ppl) { parked_packet_limit = ppl->intValue(); } @@ -3047,7 +3134,7 @@ Dhcpv4Srv::getNetmaskOption(const Subnet4Ptr& subnet) { } Pkt4Ptr -Dhcpv4Srv::processDiscover(Pkt4Ptr& discover) { +Dhcpv4Srv::processDiscover(Pkt4Ptr& discover, AllocEngine::ClientContext4Ptr& context) { // server-id is forbidden. sanityCheck(discover, FORBIDDEN); @@ -3059,7 +3146,7 @@ Dhcpv4Srv::processDiscover(Pkt4Ptr& discover) { return (Pkt4Ptr()); } - Dhcpv4Exchange ex(alloc_engine_, discover, subnet, drop); + Dhcpv4Exchange ex(alloc_engine_, discover, context, subnet, drop); // Stop here if Dhcpv4Exchange constructor decided to drop the packet if (drop) { @@ -3130,7 +3217,7 @@ Dhcpv4Srv::processRequest(Pkt4Ptr& request, AllocEngine::ClientContext4Ptr& cont return (Pkt4Ptr()); } - Dhcpv4Exchange ex(alloc_engine_, request, subnet, drop); + Dhcpv4Exchange ex(alloc_engine_, request, context, subnet, drop); // Stop here if Dhcpv4Exchange constructor decided to drop the packet if (drop) { @@ -3472,7 +3559,7 @@ Dhcpv4Srv::declineLease(const Lease4Ptr& lease, const Pkt4Ptr& decline, } Pkt4Ptr -Dhcpv4Srv::processInform(Pkt4Ptr& inform) { +Dhcpv4Srv::processInform(Pkt4Ptr& inform, AllocEngine::ClientContext4Ptr& context) { // server-id is supposed to be forbidden (as is requested address) // but ISC DHCP does not enforce either. So neither will we. sanityCheck(inform, OPTIONAL); @@ -3485,7 +3572,7 @@ Dhcpv4Srv::processInform(Pkt4Ptr& inform) { return (Pkt4Ptr()); } - Dhcpv4Exchange ex(alloc_engine_, inform, subnet, drop); + Dhcpv4Exchange ex(alloc_engine_, inform, context, subnet, drop); // Stop here if Dhcpv4Exchange constructor decided to drop the packet if (drop) { |