From ed86417d89d5b57a368d243578cbb3be82b20f25 Mon Sep 17 00:00:00 2001 From: Piotrek Zadroga Date: Tue, 5 Sep 2023 20:51:57 +0200 Subject: [PATCH] [#3038] init of new hook point lease4_offer --- src/bin/dhcp4/dhcp4_messages.cc | 10 +- src/bin/dhcp4/dhcp4_messages.h | 5 +- src/bin/dhcp4/dhcp4_messages.mes | 18 ++- src/bin/dhcp4/dhcp4_srv.cc | 234 +++++++++++++++++-------------- 4 files changed, 154 insertions(+), 113 deletions(-) diff --git a/src/bin/dhcp4/dhcp4_messages.cc b/src/bin/dhcp4/dhcp4_messages.cc index 0f33690891..0d37e69aaf 100644 --- a/src/bin/dhcp4/dhcp4_messages.cc +++ b/src/bin/dhcp4/dhcp4_messages.cc @@ -82,10 +82,13 @@ extern const isc::log::MessageID DHCP4_HOOK_BUFFER_RCVD_SKIP = "DHCP4_HOOK_BUFFE extern const isc::log::MessageID DHCP4_HOOK_BUFFER_SEND_SKIP = "DHCP4_HOOK_BUFFER_SEND_SKIP"; extern const isc::log::MessageID DHCP4_HOOK_DDNS_UPDATE = "DHCP4_HOOK_DDNS_UPDATE"; extern const isc::log::MessageID DHCP4_HOOK_DECLINE_SKIP = "DHCP4_HOOK_DECLINE_SKIP"; +extern const isc::log::MessageID DHCP4_HOOK_LEASE4_OFFER_DROP = "DHCP4_HOOK_LEASE4_OFFER_DROP"; +extern const isc::log::MessageID DHCP4_HOOK_LEASE4_OFFER_PARK = "DHCP4_HOOK_LEASE4_OFFER_PARK"; +extern const isc::log::MessageID DHCP4_HOOK_LEASE4_OFFER_PARKING_LOT_FULL = "DHCP4_HOOK_LEASE4_OFFER_PARKING_LOT_FULL"; extern const isc::log::MessageID DHCP4_HOOK_LEASE4_RELEASE_SKIP = "DHCP4_HOOK_LEASE4_RELEASE_SKIP"; extern const isc::log::MessageID DHCP4_HOOK_LEASES4_COMMITTED_DROP = "DHCP4_HOOK_LEASES4_COMMITTED_DROP"; extern const isc::log::MessageID DHCP4_HOOK_LEASES4_COMMITTED_PARK = "DHCP4_HOOK_LEASES4_COMMITTED_PARK"; -extern const isc::log::MessageID DHCP4_HOOK_LEASES4_PARKING_LOT_FULL = "DHCP4_HOOK_LEASES4_PARKING_LOT_FULL"; +extern const isc::log::MessageID DHCP4_HOOK_LEASES4_COMMITTED_PARKING_LOT_FULL = "DHCP4_HOOK_LEASES4_COMMITTED_PARKING_LOT_FULL"; extern const isc::log::MessageID DHCP4_HOOK_PACKET_RCVD_SKIP = "DHCP4_HOOK_PACKET_RCVD_SKIP"; extern const isc::log::MessageID DHCP4_HOOK_PACKET_SEND_DROP = "DHCP4_HOOK_PACKET_SEND_DROP"; extern const isc::log::MessageID DHCP4_HOOK_PACKET_SEND_SKIP = "DHCP4_HOOK_PACKET_SEND_SKIP"; @@ -255,10 +258,13 @@ const char* values[] = { "DHCP4_HOOK_BUFFER_SEND_SKIP", "%1: prepared response is dropped because a callout set the next step to SKIP.", "DHCP4_HOOK_DDNS_UPDATE", "A hook has updated the DDNS parameters: hostname %1=>%2, forward update %3=>%4, reverse update %5=>%6", "DHCP4_HOOK_DECLINE_SKIP", "Decline4 hook callouts set status to DROP, ignoring packet.", + "DHCP4_HOOK_LEASE4_OFFER_DROP", "%1: packet is dropped, because a callout set the next step to DROP", + "DHCP4_HOOK_LEASE4_OFFER_PARK", "%1: packet is parked, because a callout set the next step to PARK", + "DHCP4_HOOK_LEASE4_OFFER_PARKING_LOT_FULL", "The parked-packet-limit %1, has been reached, dropping query: %2", "DHCP4_HOOK_LEASE4_RELEASE_SKIP", "%1: lease was not released because a callout set the next step to SKIP", "DHCP4_HOOK_LEASES4_COMMITTED_DROP", "%1: packet is dropped, because a callout set the next step to DROP", "DHCP4_HOOK_LEASES4_COMMITTED_PARK", "%1: packet is parked, because a callout set the next step to PARK", - "DHCP4_HOOK_LEASES4_PARKING_LOT_FULL", "The parked-packet-limit %1, has been reached, dropping query: %2", + "DHCP4_HOOK_LEASES4_COMMITTED_PARKING_LOT_FULL", "The parked-packet-limit %1, has been reached, dropping query: %2", "DHCP4_HOOK_PACKET_RCVD_SKIP", "%1: packet is dropped, because a callout set the next step to SKIP", "DHCP4_HOOK_PACKET_SEND_DROP", "%1: prepared DHCPv4 response was not sent because a callout set the next ste to DROP", "DHCP4_HOOK_PACKET_SEND_SKIP", "%1: prepared response is not sent, because a callout set the next stp to SKIP", diff --git a/src/bin/dhcp4/dhcp4_messages.h b/src/bin/dhcp4/dhcp4_messages.h index 5c3ec57a6d..785fccb21c 100644 --- a/src/bin/dhcp4/dhcp4_messages.h +++ b/src/bin/dhcp4/dhcp4_messages.h @@ -83,10 +83,13 @@ extern const isc::log::MessageID DHCP4_HOOK_BUFFER_RCVD_SKIP; extern const isc::log::MessageID DHCP4_HOOK_BUFFER_SEND_SKIP; extern const isc::log::MessageID DHCP4_HOOK_DDNS_UPDATE; extern const isc::log::MessageID DHCP4_HOOK_DECLINE_SKIP; +extern const isc::log::MessageID DHCP4_HOOK_LEASE4_OFFER_DROP; +extern const isc::log::MessageID DHCP4_HOOK_LEASE4_OFFER_PARK; +extern const isc::log::MessageID DHCP4_HOOK_LEASE4_OFFER_PARKING_LOT_FULL; extern const isc::log::MessageID DHCP4_HOOK_LEASE4_RELEASE_SKIP; extern const isc::log::MessageID DHCP4_HOOK_LEASES4_COMMITTED_DROP; extern const isc::log::MessageID DHCP4_HOOK_LEASES4_COMMITTED_PARK; -extern const isc::log::MessageID DHCP4_HOOK_LEASES4_PARKING_LOT_FULL; +extern const isc::log::MessageID DHCP4_HOOK_LEASES4_COMMITTED_PARKING_LOT_FULL; extern const isc::log::MessageID DHCP4_HOOK_PACKET_RCVD_SKIP; extern const isc::log::MessageID DHCP4_HOOK_PACKET_SEND_DROP; extern const isc::log::MessageID DHCP4_HOOK_PACKET_SEND_SKIP; diff --git a/src/bin/dhcp4/dhcp4_messages.mes b/src/bin/dhcp4/dhcp4_messages.mes index 71d12e10a4..f432c06c80 100644 --- a/src/bin/dhcp4/dhcp4_messages.mes +++ b/src/bin/dhcp4/dhcp4_messages.mes @@ -444,6 +444,22 @@ for decline4 hook point and one of the callouts set next step status to DROP. The server will now abort processing of the packet as if it was never received. The lease will continue to be assigned to this client. +% DHCP4_HOOK_LEASE4_OFFER_DROP %1: packet is dropped, because a callout set the next step to DROP +This debug message is printed when a callout installed on the lease4_offer +hook point sets the next step to DROP. + +% DHCP4_HOOK_LEASE4_OFFER_PARK %1: packet is parked, because a callout set the next step to PARK +This debug message is printed when a callout installed on the lease4_offer +hook point sets the next step to PARK. + +% DHCP4_HOOK_LEASE4_OFFER_PARKING_LOT_FULL The parked-packet-limit %1, has been reached, dropping query: %2 +This debug message occurs when the parking lot used to hold client queries +while hook library work for them completes has reached or exceeded the +limit set by the parked-packet-limit global parameter. This can occur when +kea-dhcp4 is using hook libraries (e.g. ping-check) that implement the +"lease4-offer" callout and client queries are arriving faster than +those callouts can fulfill them. + % DHCP4_HOOK_LEASE4_RELEASE_SKIP %1: lease was not released because a callout set the next step to SKIP This debug message is printed when a callout installed on lease4_release hook point set the next step status to SKIP. For this particular hook point, the @@ -457,7 +473,7 @@ hook point sets the next step to DROP. This debug message is printed when a callout installed on the leases4_committed hook point sets the next step to PARK. -% DHCP4_HOOK_LEASES4_PARKING_LOT_FULL The parked-packet-limit %1, has been reached, dropping query: %2 +% DHCP4_HOOK_LEASES4_COMMITTED_PARKING_LOT_FULL The parked-packet-limit %1, has been reached, dropping query: %2 This debug message occurs when the parking lot used to hold client queries while hook library work for them completes has reached or exceeded the limit set by the parked-packet-limit global parameter. This can occur when diff --git a/src/bin/dhcp4/dhcp4_srv.cc b/src/bin/dhcp4/dhcp4_srv.cc index 775985df87..4d02661bbc 100644 --- a/src/bin/dhcp4/dhcp4_srv.cc +++ b/src/bin/dhcp4/dhcp4_srv.cc @@ -96,6 +96,7 @@ struct Dhcp4Hooks { int hook_index_lease4_decline_; ///< index for "lease4_decline" hook point int hook_index_host4_identifier_; ///< index for "host4_identifier" hook point int hook_index_ddns4_update_; ///< index for "ddns4_update" hook point + int hook_index_lease4_offer_; ///< index for "lease4_offer" hook point /// Constructor that registers hook points for DHCPv4 engine Dhcp4Hooks() { @@ -109,6 +110,7 @@ struct Dhcp4Hooks { hook_index_lease4_decline_ = HooksManager::registerHook("lease4_decline"); hook_index_host4_identifier_ = HooksManager::registerHook("host4_identifier"); hook_index_ddns4_update_ = HooksManager::registerHook("ddns4_update"); + hook_index_lease4_offer_ = HooksManager::registerHook("lease4_offer"); } }; @@ -1408,129 +1410,143 @@ Dhcpv4Srv::processDhcp4Query(Pkt4Ptr& query, Pkt4Ptr& rsp, } CalloutHandlePtr callout_handle = getCalloutHandle(query); - if (ctx && HooksManager::calloutsPresent(Hooks.hook_index_leases4_committed_)) { - // The ScopedCalloutHandleState class which guarantees that the task - // is added to the thread pool after the response is reset (if needed) - // and CalloutHandle state is reset. In ST it does nothing. - // A smart pointer is used to store the ScopedCalloutHandleState so that - // a copy of the pointer is created by the lambda and only on the - // destruction of the last reference the task is added. - // In MT there are 2 cases: - // 1. packet is unparked before current thread smart pointer to - // ScopedCalloutHandleState is destroyed: - // - the lambda uses the smart pointer to set the callout which adds the - // task, but the task is added after ScopedCalloutHandleState is - // destroyed, on the destruction of the last reference which is held - // by the current thread. - // 2. packet is unparked after the current thread smart pointer to - // ScopedCalloutHandleState is destroyed: - // - the current thread reference to ScopedCalloutHandleState is - // destroyed, but the reference in the lambda keeps it alive until - // the lambda is called and the last reference is released, at which - // time the task is actually added. - // Use the RAII wrapper to make sure that the callout handle state is - // reset when this object goes out of scope. All hook points must do - // it to prevent possible circular dependency between the callout - // handle and its arguments. - std::shared_ptr callout_handle_state = + if (ctx) { + int hook_idx = Hooks.hook_index_leases4_committed_; + std::string hook_label = "leases4_committed"; + MessageID pkt_park_msg = DHCP4_HOOK_LEASES4_COMMITTED_PARK; + MessageID pkt_drop_msg = DHCP4_HOOK_LEASES4_COMMITTED_DROP; + MessageID parking_lot_full_msg = DHCP4_HOOK_LEASES4_COMMITTED_PARKING_LOT_FULL; + if (ctx->fake_allocation_) { + hook_idx = Hooks.hook_index_lease4_offer_; + hook_label = "lease4_offer"; + pkt_park_msg = DHCP4_HOOK_LEASE4_OFFER_PARK; + pkt_drop_msg = DHCP4_HOOK_LEASE4_OFFER_DROP; + parking_lot_full_msg = DHCP4_HOOK_LEASE4_OFFER_PARKING_LOT_FULL; + } + + if (HooksManager::calloutsPresent(hook_idx)) { + // The ScopedCalloutHandleState class which guarantees that the task + // is added to the thread pool after the response is reset (if needed) + // and CalloutHandle state is reset. In ST it does nothing. + // A smart pointer is used to store the ScopedCalloutHandleState so that + // a copy of the pointer is created by the lambda and only on the + // destruction of the last reference the task is added. + // In MT there are 2 cases: + // 1. packet is unparked before current thread smart pointer to + // ScopedCalloutHandleState is destroyed: + // - the lambda uses the smart pointer to set the callout which adds the + // task, but the task is added after ScopedCalloutHandleState is + // destroyed, on the destruction of the last reference which is held + // by the current thread. + // 2. packet is unparked after the current thread smart pointer to + // ScopedCalloutHandleState is destroyed: + // - the current thread reference to ScopedCalloutHandleState is + // destroyed, but the reference in the lambda keeps it alive until + // the lambda is called and the last reference is released, at which + // time the task is actually added. + // Use the RAII wrapper to make sure that the callout handle state is + // reset when this object goes out of scope. All hook points must do + // it to prevent possible circular dependency between the callout + // handle and its arguments. + std::shared_ptr callout_handle_state = std::make_shared(callout_handle); - ScopedEnableOptionsCopy query4_options_copy(query); + ScopedEnableOptionsCopy query4_options_copy(query); - // Also pass the corresponding query packet as argument - callout_handle->setArgument("query4", query); + // Also pass the corresponding query packet as argument + callout_handle->setArgument("query4", query); - Lease4CollectionPtr new_leases(new Lease4Collection()); - // Filter out the new lease if it was reused so not committed. - if (ctx->new_lease_ && (ctx->new_lease_->reuseable_valid_lft_ == 0)) { - new_leases->push_back(ctx->new_lease_); - } - callout_handle->setArgument("leases4", new_leases); - - Lease4CollectionPtr deleted_leases(new Lease4Collection()); - if (ctx->old_lease_) { - if ((!ctx->new_lease_) || (ctx->new_lease_->addr_ != ctx->old_lease_->addr_)) { - deleted_leases->push_back(ctx->old_lease_); + Lease4CollectionPtr new_leases(new Lease4Collection()); + // Filter out the new lease if it was reused so not committed. + if (ctx->new_lease_ && (ctx->new_lease_->reuseable_valid_lft_ == 0)) { + new_leases->push_back(ctx->new_lease_); } - } - callout_handle->setArgument("deleted_leases4", deleted_leases); + callout_handle->setArgument("leases4", new_leases); - 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(CfgGlobals::PARKED_PACKET_LIMIT); - if (ppl) { - parked_packet_limit = ppl->intValue(); - } - - if (parked_packet_limit) { - const auto& parking_lot = ServerHooks::getServerHooks(). - getParkingLotPtr("leases4_committed"); - - if (parking_lot && (parking_lot->size() >= parked_packet_limit)) { - // We can't park it so we're going to throw it on the floor. - LOG_DEBUG(packet4_logger, DBGLVL_PKT_HANDLING, - DHCP4_HOOK_LEASES4_PARKING_LOT_FULL) - .arg(parked_packet_limit) - .arg(query->getLabel()); - isc::stats::StatsMgr::instance().addValue("pkt4-receive-drop", - static_cast(1)); - rsp.reset(); - return; + Lease4CollectionPtr deleted_leases(new Lease4Collection()); + if (ctx->old_lease_) { + if ((!ctx->new_lease_) || (ctx->new_lease_->addr_ != ctx->old_lease_->addr_)) { + deleted_leases->push_back(ctx->old_lease_); } } + callout_handle->setArgument("deleted_leases4", deleted_leases); - // We proactively park the packet. We'll unpark it without invoking - // the callback (i.e. drop) unless the callout status is set to - // NEXT_STEP_PARK. Otherwise the callback we bind here will be - // executed when the hook library unparks the packet. - HooksManager::park("leases4_committed", query, - [this, callout_handle, query, rsp, callout_handle_state]() mutable { - if (MultiThreadingMgr::instance().getMode()) { - typedef function CallBack; - boost::shared_ptr call_back = - boost::make_shared(std::bind(&Dhcpv4Srv::sendResponseNoThrow, - this, callout_handle, query, rsp)); - callout_handle_state->on_completion_ = [call_back]() { - MultiThreadingMgr::instance().getThreadPool().add(call_back); - }; - } else { - processPacketPktSend(callout_handle, query, rsp); - processPacketBufferSend(callout_handle, rsp); - } - }); - } - - try { - // Call all installed callouts - HooksManager::callCallouts(Hooks.hook_index_leases4_committed_, - *callout_handle); - } catch (...) { - // Make sure we don't orphan a parked packet. if (allow_packet_park) { - HooksManager::drop("leases4_committed", query); + // Get the parking limit. Parsing should ensure the value is present. + uint32_t parked_packet_limit = 0; + data::ConstElementPtr ppl = CfgMgr::instance().getCurrentCfg()-> + getConfiguredGlobal(CfgGlobals::PARKED_PACKET_LIMIT); + if (ppl) { + parked_packet_limit = ppl->intValue(); + } + + if (parked_packet_limit) { + const auto& parking_lot = + ServerHooks::getServerHooks().getParkingLotPtr(hook_label); + + if (parking_lot && (parking_lot->size() >= parked_packet_limit)) { + // We can't park it so we're going to throw it on the floor. + LOG_DEBUG(packet4_logger, DBGLVL_PKT_HANDLING, parking_lot_full_msg) + .arg(parked_packet_limit) + .arg(query->getLabel()); + isc::stats::StatsMgr::instance().addValue("pkt4-receive-drop", + static_cast(1)); + rsp.reset(); + return; + } + } + + // We proactively park the packet. We'll unpark it without invoking + // the callback (i.e. drop) unless the callout status is set to + // NEXT_STEP_PARK. Otherwise the callback we bind here will be + // executed when the hook library unparks the packet. + HooksManager::park( + hook_label, query, + [this, callout_handle, query, rsp, callout_handle_state]() mutable { + if (MultiThreadingMgr::instance().getMode()) { + typedef function CallBack; + boost::shared_ptr call_back = boost::make_shared( + std::bind(&Dhcpv4Srv::sendResponseNoThrow, this, callout_handle, + query, rsp)); + callout_handle_state->on_completion_ = [call_back]() { + MultiThreadingMgr::instance().getThreadPool().add(call_back); + }; + } else { + processPacketPktSend(callout_handle, query, rsp); + processPacketBufferSend(callout_handle, rsp); + } + }); } - throw; - } + try { + // Call all installed callouts + HooksManager::callCallouts(hook_idx, *callout_handle); + } catch (...) { + // Make sure we don't orphan a parked packet. + if (allow_packet_park) { + HooksManager::drop(hook_label, query); + } - if ((callout_handle->getStatus() == CalloutHandle::NEXT_STEP_PARK) - && allow_packet_park) { - LOG_DEBUG(hooks_logger, DBG_DHCP4_HOOKS, DHCP4_HOOK_LEASES4_COMMITTED_PARK) - .arg(query->getLabel()); - // Since the hook library(ies) are going to do the unparking, then - // reset the pointer to the response to indicate to the caller that - // it should return, as the packet processing will continue via - // the callback. - rsp.reset(); - } else { - // Drop the park job on the packet, it isn't needed. - HooksManager::drop("leases4_committed", query); - if (callout_handle->getStatus() == CalloutHandle::NEXT_STEP_DROP) { - LOG_DEBUG(hooks_logger, DBGLVL_PKT_HANDLING, DHCP4_HOOK_LEASES4_COMMITTED_DROP) - .arg(query->getLabel()); + throw; + } + + if ((callout_handle->getStatus() == CalloutHandle::NEXT_STEP_PARK) && + allow_packet_park) { + LOG_DEBUG(hooks_logger, DBG_DHCP4_HOOKS, pkt_park_msg) + .arg(query->getLabel()); + // Since the hook library(ies) are going to do the unparking, then + // reset the pointer to the response to indicate to the caller that + // it should return, as the packet processing will continue via + // the callback. rsp.reset(); + } else { + // Drop the park job on the packet, it isn't needed. + HooksManager::drop(hook_label, query); + if (callout_handle->getStatus() == CalloutHandle::NEXT_STEP_DROP) { + LOG_DEBUG(hooks_logger, DBGLVL_PKT_HANDLING, pkt_drop_msg) + .arg(query->getLabel()); + rsp.reset(); + } } } }