rsp->setIndex(query->getIndex());
rsp->setIface(query->getIface());
- bool packet_park = false;
-
+ CalloutHandlePtr callout_handle = getCalloutHandle(query);
if (!ctx.fake_allocation_ && (ctx.query_->getType() != DHCPV6_CONFIRM) &&
(ctx.query_->getType() != DHCPV6_INFORMATION_REQUEST) &&
HooksManager::calloutsPresent(Hooks.hook_index_leases6_committed_)) {
- 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
}
callout_handle->setArgument("deleted_leases6", deleted_leases);
- // Call all installed callouts
- HooksManager::callCallouts(Hooks.hook_index_leases6_committed_,
- *callout_handle);
-
- if (callout_handle->getStatus() == CalloutHandle::NEXT_STEP_DROP) {
- LOG_DEBUG(hooks_logger, DBG_DHCP6_HOOKS,
- DHCP6_HOOK_LEASES6_COMMITTED_DROP)
- .arg(query->getLabel());
- rsp.reset();
-
- } else if (callout_handle->getStatus() == CalloutHandle::NEXT_STEP_PARK) {
- packet_park = true;
- }
- }
-
- if (!rsp) {
- return;
- }
-
- // PARKING SPOT after leases6_committed hook point.
- CalloutHandlePtr callout_handle = getCalloutHandle(query);
- if (packet_park) {
- LOG_DEBUG(hooks_logger, DBG_DHCP6_HOOKS,
- DHCP6_HOOK_LEASES6_COMMITTED_PARK)
- .arg(query->getLabel());
-
- // Park the packet. The function we bind here will be executed when the hook
- // library unparks the packet.
+ // 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("leases6_committed", query,
[this, callout_handle, query, rsp]() mutable {
if (MultiThreadingMgr::instance().getMode()) {
}
});
- // If we have parked the packet, let's 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();
+ try {
+ // Call all installed callouts
+ HooksManager::callCallouts(Hooks.hook_index_leases6_committed_,
+ *callout_handle);
+ } catch(...) {
+ // Make sure we don't orphan a parked packet.
+ HooksManager::drop("leases4_committed", query);
+ throw;
+ }
- } else {
+ if (callout_handle->getStatus() == CalloutHandle::NEXT_STEP_PARK) {
+ LOG_DEBUG(hooks_logger, DBG_DHCP6_HOOKS, DHCP6_HOOK_LEASES6_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("leases6_committed", query);
+ if (callout_handle->getStatus() == CalloutHandle::NEXT_STEP_DROP) {
+ LOG_DEBUG(hooks_logger, DBG_DHCP6_HOOKS, DHCP6_HOOK_LEASES6_COMMITTED_DROP)
+ .arg(query->getLabel());
+ rsp.reset();
+ }
+ }
+ }
+
+ // If we have a response prep it for shipment.
+ if (rsp) {
processPacketPktSend(callout_handle, query, rsp);
}
}
return;
}
- // The callout returns this status code to indicate to the server that it
+ // The callout returns this status code to indicate to the server that it
// should leave the packet parked. It will be unparked until each hook
// library with a reference, unparks the packet.
callout_handle.setStatus(CalloutHandle::NEXT_STEP_PARK);
// pointer until we unpark the packet.
ParkingLotHandlePtr parking_lot = callout_handle.getParkingLotHandlePtr();
+ // Create a reference to the parked packet. This signals that we have a
+ // stake in unparking it.
+ parking_lot->reference(query6);
+
// Asynchronously send lease updates. In some cases no updates will be sent,
// e.g. when this server is in the partner-down state and there are no backup
// servers. In those cases we simply return without parking the DHCP query.
// The response will be sent to the client immediately.
if (service_->asyncSendLeaseUpdates(query6, leases6, deleted_leases6, parking_lot) == 0) {
+ // Dereference the parked packet. This releases our stake in it.
+ parking_lot->dereference(query6);
return;
}
- // This is required step every time we ask the server to park the packet.
- // The reference counting is required to keep the packet parked until
- // all callouts call unpark. Then, the packet gets unparked and the
- // associated callback is triggered. The callback resumes packet processing.
- parking_lot->reference(query6);
-
// The callout returns this status code to indicate to the server that it
- // should park the query packet.
+ // should leave the packet parked. It will be unparked until each hook
+ // library with a reference, unparks the packet.
callout_handle.setStatus(CalloutHandle::NEXT_STEP_PARK);
}
CalloutHandlePtr handle = query->getCalloutHandle();
ScopedCalloutHandleState handle_state(handle);
handle->setArgument("query4", query);
+
+ // Park the packtet proactively as the server normally would.
+ HooksManager::park("leases4_committed", query, []{});
+
+ // Invoke the callouts.
HooksManager::callCallouts(testHooks.hook_index_buffer4_receive_,
*handle);
EXPECT_EQ(CalloutHandle::NEXT_STEP_SKIP, handle->getStatus());
CalloutHandlePtr handle = query->getCalloutHandle();
ScopedCalloutHandleState handle_state(handle);
handle->setArgument("query6", query);
+
+ // Park the packtet proactively as the server normally would.
+ HooksManager::park("leases6_committed", query, []{});
+
+ // Invoke the callouts.
HooksManager::callCallouts(testHooks.hook_index_buffer6_receive_,
*handle);
EXPECT_EQ(CalloutHandle::NEXT_STEP_SKIP, handle->getStatus());
#include <dhcpsrv/lease.h>
#include <dhcpsrv/network_state.h>
#include <hooks/hooks_manager.h>
+#include <testutils/gtest_utils.h>
#include <boost/pointer_cast.hpp>
#include <gtest/gtest.h>
#include <string>
namespace {
+/// @brief Structure that holds registered hook indexes.
+///
+/// This allows us to park packets.
+struct TestHooks {
+ /// @brief Index of leases4_committed callout.
+ int hook_index_leases4_committed_;
+
+ /// @brief Index of leases6_committed callout.
+ int hook_index_leases6_committed_;
+
+ /// @brief Constructor
+ ///
+ /// The constructor registers hook points for callout tests.
+ TestHooks() {
+ hook_index_leases4_committed_ =
+ HooksManager::registerHook("leases4_committed");
+ hook_index_leases6_committed_ =
+ HooksManager::registerHook("leases6_committed");
+ }
+};
+
+TestHooks test_hooks;
+
/// @brief Derivation of the @c HAImpl which provides access to protected
/// methods and members.
class TestHAImpl : public HAImpl {
CalloutHandlePtr callout_handle = HooksManager::createCalloutHandle();
ASSERT_TRUE(callout_handle);
+ // Set the hook index so we can park packets.
+ callout_handle->setCurrentHook(test_hooks.hook_index_leases4_committed_);
+
// query4
Pkt4Ptr query4 = createMessage4(DHCPREQUEST, 1, 0, 0);
callout_handle->setArgument("query4", query4);
// Set initial status.
callout_handle->setStatus(CalloutHandle::NEXT_STEP_CONTINUE);
+ // Park the packet.
+ HooksManager::park("leases4_committed", query4, []{});
+
// There are no leases so the callout should return.
ASSERT_NO_THROW(ha_impl.leases4Committed(*callout_handle));
// No updates are generated so the default status should not be modified.
EXPECT_EQ(CalloutHandle::NEXT_STEP_CONTINUE, callout_handle->getStatus());
- EXPECT_FALSE(callout_handle->getParkingLotHandlePtr()->drop(query4));
+ EXPECT_TRUE(callout_handle->getParkingLotHandlePtr()->drop(query4));
// Create a lease and pass it to the callout, but temporarily disable lease
// updates.
ha_impl.config_->setSendLeaseUpdates(false);
+ // Park the packet.
+ HooksManager::park("leases4_committed", query4, []{});
+
// Run the callout again.
ASSERT_NO_THROW(ha_impl.leases4Committed(*callout_handle));
// No updates are generated so the default status should not be modified.
EXPECT_EQ(CalloutHandle::NEXT_STEP_CONTINUE, callout_handle->getStatus());
- EXPECT_FALSE(callout_handle->getParkingLotHandlePtr()->drop(query4));
+ EXPECT_TRUE(callout_handle->getParkingLotHandlePtr()->drop(query4));
// Enable updates and retry.
ha_impl.config_->setSendLeaseUpdates(true);
callout_handle->setArgument("leases4", leases4);
- ASSERT_NO_THROW(ha_impl.leases4Committed(*callout_handle));
+
+ // Park the packet.
+ HooksManager::park("leases4_committed", query4, []{});
+
+ // Run the callout again.
+ ASSERT_NO_THROW_LOG(ha_impl.leases4Committed(*callout_handle));
// This time the lease update should be generated and the status should
// be set to "park".
CalloutHandlePtr callout_handle = HooksManager::createCalloutHandle();
ASSERT_TRUE(callout_handle);
+ // Set the hook index so we can park packets.
+ callout_handle->setCurrentHook(test_hooks.hook_index_leases6_committed_);
+
// query6
Pkt6Ptr query6 = createMessage6(DHCPV6_REQUEST, 1, 0);
callout_handle->setArgument("query6", query6);
// Set initial status.
callout_handle->setStatus(CalloutHandle::NEXT_STEP_CONTINUE);
+ // Park the packet.
+ HooksManager::park("leases6_committed", query6, []{});
+
// There are no leases so the callout should return.
ASSERT_NO_THROW(ha_impl.leases6Committed(*callout_handle));
// No updates are generated so the default status should not be modified.
EXPECT_EQ(CalloutHandle::NEXT_STEP_CONTINUE, callout_handle->getStatus());
- EXPECT_FALSE(callout_handle->getParkingLotHandlePtr()->drop(query6));
+ EXPECT_TRUE(callout_handle->getParkingLotHandlePtr()->drop(query6));
// Create a lease and pass it to the callout, but temporarily disable lease
// updates.
ha_impl.config_->setSendLeaseUpdates(false);
+ // Park the packet.
+ HooksManager::park("leases6_committed", query6, []{});
+
// Run the callout again.
ASSERT_NO_THROW(ha_impl.leases6Committed(*callout_handle));
// No updates are generated so the default status should not be modified.
EXPECT_EQ(CalloutHandle::NEXT_STEP_CONTINUE, callout_handle->getStatus());
- EXPECT_FALSE(callout_handle->getParkingLotHandlePtr()->drop(query6));
+ EXPECT_TRUE(callout_handle->getParkingLotHandlePtr()->drop(query6));
// Enable updates and retry.
ha_impl.config_->setSendLeaseUpdates(true);
callout_handle->setArgument("leases6", leases6);
+
+ // Park the packet.
+ HooksManager::park("leases6_committed", query6, []{});
+
ASSERT_NO_THROW(ha_impl.leases6Committed(*callout_handle));
// This time the lease update should be generated and the status should
EXPECT_FALSE(state->isPoked());
- ASSERT_NO_THROW(parking_lot->reference(query));
-
// Let's park the packet and associate it with the callback function which
// simply records the fact that it has been called. We expect that it wasn't
// because the parked packet should be dropped as a result of lease updates
// failures.
ASSERT_NO_THROW(parking_lot->park(query, unpark_handler));
+ ASSERT_NO_THROW(parking_lot->reference(query));
+
// Actually perform the lease updates.
ASSERT_NO_THROW(runIOService(TEST_TIMEOUT, [this]() {
// Finish running IO service when there are no more pending requests.
EXPECT_FALSE(state->isPoked());
- ASSERT_NO_THROW(parking_lot->reference(query));
-
// Let's park the packet and associate it with the callback function which
// simply records the fact that it has been called. We expect that it wasn't
// because the parked packet should be dropped as a result of lease updates
// failures.
ASSERT_NO_THROW(parking_lot->park(query, unpark_handler));
+ ASSERT_NO_THROW(parking_lot->reference(query));
+
// Actually perform the lease updates.
ASSERT_NO_THROW(runIOService(TEST_TIMEOUT, [this]() {
// Finish running IO service when there are no more pending requests.
auto it = find(parked_object);
if (it == parking_.end()) {
isc_throw(InvalidOperation, "cannot reference an object"
- "that has not been parked.");
+ " that has not been parked.");
}
// Bump and return the reference count
auto it = find(parked_object);
if (it == parking_.end()) {
isc_throw(InvalidOperation, "cannot dereference an object"
- "that has not been parked.");
+ " that has not been parked.");
}
// Decrement and return the reference count.