2
0
mirror of https://gitlab.isc.org/isc-projects/kea synced 2025-09-04 07:55:18 +00:00

[#3038] init of new hook point lease4_offer

This commit is contained in:
Piotrek Zadroga
2023-09-05 20:51:57 +02:00
parent 5a62c49932
commit ed86417d89
4 changed files with 154 additions and 113 deletions

View File

@@ -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<ScopedCalloutHandleState> 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<ScopedCalloutHandleState> callout_handle_state =
std::make_shared<ScopedCalloutHandleState>(callout_handle);
ScopedEnableOptionsCopy<Pkt4> query4_options_copy(query);
ScopedEnableOptionsCopy<Pkt4> 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<int64_t>(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<void()> CallBack;
boost::shared_ptr<CallBack> call_back =
boost::make_shared<CallBack>(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<int64_t>(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<void()> CallBack;
boost::shared_ptr<CallBack> call_back = boost::make_shared<CallBack>(
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();
}
}
}
}