2
0
mirror of https://gitlab.isc.org/isc-projects/kea synced 2025-08-31 14:05:33 +00:00

[2995] 4 hook points implemented:

- pkt6_receive, subnet6_select, lease6_select, pkt6_send
 - framework updated
 - some unittests implemented
This commit is contained in:
Tomek Mrugalski
2013-06-21 19:26:54 +02:00
parent 7c20771f42
commit eff9543714
19 changed files with 434 additions and 27 deletions

View File

@@ -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

View File

@@ -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)

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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);
}
};
};

View File

@@ -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

View File

@@ -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)

View File

@@ -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.

View File

@@ -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
callout_handle->setArgument("subnet6", subnet);
// Is this solicit (fake = true) or request (fake = false)
callout_handle->setArgument("fake_allocation", fake_allocation);
callout_handle->setArgument("lease6", expired);
// This is the first callout, so no need to clear any arguments
HooksManager::getHooksManager().callCallouts(hook_index_lease6_select_,
*callout_handle);
// 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
if (callout_handle->getSkip()) {
LOG_DEBUG(dhcpsrv_logger, DHCPSRV_HOOKS, DHCPSRV_HOOK_LEASE6_IA_ADD_SKIP);
return (Lease6Ptr());
}
// Let's use whatever callout returned. Hopefully it is the same lease
// we handled to it.
callout_handle->getArgument("lease6", expired);
}
if (!fake_allocation) {
// for REQUEST we do update the lease
LeaseMgrFactory::instance().updateLease6(expired);
@@ -517,12 +563,49 @@ Lease6Ptr AllocEngine::createLease6(const Subnet6Ptr& subnet,
const DuidPtr& duid,
uint32_t iaid,
const IOAddress& addr,
const isc::hooks::CalloutHandlePtr& callout_handle,
bool fake_allocation /*= false */ ) {
Lease6Ptr lease(new Lease6(Lease6::LEASE_IA_NA, addr, duid, iaid,
subnet->getPreferred(), subnet->getValid(),
subnet->getT1(), subnet->getT2(), subnet->getID()));
// 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
callout_handle->setArgument("subnet6", subnet);
// Is this solicit (fake = true) or request (fake = false)
callout_handle->setArgument("fake_allocation", fake_allocation);
callout_handle->setArgument("lease6", lease);
// This is the first callout, so no need to clear any arguments
HooksManager::getHooksManager().callCallouts(hook_index_lease6_select_,
*callout_handle);
// 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
if (callout_handle->getSkip()) {
LOG_DEBUG(dhcpsrv_logger, DHCPSRV_HOOKS, DHCPSRV_HOOK_LEASE6_IA_ADD_SKIP);
return (Lease6Ptr());
}
// Let's use whatever callout returned. Hopefully it is the same lease
// we handled to it.
callout_handle->getArgument("lease6", lease);
}
if (!fake_allocation) {
// That is a real (REQUEST) allocation
bool status = LeaseMgrFactory::instance().addLease(lease);

View File

@@ -20,6 +20,7 @@
#include <dhcp/hwaddr.h>
#include <dhcpsrv/subnet.h>
#include <dhcpsrv/lease_mgr.h>
#include <hooks/callout_handle.h>
#include <boost/shared_ptr.hpp>
#include <boost/noncopyable.hpp>
@@ -235,13 +236,17 @@ protected:
/// @param hint a hint that the client provided
/// @param fake_allocation is this real i.e. REQUEST (false) or just picking
/// an address for SOLICIT that is not really allocated (true)
/// @param callout_handle a callout handle (used in hooks). A lease callouts
/// will be executed if this parameter is passed.
///
/// @return Allocated IPv6 lease (or NULL if allocation failed)
Lease6Ptr
allocateAddress6(const Subnet6Ptr& subnet,
const DuidPtr& duid,
uint32_t iaid,
const isc::asiolink::IOAddress& hint,
bool fake_allocation);
bool fake_allocation,
const isc::hooks::CalloutHandlePtr& callout_handle);
/// @brief Destructor. Used during DHCPv6 service shutdown.
virtual ~AllocEngine();
@@ -276,12 +281,15 @@ private:
/// @param duid client's DUID
/// @param iaid IAID from the IA_NA container the client sent to us
/// @param addr an address that was selected and is confirmed to be available
/// @param callout_handle a callout handle (used in hooks). A lease callouts
/// will be executed if this parameter is passed.
/// @param fake_allocation is this real i.e. REQUEST (false) or just picking
/// an address for SOLICIT that is not really allocated (true)
/// @return allocated lease (or NULL in the unlikely case of the lease just
/// becomed unavailable)
Lease6Ptr createLease6(const Subnet6Ptr& subnet, const DuidPtr& duid,
uint32_t iaid, const isc::asiolink::IOAddress& addr,
const isc::hooks::CalloutHandlePtr& callout_handle,
bool fake_allocation = false);
/// @brief Reuses expired IPv4 lease
@@ -313,12 +321,15 @@ private:
/// @param subnet subnet the lease is allocated from
/// @param duid client's DUID
/// @param iaid IAID from the IA_NA container the client sent to us
/// @param callout_handle a callout handle (used in hooks). A lease callouts
/// will be executed if this parameter is passed.
/// @param fake_allocation is this real i.e. REQUEST (false) or just picking
/// an address for SOLICIT that is not really allocated (true)
/// @return refreshed lease
/// @throw BadValue if trying to recycle lease that is still valid
Lease6Ptr reuseExpiredLease(Lease6Ptr& expired, const Subnet6Ptr& subnet,
const DuidPtr& duid, uint32_t iaid,
const isc::hooks::CalloutHandlePtr& callout_handle,
bool fake_allocation = false);
/// @brief a pointer to currently used allocator
@@ -326,6 +337,9 @@ private:
/// @brief number of attempts before we give up lease allocation (0=unlimited)
unsigned int attempts_;
/// @brief hook name index (used in hooks callouts)
int hook_index_lease6_select_;
};
}; // namespace isc::dhcp

