Host::IdentifierType type = Host::IDENT_FLEX;
std::vector<uint8_t> id;
- // Delete previously set arguments
- callout_handle->deleteAllArguments();
+ // 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.
+ ScopedCalloutHandleState callout_handle_state(callout_handle);
// Pass incoming packet as argument
callout_handle->setArgument("query4", context_->query_);
HooksManager::calloutsPresent(Hooks.hook_index_subnet4_select_)) {
CalloutHandlePtr callout_handle = getCalloutHandle(query);
- // We're reusing callout_handle from previous calls
- callout_handle->deleteAllArguments();
+ // 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.
+ ScopedCalloutHandleState callout_handle_state(callout_handle);
// Enable copying options from the packet within hook library.
ScopedEnableOptionsCopy<Pkt4> query4_options_copy(query);
HooksManager::calloutsPresent(Hooks.hook_index_subnet4_select_)) {
CalloutHandlePtr callout_handle = getCalloutHandle(query);
- // We're reusing callout_handle from previous calls
- callout_handle->deleteAllArguments();
+ // 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.
+ ScopedCalloutHandleState callout_handle_state(callout_handle);
// Set new arguments
callout_handle->setArgument("query4", query);
if (HooksManager::calloutsPresent(Hooks.hook_index_buffer4_receive_)) {
CalloutHandlePtr callout_handle = getCalloutHandle(query);
- // Delete previously set arguments
- callout_handle->deleteAllArguments();
+ // 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.
+ ScopedCalloutHandleState callout_handle_state(callout_handle);
// Enable copying options from the packet within hook library.
ScopedEnableOptionsCopy<Pkt4> query4_options_copy(query);
if (HooksManager::calloutsPresent(Hooks.hook_index_pkt4_receive_)) {
CalloutHandlePtr callout_handle = getCalloutHandle(query);
- // Delete previously set arguments
- callout_handle->deleteAllArguments();
+ // 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.
+ ScopedCalloutHandleState callout_handle_state(callout_handle);
// Enable copying options from the packet within hook library.
ScopedEnableOptionsCopy<Pkt4> query4_options_copy(query);
if (ctx && HooksManager::calloutsPresent(Hooks.hook_index_leases4_committed_)) {
CalloutHandlePtr callout_handle = getCalloutHandle(query);
- // Delete all previous arguments
- callout_handle->deleteAllArguments();
-
- // Clear skip flag if it was set in previous callouts
- callout_handle->setStatus(CalloutHandle::NEXT_STEP_CONTINUE);
+ // 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.
+ ScopedCalloutHandleState callout_handle_state(callout_handle);
ScopedEnableOptionsCopy<Pkt4> query4_options_copy(query);
// Execute all callouts registered for pkt4_send
if (HooksManager::calloutsPresent(Hooks.hook_index_pkt4_send_)) {
- // Delete all previous arguments
- callout_handle->deleteAllArguments();
-
- // Clear skip flag if it was set in previous callouts
- callout_handle->setStatus(CalloutHandle::NEXT_STEP_CONTINUE);
+ // 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.
+ ScopedCalloutHandleState callout_handle_state(callout_handle);
// Enable copying options from the query and response packets within
// hook library.
// Let's execute all callouts registered for buffer4_send
if (HooksManager::calloutsPresent(Hooks.hook_index_buffer4_send_)) {
- // Delete previously set arguments
- callout_handle->deleteAllArguments();
+ // 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.
+ ScopedCalloutHandleState callout_handle_state(callout_handle);
// Enable copying options from the packet within hook library.
ScopedEnableOptionsCopy<Pkt4> resp4_options_copy(rsp);
if (HooksManager::calloutsPresent(Hooks.hook_index_lease4_release_)) {
CalloutHandlePtr callout_handle = getCalloutHandle(release);
- // Delete all previous arguments
- callout_handle->deleteAllArguments();
+ // 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.
+ ScopedCalloutHandleState callout_handle_state(callout_handle);
// Enable copying options from the packet within hook library.
ScopedEnableOptionsCopy<Pkt4> query4_options_copy(release);
if (HooksManager::calloutsPresent(Hooks.hook_index_lease4_decline_)) {
CalloutHandlePtr callout_handle = getCalloutHandle(decline);
- // Delete previously set arguments
- callout_handle->deleteAllArguments();
+ // 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.
+ ScopedCalloutHandleState callout_handle_state(callout_handle);
// Enable copying options from the packet within hook library.
ScopedEnableOptionsCopy<Pkt4> query4_options_copy(decline);
Host::IdentifierType type = Host::IDENT_FLEX;
std::vector<uint8_t> id;
- // Delete previously set arguments
- callout_handle->deleteAllArguments();
+ // 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.
+ ScopedCalloutHandleState callout_handle_state(callout_handle);
// Pass incoming packet as argument
callout_handle->setArgument("query6", pkt);
if (HooksManager::calloutsPresent(Hooks.hook_index_buffer6_receive_)) {
CalloutHandlePtr callout_handle = getCalloutHandle(query);
+ // 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.
+ ScopedCalloutHandleState callout_handle_state(callout_handle);
+
// Enable copying options from the packet within hook library.
ScopedEnableOptionsCopy<Pkt6> query6_options_copy(query);
- // Delete previously set arguments
- callout_handle->deleteAllArguments();
-
// Pass incoming packet as argument
callout_handle->setArgument("query6", query);
if (HooksManager::calloutsPresent(Hooks.hook_index_pkt6_receive_)) {
CalloutHandlePtr callout_handle = getCalloutHandle(query);
- // Delete previously set arguments
- callout_handle->deleteAllArguments();
+ // 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.
+ ScopedCalloutHandleState callout_handle_state(callout_handle);
// Enable copying options from the packet within hook library.
ScopedEnableOptionsCopy<Pkt6> query6_options_copy(query);
HooksManager::calloutsPresent(Hooks.hook_index_leases6_committed_)) {
CalloutHandlePtr callout_handle = getCalloutHandle(query);
- // Delete all previous arguments
- callout_handle->deleteAllArguments();
-
- // Clear skip flag if it was set in previous callouts
- callout_handle->setStatus(CalloutHandle::NEXT_STEP_CONTINUE);
+ // 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.
+ ScopedCalloutHandleState callout_handle_state(callout_handle);
ScopedEnableOptionsCopy<Pkt6> query6_options_copy(query);
// Execute all callouts registered for packet6_send
if (HooksManager::calloutsPresent(Hooks.hook_index_pkt6_send_)) {
+ // 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.
+ ScopedCalloutHandleState callout_handle_state(callout_handle);
+
// Enable copying options from the packets within hook library.
ScopedEnableOptionsCopy<Pkt6> query_resp_options_copy(query, rsp);
- // Delete all previous arguments
- callout_handle->deleteAllArguments();
-
// Pass incoming packet as argument
callout_handle->setArgument("query6", query);
// Let's execute all callouts registered for buffer6_send
if (HooksManager::calloutsPresent(Hooks.hook_index_buffer6_send_)) {
- // Delete previously set arguments
- callout_handle->deleteAllArguments();
+ // 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.
+ ScopedCalloutHandleState callout_handle_state(callout_handle);
// Enable copying options from the packet within hook library.
ScopedEnableOptionsCopy<Pkt6> response6_options_copy(rsp);
if (HooksManager::calloutsPresent(Hooks.hook_index_subnet6_select_)) {
CalloutHandlePtr callout_handle = getCalloutHandle(question);
- // We're reusing callout_handle from previous calls
- callout_handle->deleteAllArguments();
+ // 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.
+ ScopedCalloutHandleState callout_handle_state(callout_handle);
// Enable copying options from the packet within hook library.
ScopedEnableOptionsCopy<Pkt6> query6_options_copy(question);
if (HooksManager::calloutsPresent(Hooks.hook_index_lease6_release_)) {
CalloutHandlePtr callout_handle = getCalloutHandle(query);
+ // 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.
+ ScopedCalloutHandleState callout_handle_state(callout_handle);
+
// Enable copying options from the packet within hook library.
ScopedEnableOptionsCopy<Pkt6> query6_options_copy(query);
if (HooksManager::calloutsPresent(Hooks.hook_index_lease6_release_)) {
CalloutHandlePtr callout_handle = getCalloutHandle(query);
- // Delete all previous arguments
- callout_handle->deleteAllArguments();
+ // 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.
+ ScopedCalloutHandleState callout_handle_state(callout_handle);
// Enable copying options from the packet within hook library.
ScopedEnableOptionsCopy<Pkt6> query6_options_copy(query);
if (HooksManager::calloutsPresent(Hooks.hook_index_lease6_decline_)) {
CalloutHandlePtr callout_handle = getCalloutHandle(decline);
- // Delete previously set arguments
- callout_handle->deleteAllArguments();
+ // 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.
+ ScopedCalloutHandleState callout_handle_state(callout_handle);
// Enable copying options from the packet within hook library.
ScopedEnableOptionsCopy<Pkt6> query6_options_copy(decline);
libkea_dhcpsrv_la_LIBADD += $(top_builddir)/src/lib/exceptions/libkea-exceptions.la
libkea_dhcpsrv_la_LIBADD += $(LOG4CPLUS_LIBS) $(CRYPTO_LIBS) $(BOOST_LIBS)
-libkea_dhcpsrv_la_LDFLAGS = -no-undefined -version-info 10:0:0
+libkea_dhcpsrv_la_LDFLAGS = -no-undefined -version-info 11:0:0
libkea_dhcpsrv_la_LDFLAGS += $(CRYPTO_LDFLAGS)
if HAVE_MYSQL
libkea_dhcpsrv_la_LDFLAGS += $(MYSQL_LIBS)
Pool6Ptr pool;
+ CalloutHandle::CalloutNextStep callout_status = CalloutHandle::NEXT_STEP_CONTINUE;
+
while (subnet) {
if (!subnet->clientSupported(ctx.query_->getClasses())) {
// The hint is valid and not currently used, let's create a
// lease for it
- lease = createLease6(ctx, hint, pool->getLength());
+ lease = createLease6(ctx, hint, pool->getLength(), callout_status);
// It can happen that the lease allocation failed (we could
// have lost the race condition. That means that the hint is
ctx.currentIA().old_leases_.push_back(old_lease);
/// We found a lease and it is expired, so we can reuse it
- lease = reuseExpiredLease(lease, ctx, pool->getLength());
+ lease = reuseExpiredLease(lease, ctx, pool->getLength(),
+ callout_status);
/// @todo: We support only one lease per ia for now
leases.push_back(lease);
// free. Let's allocate it.
ctx.subnet_ = subnet;
- Lease6Ptr lease = createLease6(ctx, candidate, prefix_len);
+ Lease6Ptr lease = createLease6(ctx, candidate, prefix_len, callout_status);
if (lease) {
// We are allocating a new lease (not renewing). So, the
// old lease should be NULL.
return (leases);
} else if (ctx.callout_handle_ &&
- (ctx.callout_handle_->getStatus() !=
- CalloutHandle::NEXT_STEP_CONTINUE)) {
+ (callout_status != CalloutHandle::NEXT_STEP_CONTINUE)) {
// Don't retry when the callout status is not continue.
break;
}
ctx.currentIA().old_leases_.push_back(old_lease);
ctx.subnet_ = subnet;
- existing = reuseExpiredLease(existing, ctx, prefix_len);
+ existing = reuseExpiredLease(existing, ctx, prefix_len,
+ callout_status);
leases.push_back(existing);
return (leases);
}
// Ok, let's create a new lease...
- Lease6Ptr lease = createLease6(ctx, addr, prefix_len);
+ CalloutHandle::CalloutNextStep callout_status = CalloutHandle::NEXT_STEP_CONTINUE;
+ Lease6Ptr lease = createLease6(ctx, addr, prefix_len, callout_status);
// ... and add it to the existing leases list.
existing_leases.push_back(lease);
Lease6Ptr
AllocEngine::reuseExpiredLease(Lease6Ptr& expired, ClientContext6& ctx,
- uint8_t prefix_len) {
+ uint8_t prefix_len,
+ CalloutHandle::CalloutNextStep& callout_status) {
if (!expired->expired()) {
isc_throw(BadValue, "Attempt to recycle lease that is still valid");
if (ctx.callout_handle_ &&
HooksManager::getHooksManager().calloutsPresent(hook_index_lease6_select_)) {
- // Delete all previous arguments
- ctx.callout_handle_->deleteAllArguments();
+ // 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.
+ ScopedCalloutHandleState callout_handle_state(ctx.callout_handle_);
// Enable copying options from the packet within hook library.
ScopedEnableOptionsCopy<Pkt6> query6_options_copy(ctx.query_);
// Call the callouts
HooksManager::callCallouts(hook_index_lease6_select_, *ctx.callout_handle_);
+ callout_status = ctx.callout_handle_->getStatus();
+
// Callouts decided to skip the action. This means that the lease is not
// assigned, so the client will get NoAddrAvail as a result. The lease
// won't be inserted into the database.
- if (ctx.callout_handle_->getStatus() == CalloutHandle::NEXT_STEP_SKIP) {
+ if (callout_status == CalloutHandle::NEXT_STEP_SKIP) {
LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_HOOKS, DHCPSRV_HOOK_LEASE6_SELECT_SKIP);
return (Lease6Ptr());
}
Lease6Ptr AllocEngine::createLease6(ClientContext6& ctx,
const IOAddress& addr,
- uint8_t prefix_len) {
+ uint8_t prefix_len,
+ CalloutHandle::CalloutNextStep& callout_status) {
if (ctx.currentIA().type_ != Lease::TYPE_PD) {
prefix_len = 128; // non-PD lease types must be always /128
if (ctx.callout_handle_ &&
HooksManager::getHooksManager().calloutsPresent(hook_index_lease6_select_)) {
- // Delete all previous arguments
- ctx.callout_handle_->deleteAllArguments();
+ // 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.
+ ScopedCalloutHandleState callout_handle_state(ctx.callout_handle_);
// Enable copying options from the packet within hook library.
ScopedEnableOptionsCopy<Pkt6> query6_options_copy(ctx.query_);
// This is the first callout, so no need to clear any arguments
HooksManager::callCallouts(hook_index_lease6_select_, *ctx.callout_handle_);
+ callout_status = ctx.callout_handle_->getStatus();
+
// Callouts decided to skip the action. This means that the lease is not
// assigned, so the client will get NoAddrAvail as a result. The lease
// won't be inserted into the database.
- if (ctx.callout_handle_->getStatus() == CalloutHandle::NEXT_STEP_SKIP) {
+ if (callout_status == CalloutHandle::NEXT_STEP_SKIP) {
LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_HOOKS, DHCPSRV_HOOK_LEASE6_SELECT_SKIP);
return (Lease6Ptr());
}
if (HooksManager::calloutsPresent(hook_point)) {
CalloutHandlePtr callout_handle = ctx.callout_handle_;
- // Delete all previous arguments
- callout_handle->deleteAllArguments();
+ // 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.
+ ScopedCalloutHandleState callout_handle_state(callout_handle);
// Enable copying options from the packet within hook library.
ScopedEnableOptionsCopy<Pkt6> query6_options_copy(ctx.query_);
// will not update DNS nor update the database.
bool skipped = false;
if (callout_handle) {
+
+ // 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.
+ ScopedCalloutHandleState callout_handle_state(callout_handle);
+
callout_handle->deleteAllArguments();
callout_handle->setArgument("lease6", lease);
callout_handle->setArgument("remove_lease", reclaim_mode == DB_RECLAIM_REMOVE);
// will not update DNS nor update the database.
bool skipped = false;
if (callout_handle) {
- callout_handle->deleteAllArguments();
+
+ // 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.
+ ScopedCalloutHandleState callout_handle_state(callout_handle);
+
callout_handle->setArgument("lease4", lease);
callout_handle->setArgument("remove_lease", reclaim_mode == DB_RECLAIM_REMOVE);
callout_handle = HooksManager::createCalloutHandle();
}
- // Delete all previous arguments
- callout_handle->deleteAllArguments();
+ // 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.
+ ScopedCalloutHandleState callout_handle_state(callout_handle);
// Pass necessary arguments
callout_handle->setArgument("lease4", lease);
callout_handle = HooksManager::createCalloutHandle();
}
- // Delete all previous arguments
- callout_handle->deleteAllArguments();
+ // 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.
+ ScopedCalloutHandleState callout_handle_state(callout_handle);
// Pass necessary arguments
callout_handle->setArgument("lease6", lease);
// caller.
Lease4Ptr new_lease;
+ CalloutHandle::CalloutNextStep callout_status = CalloutHandle::NEXT_STEP_CONTINUE;
+
// Check if there is a reservation for the client. If there is, we want to
// assign the reserved address, rather than any other one.
if (hasAddressReservation(ctx)) {
// Note that we don't remove the existing client's lease at this point
// because this is not a real allocation, we just offer what we can
// allocate in the DHCPREQUEST time.
- new_lease = allocateOrReuseLease4(ctx.currentHost()->getIPv4Reservation(), ctx);
+ new_lease = allocateOrReuseLease4(ctx.currentHost()->getIPv4Reservation(), ctx,
+ callout_status);
if (!new_lease) {
LOG_WARN(alloc_engine_logger, ALLOC_ENGINE_V4_DISCOVER_ADDRESS_CONFLICT)
.arg(ctx.query_->getLabel())
.arg(ctx.requested_address_.toText())
.arg(ctx.query_->getLabel());
- new_lease = allocateOrReuseLease4(ctx.requested_address_, ctx);
+ new_lease = allocateOrReuseLease4(ctx.requested_address_, ctx,
+ callout_status);
}
// The allocation engine failed to allocate all of the candidate
// or the existing lease has expired. If the allocation fails,
// e.g. because the lease is in use, we will return NULL to
// indicate that we were unable to allocate the lease.
- new_lease = allocateOrReuseLease4(ctx.requested_address_, ctx);
+ CalloutHandle::CalloutNextStep callout_status = CalloutHandle::NEXT_STEP_CONTINUE;
+ new_lease = allocateOrReuseLease4(ctx.requested_address_, ctx,
+ callout_status);
} else {
}
Lease4Ptr
-AllocEngine::createLease4(const ClientContext4& ctx, const IOAddress& addr) {
+AllocEngine::createLease4(const ClientContext4& ctx, const IOAddress& addr,
+ CalloutHandle::CalloutNextStep& callout_status) {
if (!ctx.hwaddr_) {
isc_throw(BadValue, "Can't create a lease with NULL HW address");
}
if (ctx.callout_handle_ &&
HooksManager::getHooksManager().calloutsPresent(hook_index_lease4_select_)) {
- // Delete all previous arguments
- ctx.callout_handle_->deleteAllArguments();
+ // 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.
+ ScopedCalloutHandleState callout_handle_state(ctx.callout_handle_);
// Enable copying options from the packet within hook library.
ScopedEnableOptionsCopy<Pkt4> query4_options_copy(ctx.query_);
// This is the first callout, so no need to clear any arguments
HooksManager::callCallouts(hook_index_lease4_select_, *ctx.callout_handle_);
+ callout_status = ctx.callout_handle_->getStatus();
+
// Callouts decided to skip the action. This means that the lease is not
// assigned, so the client will get NoAddrAvail as a result. The lease
// won't be inserted into the database.
- if (ctx.callout_handle_->getStatus() == CalloutHandle::NEXT_STEP_SKIP) {
+ if (callout_status == CalloutHandle::NEXT_STEP_SKIP) {
LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_HOOKS, DHCPSRV_HOOK_LEASE4_SELECT_SKIP);
return (Lease4Ptr());
}
if (HooksManager::getHooksManager().
calloutsPresent(Hooks.hook_index_lease4_renew_)) {
- // Delete all previous arguments
- ctx.callout_handle_->deleteAllArguments();
+ // 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.
+ ScopedCalloutHandleState callout_handle_state(ctx.callout_handle_);
// Enable copying options from the packet within hook library.
ScopedEnableOptionsCopy<Pkt4> query4_options_copy(ctx.query_);
Lease4Ptr
AllocEngine::reuseExpiredLease4(Lease4Ptr& expired,
- AllocEngine::ClientContext4& ctx) {
+ AllocEngine::ClientContext4& ctx,
+ CalloutHandle::CalloutNextStep& callout_status) {
if (!expired) {
isc_throw(BadValue, "null lease specified for reuseExpiredLease");
}
// Enable copying options from the packet within hook library.
ScopedEnableOptionsCopy<Pkt4> query4_options_copy(ctx.query_);
- // Delete all previous arguments
- ctx.callout_handle_->deleteAllArguments();
+ // 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.
+ ScopedCalloutHandleState callout_handle_state(ctx.callout_handle_);
// Pass necessary arguments
// Pass the original client query
// Call the callouts
HooksManager::callCallouts(hook_index_lease4_select_, *ctx.callout_handle_);
+ callout_status = ctx.callout_handle_->getStatus();
+
// Callouts decided to skip the action. This means that the lease is not
// assigned, so the client will get NoAddrAvail as a result. The lease
// won't be inserted into the database.
- if (ctx.callout_handle_->getStatus() == CalloutHandle::NEXT_STEP_SKIP) {
+ if (callout_status == CalloutHandle::NEXT_STEP_SKIP) {
LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_HOOKS,
DHCPSRV_HOOK_LEASE4_SELECT_SKIP);
return (Lease4Ptr());
}
Lease4Ptr
-AllocEngine::allocateOrReuseLease4(const IOAddress& candidate, ClientContext4& ctx) {
+AllocEngine::allocateOrReuseLease4(const IOAddress& candidate, ClientContext4& ctx,
+ CalloutHandle::CalloutNextStep& callout_status) {
ctx.conflicting_lease_.reset();
Lease4Ptr exist_lease = LeaseMgrFactory::instance().getLease4(candidate);
if (exist_lease) {
if (exist_lease->expired()) {
ctx.old_lease_ = Lease4Ptr(new Lease4(*exist_lease));
- return (reuseExpiredLease4(exist_lease, ctx));
+ return (reuseExpiredLease4(exist_lease, ctx, callout_status));
} else {
// If there is a lease and it is not expired, pass this lease back
}
} else {
- return (createLease4(ctx, candidate));
+ return (createLease4(ctx, candidate, callout_status));
}
return (Lease4Ptr());
}
max_attempts = 0;
}
- // Set the default status code in case the lease4_select callouts
- // do not exist and the callout handle has a status returned by
- // any of the callouts already invoked for this packet.
- if (ctx.callout_handle_) {
- ctx.callout_handle_->setStatus(CalloutHandle::NEXT_STEP_CONTINUE);
- }
+ CalloutHandle::CalloutNextStep callout_status = CalloutHandle::NEXT_STEP_CONTINUE;
for (uint64_t i = 0; i < max_attempts; ++i) {
IOAddress candidate = allocator->pickAddress(subnet,
// The call below will return the non-NULL pointer if we
// successfully allocate this lease. This means that the
// address is not in use by another client.
- new_lease = allocateOrReuseLease4(candidate, ctx);
+ new_lease = allocateOrReuseLease4(candidate, ctx, callout_status);
if (new_lease) {
return (new_lease);
} else if (ctx.callout_handle_ &&
- (ctx.callout_handle_->getStatus() !=
- CalloutHandle::NEXT_STEP_CONTINUE)) {
+ (callout_status != CalloutHandle::NEXT_STEP_CONTINUE)) {
// Don't retry when the callout status is not continue.
subnet.reset();
break;
/// available
/// @param prefix_len length of the prefix (for PD only)
/// should be 128 for other lease types
+ /// @param [out] callout_status callout returned by the lease6_select
///
/// The following fields of the ctx structure are used:
/// @ref ClientContext6::subnet_ subnet the lease is allocated from
/// became unavailable)
Lease6Ptr createLease6(ClientContext6& ctx,
const isc::asiolink::IOAddress& addr,
- const uint8_t prefix_len);
+ const uint8_t prefix_len,
+ hooks::CalloutHandle::CalloutNextStep& callout_status);
/// @brief Allocates a normal, in-pool, unreserved lease from the pool.
///
/// @param ctx client context that contains all details.
/// @param prefix_len prefix length (for PD leases)
/// Should be 128 for other lease types
+ /// @param [out] callout_status callout returned by the lease6_select
///
/// The following parameters are used from the ctx structure:
/// @ref ClientContext6::subnet_ subnet the lease is allocated from
///
/// @return refreshed lease
/// @throw BadValue if trying to recycle lease that is still valid
- Lease6Ptr reuseExpiredLease(Lease6Ptr& expired,
- ClientContext6& ctx,
- uint8_t prefix_len);
+ Lease6Ptr
+ reuseExpiredLease(Lease6Ptr& expired,
+ ClientContext6& ctx,
+ uint8_t prefix_len,
+ hooks::CalloutHandle::CalloutNextStep& callout_status);
/// @brief Updates FQDN and Client's Last Transmission Time
/// for a collection of leases.
///
/// @param ctx client context that contains additional parameters.
/// @param addr An address that was selected and is confirmed to be available
+ /// @param [out] callout_status callout returned by the lease6_select
///
/// In particular, the following fields from Client context are used:
/// - @ref ClientContext4::subnet_ Subnet the lease is allocated from
/// @return allocated lease (or NULL in the unlikely case of the lease just
/// become unavailable)
Lease4Ptr createLease4(const ClientContext4& ctx,
- const isc::asiolink::IOAddress& addr);
+ const isc::asiolink::IOAddress& addr,
+ hooks::CalloutHandle::CalloutNextStep& callout_status);
/// @brief Renews a DHCPv4 lease.
///
/// @param expired An old, expired lease.
/// @param ctx Message processing context. It holds various information
/// extracted from the client's message and required to allocate a lease.
+ /// @param [out] callout_status callout returned by the lease4_select
///
/// @return Updated lease instance.
/// @throw BadValue if trying to reuse a lease which is still valid or
/// when the provided parameters are invalid.
- Lease4Ptr reuseExpiredLease4(Lease4Ptr& expired, ClientContext4& ctx);
+ Lease4Ptr
+ reuseExpiredLease4(Lease4Ptr& expired, ClientContext4& ctx,
+ hooks::CalloutHandle::CalloutNextStep& callout_status);
/// @brief Allocates the lease by replacing an existing lease.
///
/// allocated.
/// @param ctx Client context holding the data extracted from the
/// client's message.
+ /// @param [out] callout_status callout returned by the lease4_select
///
/// @return A pointer to the allocated lease or NULL if the allocation
/// was not successful.
- Lease4Ptr allocateOrReuseLease4(const asiolink::IOAddress& address,
- ClientContext4& ctx);
+ Lease4Ptr
+ allocateOrReuseLease4(const asiolink::IOAddress& address,
+ ClientContext4& ctx,
+ hooks::CalloutHandle::CalloutNextStep& callout_status);
/// @brief Allocates the lease from the dynamic pool.
///
libkea_hooks_la_CXXFLAGS = $(AM_CXXFLAGS)
libkea_hooks_la_CPPFLAGS = $(AM_CPPFLAGS)
-libkea_hooks_la_LDFLAGS = $(AM_LDFLAGS) -no-undefined -version-info 6:0:0
+libkea_hooks_la_LDFLAGS = $(AM_LDFLAGS) -no-undefined -version-info 7:0:0
libkea_hooks_la_LIBADD =
libkea_hooks_la_LIBADD += $(top_builddir)/src/lib/log/libkea-log.la
libkea_hooks_la_LIBADD += $(top_builddir)/src/lib/util/threads/libkea-threads.la
return (hook);
}
-} // namespace util
+ScopedCalloutHandleState::
+ScopedCalloutHandleState(const CalloutHandlePtr& callout_handle)
+ : callout_handle_(callout_handle) {
+ if (!callout_handle_) {
+ isc_throw(BadValue, "callout_handle argument must not be null");
+ }
+
+ resetState();
+}
+
+ScopedCalloutHandleState::~ScopedCalloutHandleState() {
+ resetState();
+}
+
+void
+ScopedCalloutHandleState::resetState() {
+ // No need to check if the handle is null because the constructor
+ // already checked that.
+ callout_handle_->deleteAllArguments();
+ callout_handle_->setStatus(CalloutHandle::NEXT_STEP_CONTINUE);
+}
+
+} // namespace hooks
} // namespace isc
/// A shared pointer to a CalloutHandle object.
typedef boost::shared_ptr<CalloutHandle> CalloutHandlePtr;
+/// @brief Wrapper class around callout handle which automatically
+/// resets handle's state.
+///
+/// The Kea servers often require to associate processed packets with
+/// @c CalloutHandle instances. This is to facilitate the case when the
+/// hooks library passes information between the callouts using the
+/// 'context' stored in the callout handle. The callouts invoked throughout
+/// the packet lifetime have access to the context information for the
+/// given packet.
+///
+/// The association between the packets and the callout handles is
+/// achieved by giving the ownership of the @c CalloutHandle objects to
+/// the @c Pkt objects. When the @c Pkt object goes out of scope, it should
+/// also release the pointer to the owned @c CalloutHandle object.
+/// However, this causes a risk of circular dependency between the shared
+/// pointer to the @c Pkt object and the shared pointer to the
+/// @c CalloutHandle it owns, because the pointer to the packet is often
+/// set as an argument of the callout handle prior to invoking a callout.
+///
+/// In order to break the circular dependency, the arguments of the
+/// callout handle must be deleted as soon as they are not needed
+/// anymore. This class is a wrapper around the callout handle object,
+/// which resets its state during construction and destruction. All
+/// Kea hook points must use this class within the scope where the
+/// @c HooksManager::callCallouts is invoked to reset the state of the
+/// callout handle. The state is reset when this object goes out of
+/// scope.
+///
+/// Currently, the following operations are performed during the reset:
+/// - all arguments of the callout handle are deleted,
+/// - the next step status is set to @c CalloutHandle::NEXT_STEP CONTINUE
+///
+/// This class must never be modified to also delete the context
+/// information from the callout handle. The context is intended
+/// to be used to share stateful data across callouts and hook points
+/// and its contents must exist for the duration of the packet lifecycle.
+/// Otherwise, we could simply re-create the callout handle for
+/// each hook point and we wouldn't need this RAII class.
+class ScopedCalloutHandleState {
+public:
+
+ /// @brief Constructor.
+ ///
+ /// Resets state of the callout handle.
+ ///
+ /// @param callout_handle reference to the pointer to the callout
+ /// handle which state should be reset.
+ /// @throw isc::BadValue if the callout handle is null.
+ explicit ScopedCalloutHandleState(const CalloutHandlePtr& callout_handle);
+
+ /// @brief Destructor.
+ ///
+ /// Resets state of the callout handle.
+ ~ScopedCalloutHandleState();
+
+private:
+
+ /// @brief Resets the callout handle state.
+ ///
+ /// It is used internally by the constructor and destructor.
+ void resetState();
+
+ /// @brief Holds pointer to the wrapped callout handle.
+ CalloutHandlePtr callout_handle_;
+};
+
} // namespace hooks
} // namespace isc