Commit eff95437 authored by Tomek Mrugalski's avatar Tomek Mrugalski 🛰
Browse files

[2995] 4 hook points implemented:

 - pkt6_receive, subnet6_select, lease6_select, pkt6_send
 - framework updated
 - some unittests implemented
parent 7c20771f
......@@ -63,6 +63,7 @@ b10_dhcp4_LDADD += $(top_builddir)/src/lib/asiolink/libb10-asiolink.la
b10_dhcp4_LDADD += $(top_builddir)/src/lib/log/libb10-log.la
b10_dhcp4_LDADD += $(top_builddir)/src/lib/config/libb10-cfgclient.la
b10_dhcp4_LDADD += $(top_builddir)/src/lib/cc/libb10-cc.la
b10_dhcp4_LDADD += $(top_builddir)/src/lib/hooks/libb10-hooks.la
b10_dhcp4dir = $(pkgdatadir)
b10_dhcp4_DATA = dhcp4.spec
......@@ -71,6 +71,7 @@ dhcp4_unittests_LDADD += $(top_builddir)/src/lib/dhcpsrv/libb10-dhcpsrv.la
dhcp4_unittests_LDADD += $(top_builddir)/src/lib/exceptions/libb10-exceptions.la
dhcp4_unittests_LDADD += $(top_builddir)/src/lib/log/libb10-log.la
dhcp4_unittests_LDADD += $(top_builddir)/src/lib/util/libb10-util.la
dhcp4_unittests_LDADD += $(top_builddir)/src/lib/hooks/libb10-hooks.la
endif
noinst_PROGRAMS = $(TESTS)
......@@ -65,6 +65,7 @@ b10_dhcp6_LDADD += $(top_builddir)/src/lib/dhcpsrv/libb10-dhcpsrv.la
b10_dhcp6_LDADD += $(top_builddir)/src/lib/exceptions/libb10-exceptions.la
b10_dhcp6_LDADD += $(top_builddir)/src/lib/log/libb10-log.la
b10_dhcp6_LDADD += $(top_builddir)/src/lib/util/libb10-util.la
b10_dhcp6_LDADD += $(top_builddir)/src/lib/hooks/libb10-hooks.la
b10_dhcp6dir = $(pkgdatadir)
b10_dhcp6_DATA = dhcp6.spec
......@@ -38,6 +38,9 @@ const int DBG_DHCP6_COMMAND = DBGLVL_COMMAND;
// Trace basic operations within the code.
const int DBG_DHCP6_BASIC = DBGLVL_TRACE_BASIC;
// Trace hook related operations
const int DBG_DHCP6_HOOKS = DBGLVL_TRACE_BASIC;
// Trace detailed operations, including errors raised when processing invalid
// packets. (These are not logged at severities of WARN or higher for fear
// that a set of deliberately invalid packets set to the server could overwhelm
......
......@@ -65,6 +65,24 @@ This informational message is printed every time the IPv6 DHCP server
is started. It indicates what database backend type is being to store
lease and other information.
% DHCP6_HOOK_PACKET_RCVD_SKIP received DHCPv6 packet was dropped, because a callout set skip flag.
This debug message is printed when a callout installed on pkt6_received
hook point sets skip flag. This flag instructs the server to skip the next processing
stage, which would be to handle the packet. This effectively means drop the packet.
% DHCP6_HOOK_PACKET_SEND_SKIP Prepared DHCPv6 response was not sent, because a callout set skip flag.
This debug message is printed when a callout installed on pkt6_send
hook point sets skip flag. This flag instructs the server to skip the next processing
stage, which would be to send a response. This effectively means that the client will
not get any response, even though the server processed client's request and acted on
it (e.g. could possible allocate a lease).
% DHCP6_HOOK_SUBNET6_SELECT_SKIP No subnet was selected, because a callout set skip flag.
This debug message is printed when a callout installed on subnet6_select
hook point sets a skip flag. It means that the server was told that no subnet
should be selected. This severely limits further processing - server will be only
able to offer global options. No addresses or prefixes could be assigned.
% DHCP6_LEASE_ADVERT lease %1 advertised (client duid=%2, iaid=%3)
This debug message indicates that the server successfully advertised
a lease. It is up to the client to choose one server out of the
......
......@@ -37,6 +37,9 @@
#include <util/io_utilities.h>
#include <util/range_utilities.h>
#include <util/encode/hex.h>
#include <hooks/server_hooks.h>
#include <hooks/hooks_manager.h>
#include <hooks/callout_handle.h>
#include <boost/foreach.hpp>
#include <boost/tokenizer.hpp>
......@@ -50,6 +53,7 @@
using namespace isc;
using namespace isc::asiolink;
using namespace isc::dhcp;
using namespace isc::hooks;
using namespace isc::util;
using namespace std;
......@@ -67,7 +71,9 @@ namespace dhcp {
static const char* SERVER_DUID_FILE = "b10-dhcp6-serverid";
Dhcpv6Srv::Dhcpv6Srv(uint16_t port)
: alloc_engine_(), serverid_(), shutdown_(true) {
:alloc_engine_(), serverid_(), shutdown_(true), hook_index_pkt6_receive_(100),
hook_index_subnet6_select_(101), hook_index_pkt6_send_(102)
{
LOG_DEBUG(dhcp6_logger, DBG_DHCP6_START, DHCP6_OPEN_SOCKET).arg(port);
......@@ -106,6 +112,15 @@ Dhcpv6Srv::Dhcpv6Srv(uint16_t port)
// Instantiate allocation engine
alloc_engine_.reset(new AllocEngine(AllocEngine::ALLOC_ITERATIVE, 100));
// Register hook points
hook_index_pkt6_receive_ = ServerHooks::getServerHooks().registerHook("pkt6_receive");
hook_index_subnet6_select_ = ServerHooks::getServerHooks().registerHook("subnet6_select");
hook_index_pkt6_send_ = ServerHooks::getServerHooks().registerHook("pkt6_send");
/// @todo call loadLibraries() when handling configuration changes
vector<string> libraries; // no libraries at this time
HooksManager::getHooksManager().loadLibraries(libraries);
} catch (const std::exception &e) {
LOG_ERROR(dhcp6_logger, DHCP6_SRV_CONSTRUCT_ERROR).arg(e.what());
return;
......@@ -126,6 +141,10 @@ void Dhcpv6Srv::shutdown() {
shutdown_ = true;
}
Pkt6Ptr Dhcpv6Srv::receivePacket(int timeout) {
return (IfaceMgr::instance().receive6(timeout));
}
bool Dhcpv6Srv::run() {
while (!shutdown_) {
/// @todo: calculate actual timeout to the next event (e.g. lease
......@@ -134,14 +153,15 @@ bool Dhcpv6Srv::run() {
/// For now, we are just calling select for 1000 seconds. There
/// were some issues reported on some systems when calling select()
/// with too large values. Unfortunately, I don't recall the details.
int timeout = 1000;
//cppcheck-suppress variableScope This is temporary anyway
const int timeout = 1000;
// client's message and server's response
Pkt6Ptr query;
Pkt6Ptr rsp;
try {
query = IfaceMgr::instance().receive6(timeout);
query = receivePacket(timeout);
} catch (const std::exception& e) {
LOG_ERROR(dhcp6_logger, DHCP6_PACKET_RECEIVE_FAIL).arg(e.what());
}
......@@ -159,6 +179,24 @@ bool Dhcpv6Srv::run() {
.arg(query->getBuffer().getLength())
.arg(query->toText());
// Let's execute all callouts registered for packet_received
if (HooksManager::getHooksManager().calloutsPresent(hook_index_pkt6_receive_)) {
CalloutHandlePtr callout_handle = getCalloutHandle(query);
// This is the first callout, so no need to clear any arguments
callout_handle->setArgument("pkt6", query);
HooksManager::getHooksManager().callCallouts(hook_index_pkt6_receive_,
*callout_handle);
// Callouts decided to skip the next processing step. The next
// processing step would to process the packet, so skip at this
// stage means drop.
if (callout_handle->getSkip()) {
LOG_DEBUG(dhcp6_logger, DBG_DHCP6_HOOKS, DHCP6_HOOK_PACKET_RCVD_SKIP);
continue;
}
}
try {
switch (query->getType()) {
case DHCPV6_SOLICIT:
......@@ -203,7 +241,7 @@ bool Dhcpv6Srv::run() {
} catch (const RFCViolation& e) {
LOG_DEBUG(dhcp6_logger, DBG_DHCP6_BASIC, DHCP6_REQUIRED_OPTIONS_CHECK_FAIL)
.arg(query->getName())
.arg(query->getRemoteAddr())
.arg(query->getRemoteAddr().toText())
.arg(e.what());
} catch (const isc::Exception& e) {
......@@ -217,7 +255,7 @@ bool Dhcpv6Srv::run() {
// packets.)
LOG_DEBUG(dhcp6_logger, DBG_DHCP6_BASIC, DHCP6_PACKET_PROCESS_FAIL)
.arg(query->getName())
.arg(query->getRemoteAddr())
.arg(query->getRemoteAddr().toText())
.arg(e.what());
}
......@@ -229,6 +267,32 @@ bool Dhcpv6Srv::run() {
rsp->setIndex(query->getIndex());
rsp->setIface(query->getIface());
// Execute all callouts registered for packet6_send
if (HooksManager::getHooksManager().calloutsPresent(hook_index_pkt6_send_)) {
boost::shared_ptr<CalloutHandle> callout_handle = getCalloutHandle(query);
// Delete all previous arguments
callout_handle->deleteAllArguments();
// Clear skip flag if it was set in previous callouts
callout_handle->setSkip(false);
// Set our response
callout_handle->setArgument("pkt6", rsp);
// Call all installed callouts
HooksManager::getHooksManager().callCallouts(hook_index_pkt6_send_,
*callout_handle);
// Callouts decided to skip the next processing step. The next
// processing step would to send the packet, so skip at this
// stage means "drop response".
if (callout_handle->getSkip()) {
LOG_DEBUG(dhcp6_logger, DBG_DHCP6_HOOKS, DHCP6_HOOK_PACKET_SEND_SKIP);
continue;
}
}
LOG_DEBUG(dhcp6_logger, DBG_DHCP6_DETAIL_DATA,
DHCP6_RESPONSE_DATA)
.arg(static_cast<int>(rsp->getType())).arg(rsp->toText());
......@@ -559,6 +623,29 @@ Dhcpv6Srv::selectSubnet(const Pkt6Ptr& question) {
}
}
// Let's execute all callouts registered for packet_received
if (HooksManager::getHooksManager().calloutsPresent(hook_index_subnet6_select_)) {
boost::shared_ptr<CalloutHandle> callout_handle = getCalloutHandle(question);
// This is the first callout, so no need to clear any arguments
callout_handle->setArgument("pkt6", question);
callout_handle->setArgument("subnet6", subnet);
callout_handle->setArgument("subnet6collection", CfgMgr::instance().getSubnets6());
HooksManager::getHooksManager().callCallouts(hook_index_subnet6_select_,
*callout_handle);
// Callouts decided to skip this step. This means that no subnet will be
// selected. Packet processing will continue, but it will be severly limited
// (i.e. only global options will be assigned)
if (callout_handle->getSkip()) {
LOG_DEBUG(dhcp6_logger, DBG_DHCP6_HOOKS, DHCP6_HOOK_SUBNET6_SELECT_SKIP);
return (Subnet6Ptr());
}
// Use whatever subnet was specified by the callout
callout_handle->getArgument("subnet6", subnet);
}
return (subnet);
}
......@@ -618,7 +705,8 @@ Dhcpv6Srv::assignLeases(const Pkt6Ptr& question, Pkt6Ptr& answer) {
switch (opt->second->getType()) {
case D6O_IA_NA: {
OptionPtr answer_opt = assignIA_NA(subnet, duid, question,
boost::dynamic_pointer_cast<Option6IA>(opt->second));
boost::dynamic_pointer_cast<Option6IA>(opt->second),
question);
if (answer_opt) {
answer->addOption(answer_opt);
}
......@@ -632,7 +720,7 @@ Dhcpv6Srv::assignLeases(const Pkt6Ptr& question, Pkt6Ptr& answer) {
OptionPtr
Dhcpv6Srv::assignIA_NA(const Subnet6Ptr& subnet, const DuidPtr& duid,
Pkt6Ptr question, boost::shared_ptr<Option6IA> ia) {
Pkt6Ptr question, boost::shared_ptr<Option6IA> ia, const Pkt6Ptr& query) {
// If there is no subnet selected for handling this IA_NA, the only thing to do left is
// to say that we are sorry, but the user won't get an address. As a convenience, we
// use a different status text to indicate that (compare to the same status code,
......@@ -676,12 +764,15 @@ Dhcpv6Srv::assignIA_NA(const Subnet6Ptr& subnet, const DuidPtr& duid,
fake_allocation = true;
}
CalloutHandlePtr callout_handle = getCalloutHandle(query);
// Use allocation engine to pick a lease for this client. Allocation engine
// will try to honour the hint, but it is just a hint - some other address
// may be used instead. If fake_allocation is set to false, the lease will
// be inserted into the LeaseMgr as well.
Lease6Ptr lease = alloc_engine_->allocateAddress6(subnet, duid, ia->getIAID(),
hint, fake_allocation);
hint, fake_allocation,
callout_handle);
// Create IA_NA that we will put in the response.
// Do not use OptionDefinition to create option's instance so
......@@ -1102,5 +1193,24 @@ Dhcpv6Srv::processInfRequest(const Pkt6Ptr& infRequest) {
return reply;
}
isc::hooks::CalloutHandlePtr Dhcpv6Srv::getCalloutHandle(const Pkt6Ptr& pkt) {
CalloutHandlePtr callout_handle;
static Pkt6Ptr old_pointer;
if (!callout_handle ||
old_pointer != pkt) {
// This is the first packet or a different packet than previously
// passed to getCalloutHandle()
// Remember the pointer to this packet
old_pointer = pkt;
callout_handle = HooksManager::getHooksManager().createCalloutHandle();
}
return (callout_handle);
}
};
};
......@@ -23,6 +23,7 @@
#include <dhcp/pkt6.h>
#include <dhcpsrv/alloc_engine.h>
#include <dhcpsrv/subnet.h>
#include <hooks/hooks_manager.h>
#include <boost/noncopyable.hpp>
......@@ -87,6 +88,20 @@ public:
/// @brief Instructs the server to shut down.
void shutdown();
/// @brief returns ServerHooks object
/// @todo: remove this as soon as ServerHooks object is converted
/// to a signleton.
//static boost::shared_ptr<isc::util::ServerHooks> getServerHooks();
/// @brief returns Callout Manager object
///
/// This manager is used to manage callouts registered on various hook
/// points. @todo exact access method for HooksManager manager will change
/// when it will be converted to a singleton.
///
/// @return CalloutManager instance
//static boost::shared_ptr<isc::util::HooksManager> getHooksManager();
protected:
/// @brief verifies if specified packet meets RFC requirements
......@@ -189,7 +204,8 @@ protected:
OptionPtr assignIA_NA(const isc::dhcp::Subnet6Ptr& subnet,
const isc::dhcp::DuidPtr& duid,
isc::dhcp::Pkt6Ptr question,
boost::shared_ptr<Option6IA> ia);
boost::shared_ptr<Option6IA> ia,
const Pkt6Ptr& query);
/// @brief Renews specific IA_NA option
///
......@@ -321,6 +337,13 @@ protected:
/// @return string representation
static std::string duidToString(const OptionPtr& opt);
/// @brief dummy wrapper around IfaceMgr::receive6
///
/// This method is useful for testing purposes, where its replacement
/// simulates reception of a packet. For that purpose it is protected.
virtual Pkt6Ptr receivePacket(int timeout);
private:
/// @brief Allocation Engine.
/// Pointer to the allocation engine that we are currently using
......@@ -334,6 +357,17 @@ private:
/// Indicates if shutdown is in progress. Setting it to true will
/// initiate server shutdown procedure.
volatile bool shutdown_;
isc::hooks::CalloutHandlePtr getCalloutHandle(const Pkt6Ptr& pkt);
void packetProcessStart(const Pkt6Ptr& pkt);
void packetProcessEnd(const Pkt6Ptr& pkt);
/// Indexes for registered hook points
int hook_index_pkt6_receive_;
int hook_index_subnet6_select_;
int hook_index_pkt6_send_;
};
}; // namespace isc::dhcp
......
......@@ -68,6 +68,7 @@ dhcp6_unittests_LDADD += $(top_builddir)/src/lib/dhcpsrv/libb10-dhcpsrv.la
dhcp6_unittests_LDADD += $(top_builddir)/src/lib/exceptions/libb10-exceptions.la
dhcp6_unittests_LDADD += $(top_builddir)/src/lib/log/libb10-log.la
dhcp6_unittests_LDADD += $(top_builddir)/src/lib/util/libb10-util.la
dhcp6_unittests_LDADD += $(top_builddir)/src/lib/hooks/libb10-hooks.la
endif
noinst_PROGRAMS = $(TESTS)
......@@ -33,12 +33,16 @@
#include <util/buffer.h>
#include <util/range_utilities.h>
#include <hooks/server_hooks.h>
#include <hooks/callout_manager.h>
#include <boost/scoped_ptr.hpp>
#include <gtest/gtest.h>
#include <unistd.h>
#include <fstream>
#include <iostream>
#include <sstream>
#include <list>
using namespace isc;
using namespace isc::asiolink;
......@@ -46,6 +50,7 @@ using namespace isc::config;
using namespace isc::data;
using namespace isc::dhcp;
using namespace isc::util;
using namespace isc::hooks;
using namespace std;
// namespace has to be named, because friends are defined in Dhcpv6Srv class
......@@ -61,6 +66,26 @@ public:
LeaseMgrFactory::create(memfile);
}
virtual Pkt6Ptr receivePacket(int /*timeout*/) {
// If there is anything prepared as fake incoming
// traffic, use it
if (!to_be_received_.empty()) {
Pkt6Ptr pkt = to_be_received_.front();
to_be_received_.pop_front();
return (pkt);
}
// If not, just trigger shutdown and
// return immediately
shutdown();
return (Pkt6Ptr());
}
void fakeReceive(const Pkt6Ptr& pkt) {
to_be_received_.push_back(pkt);
}
virtual ~NakedDhcpv6Srv() {
// Close the lease database
LeaseMgrFactory::destroy();
......@@ -75,6 +100,8 @@ public:
using Dhcpv6Srv::sanityCheck;
using Dhcpv6Srv::loadServerID;
using Dhcpv6Srv::writeServerID;
list<Pkt6Ptr> to_be_received_;
};
static const char* DUID_FILE = "server-id-test.txt";
......@@ -226,6 +253,9 @@ public:
}
virtual ~NakedDhcpv6SrvTest() {
// Remove all registered hook points
ServerHooks::getServerHooks().reset();
// Let's clean up if there is such a file.
unlink(DUID_FILE);
};
......@@ -1756,6 +1786,85 @@ TEST_F(Dhcpv6SrvTest, ServerID) {
EXPECT_EQ(duid1_text, text);
}
// Checks if hooks are implemented properly.
TEST_F(Dhcpv6SrvTest, Hooks) {
NakedDhcpv6Srv srv(0);
// check if appropriate hooks are registered
// check if appropriate indexes are set
int hook_index_pkt6_received = ServerHooks::getServerHooks().getIndex("pkt6_receive");
int hook_index_select_subnet = ServerHooks::getServerHooks().getIndex("subnet6_select");
int hook_index_pkt6_send = ServerHooks::getServerHooks().getIndex("pkt6_send");
EXPECT_TRUE(hook_index_pkt6_received > 0);
EXPECT_TRUE(hook_index_select_subnet > 0);
EXPECT_TRUE(hook_index_pkt6_send > 0);
}
// This function returns buffer for empty packet (just DHCPv6 header)
Pkt6* captureEmpty() {
Pkt6* pkt;
uint8_t data[4];
data[0] = 1; // type 1 = SOLICIT
data[1] = 0xca; // trans-id = 0xcafe01
data[2] = 0xfe;
data[3] = 0x01;
pkt = new Pkt6(data, sizeof(data));
pkt->setRemotePort(546);
pkt->setRemoteAddr(IOAddress("fe80::1"));
pkt->setLocalPort(0);
pkt->setLocalAddr(IOAddress("ff02::1:2"));
pkt->setIndex(2);
pkt->setIface("eth0");
return (pkt);
}
string callback_name("");
Pkt6Ptr callback_packet;
int
pkt6_receive_callout(CalloutHandle& callout_handle) {
printf("pkt6_receive_callout called!");
callback_name = string("pkt6_receive");
callout_handle.getArgument("pkt6", callback_packet);
return (0);
}
// Checks if callouts installed on pkt6_received are indeed called
// Note that the test name does not follow test naming convention,
// but the proper hook name is "pkt6_receive".
TEST_F(Dhcpv6SrvTest, Hook_pkt6_receive) {
// This calls Dhcpv6Srv::ctor, which registers hook names
NakedDhcpv6Srv srv(0);
// Let's pretend there will be 3 libraries
CalloutManager callout_mgr(3);
// Let's pretent we're the library 0
EXPECT_NO_THROW(callout_mgr.setLibraryIndex(0));
EXPECT_NO_THROW( callout_mgr.registerCallout("pkt6_receive", pkt6_receive_callout) );
// Let's create a REQUEST
Pkt6Ptr req = Pkt6Ptr(captureEmpty());
// Simulate that we have received that traffic
srv.fakeReceive(req);
// Server will now process to run its normal loop, but instead of calling
// IfaceMgr::receive6(), it will read all packets from the list set by
// fakeReceive()
// In particular, it should call registered pkt6_receive callback.
srv.run();
}
/// @todo: Add more negative tests for processX(), e.g. extend sanityCheck() test
/// to call processX() methods.
......
......@@ -16,11 +16,15 @@
#include <dhcpsrv/dhcpsrv_log.h>
#include <dhcpsrv/lease_mgr_factory.h>
#include <hooks/server_hooks.h>
#include <hooks/hooks_manager.h>
#include <cstring>
#include <vector>
#include <string.h>
using namespace isc::asiolink;
using namespace isc::hooks;
namespace isc {
namespace dhcp {
......@@ -161,6 +165,9 @@ AllocEngine::AllocEngine(AllocType engine_type, unsigned int attempts)
default:
isc_throw(BadValue, "Invalid/unsupported allocation algorithm");
}
// Register hook points
hook_index_lease6_select_ = ServerHooks::getServerHooks().registerHook("lease6_select");
}
Lease6Ptr
......@@ -168,7 +175,8 @@ AllocEngine::allocateAddress6(const Subnet6Ptr& subnet,
const DuidPtr& duid,
uint32_t iaid,
const IOAddress& hint,
bool fake_allocation /* = false */ ) {
bool fake_allocation,
const isc::hooks::CalloutHandlePtr& callout_handle) {
try {
// That check is not necessary. We create allocator in AllocEngine
......@@ -201,7 +209,8 @@ AllocEngine::allocateAddress6(const Subnet6Ptr& subnet,
/// implemented
// the hint is valid and not currently used, let's create a lease for it
Lease6Ptr lease = createLease6(subnet, duid, iaid, hint, fake_allocation);
Lease6Ptr lease = createLease6(subnet, duid, iaid, hint, callout_handle,
fake_allocation);
// It can happen that the lease allocation failed (we could have lost
// the race condition. That means that the hint is lo longer usable and
......@@ -212,7 +221,7 @@ AllocEngine::allocateAddress6(const Subnet6Ptr& subnet,
} else {
if (existing->expired()) {
return (reuseExpiredLease(existing, subnet, duid, iaid,
fake_allocation));
callout_handle, fake_allocation));
}
}
......@@ -246,7 +255,7 @@ AllocEngine::allocateAddress6(const Subnet6Ptr& subnet,
// there's no existing lease for selected candidate, so it is
// free. Let's allocate it.
Lease6Ptr lease = createLease6(subnet, duid, iaid, candidate,
fake_allocation);
callout_handle, fake_allocation);
if (lease) {
return (lease);
}
......@@ -257,7 +266,7 @@ AllocEngine::allocateAddress6(const Subnet6Ptr& subnet,
} else {
if (existing->expired()) {
return (reuseExpiredLease(existing, subnet, duid, iaid,
fake_allocation));
callout_handle, fake_allocation));
}
}
......@@ -438,6 +447,7 @@ Lease6Ptr AllocEngine::reuseExpiredLease(Lease6Ptr& expired,
const Subnet6Ptr& subnet,
const DuidPtr& duid,
uint32_t iaid,
const isc::hooks::CalloutHandlePtr& callout_handle,
bool fake_allocation /*= false */ ) {
if (!expired->expired()) {
......@@ -461,6 +471,42 @@ Lease6Ptr AllocEngine::reuseExpiredLease(Lease6Ptr& expired,
/// @todo: log here that the lease was reused (there's ticket #2524 for
/// logging in libdhcpsrv)
// Let's execute all callouts registered for lease6_ia_added
if (callout_handle &&
HooksManager::getHooksManager().calloutsPresent(hook_index_lease6_select_)) {
// Delete all previous arguments
callout_handle->deleteAllArguments();
// Clear skip flag if it was set in previous callouts
callout_handle->setSkip(false);
// Pass necessary arguments
// Subnet from which we do the allocation