View File

@@ -262,6 +262,11 @@ void CfgMgr::deleteSubnets6() {
subnets6_.clear();
}
const Subnet6Collection& CfgMgr::getSubnets6() {
return (subnets6_);
}
std::string CfgMgr::getDataDir() {
return (datadir_);
}

View File

@@ -201,6 +201,15 @@ public:
/// completely new?
void deleteSubnets6();
/// @brief returns const reference to all subnets6
///
/// This is used in a hook (subnet6_select), where the hook is able
/// to choose a different subnet. Server code has to offer a list
/// of possible choices (i.e. all subnets).
/// @return const reference to Subnet6 collection
const Subnet6Collection& getSubnets6();
/// @brief get IPv4 subnet by address
///
/// Finds a matching subnet, based on an address. This can be used

View File

@@ -50,6 +50,9 @@ const int DHCPSRV_DBG_TRACE_DETAIL = DBGLVL_TRACE_DETAIL;
/// Record detailed (and verbose) data on the server.
const int DHCPSRV_DBG_TRACE_DETAIL_DATA = DBGLVL_TRACE_DETAIL_DATA;
// Trace hook related operations
const int DHCPSRV_HOOKS = DBGLVL_TRACE_BASIC;
///@}

View File

@@ -121,6 +121,12 @@ the database access parameters are changed: in the latter case, the
server closes the currently open database, and opens a database using
the new parameters.
% DHCPSRV_HOOK_LEASE6_IA_ADD_SKIP Lease6 (non-temporary) creation was skipped, because of callout skip flag.
This debug message is printed when a callout installed on lease6_ia_assign
hook point sets a skip flag. It means that the server was told that no lease6
should be assigned. Server will not put that lease in its database and the client
will get a NoAddrsAvail for that IA_NA option.
% DHCPSRV_INVALID_ACCESS invalid database access string: %1
This is logged when an attempt has been made to parse a database access string
and the attempt ended in error. The access string in question - which

View File

@@ -69,6 +69,7 @@ libdhcpsrv_unittests_LDADD += $(top_builddir)/src/lib/cc/libb10-cc.la
libdhcpsrv_unittests_LDADD += $(top_builddir)/src/lib/asiolink/libb10-asiolink.la
libdhcpsrv_unittests_LDADD += $(top_builddir)/src/lib/exceptions/libb10-exceptions.la
libdhcpsrv_unittests_LDADD += $(top_builddir)/src/lib/log/libb10-log.la
libdhcpsrv_unittests_LDADD += $(top_builddir)/src/lib/hooks/libb10-hooks.la
libdhcpsrv_unittests_LDADD += $(GTEST_LDADD)
endif

View File

@@ -25,6 +25,8 @@
#include <dhcpsrv/tests/test_utils.h>
#include <hooks/callout_handle.h>
#include <boost/shared_ptr.hpp>
#include <boost/scoped_ptr.hpp>
#include <gtest/gtest.h>
@@ -37,6 +39,7 @@
using namespace std;
using namespace isc;
using namespace isc::asiolink;
using namespace isc::hooks;
using namespace isc::dhcp;
using namespace isc::dhcp::test;
@@ -203,7 +206,7 @@ TEST_F(AllocEngine6Test, simpleAlloc6) {
ASSERT_TRUE(engine);
Lease6Ptr lease = engine->allocateAddress6(subnet_, duid_, iaid_, IOAddress("::"),
false);
false, CalloutHandlePtr());
// Check that we got a lease
ASSERT_TRUE(lease);
@@ -226,7 +229,7 @@ TEST_F(AllocEngine6Test, fakeAlloc6) {
ASSERT_TRUE(engine);
Lease6Ptr lease = engine->allocateAddress6(subnet_, duid_, iaid_, IOAddress("::"),
true);
true, CalloutHandlePtr());
// Check that we got a lease
ASSERT_TRUE(lease);
@@ -248,7 +251,7 @@ TEST_F(AllocEngine6Test, allocWithValidHint6) {
Lease6Ptr lease = engine->allocateAddress6(subnet_, duid_, iaid_,
IOAddress("2001:db8:1::15"),
false);
false, CalloutHandlePtr());
// Check that we got a lease
ASSERT_TRUE(lease);
@@ -286,7 +289,7 @@ TEST_F(AllocEngine6Test, allocWithUsedHint6) {
// twice.
Lease6Ptr lease = engine->allocateAddress6(subnet_, duid_, iaid_,
IOAddress("2001:db8:1::1f"),
false);
false, CalloutHandlePtr());
// Check that we got a lease
ASSERT_TRUE(lease);
@@ -319,7 +322,7 @@ TEST_F(AllocEngine6Test, allocBogusHint6) {
// with the normal allocation
Lease6Ptr lease = engine->allocateAddress6(subnet_, duid_, iaid_,
IOAddress("3000::abc"),
false);
false, CalloutHandlePtr());
// Check that we got a lease
ASSERT_TRUE(lease);
@@ -345,12 +348,12 @@ TEST_F(AllocEngine6Test, allocateAddress6Nulls) {
// Allocations without subnet are not allowed
Lease6Ptr lease = engine->allocateAddress6(Subnet6Ptr(), duid_, iaid_,
IOAddress("::"), false);
IOAddress("::"), false, CalloutHandlePtr());
ASSERT_FALSE(lease);
// Allocations without DUID are not allowed either
lease = engine->allocateAddress6(subnet_, DuidPtr(), iaid_,
IOAddress("::"), false);
IOAddress("::"), false, CalloutHandlePtr());
ASSERT_FALSE(lease);
}
@@ -439,7 +442,7 @@ TEST_F(AllocEngine6Test, smallPool6) {
cfg_mgr.addSubnet6(subnet_);
Lease6Ptr lease = engine->allocateAddress6(subnet_, duid_, iaid_, IOAddress("::"),
false);
false, CalloutHandlePtr());
// Check that we got that single lease
ASSERT_TRUE(lease);
@@ -485,7 +488,7 @@ TEST_F(AllocEngine6Test, outOfAddresses6) {
// There is just a single address in the pool and allocated it to someone
// else, so the allocation should fail
Lease6Ptr lease2 = engine->allocateAddress6(subnet_, duid_, iaid_,
IOAddress("::"), false);
IOAddress("::"), false, CalloutHandlePtr());
EXPECT_FALSE(lease2);
}
@@ -519,7 +522,7 @@ TEST_F(AllocEngine6Test, solicitReuseExpiredLease6) {
// CASE 1: Asking for any address
lease = engine->allocateAddress6(subnet_, duid_, iaid_, IOAddress("::"),
true);
true, CalloutHandlePtr());
// Check that we got that single lease
ASSERT_TRUE(lease);
EXPECT_EQ(addr.toText(), lease->addr_.toText());
@@ -529,7 +532,7 @@ TEST_F(AllocEngine6Test, solicitReuseExpiredLease6) {
// CASE 2: Asking specifically for this address
lease = engine->allocateAddress6(subnet_, duid_, iaid_, IOAddress(addr.toText()),
true);
true, CalloutHandlePtr());
// Check that we got that single lease
ASSERT_TRUE(lease);
EXPECT_EQ(addr.toText(), lease->addr_.toText());
@@ -563,7 +566,8 @@ TEST_F(AllocEngine6Test, requestReuseExpiredLease6) {
// A client comes along, asking specifically for this address
lease = engine->allocateAddress6(subnet_, duid_, iaid_,
IOAddress(addr.toText()), false);
IOAddress(addr.toText()), false,
CalloutHandlePtr());
// Check that he got that single lease
ASSERT_TRUE(lease);

View File

@@ -34,6 +34,7 @@ libb10_hooks_la_SOURCES += hooks_log.cc hooks_log.h
libb10_hooks_la_SOURCES += library_handle.cc library_handle.h
libb10_hooks_la_SOURCES += library_manager.cc library_manager.h
libb10_hooks_la_SOURCES += server_hooks.cc server_hooks.h
libb10_hooks_la_SOURCES += hooks_manager.cc hooks_manager.h
nodist_libb10_hooks_la_SOURCES = hooks_messages.cc hooks_messages.h

View File

@@ -353,6 +353,9 @@ private:
bool skip_;
};
/// a shared pointer to CalloutHandle object
typedef boost::shared_ptr<CalloutHandle> CalloutHandlePtr;
} // namespace util
} // namespace isc