mirror of
https://gitlab.isc.org/isc-projects/kea
synced 2025-08-29 21:18:02 +00:00
[master] Merge branch 'trac5457'
# Conflicts: # src/bin/dhcp4/tests/callout_library_1.cc # src/bin/dhcp4/tests/callout_library_2.cc
This commit is contained in:
commit
af43f07b0e
@ -1,4 +1,4 @@
|
|||||||
// Copyright (C) 2014-2017 Internet Systems Consortium, Inc. ("ISC")
|
// Copyright (C) 2014-2018 Internet Systems Consortium, Inc. ("ISC")
|
||||||
//
|
//
|
||||||
// This Source Code Form is subject to the terms of the Mozilla Public
|
// This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
@ -15,6 +15,7 @@
|
|||||||
#include <dhcp4/json_config_parser.h>
|
#include <dhcp4/json_config_parser.h>
|
||||||
#include <dhcpsrv/cfgmgr.h>
|
#include <dhcpsrv/cfgmgr.h>
|
||||||
#include <dhcpsrv/cfg_db_access.h>
|
#include <dhcpsrv/cfg_db_access.h>
|
||||||
|
#include <hooks/hooks.h>
|
||||||
#include <hooks/hooks_manager.h>
|
#include <hooks/hooks_manager.h>
|
||||||
#include <stats/stats_mgr.h>
|
#include <stats/stats_mgr.h>
|
||||||
#include <cfgrpt/config_report.h>
|
#include <cfgrpt/config_report.h>
|
||||||
@ -30,6 +31,23 @@ using namespace std;
|
|||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
|
|
||||||
|
/// Structure that holds registered hook indexes.
|
||||||
|
struct CtrlDhcp4Hooks {
|
||||||
|
int hooks_index_dhcp4_srv_configured_;
|
||||||
|
|
||||||
|
/// Constructor that registers hook points for the DHCPv4 server.
|
||||||
|
CtrlDhcp4Hooks() {
|
||||||
|
hooks_index_dhcp4_srv_configured_ = HooksManager::registerHook("dhcp4_srv_configured");
|
||||||
|
}
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
// Declare a Hooks object. As this is outside any function or method, it
|
||||||
|
// will be instantiated (and the constructor run) when the module is loaded.
|
||||||
|
// As a result, the hook indexes will be defined before any method in this
|
||||||
|
// module is called.
|
||||||
|
CtrlDhcp4Hooks Hooks;
|
||||||
|
|
||||||
/// @brief Signals handler for DHCPv4 server.
|
/// @brief Signals handler for DHCPv4 server.
|
||||||
///
|
///
|
||||||
/// This signal handler handles the following signals received by the DHCPv4
|
/// This signal handler handles the following signals received by the DHCPv4
|
||||||
@ -637,6 +655,25 @@ ControlledDhcpv4Srv::processConfig(isc::data::ConstElementPtr config) {
|
|||||||
return (isc::config::createAnswer(1, err.str()));
|
return (isc::config::createAnswer(1, err.str()));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// This hook point notifies hooks libraries that the configuration of the
|
||||||
|
// DHCPv4 server has completed. It provides the hook library with the pointer
|
||||||
|
// to the common IO service object, new server configuration in the JSON
|
||||||
|
// format and with the pointer to the configuration storage where the
|
||||||
|
// parsed configuration is stored.
|
||||||
|
if (HooksManager::calloutsPresent(Hooks.hooks_index_dhcp4_srv_configured_)) {
|
||||||
|
CalloutHandlePtr callout_handle = HooksManager::createCalloutHandle();
|
||||||
|
|
||||||
|
callout_handle->setArgument("io_context", srv->getIOService());
|
||||||
|
callout_handle->setArgument("json_config", config);
|
||||||
|
callout_handle->setArgument("server_config", CfgMgr::instance().getStagingCfg());
|
||||||
|
|
||||||
|
HooksManager::callCallouts(Hooks.hooks_index_dhcp4_srv_configured_,
|
||||||
|
*callout_handle);
|
||||||
|
|
||||||
|
// Ignore status code as none of them would have an effect on further
|
||||||
|
// operation.
|
||||||
|
}
|
||||||
|
|
||||||
return (answer);
|
return (answer);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -46,6 +46,23 @@ packet processing, but the exact order depends on the actual processing. Hook po
|
|||||||
that are not specific to packet processing (e.g. lease expiration) will be added
|
that are not specific to packet processing (e.g. lease expiration) will be added
|
||||||
to the end of this list.
|
to the end of this list.
|
||||||
|
|
||||||
|
@subsection dhcp4HooksDhcpv4SrvConfigured dhcp4_srv_configured
|
||||||
|
- @b Arguments:
|
||||||
|
- name: @b io_context, type: isc::asiolink::IOServicePtr, direction: <b>in</b>
|
||||||
|
- name: @b json_config, type: isc::data::ConstElementPtr, direction: <b>in</b>
|
||||||
|
- name: @b server_config, type: isc::dhcp::SrvConfigPtr, direction: <b>in</b>
|
||||||
|
|
||||||
|
- @b Description: this callout is executed when the server has completed
|
||||||
|
its (re)configuration. The server provides received and parsed configuration
|
||||||
|
structures to the hook library. It also provides a pointer to the IOService
|
||||||
|
object which is used by the server to run asynchronous operations. The hooks
|
||||||
|
libraries can use this IOService object to schedule asynchronous tasks which
|
||||||
|
are triggered by the DHCP server's main loop. The hook library should hold the
|
||||||
|
provided pointer until the library is unloaded.
|
||||||
|
|
||||||
|
- <b>Next step status</b>: Status codes retured by the callouts installed on
|
||||||
|
this hook point are ignored.
|
||||||
|
|
||||||
@subsection dhcpv4HooksBuffer4Receive buffer4_receive
|
@subsection dhcpv4HooksBuffer4Receive buffer4_receive
|
||||||
|
|
||||||
- @b Arguments:
|
- @b Arguments:
|
||||||
@ -215,6 +232,37 @@ to the end of this list.
|
|||||||
marked this lease as unavailable. If the client restarts its configuration,
|
marked this lease as unavailable. If the client restarts its configuration,
|
||||||
it will get the same (not declined) lease as a result.
|
it will get the same (not declined) lease as a result.
|
||||||
|
|
||||||
|
@subsection dhcpv4Leases4Committed leases4_committed
|
||||||
|
|
||||||
|
- @b Arguments:
|
||||||
|
- name: @b query4, type: isc::dhcp::Pkt4Ptr, direction: <b>in</b>
|
||||||
|
- name: @b leases4, type: isc::dhcp::Leases4CollectionPtr, direction: <b>in</b>
|
||||||
|
- name: @b deleted_leases4, type: isc::dhcp::Leases4CollectionPtr, direction: <b>in</b>
|
||||||
|
|
||||||
|
- @b Description: this callout is executed when the server has applied all
|
||||||
|
lease changes as a result of DHCP message processing. This includes
|
||||||
|
writing new lease into the database, releasing an old lease for this
|
||||||
|
client or declining a lease. This callout is executed only for the
|
||||||
|
DHCP client messages which may cause lease changes, i.e. DHCPREQUEST,
|
||||||
|
DHCPRELEASE and DHCPDECLINE. This callout is not executed for DHCPDISCOVER
|
||||||
|
and DHCPINFORM. If the callouts are executed as a result of DHCPREQUEST
|
||||||
|
message, it is possible that both leases collections hold leases to be
|
||||||
|
handled. This is the case when the new lease allocation replaces an existing
|
||||||
|
lease for the client. The "deleted_leases4" object will hold a previous
|
||||||
|
lease instance and the "leases4" object will hold the new lease for this
|
||||||
|
client. The callouts should be prepared to handle such situation. When
|
||||||
|
the callout is executed as a result DHCPRELEASE, the callout will typically
|
||||||
|
receive only one lease (being released) in the "deleted_leases4" object.
|
||||||
|
Both leases collections are always provided to the callouts,
|
||||||
|
even though they may sometimes be empty.
|
||||||
|
|
||||||
|
- <b>Next step status</b>: If any callout installed on the "leases4_committed"
|
||||||
|
sets the next step action to DROP the server will drop the processed query.
|
||||||
|
If it sets the next step action to PARK, the server will park the processed
|
||||||
|
packet (hold packet processing) until the hook libraries explicitly unpark
|
||||||
|
the packet after they are done performing asynchronous operations.
|
||||||
|
|
||||||
|
|
||||||
@subsection dhcpv4HooksPkt4Send pkt4_send
|
@subsection dhcpv4HooksPkt4Send pkt4_send
|
||||||
|
|
||||||
- @b Arguments:
|
- @b Arguments:
|
||||||
|
@ -276,6 +276,14 @@ This debug message is printed when a callout installed on lease4_release
|
|||||||
hook point set the next step status to SKIP. For this particular hook point, the
|
hook point set the next step status to SKIP. For this particular hook point, the
|
||||||
value set by a callout instructs the server to not release a lease.
|
value set by a callout instructs the server to not release a lease.
|
||||||
|
|
||||||
|
% DHCP4_HOOK_LEASES4_COMMITTED_DROP %1: packet is dropped, because a callout set the next step to DROP
|
||||||
|
This debug message is printed when a callout installed on the leases4_committed
|
||||||
|
hook point sets the next step to DROP.
|
||||||
|
|
||||||
|
% DHCP4_HOOK_LEASES4_COMMITTED_PARK %1: packet is parked, because a callout set the next step to PARK
|
||||||
|
This debug message is printed when a callout installed on the lease4_committed
|
||||||
|
hook point sets the next step to PARK.
|
||||||
|
|
||||||
% DHCP4_HOOK_PACKET_RCVD_SKIP %1: packet is dropped, because a callout set the next step to SKIP
|
% DHCP4_HOOK_PACKET_RCVD_SKIP %1: packet is dropped, because a callout set the next step to SKIP
|
||||||
This debug message is printed when a callout installed on the pkt4_receive
|
This debug message is printed when a callout installed on the pkt4_receive
|
||||||
hook point sets the next step to SKIP. For this particular hook point, the
|
hook point sets the next step to SKIP. For this particular hook point, the
|
||||||
|
@ -24,7 +24,6 @@
|
|||||||
#include <dhcp4/dhcp4_log.h>
|
#include <dhcp4/dhcp4_log.h>
|
||||||
#include <dhcp4/dhcp4_srv.h>
|
#include <dhcp4/dhcp4_srv.h>
|
||||||
#include <dhcpsrv/addr_utilities.h>
|
#include <dhcpsrv/addr_utilities.h>
|
||||||
#include <dhcpsrv/callout_handle_store.h>
|
|
||||||
#include <dhcpsrv/cfgmgr.h>
|
#include <dhcpsrv/cfgmgr.h>
|
||||||
#include <dhcpsrv/cfg_host_operations.h>
|
#include <dhcpsrv/cfg_host_operations.h>
|
||||||
#include <dhcpsrv/cfg_iface.h>
|
#include <dhcpsrv/cfg_iface.h>
|
||||||
@ -79,22 +78,26 @@ using namespace isc::log;
|
|||||||
using namespace isc::stats;
|
using namespace isc::stats;
|
||||||
using namespace std;
|
using namespace std;
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
|
||||||
/// Structure that holds registered hook indexes
|
/// Structure that holds registered hook indexes
|
||||||
struct Dhcp4Hooks {
|
struct Dhcp4Hooks {
|
||||||
int hook_index_buffer4_receive_; ///< index for "buffer4_receive" hook point
|
int hook_index_buffer4_receive_; ///< index for "buffer4_receive" hook point
|
||||||
int hook_index_pkt4_receive_; ///< index for "pkt4_receive" hook point
|
int hook_index_pkt4_receive_; ///< index for "pkt4_receive" hook point
|
||||||
int hook_index_subnet4_select_; ///< index for "subnet4_select" hook point
|
int hook_index_subnet4_select_; ///< index for "subnet4_select" hook point
|
||||||
|
int hook_index_leases4_committed_; ///< index for "leases4_committed" hook point
|
||||||
int hook_index_lease4_release_; ///< index for "lease4_release" hook point
|
int hook_index_lease4_release_; ///< index for "lease4_release" hook point
|
||||||
int hook_index_pkt4_send_; ///< index for "pkt4_send" hook point
|
int hook_index_pkt4_send_; ///< index for "pkt4_send" hook point
|
||||||
int hook_index_buffer4_send_; ///< index for "buffer4_send" hook point
|
int hook_index_buffer4_send_; ///< index for "buffer4_send" hook point
|
||||||
int hook_index_lease4_decline_; ///< index for "lease4_decline" hook point
|
int hook_index_lease4_decline_; ///< index for "lease4_decline" hook point
|
||||||
int hook_index_host4_identifier_;///< index for "host4_identifier" hook point
|
int hook_index_host4_identifier_; ///< index for "host4_identifier" hook point
|
||||||
|
|
||||||
/// Constructor that registers hook points for DHCPv4 engine
|
/// Constructor that registers hook points for DHCPv4 engine
|
||||||
Dhcp4Hooks() {
|
Dhcp4Hooks() {
|
||||||
hook_index_buffer4_receive_ = HooksManager::registerHook("buffer4_receive");
|
hook_index_buffer4_receive_ = HooksManager::registerHook("buffer4_receive");
|
||||||
hook_index_pkt4_receive_ = HooksManager::registerHook("pkt4_receive");
|
hook_index_pkt4_receive_ = HooksManager::registerHook("pkt4_receive");
|
||||||
hook_index_subnet4_select_ = HooksManager::registerHook("subnet4_select");
|
hook_index_subnet4_select_ = HooksManager::registerHook("subnet4_select");
|
||||||
|
hook_index_leases4_committed_ = HooksManager::registerHook("leases4_committed");
|
||||||
hook_index_pkt4_send_ = HooksManager::registerHook("pkt4_send");
|
hook_index_pkt4_send_ = HooksManager::registerHook("pkt4_send");
|
||||||
hook_index_lease4_release_ = HooksManager::registerHook("lease4_release");
|
hook_index_lease4_release_ = HooksManager::registerHook("lease4_release");
|
||||||
hook_index_buffer4_send_ = HooksManager::registerHook("buffer4_send");
|
hook_index_buffer4_send_ = HooksManager::registerHook("buffer4_send");
|
||||||
@ -103,12 +106,15 @@ struct Dhcp4Hooks {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
} // end of anonymous namespace
|
||||||
|
|
||||||
// Declare a Hooks object. As this is outside any function or method, it
|
// Declare a Hooks object. As this is outside any function or method, it
|
||||||
// will be instantiated (and the constructor run) when the module is loaded.
|
// will be instantiated (and the constructor run) when the module is loaded.
|
||||||
// As a result, the hook indexes will be defined before any method in this
|
// As a result, the hook indexes will be defined before any method in this
|
||||||
// module is called.
|
// module is called.
|
||||||
Dhcp4Hooks Hooks;
|
Dhcp4Hooks Hooks;
|
||||||
|
|
||||||
|
|
||||||
namespace isc {
|
namespace isc {
|
||||||
namespace dhcp {
|
namespace dhcp {
|
||||||
|
|
||||||
@ -834,72 +840,12 @@ Dhcpv4Srv::run_one() {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
|
||||||
// Now all fields and options are constructed into output wire buffer.
|
|
||||||
// Option objects modification does not make sense anymore. Hooks
|
|
||||||
// can only manipulate wire buffer at this stage.
|
|
||||||
// Let's execute all callouts registered for buffer4_send
|
|
||||||
if (HooksManager::calloutsPresent(Hooks.hook_index_buffer4_send_)) {
|
|
||||||
CalloutHandlePtr callout_handle = getCalloutHandle(query);
|
CalloutHandlePtr callout_handle = getCalloutHandle(query);
|
||||||
|
processPacketBufferSend(callout_handle, rsp);
|
||||||
// Delete previously set arguments
|
|
||||||
callout_handle->deleteAllArguments();
|
|
||||||
|
|
||||||
// Enable copying options from the packet within hook library.
|
|
||||||
ScopedEnableOptionsCopy<Pkt4> resp4_options_copy(rsp);
|
|
||||||
|
|
||||||
// Pass incoming packet as argument
|
|
||||||
callout_handle->setArgument("response4", rsp);
|
|
||||||
|
|
||||||
// Call callouts
|
|
||||||
HooksManager::callCallouts(Hooks.hook_index_buffer4_send_,
|
|
||||||
*callout_handle);
|
|
||||||
|
|
||||||
// Callouts decided to skip the next processing step. The next
|
|
||||||
// processing step would to parse the packet, so skip at this
|
|
||||||
// stage means drop.
|
|
||||||
if ((callout_handle->getStatus() == CalloutHandle::NEXT_STEP_SKIP) ||
|
|
||||||
(callout_handle->getStatus() == CalloutHandle::NEXT_STEP_DROP)) {
|
|
||||||
LOG_DEBUG(hooks_logger, DBG_DHCP4_HOOKS,
|
|
||||||
DHCP4_HOOK_BUFFER_SEND_SKIP)
|
|
||||||
.arg(rsp->getLabel());
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
callout_handle->getArgument("response4", rsp);
|
|
||||||
}
|
|
||||||
|
|
||||||
LOG_DEBUG(packet4_logger, DBG_DHCP4_BASIC, DHCP4_PACKET_SEND)
|
|
||||||
.arg(rsp->getLabel())
|
|
||||||
.arg(rsp->getName())
|
|
||||||
.arg(static_cast<int>(rsp->getType()))
|
|
||||||
.arg(rsp->getLocalAddr().isV4Zero() ? "*" : rsp->getLocalAddr().toText())
|
|
||||||
.arg(rsp->getLocalPort())
|
|
||||||
.arg(rsp->getRemoteAddr())
|
|
||||||
.arg(rsp->getRemotePort())
|
|
||||||
.arg(rsp->getIface().empty() ? "to be determined from routing" :
|
|
||||||
rsp->getIface());
|
|
||||||
|
|
||||||
LOG_DEBUG(packet4_logger, DBG_DHCP4_DETAIL_DATA,
|
|
||||||
DHCP4_RESPONSE_DATA)
|
|
||||||
.arg(rsp->getLabel())
|
|
||||||
.arg(rsp->getName())
|
|
||||||
.arg(static_cast<int>(rsp->getType()))
|
|
||||||
.arg(rsp->toText());
|
|
||||||
sendPacket(rsp);
|
|
||||||
|
|
||||||
// Update statistics accordingly for sent packet.
|
|
||||||
processStatsSent(rsp);
|
|
||||||
|
|
||||||
} catch (const std::exception& e) {
|
|
||||||
LOG_ERROR(packet4_logger, DHCP4_PACKET_SEND_FAIL)
|
|
||||||
.arg(rsp->getLabel())
|
|
||||||
.arg(e.what());
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
Dhcpv4Srv::processPacket(Pkt4Ptr& query, Pkt4Ptr& rsp) {
|
Dhcpv4Srv::processPacket(Pkt4Ptr& query, Pkt4Ptr& rsp, bool allow_packet_park) {
|
||||||
// Log reception of the packet. We need to increase it early, as any
|
// Log reception of the packet. We need to increase it early, as any
|
||||||
// failures in unpacking will cause the packet to be dropped. We
|
// failures in unpacking will cause the packet to be dropped. We
|
||||||
// will increase type specific statistic further down the road.
|
// will increase type specific statistic further down the road.
|
||||||
@ -1047,6 +993,8 @@ Dhcpv4Srv::processPacket(Pkt4Ptr& query, Pkt4Ptr& rsp) {
|
|||||||
callout_handle->getArgument("query4", query);
|
callout_handle->getArgument("query4", query);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
AllocEngine::ClientContext4Ptr ctx;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
switch (query->getType()) {
|
switch (query->getType()) {
|
||||||
case DHCPDISCOVER:
|
case DHCPDISCOVER:
|
||||||
@ -1057,15 +1005,15 @@ Dhcpv4Srv::processPacket(Pkt4Ptr& query, Pkt4Ptr& rsp) {
|
|||||||
// Note that REQUEST is used for many things in DHCPv4: for
|
// Note that REQUEST is used for many things in DHCPv4: for
|
||||||
// requesting new leases, renewing existing ones and even
|
// requesting new leases, renewing existing ones and even
|
||||||
// for rebinding.
|
// for rebinding.
|
||||||
rsp = processRequest(query);
|
rsp = processRequest(query, ctx);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case DHCPRELEASE:
|
case DHCPRELEASE:
|
||||||
processRelease(query);
|
processRelease(query, ctx);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case DHCPDECLINE:
|
case DHCPDECLINE:
|
||||||
processDecline(query);
|
processDecline(query, ctx);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case DHCPINFORM:
|
case DHCPINFORM:
|
||||||
@ -1097,6 +1045,84 @@ Dhcpv4Srv::processPacket(Pkt4Ptr& query, Pkt4Ptr& rsp) {
|
|||||||
static_cast<int64_t>(1));
|
static_cast<int64_t>(1));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool packet_park = false;
|
||||||
|
|
||||||
|
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);
|
||||||
|
|
||||||
|
ScopedEnableOptionsCopy<Pkt4> query4_options_copy(query);
|
||||||
|
|
||||||
|
// Also pass the corresponding query packet as argument
|
||||||
|
callout_handle->setArgument("query4", query);
|
||||||
|
|
||||||
|
Lease4CollectionPtr new_leases(new Lease4Collection());
|
||||||
|
if (ctx->new_lease_) {
|
||||||
|
new_leases->push_back(ctx->new_lease_);
|
||||||
|
}
|
||||||
|
callout_handle->setArgument("leases4", new_leases);
|
||||||
|
|
||||||
|
Lease4CollectionPtr deleted_leases(new Lease4Collection());
|
||||||
|
if (ctx->old_lease_) {
|
||||||
|
if ((!ctx->new_lease_) || (ctx->new_lease_->addr_ != ctx->old_lease_->addr_)) {
|
||||||
|
deleted_leases->push_back(ctx->old_lease_);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
callout_handle->setArgument("deleted_leases4", deleted_leases);
|
||||||
|
|
||||||
|
// Call all installed callouts
|
||||||
|
HooksManager::callCallouts(Hooks.hook_index_leases4_committed_,
|
||||||
|
*callout_handle);
|
||||||
|
|
||||||
|
if (callout_handle->getStatus() == CalloutHandle::NEXT_STEP_DROP) {
|
||||||
|
LOG_DEBUG(hooks_logger, DBG_DHCP4_HOOKS,
|
||||||
|
DHCP4_HOOK_LEASES4_COMMITTED_DROP)
|
||||||
|
.arg(query->getLabel());
|
||||||
|
rsp.reset();
|
||||||
|
|
||||||
|
} else if ((callout_handle->getStatus() == CalloutHandle::NEXT_STEP_PARK)
|
||||||
|
&& allow_packet_park) {
|
||||||
|
packet_park = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!rsp) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// PARKING SPOT after leases4_committed hook point.
|
||||||
|
CalloutHandlePtr callout_handle = getCalloutHandle(query);
|
||||||
|
if (packet_park) {
|
||||||
|
LOG_DEBUG(hooks_logger, DBG_DHCP4_HOOKS,
|
||||||
|
DHCP4_HOOK_LEASES4_COMMITTED_PARK)
|
||||||
|
.arg(query->getLabel());
|
||||||
|
|
||||||
|
// Park the packet. The function we bind here will be executed when the hook
|
||||||
|
// library unparks the packet.
|
||||||
|
HooksManager::park("leases4_committed", query,
|
||||||
|
[this, callout_handle, query, rsp]() mutable {
|
||||||
|
processPacketPktSend(callout_handle, query, rsp);
|
||||||
|
processPacketBufferSend(callout_handle, rsp);
|
||||||
|
});
|
||||||
|
|
||||||
|
// 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();
|
||||||
|
|
||||||
|
} else {
|
||||||
|
processPacketPktSend(callout_handle, query, rsp);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
Dhcpv4Srv::processPacketPktSend(hooks::CalloutHandlePtr& callout_handle,
|
||||||
|
Pkt4Ptr& query, Pkt4Ptr& rsp) {
|
||||||
if (!rsp) {
|
if (!rsp) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -1106,7 +1132,6 @@ Dhcpv4Srv::processPacket(Pkt4Ptr& query, Pkt4Ptr& rsp) {
|
|||||||
|
|
||||||
// Execute all callouts registered for pkt4_send
|
// Execute all callouts registered for pkt4_send
|
||||||
if (HooksManager::calloutsPresent(Hooks.hook_index_pkt4_send_)) {
|
if (HooksManager::calloutsPresent(Hooks.hook_index_pkt4_send_)) {
|
||||||
CalloutHandlePtr callout_handle = getCalloutHandle(query);
|
|
||||||
|
|
||||||
// Delete all previous arguments
|
// Delete all previous arguments
|
||||||
callout_handle->deleteAllArguments();
|
callout_handle->deleteAllArguments();
|
||||||
@ -1153,6 +1178,76 @@ Dhcpv4Srv::processPacket(Pkt4Ptr& query, Pkt4Ptr& rsp) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
Dhcpv4Srv::processPacketBufferSend(CalloutHandlePtr& callout_handle,
|
||||||
|
Pkt4Ptr& rsp) {
|
||||||
|
if (!rsp) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
// Now all fields and options are constructed into output wire buffer.
|
||||||
|
// Option objects modification does not make sense anymore. Hooks
|
||||||
|
// can only manipulate wire buffer at this stage.
|
||||||
|
// Let's execute all callouts registered for buffer4_send
|
||||||
|
if (HooksManager::calloutsPresent(Hooks.hook_index_buffer4_send_)) {
|
||||||
|
|
||||||
|
// Delete previously set arguments
|
||||||
|
callout_handle->deleteAllArguments();
|
||||||
|
|
||||||
|
// Enable copying options from the packet within hook library.
|
||||||
|
ScopedEnableOptionsCopy<Pkt4> resp4_options_copy(rsp);
|
||||||
|
|
||||||
|
// Pass incoming packet as argument
|
||||||
|
callout_handle->setArgument("response4", rsp);
|
||||||
|
|
||||||
|
// Call callouts
|
||||||
|
HooksManager::callCallouts(Hooks.hook_index_buffer4_send_,
|
||||||
|
*callout_handle);
|
||||||
|
|
||||||
|
// Callouts decided to skip the next processing step. The next
|
||||||
|
// processing step would to parse the packet, so skip at this
|
||||||
|
// stage means drop.
|
||||||
|
if ((callout_handle->getStatus() == CalloutHandle::NEXT_STEP_SKIP) ||
|
||||||
|
(callout_handle->getStatus() == CalloutHandle::NEXT_STEP_DROP)) {
|
||||||
|
LOG_DEBUG(hooks_logger, DBG_DHCP4_HOOKS,
|
||||||
|
DHCP4_HOOK_BUFFER_SEND_SKIP)
|
||||||
|
.arg(rsp->getLabel());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
callout_handle->getArgument("response4", rsp);
|
||||||
|
}
|
||||||
|
|
||||||
|
LOG_DEBUG(packet4_logger, DBG_DHCP4_BASIC, DHCP4_PACKET_SEND)
|
||||||
|
.arg(rsp->getLabel())
|
||||||
|
.arg(rsp->getName())
|
||||||
|
.arg(static_cast<int>(rsp->getType()))
|
||||||
|
.arg(rsp->getLocalAddr().isV4Zero() ? "*" : rsp->getLocalAddr().toText())
|
||||||
|
.arg(rsp->getLocalPort())
|
||||||
|
.arg(rsp->getRemoteAddr())
|
||||||
|
.arg(rsp->getRemotePort())
|
||||||
|
.arg(rsp->getIface().empty() ? "to be determined from routing" :
|
||||||
|
rsp->getIface());
|
||||||
|
|
||||||
|
LOG_DEBUG(packet4_logger, DBG_DHCP4_DETAIL_DATA,
|
||||||
|
DHCP4_RESPONSE_DATA)
|
||||||
|
.arg(rsp->getLabel())
|
||||||
|
.arg(rsp->getName())
|
||||||
|
.arg(static_cast<int>(rsp->getType()))
|
||||||
|
.arg(rsp->toText());
|
||||||
|
sendPacket(rsp);
|
||||||
|
|
||||||
|
// Update statistics accordingly for sent packet.
|
||||||
|
processStatsSent(rsp);
|
||||||
|
|
||||||
|
} catch (const std::exception& e) {
|
||||||
|
LOG_ERROR(packet4_logger, DHCP4_PACKET_SEND_FAIL)
|
||||||
|
.arg(rsp->getLabel())
|
||||||
|
.arg(e.what());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
string
|
string
|
||||||
Dhcpv4Srv::srvidToString(const OptionPtr& srvid) {
|
Dhcpv4Srv::srvidToString(const OptionPtr& srvid) {
|
||||||
if (!srvid) {
|
if (!srvid) {
|
||||||
@ -2440,7 +2535,7 @@ Dhcpv4Srv::processDiscover(Pkt4Ptr& discover) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Pkt4Ptr
|
Pkt4Ptr
|
||||||
Dhcpv4Srv::processRequest(Pkt4Ptr& request) {
|
Dhcpv4Srv::processRequest(Pkt4Ptr& request, AllocEngine::ClientContext4Ptr& context) {
|
||||||
/// @todo Uncomment this (see ticket #3116)
|
/// @todo Uncomment this (see ticket #3116)
|
||||||
/// sanityCheck(request, MANDATORY);
|
/// sanityCheck(request, MANDATORY);
|
||||||
|
|
||||||
@ -2492,11 +2587,15 @@ Dhcpv4Srv::processRequest(Pkt4Ptr& request) {
|
|||||||
|
|
||||||
appendServerID(ex);
|
appendServerID(ex);
|
||||||
|
|
||||||
|
// Return the pointer to the context, which will be required by the
|
||||||
|
// leases4_comitted callouts.
|
||||||
|
context = ex.getContext();
|
||||||
|
|
||||||
return (ex.getResponse());
|
return (ex.getResponse());
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
Dhcpv4Srv::processRelease(Pkt4Ptr& release) {
|
Dhcpv4Srv::processRelease(Pkt4Ptr& release, AllocEngine::ClientContext4Ptr& context) {
|
||||||
/// @todo Uncomment this (see ticket #3116)
|
/// @todo Uncomment this (see ticket #3116)
|
||||||
/// sanityCheck(release, MANDATORY);
|
/// sanityCheck(release, MANDATORY);
|
||||||
|
|
||||||
@ -2575,6 +2674,10 @@ Dhcpv4Srv::processRelease(Pkt4Ptr& release) {
|
|||||||
bool success = LeaseMgrFactory::instance().deleteLease(lease->addr_);
|
bool success = LeaseMgrFactory::instance().deleteLease(lease->addr_);
|
||||||
|
|
||||||
if (success) {
|
if (success) {
|
||||||
|
|
||||||
|
context.reset(new AllocEngine::ClientContext4());
|
||||||
|
context->old_lease_ = lease;
|
||||||
|
|
||||||
// Release successful
|
// Release successful
|
||||||
LOG_INFO(lease4_logger, DHCP4_RELEASE)
|
LOG_INFO(lease4_logger, DHCP4_RELEASE)
|
||||||
.arg(release->getLabel())
|
.arg(release->getLabel())
|
||||||
@ -2604,7 +2707,7 @@ Dhcpv4Srv::processRelease(Pkt4Ptr& release) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
Dhcpv4Srv::processDecline(Pkt4Ptr& decline) {
|
Dhcpv4Srv::processDecline(Pkt4Ptr& decline, AllocEngine::ClientContext4Ptr& context) {
|
||||||
|
|
||||||
// Server-id is mandatory in DHCPDECLINE (see table 5, RFC2131)
|
// Server-id is mandatory in DHCPDECLINE (see table 5, RFC2131)
|
||||||
/// @todo Uncomment this (see ticket #3116)
|
/// @todo Uncomment this (see ticket #3116)
|
||||||
@ -2674,11 +2777,12 @@ Dhcpv4Srv::processDecline(Pkt4Ptr& decline) {
|
|||||||
|
|
||||||
// Ok, all is good. The client is reporting its own address. Let's
|
// Ok, all is good. The client is reporting its own address. Let's
|
||||||
// process it.
|
// process it.
|
||||||
declineLease(lease, decline);
|
declineLease(lease, decline, context);
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
Dhcpv4Srv::declineLease(const Lease4Ptr& lease, const Pkt4Ptr& decline) {
|
Dhcpv4Srv::declineLease(const Lease4Ptr& lease, const Pkt4Ptr& decline,
|
||||||
|
AllocEngine::ClientContext4Ptr& context) {
|
||||||
|
|
||||||
// Let's check if there are hooks installed for decline4 hook point.
|
// Let's check if there are hooks installed for decline4 hook point.
|
||||||
// If they are, let's pass the lease and client's packet. If the hook
|
// If they are, let's pass the lease and client's packet. If the hook
|
||||||
@ -2742,6 +2846,9 @@ Dhcpv4Srv::declineLease(const Lease4Ptr& lease, const Pkt4Ptr& decline) {
|
|||||||
|
|
||||||
LeaseMgrFactory::instance().updateLease4(lease);
|
LeaseMgrFactory::instance().updateLease4(lease);
|
||||||
|
|
||||||
|
context.reset(new AllocEngine::ClientContext4());
|
||||||
|
context->new_lease_ = lease;
|
||||||
|
|
||||||
LOG_INFO(lease4_logger, DHCP4_DECLINE_LEASE).arg(lease->addr_.toText())
|
LOG_INFO(lease4_logger, DHCP4_DECLINE_LEASE).arg(lease->addr_.toText())
|
||||||
.arg(decline->getLabel()).arg(lease->valid_lft_);
|
.arg(decline->getLabel()).arg(lease->valid_lft_);
|
||||||
}
|
}
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
// Copyright (C) 2011-2017 Internet Systems Consortium, Inc. ("ISC")
|
// Copyright (C) 2011-2018 Internet Systems Consortium, Inc. ("ISC")
|
||||||
//
|
//
|
||||||
// This Source Code Form is subject to the terms of the Mozilla Public
|
// This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
@ -17,6 +17,7 @@
|
|||||||
#include <dhcp_ddns/ncr_msg.h>
|
#include <dhcp_ddns/ncr_msg.h>
|
||||||
#include <dhcpsrv/alloc_engine.h>
|
#include <dhcpsrv/alloc_engine.h>
|
||||||
#include <dhcpsrv/cfg_option.h>
|
#include <dhcpsrv/cfg_option.h>
|
||||||
|
#include <dhcpsrv/callout_handle_store.h>
|
||||||
#include <dhcpsrv/d2_client_mgr.h>
|
#include <dhcpsrv/d2_client_mgr.h>
|
||||||
#include <dhcpsrv/network_state.h>
|
#include <dhcpsrv/network_state.h>
|
||||||
#include <dhcpsrv/subnet.h>
|
#include <dhcpsrv/subnet.h>
|
||||||
@ -25,6 +26,7 @@
|
|||||||
|
|
||||||
#include <boost/noncopyable.hpp>
|
#include <boost/noncopyable.hpp>
|
||||||
|
|
||||||
|
#include <functional>
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
#include <queue>
|
#include <queue>
|
||||||
|
|
||||||
@ -259,7 +261,10 @@ public:
|
|||||||
///
|
///
|
||||||
/// @param query A pointer to the packet to be processed.
|
/// @param query A pointer to the packet to be processed.
|
||||||
/// @param rsp A pointer to the response
|
/// @param rsp A pointer to the response
|
||||||
void processPacket(Pkt4Ptr& query, Pkt4Ptr& rsp);
|
/// @param allow_packet_park Indicates if parking a packet is allowed.
|
||||||
|
void processPacket(Pkt4Ptr& query, Pkt4Ptr& rsp,
|
||||||
|
bool allow_packet_park = true);
|
||||||
|
|
||||||
|
|
||||||
/// @brief Instructs the server to shut down.
|
/// @brief Instructs the server to shut down.
|
||||||
void shutdown();
|
void shutdown();
|
||||||
@ -446,9 +451,11 @@ protected:
|
|||||||
/// Returns ACK message, NAK message, or NULL
|
/// Returns ACK message, NAK message, or NULL
|
||||||
///
|
///
|
||||||
/// @param request a message received from client
|
/// @param request a message received from client
|
||||||
|
/// @param [out] context pointer to the client context where allocated
|
||||||
|
/// and deleted leases are stored.
|
||||||
///
|
///
|
||||||
/// @return ACK or NAK message
|
/// @return ACK or NAK message
|
||||||
Pkt4Ptr processRequest(Pkt4Ptr& request);
|
Pkt4Ptr processRequest(Pkt4Ptr& request, AllocEngine::ClientContext4Ptr& context);
|
||||||
|
|
||||||
/// @brief Processes incoming DHCPRELEASE messages.
|
/// @brief Processes incoming DHCPRELEASE messages.
|
||||||
///
|
///
|
||||||
@ -456,7 +463,9 @@ protected:
|
|||||||
/// this function does not return anything.
|
/// this function does not return anything.
|
||||||
///
|
///
|
||||||
/// @param release message received from client
|
/// @param release message received from client
|
||||||
void processRelease(Pkt4Ptr& release);
|
/// @param [out] context pointer to the client context where released
|
||||||
|
/// lease is stored.
|
||||||
|
void processRelease(Pkt4Ptr& release, AllocEngine::ClientContext4Ptr& context);
|
||||||
|
|
||||||
/// @brief Process incoming DHCPDECLINE messages.
|
/// @brief Process incoming DHCPDECLINE messages.
|
||||||
///
|
///
|
||||||
@ -465,7 +474,9 @@ protected:
|
|||||||
/// the client and if it does, calls @ref declineLease.
|
/// the client and if it does, calls @ref declineLease.
|
||||||
///
|
///
|
||||||
/// @param decline message received from client
|
/// @param decline message received from client
|
||||||
void processDecline(Pkt4Ptr& decline);
|
/// @param [out] context pointer to the client context where declined
|
||||||
|
/// lease is stored.
|
||||||
|
void processDecline(Pkt4Ptr& decline, AllocEngine::ClientContext4Ptr& context);
|
||||||
|
|
||||||
/// @brief Processes incoming DHCPINFORM messages.
|
/// @brief Processes incoming DHCPINFORM messages.
|
||||||
///
|
///
|
||||||
@ -650,7 +661,9 @@ private:
|
|||||||
///
|
///
|
||||||
/// @param lease lease to be declined
|
/// @param lease lease to be declined
|
||||||
/// @param decline client's message
|
/// @param decline client's message
|
||||||
void declineLease(const Lease4Ptr& lease, const Pkt4Ptr& decline);
|
/// @param context reference to a client context
|
||||||
|
void declineLease(const Lease4Ptr& lease, const Pkt4Ptr& decline,
|
||||||
|
AllocEngine::ClientContext4Ptr& context);
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
|
|
||||||
@ -819,6 +832,21 @@ protected:
|
|||||||
/// @param query Pointer to the client message.
|
/// @param query Pointer to the client message.
|
||||||
void deferredUnpack(Pkt4Ptr& query);
|
void deferredUnpack(Pkt4Ptr& query);
|
||||||
|
|
||||||
|
/// @brief Executes pkt4_send callout.
|
||||||
|
///
|
||||||
|
/// @param callout_handle pointer to the callout handle.
|
||||||
|
/// @param query Pointer to a query.
|
||||||
|
/// @param rsp Pointer to a response.
|
||||||
|
void processPacketPktSend(hooks::CalloutHandlePtr& callout_handle,
|
||||||
|
Pkt4Ptr& query, Pkt4Ptr& rsp);
|
||||||
|
|
||||||
|
/// @brief Executes buffer4_send callout and sends the response.
|
||||||
|
///
|
||||||
|
/// @param callout_handle pointer to the callout handle.
|
||||||
|
/// @param rsp pointer to a response.
|
||||||
|
void processPacketBufferSend(hooks::CalloutHandlePtr& callout_handle,
|
||||||
|
Pkt4Ptr& rsp);
|
||||||
|
|
||||||
/// @brief Allocation Engine.
|
/// @brief Allocation Engine.
|
||||||
/// Pointer to the allocation engine that we are currently using
|
/// Pointer to the allocation engine that we are currently using
|
||||||
/// It must be a pointer, because we will support changing engines
|
/// It must be a pointer, because we will support changing engines
|
||||||
|
@ -103,7 +103,7 @@ void Dhcp4to6Ipc::handler() {
|
|||||||
// From Dhcpv4Srv::run_one() processing and after
|
// From Dhcpv4Srv::run_one() processing and after
|
||||||
Pkt4Ptr rsp;
|
Pkt4Ptr rsp;
|
||||||
|
|
||||||
ControlledDhcpv4Srv::getInstance()->processPacket(query, rsp);
|
ControlledDhcpv4Srv::getInstance()->processPacket(query, rsp, false);
|
||||||
|
|
||||||
if (!rsp) {
|
if (!rsp) {
|
||||||
return;
|
return;
|
||||||
|
@ -56,7 +56,7 @@ if HAVE_GTEST
|
|||||||
# to unexpected errors. For this reason, the --enable-static-link option is
|
# to unexpected errors. For this reason, the --enable-static-link option is
|
||||||
# ignored for unit tests built here.
|
# ignored for unit tests built here.
|
||||||
|
|
||||||
noinst_LTLIBRARIES = libco1.la libco2.la
|
noinst_LTLIBRARIES = libco1.la libco2.la libco3.la
|
||||||
|
|
||||||
# -rpath /nowhere is a hack to trigger libtool to not create a
|
# -rpath /nowhere is a hack to trigger libtool to not create a
|
||||||
# convenience archive, resulting in shared modules
|
# convenience archive, resulting in shared modules
|
||||||
@ -71,6 +71,11 @@ libco2_la_CXXFLAGS = $(AM_CXXFLAGS)
|
|||||||
libco2_la_CPPFLAGS = $(AM_CPPFLAGS)
|
libco2_la_CPPFLAGS = $(AM_CPPFLAGS)
|
||||||
libco2_la_LDFLAGS = -avoid-version -export-dynamic -module -rpath /nowhere
|
libco2_la_LDFLAGS = -avoid-version -export-dynamic -module -rpath /nowhere
|
||||||
|
|
||||||
|
libco3_la_SOURCES = callout_library_3.cc callout_library_common.h
|
||||||
|
libco3_la_CXXFLAGS = $(AM_CXXFLAGS)
|
||||||
|
libco3_la_CPPFLAGS = $(AM_CPPFLAGS)
|
||||||
|
libco3_la_LDFLAGS = -avoid-version -export-dynamic -module -rpath /nowhere
|
||||||
|
|
||||||
TESTS += dhcp4_unittests
|
TESTS += dhcp4_unittests
|
||||||
|
|
||||||
dhcp4_unittests_SOURCES = d2_unittest.h d2_unittest.cc
|
dhcp4_unittests_SOURCES = d2_unittest.h d2_unittest.cc
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
// Copyright (C) 2013-2015 Internet Systems Consortium, Inc. ("ISC")
|
// Copyright (C) 2013-2018 Internet Systems Consortium, Inc. ("ISC")
|
||||||
//
|
//
|
||||||
// This Source Code Form is subject to the terms of the Mozilla Public
|
// This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
@ -12,5 +12,5 @@
|
|||||||
|
|
||||||
static const int LIBRARY_NUMBER = 1;
|
static const int LIBRARY_NUMBER = 1;
|
||||||
#include <config.h>
|
#include <config.h>
|
||||||
|
#include <dhcp4/tests/callout_library_common.h>
|
||||||
|
|
||||||
#include "callout_library_common.h"
|
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
// Copyright (C) 2013-2015 Internet Systems Consortium, Inc. ("ISC")
|
// Copyright (C) 2013-2018 Internet Systems Consortium, Inc. ("ISC")
|
||||||
//
|
//
|
||||||
// This Source Code Form is subject to the terms of the Mozilla Public
|
// This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
@ -12,5 +12,5 @@
|
|||||||
|
|
||||||
static const int LIBRARY_NUMBER = 2;
|
static const int LIBRARY_NUMBER = 2;
|
||||||
#include <config.h>
|
#include <config.h>
|
||||||
|
#include <dhcp4/tests/callout_library_common.h>
|
||||||
|
|
||||||
#include "callout_library_common.h"
|
|
||||||
|
44
src/bin/dhcp4/tests/callout_library_3.cc
Normal file
44
src/bin/dhcp4/tests/callout_library_3.cc
Normal file
@ -0,0 +1,44 @@
|
|||||||
|
// Copyright (C) 2018 Internet Systems Consortium, Inc. ("ISC")
|
||||||
|
//
|
||||||
|
// This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
|
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||||
|
|
||||||
|
/// @file
|
||||||
|
/// @brief Callout library for tesing execution of the dhcp4_srv_configured
|
||||||
|
/// hook point.
|
||||||
|
///
|
||||||
|
static const int LIBRARY_NUMBER = 3;
|
||||||
|
#include <dhcp4/tests/callout_library_common.h>
|
||||||
|
#include <string>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
using namespace isc::hooks;
|
||||||
|
|
||||||
|
extern "C" {
|
||||||
|
|
||||||
|
/// @brief Callout which appends library number and provided arguments to
|
||||||
|
/// the marker file for dhcp4_srv_configured callout.
|
||||||
|
///
|
||||||
|
/// @param handle callout handle passed to the callout.
|
||||||
|
///
|
||||||
|
/// @return 0 on success, 1 otherwise.
|
||||||
|
int
|
||||||
|
dhcp4_srv_configured(CalloutHandle& handle) {
|
||||||
|
// Append library number.
|
||||||
|
if (appendDigit(SRV_CONFIG_MARKER_FILE)) {
|
||||||
|
return (1);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Append argument names.
|
||||||
|
std::vector<std::string> args = handle.getArgumentNames();
|
||||||
|
for (auto arg = args.begin(); arg != args.end(); ++arg) {
|
||||||
|
if (appendArgument(SRV_CONFIG_MARKER_FILE, arg->c_str()) != 0) {
|
||||||
|
return (1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return (0);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -1,4 +1,4 @@
|
|||||||
// Copyright (C) 2013-2015 Internet Systems Consortium, Inc. ("ISC")
|
// Copyright (C) 2013-2018 Internet Systems Consortium, Inc. ("ISC")
|
||||||
//
|
//
|
||||||
// This Source Code Form is subject to the terms of the Mozilla Public
|
// This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
@ -17,6 +17,10 @@
|
|||||||
/// can determine whether the load/unload functions have been run and, if so,
|
/// can determine whether the load/unload functions have been run and, if so,
|
||||||
/// in what order.
|
/// in what order.
|
||||||
///
|
///
|
||||||
|
/// The additional marker file is created for the dhcp4_srv_configured hook
|
||||||
|
/// point. It records the library number and the names of the parameters
|
||||||
|
/// provided to the callout.
|
||||||
|
///
|
||||||
/// This file is the common library file for the tests. It will not compile
|
/// This file is the common library file for the tests. It will not compile
|
||||||
/// by itself - it is included into each callout library which specifies the
|
/// by itself - it is included into each callout library which specifies the
|
||||||
/// missing constant LIBRARY_NUMBER before the inclusion.
|
/// missing constant LIBRARY_NUMBER before the inclusion.
|
||||||
@ -53,6 +57,26 @@ appendDigit(const char* name) {
|
|||||||
return (0);
|
return (0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// @brief Append argument name passed to the callout to a marker file.
|
||||||
|
///
|
||||||
|
/// @param file_name Name of the file to open.
|
||||||
|
/// @param parameter Parameter name.
|
||||||
|
///
|
||||||
|
/// @return 0 on success, non-zero on error.
|
||||||
|
int appendArgument(const char* file_name, const char* argument) {
|
||||||
|
// Open the file and check if successful.
|
||||||
|
std::fstream file(file_name, std::fstream::out | std::fstream::app);
|
||||||
|
if (!file.good()) {
|
||||||
|
return (1);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add the library number to it and close.
|
||||||
|
file << argument;
|
||||||
|
file.close();
|
||||||
|
|
||||||
|
return (0);
|
||||||
|
}
|
||||||
|
|
||||||
// Framework functions
|
// Framework functions
|
||||||
int
|
int
|
||||||
version() {
|
version() {
|
||||||
|
@ -406,6 +406,15 @@ Dhcp4Client::doRequest() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
Dhcp4Client::receiveResponse() {
|
||||||
|
context_.response_ = receiveOneMsg();
|
||||||
|
// If the server has responded, store the configuration received.
|
||||||
|
if (context_.response_) {
|
||||||
|
applyConfiguration();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
Dhcp4Client::includeClientId(const std::string& clientid) {
|
Dhcp4Client::includeClientId(const std::string& clientid) {
|
||||||
if (clientid.empty()) {
|
if (clientid.empty()) {
|
||||||
@ -519,7 +528,14 @@ Dhcp4Client::sendMsg(const Pkt4Ptr& msg) {
|
|||||||
msg_copy->setLocalAddr(dest_addr_);
|
msg_copy->setLocalAddr(dest_addr_);
|
||||||
msg_copy->setIface(iface_name_);
|
msg_copy->setIface(iface_name_);
|
||||||
srv_->fakeReceive(msg_copy);
|
srv_->fakeReceive(msg_copy);
|
||||||
srv_->run();
|
|
||||||
|
try {
|
||||||
|
// Invoke run_one instead of run, because we want to avoid triggering
|
||||||
|
// IO service.
|
||||||
|
srv_->run_one();
|
||||||
|
} catch (...) {
|
||||||
|
// Suppress errors, as the DHCPv4 server does.
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
|
@ -206,6 +206,15 @@ public:
|
|||||||
/// DHCPACK message is applied and can be obtained from the @c config_.
|
/// DHCPACK message is applied and can be obtained from the @c config_.
|
||||||
void doRequest();
|
void doRequest();
|
||||||
|
|
||||||
|
/// @brief Receives a response from the server.
|
||||||
|
///
|
||||||
|
/// This method is useful to receive response from the server after
|
||||||
|
/// parking a packet. In this case, the packet is not received as a
|
||||||
|
/// result of initial exchange, e.g. @c doRequest. The test can call
|
||||||
|
/// this method to complete the transaction when it expects that the
|
||||||
|
/// packet has been unparked.
|
||||||
|
void receiveResponse();
|
||||||
|
|
||||||
/// @brief Generates a hardware address used by the client.
|
/// @brief Generates a hardware address used by the client.
|
||||||
///
|
///
|
||||||
/// It assigns random values to the bytes of the hardware address.
|
/// It assigns random values to the bytes of the hardware address.
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
// Copyright (C) 2013-2017 Internet Systems Consortium, Inc. ("ISC")
|
// Copyright (C) 2013-2018 Internet Systems Consortium, Inc. ("ISC")
|
||||||
//
|
//
|
||||||
// This Source Code Form is subject to the terms of the Mozilla Public
|
// This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
@ -17,6 +17,7 @@
|
|||||||
#include <dhcp/pkt4.h>
|
#include <dhcp/pkt4.h>
|
||||||
#include <dhcp/pkt_filter.h>
|
#include <dhcp/pkt_filter.h>
|
||||||
#include <dhcp/pkt_filter_inet.h>
|
#include <dhcp/pkt_filter_inet.h>
|
||||||
|
#include <dhcpsrv/alloc_engine.h>
|
||||||
#include <dhcpsrv/subnet.h>
|
#include <dhcpsrv/subnet.h>
|
||||||
#include <dhcpsrv/lease.h>
|
#include <dhcpsrv/lease.h>
|
||||||
#include <dhcpsrv/lease_mgr_factory.h>
|
#include <dhcpsrv/lease_mgr_factory.h>
|
||||||
@ -170,6 +171,31 @@ public:
|
|||||||
virtual ~NakedDhcpv4Srv() {
|
virtual ~NakedDhcpv4Srv() {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// @brief Runs processing DHCPREQUEST.
|
||||||
|
///
|
||||||
|
/// @param request a message received from client
|
||||||
|
/// @return DHCPACK or DHCPNAK message
|
||||||
|
Pkt4Ptr processRequest(Pkt4Ptr& request) {
|
||||||
|
AllocEngine::ClientContext4Ptr context(new AllocEngine::ClientContext4());
|
||||||
|
return (processRequest(request, context));
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @brief Runs processing DHCPRELEASE.
|
||||||
|
///
|
||||||
|
/// @param release message received from client
|
||||||
|
void processRelease(Pkt4Ptr& release) {
|
||||||
|
AllocEngine::ClientContext4Ptr context(new AllocEngine::ClientContext4());
|
||||||
|
processRelease(release, context);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @brief Runs processing DHCPDECLINE
|
||||||
|
///
|
||||||
|
/// @param decline message received from client
|
||||||
|
void processDecline(Pkt4Ptr& decline) {
|
||||||
|
AllocEngine::ClientContext4Ptr context(new AllocEngine::ClientContext4());
|
||||||
|
processDecline(decline, context);
|
||||||
|
}
|
||||||
|
|
||||||
/// @brief Dummy server identifier option used by various tests.
|
/// @brief Dummy server identifier option used by various tests.
|
||||||
OptionPtr server_id_;
|
OptionPtr server_id_;
|
||||||
|
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
// Copyright (C) 2015-2017 Internet Systems Consortium, Inc. ("ISC")
|
// Copyright (C) 2015-2018 Internet Systems Consortium, Inc. ("ISC")
|
||||||
//
|
//
|
||||||
// This Source Code Form is subject to the terms of the Mozilla Public
|
// This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
@ -8,7 +8,9 @@
|
|||||||
|
|
||||||
#include <dhcp4/tests/dhcp4_test_utils.h>
|
#include <dhcp4/tests/dhcp4_test_utils.h>
|
||||||
#include <dhcp4/tests/dhcp4_client.h>
|
#include <dhcp4/tests/dhcp4_client.h>
|
||||||
|
#include <dhcp4/ctrl_dhcp4_srv.h>
|
||||||
#include <dhcp4/json_config_parser.h>
|
#include <dhcp4/json_config_parser.h>
|
||||||
|
#include <asiolink/io_service.h>
|
||||||
#include <cc/command_interpreter.h>
|
#include <cc/command_interpreter.h>
|
||||||
#include <config/command_mgr.h>
|
#include <config/command_mgr.h>
|
||||||
#include <hooks/server_hooks.h>
|
#include <hooks/server_hooks.h>
|
||||||
@ -18,6 +20,7 @@
|
|||||||
#include <dhcp/tests/iface_mgr_test_config.h>
|
#include <dhcp/tests/iface_mgr_test_config.h>
|
||||||
#include <dhcp/option.h>
|
#include <dhcp/option.h>
|
||||||
#include <asiolink/io_address.h>
|
#include <asiolink/io_address.h>
|
||||||
|
#include <dhcp4/tests/dhcp4_client.h>
|
||||||
#include <dhcp4/tests/marker_file.h>
|
#include <dhcp4/tests/marker_file.h>
|
||||||
#include <dhcp4/tests/test_libraries.h>
|
#include <dhcp4/tests/test_libraries.h>
|
||||||
|
|
||||||
@ -39,6 +42,7 @@ TEST_F(Dhcpv4SrvTest, Hooks) {
|
|||||||
int hook_index_buffer4_receive = -1;
|
int hook_index_buffer4_receive = -1;
|
||||||
int hook_index_pkt4_receive = -1;
|
int hook_index_pkt4_receive = -1;
|
||||||
int hook_index_select_subnet = -1;
|
int hook_index_select_subnet = -1;
|
||||||
|
int hook_index_leases4_committed = -1;
|
||||||
int hook_index_lease4_release = -1;
|
int hook_index_lease4_release = -1;
|
||||||
int hook_index_pkt4_send = -1;
|
int hook_index_pkt4_send = -1;
|
||||||
int hook_index_buffer4_send = -1;
|
int hook_index_buffer4_send = -1;
|
||||||
@ -51,6 +55,8 @@ TEST_F(Dhcpv4SrvTest, Hooks) {
|
|||||||
.getIndex("pkt4_receive"));
|
.getIndex("pkt4_receive"));
|
||||||
EXPECT_NO_THROW(hook_index_select_subnet = ServerHooks::getServerHooks()
|
EXPECT_NO_THROW(hook_index_select_subnet = ServerHooks::getServerHooks()
|
||||||
.getIndex("subnet4_select"));
|
.getIndex("subnet4_select"));
|
||||||
|
EXPECT_NO_THROW(hook_index_leases4_committed = ServerHooks::getServerHooks()
|
||||||
|
.getIndex("leases4_committed"));
|
||||||
EXPECT_NO_THROW(hook_index_lease4_release = ServerHooks::getServerHooks()
|
EXPECT_NO_THROW(hook_index_lease4_release = ServerHooks::getServerHooks()
|
||||||
.getIndex("lease4_release"));
|
.getIndex("lease4_release"));
|
||||||
EXPECT_NO_THROW(hook_index_pkt4_send = ServerHooks::getServerHooks()
|
EXPECT_NO_THROW(hook_index_pkt4_send = ServerHooks::getServerHooks()
|
||||||
@ -63,6 +69,7 @@ TEST_F(Dhcpv4SrvTest, Hooks) {
|
|||||||
EXPECT_TRUE(hook_index_buffer4_receive > 0);
|
EXPECT_TRUE(hook_index_buffer4_receive > 0);
|
||||||
EXPECT_TRUE(hook_index_pkt4_receive > 0);
|
EXPECT_TRUE(hook_index_pkt4_receive > 0);
|
||||||
EXPECT_TRUE(hook_index_select_subnet > 0);
|
EXPECT_TRUE(hook_index_select_subnet > 0);
|
||||||
|
EXPECT_TRUE(hook_index_leases4_committed > 0);
|
||||||
EXPECT_TRUE(hook_index_lease4_release > 0);
|
EXPECT_TRUE(hook_index_lease4_release > 0);
|
||||||
EXPECT_TRUE(hook_index_pkt4_send > 0);
|
EXPECT_TRUE(hook_index_pkt4_send > 0);
|
||||||
EXPECT_TRUE(hook_index_buffer4_send > 0);
|
EXPECT_TRUE(hook_index_buffer4_send > 0);
|
||||||
@ -102,11 +109,14 @@ public:
|
|||||||
|
|
||||||
// clear static buffers
|
// clear static buffers
|
||||||
resetCalloutBuffers();
|
resetCalloutBuffers();
|
||||||
|
|
||||||
|
io_service_ = boost::make_shared<IOService>();
|
||||||
}
|
}
|
||||||
|
|
||||||
/// @brief destructor (deletes Dhcpv4Srv)
|
/// @brief destructor (deletes Dhcpv4Srv)
|
||||||
virtual ~HooksDhcpv4SrvTest() {
|
virtual ~HooksDhcpv4SrvTest() {
|
||||||
|
|
||||||
|
HooksManager::preCalloutsLibraryHandle().deregisterAllCallouts("dhcp4_srv_configured");
|
||||||
HooksManager::preCalloutsLibraryHandle().deregisterAllCallouts("buffer4_receive");
|
HooksManager::preCalloutsLibraryHandle().deregisterAllCallouts("buffer4_receive");
|
||||||
HooksManager::preCalloutsLibraryHandle().deregisterAllCallouts("buffer4_send");
|
HooksManager::preCalloutsLibraryHandle().deregisterAllCallouts("buffer4_send");
|
||||||
HooksManager::preCalloutsLibraryHandle().deregisterAllCallouts("pkt4_receive");
|
HooksManager::preCalloutsLibraryHandle().deregisterAllCallouts("pkt4_receive");
|
||||||
@ -124,7 +134,7 @@ public:
|
|||||||
/// @brief creates an option with specified option code
|
/// @brief creates an option with specified option code
|
||||||
///
|
///
|
||||||
/// This method is static, because it is used from callouts
|
/// This method is static, because it is used from callouts
|
||||||
/// that do not have a pointer to HooksDhcpv4SSrvTest object
|
/// that do not have a pointer to HooksDhcpv4SrvTest object
|
||||||
///
|
///
|
||||||
/// @param option_code code of option to be created
|
/// @param option_code code of option to be created
|
||||||
///
|
///
|
||||||
@ -225,7 +235,7 @@ public:
|
|||||||
|
|
||||||
// If there is at least one option with data
|
// If there is at least one option with data
|
||||||
if (pkt->data_.size() >= Pkt4::DHCPV4_PKT_HDR_LEN) {
|
if (pkt->data_.size() >= Pkt4::DHCPV4_PKT_HDR_LEN) {
|
||||||
// Offset of the first byte of the CHWADDR field. Let's the first
|
// Offset of the first byte of the CHADDR field. Let's the first
|
||||||
// byte to some new value that we could later check
|
// byte to some new value that we could later check
|
||||||
pkt->data_[28] = 0xff;
|
pkt->data_[28] = 0xff;
|
||||||
}
|
}
|
||||||
@ -621,6 +631,76 @@ public:
|
|||||||
return (lease4_decline_callout(callout_handle));
|
return (lease4_decline_callout(callout_handle));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Test callback that stores values passed to leases4_committed.
|
||||||
|
static int
|
||||||
|
leases4_committed_callout(CalloutHandle& callout_handle) {
|
||||||
|
callback_name_ = string("leases4_committed");
|
||||||
|
|
||||||
|
callout_handle.getArgument("query4", callback_qry_pkt4_);
|
||||||
|
|
||||||
|
Lease4CollectionPtr leases4;
|
||||||
|
callout_handle.getArgument("leases4", leases4);
|
||||||
|
if (leases4->size() > 0) {
|
||||||
|
callback_lease4_ = leases4->at(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
Lease4CollectionPtr deleted_leases4;
|
||||||
|
callout_handle.getArgument("deleted_leases4", deleted_leases4);
|
||||||
|
if (deleted_leases4->size() > 0) {
|
||||||
|
callback_deleted_lease4_ = deleted_leases4->at(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
callback_argument_names_ = callout_handle.getArgumentNames();
|
||||||
|
sort(callback_argument_names_.begin(), callback_argument_names_.end());
|
||||||
|
|
||||||
|
if (callback_qry_pkt4_) {
|
||||||
|
callback_qry_options_copy_ = callback_qry_pkt4_->isCopyRetrievedOptions();
|
||||||
|
}
|
||||||
|
|
||||||
|
return (0);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
leases4_committed_unpark(ParkingLotHandlePtr parking_lot, Pkt4Ptr query) {
|
||||||
|
parking_lot->unpark(query);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Test callback which asks the server to park the packet.
|
||||||
|
static int
|
||||||
|
leases4_committed_park_callout(CalloutHandle& callout_handle) {
|
||||||
|
callback_name_ = string("leases4_committed");
|
||||||
|
|
||||||
|
callout_handle.getArgument("query4", callback_qry_pkt4_);
|
||||||
|
|
||||||
|
io_service_->post(boost::bind(&HooksDhcpv4SrvTest::leases4_committed_unpark,
|
||||||
|
callout_handle.getParkingLotHandlePtr(),
|
||||||
|
callback_qry_pkt4_));
|
||||||
|
|
||||||
|
callout_handle.getParkingLotHandlePtr()->reference(callback_qry_pkt4_);
|
||||||
|
callout_handle.setStatus(CalloutHandle::NEXT_STEP_PARK);
|
||||||
|
|
||||||
|
Lease4CollectionPtr leases4;
|
||||||
|
callout_handle.getArgument("leases4", leases4);
|
||||||
|
if (leases4->size() > 0) {
|
||||||
|
callback_lease4_ = leases4->at(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
Lease4CollectionPtr deleted_leases4;
|
||||||
|
callout_handle.getArgument("deleted_leases4", deleted_leases4);
|
||||||
|
if (deleted_leases4->size() > 0) {
|
||||||
|
callback_deleted_lease4_ = deleted_leases4->at(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
callback_argument_names_ = callout_handle.getArgumentNames();
|
||||||
|
sort(callback_argument_names_.begin(), callback_argument_names_.end());
|
||||||
|
|
||||||
|
if (callback_qry_pkt4_) {
|
||||||
|
callback_qry_options_copy_ = callback_qry_pkt4_->isCopyRetrievedOptions();
|
||||||
|
}
|
||||||
|
|
||||||
|
return (0);
|
||||||
|
}
|
||||||
|
|
||||||
/// @brief Test host4_identifier callout by setting identifier to "foo"
|
/// @brief Test host4_identifier callout by setting identifier to "foo"
|
||||||
///
|
///
|
||||||
/// @param callout_handle handle passed by the hooks framework
|
/// @param callout_handle handle passed by the hooks framework
|
||||||
@ -683,6 +763,7 @@ public:
|
|||||||
callback_qry_pkt4_.reset();
|
callback_qry_pkt4_.reset();
|
||||||
callback_qry_pkt4_.reset();
|
callback_qry_pkt4_.reset();
|
||||||
callback_lease4_.reset();
|
callback_lease4_.reset();
|
||||||
|
callback_deleted_lease4_.reset();
|
||||||
callback_hwaddr_.reset();
|
callback_hwaddr_.reset();
|
||||||
callback_clientid_.reset();
|
callback_clientid_.reset();
|
||||||
callback_subnet4_.reset();
|
callback_subnet4_.reset();
|
||||||
@ -695,6 +776,9 @@ public:
|
|||||||
/// pointer to Dhcpv4Srv that is used in tests
|
/// pointer to Dhcpv4Srv that is used in tests
|
||||||
NakedDhcpv4Srv* srv_;
|
NakedDhcpv4Srv* srv_;
|
||||||
|
|
||||||
|
/// Pointer to the IO service used in the tests.
|
||||||
|
static IOServicePtr io_service_;
|
||||||
|
|
||||||
// The following fields are used in testing pkt4_receive_callout
|
// The following fields are used in testing pkt4_receive_callout
|
||||||
|
|
||||||
/// String name of the received callout
|
/// String name of the received callout
|
||||||
@ -706,9 +790,12 @@ public:
|
|||||||
/// Server/response Pkt4 structure returned in the callout
|
/// Server/response Pkt4 structure returned in the callout
|
||||||
static Pkt4Ptr callback_resp_pkt4_;
|
static Pkt4Ptr callback_resp_pkt4_;
|
||||||
|
|
||||||
/// Lease4 structure returned in the callout
|
/// Lease4 structure returned in the leases4_committed callout
|
||||||
static Lease4Ptr callback_lease4_;
|
static Lease4Ptr callback_lease4_;
|
||||||
|
|
||||||
|
/// Lease4 structure returned in the leases4_committed callout
|
||||||
|
static Lease4Ptr callback_deleted_lease4_;
|
||||||
|
|
||||||
/// Hardware address returned in the callout
|
/// Hardware address returned in the callout
|
||||||
static HWAddrPtr callback_hwaddr_;
|
static HWAddrPtr callback_hwaddr_;
|
||||||
|
|
||||||
@ -736,6 +823,7 @@ public:
|
|||||||
|
|
||||||
// The following fields are used in testing pkt4_receive_callout.
|
// The following fields are used in testing pkt4_receive_callout.
|
||||||
// See fields description in the class for details
|
// See fields description in the class for details
|
||||||
|
IOServicePtr HooksDhcpv4SrvTest::io_service_;
|
||||||
string HooksDhcpv4SrvTest::callback_name_;
|
string HooksDhcpv4SrvTest::callback_name_;
|
||||||
Pkt4Ptr HooksDhcpv4SrvTest::callback_qry_pkt4_;
|
Pkt4Ptr HooksDhcpv4SrvTest::callback_qry_pkt4_;
|
||||||
Pkt4Ptr HooksDhcpv4SrvTest::callback_resp_pkt4_;
|
Pkt4Ptr HooksDhcpv4SrvTest::callback_resp_pkt4_;
|
||||||
@ -743,6 +831,7 @@ Subnet4Ptr HooksDhcpv4SrvTest::callback_subnet4_;
|
|||||||
HWAddrPtr HooksDhcpv4SrvTest::callback_hwaddr_;
|
HWAddrPtr HooksDhcpv4SrvTest::callback_hwaddr_;
|
||||||
ClientIdPtr HooksDhcpv4SrvTest::callback_clientid_;
|
ClientIdPtr HooksDhcpv4SrvTest::callback_clientid_;
|
||||||
Lease4Ptr HooksDhcpv4SrvTest::callback_lease4_;
|
Lease4Ptr HooksDhcpv4SrvTest::callback_lease4_;
|
||||||
|
Lease4Ptr HooksDhcpv4SrvTest::callback_deleted_lease4_;
|
||||||
const Subnet4Collection* HooksDhcpv4SrvTest::callback_subnet4collection_;
|
const Subnet4Collection* HooksDhcpv4SrvTest::callback_subnet4collection_;
|
||||||
vector<string> HooksDhcpv4SrvTest::callback_argument_names_;
|
vector<string> HooksDhcpv4SrvTest::callback_argument_names_;
|
||||||
bool HooksDhcpv4SrvTest::callback_qry_options_copy_;
|
bool HooksDhcpv4SrvTest::callback_qry_options_copy_;
|
||||||
@ -775,10 +864,12 @@ public:
|
|||||||
// Get rid of any marker files.
|
// Get rid of any marker files.
|
||||||
static_cast<void>(remove(LOAD_MARKER_FILE));
|
static_cast<void>(remove(LOAD_MARKER_FILE));
|
||||||
static_cast<void>(remove(UNLOAD_MARKER_FILE));
|
static_cast<void>(remove(UNLOAD_MARKER_FILE));
|
||||||
|
static_cast<void>(remove(SRV_CONFIG_MARKER_FILE));
|
||||||
CfgMgr::instance().clear();
|
CfgMgr::instance().clear();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
// Checks if callouts installed on pkt4_receive are indeed called and the
|
// Checks if callouts installed on pkt4_receive are indeed called and the
|
||||||
// all necessary parameters are passed.
|
// all necessary parameters are passed.
|
||||||
//
|
//
|
||||||
@ -1526,29 +1617,46 @@ TEST_F(HooksDhcpv4SrvTest, subnet4SelectChange) {
|
|||||||
EXPECT_TRUE((*subnets)[1]->inPool(Lease::TYPE_V4, addr));
|
EXPECT_TRUE((*subnets)[1]->inPool(Lease::TYPE_V4, addr));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Checks that subnet4_select is able to drop the packet.
|
// This test verifies that the leases4_committed hook point is not triggered
|
||||||
TEST_F(HooksDhcpv4SrvTest, subnet4SelectDrop) {
|
// for the DHCPDISCOVER.
|
||||||
|
TEST_F(HooksDhcpv4SrvTest, leases4CommittedDiscover) {
|
||||||
IfaceMgrTestConfig test_config(true);
|
IfaceMgrTestConfig test_config(true);
|
||||||
IfaceMgr::instance().openSockets4();
|
IfaceMgr::instance().openSockets4();
|
||||||
|
|
||||||
// Install subnet4_select_drop callout
|
ASSERT_NO_THROW(HooksManager::preCalloutsLibraryHandle().registerCallout(
|
||||||
EXPECT_NO_THROW(HooksManager::preCalloutsLibraryHandle().registerCallout(
|
"leases4_committed", leases4_committed_callout));
|
||||||
"subnet4_select", subnet4_select_drop_callout));
|
|
||||||
|
|
||||||
// Let's create a simple DISCOVER
|
|
||||||
Pkt4Ptr discover = generateSimpleDiscover();
|
|
||||||
|
|
||||||
// Simulate that we have received that traffic
|
Dhcp4Client client(Dhcp4Client::SELECTING);
|
||||||
srv_->fakeReceive(discover);
|
client.setIfaceName("eth1");
|
||||||
|
ASSERT_NO_THROW(client.doDiscover());
|
||||||
|
|
||||||
// Server will now process to run its normal loop, but instead of calling
|
// Make sure that we received a response
|
||||||
// IfaceMgr::receive4(), it will read all packets from the list set by
|
ASSERT_TRUE(client.getContext().response_);
|
||||||
// fakeReceive()
|
|
||||||
// In particular, it should call registered subnet4_select callback.
|
|
||||||
srv_->run();
|
|
||||||
|
|
||||||
// Check that there is no packet sent
|
// Make sure that the callout wasn't called.
|
||||||
EXPECT_EQ(0, srv_->fake_sent_.size());
|
EXPECT_TRUE(callback_name_.empty());
|
||||||
|
}
|
||||||
|
|
||||||
|
// This test verifies that the leases4_committed hook point is not triggered
|
||||||
|
// for the DHCPINFORM.
|
||||||
|
TEST_F(HooksDhcpv4SrvTest, leases4CommittedInform) {
|
||||||
|
IfaceMgrTestConfig test_config(true);
|
||||||
|
IfaceMgr::instance().openSockets4();
|
||||||
|
|
||||||
|
ASSERT_NO_THROW(HooksManager::preCalloutsLibraryHandle().registerCallout(
|
||||||
|
"leases4_committed", leases4_committed_callout));
|
||||||
|
|
||||||
|
|
||||||
|
Dhcp4Client client(Dhcp4Client::SELECTING);
|
||||||
|
client.useRelay();
|
||||||
|
ASSERT_NO_THROW(client.doInform());
|
||||||
|
|
||||||
|
// Make sure that we received a response
|
||||||
|
ASSERT_TRUE(client.getContext().response_);
|
||||||
|
|
||||||
|
// Make sure that the callout wasn't called.
|
||||||
|
EXPECT_TRUE(callback_name_.empty());
|
||||||
}
|
}
|
||||||
|
|
||||||
// This test verifies that incoming (positive) REQUEST/Renewing can be handled
|
// This test verifies that incoming (positive) REQUEST/Renewing can be handled
|
||||||
@ -1719,6 +1827,189 @@ TEST_F(HooksDhcpv4SrvTest, lease4RenewSkip) {
|
|||||||
EXPECT_TRUE(LeaseMgrFactory::instance().deleteLease(addr));
|
EXPECT_TRUE(LeaseMgrFactory::instance().deleteLease(addr));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// This test verifies that the callout installed on the leases4_committed hook
|
||||||
|
// point is executed as a result of DHCPREQUEST message sent to allocate new
|
||||||
|
// lease or renew an existing lease.
|
||||||
|
TEST_F(HooksDhcpv4SrvTest, leases4CommittedRequest) {
|
||||||
|
IfaceMgrTestConfig test_config(true);
|
||||||
|
IfaceMgr::instance().openSockets4();
|
||||||
|
|
||||||
|
ASSERT_NO_THROW(HooksManager::preCalloutsLibraryHandle().registerCallout(
|
||||||
|
"leases4_committed", leases4_committed_callout));
|
||||||
|
|
||||||
|
|
||||||
|
Dhcp4Client client(Dhcp4Client::SELECTING);
|
||||||
|
client.setIfaceName("eth1");
|
||||||
|
ASSERT_NO_THROW(client.doDORA(boost::shared_ptr<IOAddress>(new IOAddress("192.0.2.100"))));
|
||||||
|
|
||||||
|
// Make sure that we received a response
|
||||||
|
ASSERT_TRUE(client.getContext().response_);
|
||||||
|
|
||||||
|
// Check that the callback called is indeed the one we installed
|
||||||
|
EXPECT_EQ("leases4_committed", callback_name_);
|
||||||
|
|
||||||
|
// Check if all expected parameters were really received
|
||||||
|
vector<string> expected_argument_names;
|
||||||
|
expected_argument_names.push_back("query4");
|
||||||
|
expected_argument_names.push_back("deleted_leases4");
|
||||||
|
expected_argument_names.push_back("leases4");
|
||||||
|
|
||||||
|
sort(expected_argument_names.begin(), expected_argument_names.end());
|
||||||
|
EXPECT_TRUE(callback_argument_names_ == expected_argument_names);
|
||||||
|
|
||||||
|
// Newly allocated lease should be returned.
|
||||||
|
ASSERT_TRUE(callback_lease4_);
|
||||||
|
EXPECT_EQ("192.0.2.100", callback_lease4_->addr_.toText());
|
||||||
|
|
||||||
|
// Deleted lease must not be present, because it is a new allocation.
|
||||||
|
EXPECT_FALSE(callback_deleted_lease4_);
|
||||||
|
|
||||||
|
// Pkt passed to a callout must be configured to copy retrieved options.
|
||||||
|
EXPECT_TRUE(callback_qry_options_copy_);
|
||||||
|
|
||||||
|
resetCalloutBuffers();
|
||||||
|
|
||||||
|
// Renew the lease and make sure that the callout has been executed.
|
||||||
|
client.setState(Dhcp4Client::RENEWING);
|
||||||
|
ASSERT_NO_THROW(client.doRequest());
|
||||||
|
|
||||||
|
// Make sure that we received a response
|
||||||
|
ASSERT_TRUE(client.getContext().response_);
|
||||||
|
|
||||||
|
// Check that the callback called is indeed the one we installed
|
||||||
|
EXPECT_EQ("leases4_committed", callback_name_);
|
||||||
|
|
||||||
|
// Renewed lease should be returned.
|
||||||
|
ASSERT_TRUE(callback_lease4_);
|
||||||
|
EXPECT_EQ("192.0.2.100", callback_lease4_->addr_.toText());
|
||||||
|
|
||||||
|
// Deleted lease must not be present, because it is a new allocation.
|
||||||
|
EXPECT_FALSE(callback_deleted_lease4_);
|
||||||
|
|
||||||
|
// Pkt passed to a callout must be configured to copy retrieved options.
|
||||||
|
EXPECT_TRUE(callback_qry_options_copy_);
|
||||||
|
|
||||||
|
resetCalloutBuffers();
|
||||||
|
|
||||||
|
// Let's try to renew again but force the client to request a different
|
||||||
|
// address.
|
||||||
|
client.ciaddr_ = IOAddress("192.0.2.101");
|
||||||
|
|
||||||
|
ASSERT_NO_THROW(client.doRequest());
|
||||||
|
|
||||||
|
// Make sure that we received a response
|
||||||
|
ASSERT_TRUE(client.getContext().response_);
|
||||||
|
|
||||||
|
// Check that the callback called is indeed the one we installed
|
||||||
|
EXPECT_EQ("leases4_committed", callback_name_);
|
||||||
|
|
||||||
|
// New lease should be returned.
|
||||||
|
ASSERT_TRUE(callback_lease4_);
|
||||||
|
EXPECT_EQ("192.0.2.101", callback_lease4_->addr_.toText());
|
||||||
|
|
||||||
|
// The old lease should have been deleted.
|
||||||
|
ASSERT_TRUE(callback_deleted_lease4_);
|
||||||
|
EXPECT_EQ("192.0.2.100", callback_deleted_lease4_->addr_.toText());
|
||||||
|
|
||||||
|
// Pkt passed to a callout must be configured to copy retrieved options.
|
||||||
|
EXPECT_TRUE(callback_qry_options_copy_);
|
||||||
|
|
||||||
|
resetCalloutBuffers();
|
||||||
|
|
||||||
|
// Now request an address that can't be allocated. The client should receive
|
||||||
|
// the DHCPNAK.
|
||||||
|
client.ciaddr_ = IOAddress("10.0.0.1");
|
||||||
|
|
||||||
|
ASSERT_NO_THROW(client.doRequest());
|
||||||
|
|
||||||
|
// Make sure that we received a response
|
||||||
|
ASSERT_TRUE(client.getContext().response_);
|
||||||
|
|
||||||
|
// Check that the callback called is indeed the one we installed
|
||||||
|
EXPECT_EQ("leases4_committed", callback_name_);
|
||||||
|
|
||||||
|
EXPECT_FALSE(callback_lease4_);
|
||||||
|
EXPECT_FALSE(callback_deleted_lease4_);
|
||||||
|
}
|
||||||
|
|
||||||
|
// This test verifies that it is possible to park a packet as a result of
|
||||||
|
// the leases4_committed callouts.
|
||||||
|
TEST_F(HooksDhcpv4SrvTest, leases4CommittedParkRequests) {
|
||||||
|
IfaceMgrTestConfig test_config(true);
|
||||||
|
IfaceMgr::instance().openSockets4();
|
||||||
|
|
||||||
|
// This callout uses provided IO service object to post a function
|
||||||
|
// that unparks the packet. The packet is parked and can be unparked
|
||||||
|
// by simply calling IOService::poll.
|
||||||
|
ASSERT_NO_THROW(HooksManager::preCalloutsLibraryHandle().registerCallout(
|
||||||
|
"leases4_committed", leases4_committed_park_callout));
|
||||||
|
|
||||||
|
// Create first client and perform DORA.
|
||||||
|
Dhcp4Client client1(Dhcp4Client::SELECTING);
|
||||||
|
client1.setIfaceName("eth1");
|
||||||
|
ASSERT_NO_THROW(client1.doDORA(boost::shared_ptr<IOAddress>(new IOAddress("192.0.2.100"))));
|
||||||
|
|
||||||
|
// We should be offered an address but the DHCPACK should not arrive
|
||||||
|
// at this point, because the packet is parked.
|
||||||
|
ASSERT_FALSE(client1.getContext().response_);
|
||||||
|
|
||||||
|
// Check that the callback called is indeed the one we installed
|
||||||
|
EXPECT_EQ("leases4_committed", callback_name_);
|
||||||
|
|
||||||
|
// Check if all expected parameters were really received
|
||||||
|
vector<string> expected_argument_names;
|
||||||
|
expected_argument_names.push_back("query4");
|
||||||
|
expected_argument_names.push_back("deleted_leases4");
|
||||||
|
expected_argument_names.push_back("leases4");
|
||||||
|
|
||||||
|
sort(expected_argument_names.begin(), expected_argument_names.end());
|
||||||
|
EXPECT_TRUE(callback_argument_names_ == expected_argument_names);
|
||||||
|
|
||||||
|
// Newly allocated lease should be passed to the callout.
|
||||||
|
ASSERT_TRUE(callback_lease4_);
|
||||||
|
EXPECT_EQ("192.0.2.100", callback_lease4_->addr_.toText());
|
||||||
|
|
||||||
|
// Deleted lease must not be present, because it is a new allocation.
|
||||||
|
EXPECT_FALSE(callback_deleted_lease4_);
|
||||||
|
|
||||||
|
// Pkt passed to a callout must be configured to copy retrieved options.
|
||||||
|
EXPECT_TRUE(callback_qry_options_copy_);
|
||||||
|
|
||||||
|
// Reset all indicators because we'll be now creating a second client.
|
||||||
|
resetCalloutBuffers();
|
||||||
|
|
||||||
|
// Create the second client to test that it may communicate with the
|
||||||
|
// server while the previous packet is parked.
|
||||||
|
Dhcp4Client client2(client1.getServer(), Dhcp4Client::SELECTING);
|
||||||
|
client2.setIfaceName("eth1");
|
||||||
|
ASSERT_NO_THROW(client2.doDORA(boost::shared_ptr<IOAddress>(new IOAddress("192.0.2.101"))));
|
||||||
|
|
||||||
|
// The DHCPOFFER should have been returned but not DHCPACK, as this
|
||||||
|
// packet got parked too.
|
||||||
|
ASSERT_FALSE(client2.getContext().response_);
|
||||||
|
|
||||||
|
// Check that the callback called is indeed the one we installed.
|
||||||
|
EXPECT_EQ("leases4_committed", callback_name_);
|
||||||
|
|
||||||
|
// There should be now two actions scheduled on our IO service
|
||||||
|
// by the invoked callouts. They unpark both DHCPACK messages.
|
||||||
|
ASSERT_NO_THROW(io_service_->poll());
|
||||||
|
|
||||||
|
// Receive and check the first response.
|
||||||
|
ASSERT_NO_THROW(client1.receiveResponse());
|
||||||
|
ASSERT_TRUE(client1.getContext().response_);
|
||||||
|
Pkt4Ptr rsp = client1.getContext().response_;
|
||||||
|
EXPECT_EQ(DHCPACK, rsp->getType());
|
||||||
|
EXPECT_EQ("192.0.2.100", rsp->getYiaddr().toText());
|
||||||
|
|
||||||
|
// Receive and check the second response.
|
||||||
|
ASSERT_NO_THROW(client2.receiveResponse());
|
||||||
|
ASSERT_TRUE(client2.getContext().response_);
|
||||||
|
rsp = client2.getContext().response_;
|
||||||
|
EXPECT_EQ(DHCPACK, rsp->getType());
|
||||||
|
EXPECT_EQ("192.0.2.101", rsp->getYiaddr().toText());
|
||||||
|
}
|
||||||
|
|
||||||
// This test verifies that valid RELEASE triggers lease4_release callouts
|
// This test verifies that valid RELEASE triggers lease4_release callouts
|
||||||
TEST_F(HooksDhcpv4SrvTest, lease4ReleaseSimple) {
|
TEST_F(HooksDhcpv4SrvTest, lease4ReleaseSimple) {
|
||||||
IfaceMgrTestConfig test_config(true);
|
IfaceMgrTestConfig test_config(true);
|
||||||
@ -1873,6 +2164,50 @@ TEST_F(HooksDhcpv4SrvTest, lease4ReleaseSkip) {
|
|||||||
EXPECT_EQ(leases.size(), 1);
|
EXPECT_EQ(leases.size(), 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// This test verifies that the leases4_committed callout is executed
|
||||||
|
// with deleted leases as argument when DHCPRELEASE is processed.
|
||||||
|
TEST_F(HooksDhcpv4SrvTest, leases4CommittedRelease) {
|
||||||
|
IfaceMgrTestConfig test_config(true);
|
||||||
|
IfaceMgr::instance().openSockets4();
|
||||||
|
|
||||||
|
ASSERT_NO_THROW(HooksManager::preCalloutsLibraryHandle().registerCallout(
|
||||||
|
"leases4_committed", leases4_committed_callout));
|
||||||
|
|
||||||
|
|
||||||
|
Dhcp4Client client(Dhcp4Client::SELECTING);
|
||||||
|
client.setIfaceName("eth1");
|
||||||
|
ASSERT_NO_THROW(client.doDORA(boost::shared_ptr<IOAddress>(new IOAddress("192.0.2.100"))));
|
||||||
|
|
||||||
|
// Make sure that we received a response
|
||||||
|
ASSERT_TRUE(client.getContext().response_);
|
||||||
|
|
||||||
|
resetCalloutBuffers();
|
||||||
|
|
||||||
|
ASSERT_NO_THROW(client.doRelease());
|
||||||
|
|
||||||
|
// Check that the callback called is indeed the one we installed
|
||||||
|
EXPECT_EQ("leases4_committed", callback_name_);
|
||||||
|
|
||||||
|
// Check if all expected parameters were really received
|
||||||
|
vector<string> expected_argument_names;
|
||||||
|
expected_argument_names.push_back("query4");
|
||||||
|
expected_argument_names.push_back("deleted_leases4");
|
||||||
|
expected_argument_names.push_back("leases4");
|
||||||
|
|
||||||
|
sort(expected_argument_names.begin(), expected_argument_names.end());
|
||||||
|
EXPECT_TRUE(callback_argument_names_ == expected_argument_names);
|
||||||
|
|
||||||
|
// No new allocations.
|
||||||
|
EXPECT_FALSE(callback_lease4_);
|
||||||
|
|
||||||
|
// Released lease should be returned.
|
||||||
|
ASSERT_TRUE(callback_deleted_lease4_);
|
||||||
|
EXPECT_EQ("192.0.2.100", callback_deleted_lease4_->addr_.toText());
|
||||||
|
|
||||||
|
// Pkt passed to a callout must be configured to copy retrieved options.
|
||||||
|
EXPECT_TRUE(callback_qry_options_copy_);
|
||||||
|
}
|
||||||
|
|
||||||
// This test verifies that drop flag returned by a callout installed on the
|
// This test verifies that drop flag returned by a callout installed on the
|
||||||
// lease4_release hook point will keep the lease
|
// lease4_release hook point will keep the lease
|
||||||
TEST_F(HooksDhcpv4SrvTest, lease4ReleaseDrop) {
|
TEST_F(HooksDhcpv4SrvTest, lease4ReleaseDrop) {
|
||||||
@ -2083,6 +2418,50 @@ TEST_F(HooksDhcpv4SrvTest, HooksDeclineDrop) {
|
|||||||
EXPECT_EQ(addr, callback_lease4_->addr_);
|
EXPECT_EQ(addr, callback_lease4_->addr_);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// This test verifies that the leases4_committed callout is executed
|
||||||
|
// with declined leases as argument when DHCPDECLINE is processed.
|
||||||
|
TEST_F(HooksDhcpv4SrvTest, leases4CommittedDecline) {
|
||||||
|
IfaceMgrTestConfig test_config(true);
|
||||||
|
IfaceMgr::instance().openSockets4();
|
||||||
|
|
||||||
|
ASSERT_NO_THROW(HooksManager::preCalloutsLibraryHandle().registerCallout(
|
||||||
|
"leases4_committed", leases4_committed_callout));
|
||||||
|
|
||||||
|
|
||||||
|
Dhcp4Client client(Dhcp4Client::SELECTING);
|
||||||
|
client.useRelay();
|
||||||
|
ASSERT_NO_THROW(client.doDORA(boost::shared_ptr<IOAddress>(new IOAddress("192.0.2.100"))));
|
||||||
|
|
||||||
|
// Make sure that we received a response
|
||||||
|
ASSERT_TRUE(client.getContext().response_);
|
||||||
|
|
||||||
|
resetCalloutBuffers();
|
||||||
|
|
||||||
|
ASSERT_NO_THROW(client.doDecline());
|
||||||
|
|
||||||
|
// Check that the callback called is indeed the one we installed
|
||||||
|
EXPECT_EQ("leases4_committed", callback_name_);
|
||||||
|
|
||||||
|
// Check if all expected parameters were really received
|
||||||
|
vector<string> expected_argument_names;
|
||||||
|
expected_argument_names.push_back("query4");
|
||||||
|
expected_argument_names.push_back("deleted_leases4");
|
||||||
|
expected_argument_names.push_back("leases4");
|
||||||
|
|
||||||
|
sort(expected_argument_names.begin(), expected_argument_names.end());
|
||||||
|
EXPECT_TRUE(callback_argument_names_ == expected_argument_names);
|
||||||
|
|
||||||
|
// No new allocations.
|
||||||
|
ASSERT_TRUE(callback_lease4_);
|
||||||
|
EXPECT_EQ("192.0.2.100", callback_lease4_->addr_.toText());
|
||||||
|
EXPECT_EQ(Lease::STATE_DECLINED, callback_lease4_->state_);
|
||||||
|
|
||||||
|
// Released lease should be returned.
|
||||||
|
EXPECT_FALSE(callback_deleted_lease4_);
|
||||||
|
|
||||||
|
// Pkt passed to a callout must be configured to copy retrieved options.
|
||||||
|
EXPECT_TRUE(callback_qry_options_copy_);
|
||||||
|
}
|
||||||
|
|
||||||
// Checks if callout installed on host4_identifier can generate an
|
// Checks if callout installed on host4_identifier can generate an
|
||||||
// identifier and whether that identifier is actually used.
|
// identifier and whether that identifier is actually used.
|
||||||
@ -2258,3 +2637,66 @@ TEST_F(LoadUnloadDhcpv4SrvTest, unloadLibraries) {
|
|||||||
EXPECT_TRUE(checkMarkerFile(UNLOAD_MARKER_FILE, "21"));
|
EXPECT_TRUE(checkMarkerFile(UNLOAD_MARKER_FILE, "21"));
|
||||||
EXPECT_TRUE(checkMarkerFile(LOAD_MARKER_FILE, "12"));
|
EXPECT_TRUE(checkMarkerFile(LOAD_MARKER_FILE, "12"));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Checks if callouts installed on the dhcp4_srv_configured ared indeed called
|
||||||
|
// and all the necessary parameters are passed.
|
||||||
|
TEST_F(LoadUnloadDhcpv4SrvTest, Dhcpv4SrvConfigured) {
|
||||||
|
boost::shared_ptr<ControlledDhcpv4Srv> srv(new ControlledDhcpv4Srv(0));
|
||||||
|
|
||||||
|
// Ensure no marker files to start with.
|
||||||
|
ASSERT_FALSE(checkMarkerFileExists(LOAD_MARKER_FILE));
|
||||||
|
ASSERT_FALSE(checkMarkerFileExists(UNLOAD_MARKER_FILE));
|
||||||
|
ASSERT_FALSE(checkMarkerFileExists(SRV_CONFIG_MARKER_FILE));
|
||||||
|
|
||||||
|
// Minimal valid configuration for the server. It includes the
|
||||||
|
// section which loads the callout library #3, which implements
|
||||||
|
// dhcp4_srv_configured callout.
|
||||||
|
std::string config_str =
|
||||||
|
"{"
|
||||||
|
" \"interfaces-config\": {"
|
||||||
|
" \"interfaces\": [ ]"
|
||||||
|
" },"
|
||||||
|
" \"rebind-timer\": 2000,"
|
||||||
|
" \"renew-timer\": 1000,"
|
||||||
|
" \"subnet4\": [ ],"
|
||||||
|
" \"valid-lifetime\": 4000,"
|
||||||
|
" \"lease-database\": {"
|
||||||
|
" \"type\": \"memfile\","
|
||||||
|
" \"persist\": false"
|
||||||
|
" },"
|
||||||
|
" \"hooks-libraries\": ["
|
||||||
|
" {"
|
||||||
|
" \"library\": \"" + std::string(CALLOUT_LIBRARY_3) + "\""
|
||||||
|
" }"
|
||||||
|
" ]"
|
||||||
|
"}";
|
||||||
|
|
||||||
|
ConstElementPtr config = Element::fromJSON(config_str);
|
||||||
|
|
||||||
|
// Configure the server.
|
||||||
|
ConstElementPtr answer;
|
||||||
|
ASSERT_NO_THROW(answer = srv->processConfig(config));
|
||||||
|
|
||||||
|
// Make sure there were no errors.
|
||||||
|
int status_code;
|
||||||
|
parseAnswer(status_code, answer);
|
||||||
|
ASSERT_EQ(0, status_code);
|
||||||
|
|
||||||
|
// The hook library should have been loaded.
|
||||||
|
EXPECT_TRUE(checkMarkerFile(LOAD_MARKER_FILE, "3"));
|
||||||
|
EXPECT_FALSE(checkMarkerFileExists(UNLOAD_MARKER_FILE));
|
||||||
|
// The dhcp4_srv_configured should have been invoked and the provided
|
||||||
|
// parameters should be recorded.
|
||||||
|
EXPECT_TRUE(checkMarkerFile(SRV_CONFIG_MARKER_FILE,
|
||||||
|
"3io_contextjson_configserver_config"));
|
||||||
|
|
||||||
|
// Destroy the server, instance which should unload the libraries.
|
||||||
|
srv.reset();
|
||||||
|
|
||||||
|
// The server was destroyed, so the unload() function should now
|
||||||
|
// include the library number in its marker file.
|
||||||
|
EXPECT_TRUE(checkMarkerFile(LOAD_MARKER_FILE, "3"));
|
||||||
|
EXPECT_TRUE(checkMarkerFile(UNLOAD_MARKER_FILE, "3"));
|
||||||
|
EXPECT_TRUE(checkMarkerFile(SRV_CONFIG_MARKER_FILE,
|
||||||
|
"3io_contextjson_configserver_config"));
|
||||||
|
}
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
// Copyright (C) 2013-2017 Internet Systems Consortium, Inc. ("ISC")
|
// Copyright (C) 2013-2018 Internet Systems Consortium, Inc. ("ISC")
|
||||||
//
|
//
|
||||||
// This Source Code Form is subject to the terms of the Mozilla Public
|
// This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
@ -14,6 +14,7 @@
|
|||||||
namespace {
|
namespace {
|
||||||
const char* const LOAD_MARKER_FILE = "@abs_builddir@/load_marker.txt";
|
const char* const LOAD_MARKER_FILE = "@abs_builddir@/load_marker.txt";
|
||||||
const char* const UNLOAD_MARKER_FILE = "@abs_builddir@/unload_marker.txt";
|
const char* const UNLOAD_MARKER_FILE = "@abs_builddir@/unload_marker.txt";
|
||||||
|
const char* const SRV_CONFIG_MARKER_FILE = "@abs_builddir@/srv_config_marker_file.txt";
|
||||||
}
|
}
|
||||||
|
|
||||||
namespace isc {
|
namespace isc {
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
// Copyright (C) 2013-2015 Internet Systems Consortium, Inc. ("ISC")
|
// Copyright (C) 2013-2018 Internet Systems Consortium, Inc. ("ISC")
|
||||||
//
|
//
|
||||||
// This Source Code Form is subject to the terms of the Mozilla Public
|
// This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
@ -21,6 +21,7 @@ namespace {
|
|||||||
// operation.
|
// operation.
|
||||||
const char* const CALLOUT_LIBRARY_1 = "@abs_builddir@/.libs/libco1.so";
|
const char* const CALLOUT_LIBRARY_1 = "@abs_builddir@/.libs/libco1.so";
|
||||||
const char* const CALLOUT_LIBRARY_2 = "@abs_builddir@/.libs/libco2.so";
|
const char* const CALLOUT_LIBRARY_2 = "@abs_builddir@/.libs/libco2.so";
|
||||||
|
const char* const CALLOUT_LIBRARY_3 = "@abs_builddir@/.libs/libco3.so";
|
||||||
|
|
||||||
// Name of a library which is not present.
|
// Name of a library which is not present.
|
||||||
const char* const NOT_PRESENT_LIBRARY = "@abs_builddir@/.libs/libnothere.so";
|
const char* const NOT_PRESENT_LIBRARY = "@abs_builddir@/.libs/libnothere.so";
|
||||||
|
@ -2734,8 +2734,8 @@ AllocEngine::ClientContext4::ClientContext4()
|
|||||||
requested_address_(IOAddress::IPV4_ZERO_ADDRESS()),
|
requested_address_(IOAddress::IPV4_ZERO_ADDRESS()),
|
||||||
fwd_dns_update_(false), rev_dns_update_(false),
|
fwd_dns_update_(false), rev_dns_update_(false),
|
||||||
hostname_(""), callout_handle_(), fake_allocation_(false),
|
hostname_(""), callout_handle_(), fake_allocation_(false),
|
||||||
old_lease_(), hosts_(), conflicting_lease_(), query_(),
|
old_lease_(), new_lease_(), hosts_(), conflicting_lease_(),
|
||||||
host_identifiers_() {
|
query_(), host_identifiers_() {
|
||||||
}
|
}
|
||||||
|
|
||||||
AllocEngine::ClientContext4::ClientContext4(const Subnet4Ptr& subnet,
|
AllocEngine::ClientContext4::ClientContext4(const Subnet4Ptr& subnet,
|
||||||
@ -2750,8 +2750,8 @@ AllocEngine::ClientContext4::ClientContext4(const Subnet4Ptr& subnet,
|
|||||||
requested_address_(requested_addr),
|
requested_address_(requested_addr),
|
||||||
fwd_dns_update_(fwd_dns_update), rev_dns_update_(rev_dns_update),
|
fwd_dns_update_(fwd_dns_update), rev_dns_update_(rev_dns_update),
|
||||||
hostname_(hostname), callout_handle_(),
|
hostname_(hostname), callout_handle_(),
|
||||||
fake_allocation_(fake_allocation), old_lease_(), hosts_(),
|
fake_allocation_(fake_allocation), old_lease_(), new_lease_(),
|
||||||
host_identifiers_() {
|
hosts_(), host_identifiers_() {
|
||||||
|
|
||||||
// Initialize host identifiers.
|
// Initialize host identifiers.
|
||||||
if (hwaddr) {
|
if (hwaddr) {
|
||||||
@ -2776,8 +2776,7 @@ AllocEngine::allocateLease4(ClientContext4& ctx) {
|
|||||||
// be later set to non NULL value if existing lease is found in the
|
// be later set to non NULL value if existing lease is found in the
|
||||||
// database.
|
// database.
|
||||||
ctx.old_lease_.reset();
|
ctx.old_lease_.reset();
|
||||||
|
ctx.new_lease_.reset();
|
||||||
Lease4Ptr new_lease;
|
|
||||||
|
|
||||||
// Before we start allocation process, we need to make sure that the
|
// Before we start allocation process, we need to make sure that the
|
||||||
// selected subnet is allowed for this client. If not, we'll try to
|
// selected subnet is allowed for this client. If not, we'll try to
|
||||||
@ -2798,7 +2797,12 @@ AllocEngine::allocateLease4(ClientContext4& ctx) {
|
|||||||
isc_throw(BadValue, "HWAddr must be defined");
|
isc_throw(BadValue, "HWAddr must be defined");
|
||||||
}
|
}
|
||||||
|
|
||||||
new_lease = ctx.fake_allocation_ ? discoverLease4(ctx) : requestLease4(ctx);
|
if (ctx.fake_allocation_) {
|
||||||
|
return (discoverLease4(ctx));
|
||||||
|
|
||||||
|
} else {
|
||||||
|
ctx.new_lease_ = requestLease4(ctx);
|
||||||
|
}
|
||||||
|
|
||||||
} catch (const isc::Exception& e) {
|
} catch (const isc::Exception& e) {
|
||||||
// Some other error, return an empty lease.
|
// Some other error, return an empty lease.
|
||||||
@ -2807,7 +2811,7 @@ AllocEngine::allocateLease4(ClientContext4& ctx) {
|
|||||||
.arg(e.what());
|
.arg(e.what());
|
||||||
}
|
}
|
||||||
|
|
||||||
return (new_lease);
|
return (ctx.new_lease_);
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
@ -2992,6 +2996,7 @@ AllocEngine::discoverLease4(AllocEngine::ClientContext4& ctx) {
|
|||||||
if (!ctx.old_lease_ && client_lease) {
|
if (!ctx.old_lease_ && client_lease) {
|
||||||
ctx.old_lease_ = client_lease;
|
ctx.old_lease_ = client_lease;
|
||||||
}
|
}
|
||||||
|
|
||||||
return (new_lease);
|
return (new_lease);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1104,6 +1104,9 @@ public:
|
|||||||
/// @brief A pointer to an old lease that the client had before update.
|
/// @brief A pointer to an old lease that the client had before update.
|
||||||
Lease4Ptr old_lease_;
|
Lease4Ptr old_lease_;
|
||||||
|
|
||||||
|
/// @brief A pointer to a newly allocated lease.
|
||||||
|
Lease4Ptr new_lease_;
|
||||||
|
|
||||||
/// @brief Holds a map of hosts belonging to the client within different
|
/// @brief Holds a map of hosts belonging to the client within different
|
||||||
/// subnets.
|
/// subnets.
|
||||||
///
|
///
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
// Copyright (C) 2013-2017 Internet Systems Consortium, Inc. ("ISC")
|
// Copyright (C) 2013-2018 Internet Systems Consortium, Inc. ("ISC")
|
||||||
//
|
//
|
||||||
// This Source Code Form is subject to the terms of the Mozilla Public
|
// This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
@ -413,6 +413,9 @@ typedef boost::shared_ptr<Lease4> Lease4Ptr;
|
|||||||
/// @brief A collection of IPv4 leases.
|
/// @brief A collection of IPv4 leases.
|
||||||
typedef std::vector<Lease4Ptr> Lease4Collection;
|
typedef std::vector<Lease4Ptr> Lease4Collection;
|
||||||
|
|
||||||
|
/// @brief A shared pointer to the collection of IPv4 leases.
|
||||||
|
typedef boost::shared_ptr<Lease4Collection> Lease4CollectionPtr;
|
||||||
|
|
||||||
/// @brief Structure that holds a lease for IPv6 address and/or prefix
|
/// @brief Structure that holds a lease for IPv6 address and/or prefix
|
||||||
///
|
///
|
||||||
/// For performance reasons it is a simple structure, not a class. If we chose
|
/// For performance reasons it is a simple structure, not a class. If we chose
|
||||||
@ -550,6 +553,10 @@ typedef boost::shared_ptr<const Lease6> ConstLease6Ptr;
|
|||||||
/// @brief A collection of IPv6 leases.
|
/// @brief A collection of IPv6 leases.
|
||||||
typedef std::vector<Lease6Ptr> Lease6Collection;
|
typedef std::vector<Lease6Ptr> Lease6Collection;
|
||||||
|
|
||||||
|
|
||||||
|
/// @brief A shared pointer to the collection of IPv6 leases.
|
||||||
|
typedef boost::shared_ptr<Lease6Collection> Lease6CollectionPtr;
|
||||||
|
|
||||||
/// @brief Stream output operator.
|
/// @brief Stream output operator.
|
||||||
///
|
///
|
||||||
/// Dumps the output of Lease::toText to the given stream.
|
/// Dumps the output of Lease::toText to the given stream.
|
||||||
|
@ -42,6 +42,7 @@ libkea_hooks_la_SOURCES += libinfo.cc libinfo.h
|
|||||||
libkea_hooks_la_SOURCES += library_handle.cc library_handle.h
|
libkea_hooks_la_SOURCES += library_handle.cc library_handle.h
|
||||||
libkea_hooks_la_SOURCES += library_manager.cc library_manager.h
|
libkea_hooks_la_SOURCES += library_manager.cc library_manager.h
|
||||||
libkea_hooks_la_SOURCES += library_manager_collection.cc library_manager_collection.h
|
libkea_hooks_la_SOURCES += library_manager_collection.cc library_manager_collection.h
|
||||||
|
libkea_hooks_la_SOURCES += parking_lots.h
|
||||||
libkea_hooks_la_SOURCES += pointer_converter.h
|
libkea_hooks_la_SOURCES += pointer_converter.h
|
||||||
libkea_hooks_la_SOURCES += server_hooks.cc server_hooks.h
|
libkea_hooks_la_SOURCES += server_hooks.cc server_hooks.h
|
||||||
|
|
||||||
|
@ -74,6 +74,11 @@ CalloutHandle::getArgumentNames() const {
|
|||||||
return (names);
|
return (names);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ParkingLotHandlePtr
|
||||||
|
CalloutHandle::getParkingLotHandlePtr() const {
|
||||||
|
return (boost::make_shared<ParkingLotHandle>(server_hooks_.getParkingLotPtr(manager_->getHookIndex())));
|
||||||
|
}
|
||||||
|
|
||||||
// Return the library handle allowing the callout to access the CalloutManager
|
// Return the library handle allowing the callout to access the CalloutManager
|
||||||
// registration/deregistration functions.
|
// registration/deregistration functions.
|
||||||
|
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
// Copyright (C) 2013-2015 Internet Systems Consortium, Inc. ("ISC")
|
// Copyright (C) 2013-2018 Internet Systems Consortium, Inc. ("ISC")
|
||||||
//
|
//
|
||||||
// This Source Code Form is subject to the terms of the Mozilla Public
|
// This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
@ -9,6 +9,7 @@
|
|||||||
|
|
||||||
#include <exceptions/exceptions.h>
|
#include <exceptions/exceptions.h>
|
||||||
#include <hooks/library_handle.h>
|
#include <hooks/library_handle.h>
|
||||||
|
#include <hooks/parking_lots.h>
|
||||||
|
|
||||||
#include <boost/any.hpp>
|
#include <boost/any.hpp>
|
||||||
#include <boost/shared_ptr.hpp>
|
#include <boost/shared_ptr.hpp>
|
||||||
@ -91,7 +92,8 @@ public:
|
|||||||
enum CalloutNextStep {
|
enum CalloutNextStep {
|
||||||
NEXT_STEP_CONTINUE = 0, ///< continue normally
|
NEXT_STEP_CONTINUE = 0, ///< continue normally
|
||||||
NEXT_STEP_SKIP = 1, ///< skip the next processing step
|
NEXT_STEP_SKIP = 1, ///< skip the next processing step
|
||||||
NEXT_STEP_DROP = 2 ///< drop the packet
|
NEXT_STEP_DROP = 2, ///< drop the packet
|
||||||
|
NEXT_STEP_PARK = 3 ///< park the packet
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
@ -223,6 +225,11 @@ public:
|
|||||||
/// NEXT_STEP_DROP - tells the server to unconditionally drop the packet
|
/// NEXT_STEP_DROP - tells the server to unconditionally drop the packet
|
||||||
/// and do not process it further.
|
/// and do not process it further.
|
||||||
///
|
///
|
||||||
|
/// NEXT_STEP_PARK - tells the server to "park" the packet. The packet will
|
||||||
|
/// wait in the queue for being unparked, e.g. as a result
|
||||||
|
/// of completion of the asynchronous performed by the
|
||||||
|
/// hooks library operation.
|
||||||
|
///
|
||||||
/// This variable is interrogated by the server to see if the remaining
|
/// This variable is interrogated by the server to see if the remaining
|
||||||
/// callouts associated with the current hook should be bypassed.
|
/// callouts associated with the current hook should be bypassed.
|
||||||
///
|
///
|
||||||
@ -337,7 +344,13 @@ public:
|
|||||||
/// @return Name of the current hook or the empty string if none.
|
/// @return Name of the current hook or the empty string if none.
|
||||||
std::string getHookName() const;
|
std::string getHookName() const;
|
||||||
|
|
||||||
|
/// @brief Returns pointer to the parking lot handle for this hook point.
|
||||||
|
///
|
||||||
|
/// @return pointer to the parking lot handle
|
||||||
|
ParkingLotHandlePtr getParkingLotHandlePtr() const;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|
||||||
/// @brief Check index
|
/// @brief Check index
|
||||||
///
|
///
|
||||||
/// Gets the current library index, throwing an exception if it is not set
|
/// Gets the current library index, throwing an exception if it is not set
|
||||||
|
@ -130,6 +130,7 @@ HooksManager::unloadLibrariesInternal() {
|
|||||||
// ease debugging.
|
// ease debugging.
|
||||||
lm_collection_.reset();
|
lm_collection_.reset();
|
||||||
callout_manager_.reset();
|
callout_manager_.reset();
|
||||||
|
ServerHooks::getServerHooks().getParkingLotsPtr()->clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
void HooksManager::unloadLibraries() {
|
void HooksManager::unloadLibraries() {
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
// Copyright (C) 2013-2017 Internet Systems Consortium, Inc. ("ISC")
|
// Copyright (C) 2013-2018 Internet Systems Consortium, Inc. ("ISC")
|
||||||
//
|
//
|
||||||
// This Source Code Form is subject to the terms of the Mozilla Public
|
// This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
@ -234,6 +234,82 @@ public:
|
|||||||
/// @return A reference to the shared callout manager
|
/// @return A reference to the shared callout manager
|
||||||
static boost::shared_ptr<CalloutManager>& getSharedCalloutManager();
|
static boost::shared_ptr<CalloutManager>& getSharedCalloutManager();
|
||||||
|
|
||||||
|
/// @brief Park an object (packet).
|
||||||
|
///
|
||||||
|
/// The typical use case for parking an object is when the server needs to
|
||||||
|
/// suspend processing of a packet to perform an asynchronous operation,
|
||||||
|
/// before the response is sent to a client. In this case, the object type
|
||||||
|
/// is a pointer to the processed packet. Therefore, further in this
|
||||||
|
/// description we're going to refer to the parked objects as "parked
|
||||||
|
/// packets". However, any other object can be parked if necessary.
|
||||||
|
///
|
||||||
|
/// The following is the typical flow when packets are parked. The callouts
|
||||||
|
/// responsible for performing an asynchronous operation signal this need
|
||||||
|
/// to the server by returning the status @c NEXT_STEP_PARK, which instructs
|
||||||
|
/// the server to call this function. This function stops processing the
|
||||||
|
/// packet and puts it in, so called, parking lot. In order to be able to
|
||||||
|
/// resume the packet processing when instructed by the hooks library, the
|
||||||
|
/// parked packet is associated with the callback which, when called, will
|
||||||
|
/// resume packet processing.
|
||||||
|
///
|
||||||
|
/// The hook library must increase a reference count on the parked object
|
||||||
|
/// by calling @c ParkingLotHandle::reference prior to returning the
|
||||||
|
/// @c NEXT_STEP_PARK status. This is important when multiple callouts
|
||||||
|
/// are installed on the same hook point and each of them schedules an
|
||||||
|
/// asynchronous operation. In this case, the packet must not be unparked
|
||||||
|
/// until all hook libraries call @c ParkingLotHandle::unpark to mark
|
||||||
|
/// that respective asynchronous operations are completed.
|
||||||
|
///
|
||||||
|
/// @tparam Type of the parked object.
|
||||||
|
/// @param hook_name name of the hook point for which the packet is parked.
|
||||||
|
/// @param parked_object packet to be parked.
|
||||||
|
/// @param unpark_callback callback invoked when the packet is unparked.
|
||||||
|
template<typename T>
|
||||||
|
static void park(const std::string& hook_name, T parked_object,
|
||||||
|
std::function<void()> unpark_callback) {
|
||||||
|
getHooksManager().parkInternal(hook_name, parked_object, unpark_callback);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @brief Forces unparking the object (packet).
|
||||||
|
///
|
||||||
|
/// This method unparks the object regardless of the reference counting
|
||||||
|
/// value. This is used in the situations when the callouts fail to unpark
|
||||||
|
/// the packet for some reason.
|
||||||
|
///
|
||||||
|
/// @tparam T type of the parked object.
|
||||||
|
/// @param hook_name name of the hook point for which the packet is parked.
|
||||||
|
/// @param parked_object parked object to be unparked.
|
||||||
|
/// @return true if the specified object has been found, false otherwise.
|
||||||
|
template<typename T>
|
||||||
|
static bool unpark(const std::string& hook_name, T parked_object) {
|
||||||
|
return (getHooksManager().unparkInternal(hook_name, parked_object));
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @brief Removes parked object without calling a callback.
|
||||||
|
///
|
||||||
|
/// @tparam T type of the parked object.
|
||||||
|
/// @param hook_name name of the hook point for which the packet is parked.
|
||||||
|
/// @param parked_object parked object to be removed.
|
||||||
|
/// @return true if the specified object has been found false otherwise.
|
||||||
|
template<typename T>
|
||||||
|
static bool drop(const std::string& hook_name, T parked_object) {
|
||||||
|
return (getHooksManager().dropInternal(hook_name, parked_object));
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @brief Increases reference counter for the parked object.
|
||||||
|
///
|
||||||
|
/// Reference counter must be increased at least to 1 before the @c park()
|
||||||
|
/// method can be called.
|
||||||
|
///
|
||||||
|
/// @tparam Type of the parked object.
|
||||||
|
/// @param hook_name name of the hook point for which the packet is parked.
|
||||||
|
/// @param parked_object parked object for which reference counter should
|
||||||
|
/// be increased.
|
||||||
|
template<typename T>
|
||||||
|
static void reference(const std::string& hook_name, T parked_object) {
|
||||||
|
getHooksManager().referenceInternal(hook_name, parked_object);
|
||||||
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|
||||||
/// @brief Constructor
|
/// @brief Constructor
|
||||||
@ -242,6 +318,55 @@ private:
|
|||||||
/// through the getHooksManager() static method.
|
/// through the getHooksManager() static method.
|
||||||
HooksManager();
|
HooksManager();
|
||||||
|
|
||||||
|
/// @brief Park an object (packet).
|
||||||
|
///
|
||||||
|
/// @tparam Type of the parked object.
|
||||||
|
/// @param hook_name Name of the hook point for which the packet is parked.
|
||||||
|
/// @param parked_object packet to be parked.
|
||||||
|
/// @param unpark_callback callback invoked when the packet is unparked.
|
||||||
|
template<typename T>
|
||||||
|
void parkInternal(const std::string& hook_name, T parked_object,
|
||||||
|
std::function<void()> unpark_callback) {
|
||||||
|
ServerHooks::getServerHooks().
|
||||||
|
getParkingLotPtr(hook_name)->park(parked_object, unpark_callback);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @brief Signals that the object (packet) should be unparked.
|
||||||
|
///
|
||||||
|
/// @tparam Type of the parked object.
|
||||||
|
/// @param hook_name name of the hook point for which the packet is parked.
|
||||||
|
/// @param parked_object parked object to be unparked.
|
||||||
|
/// @return true if the specified object has been found, false otherwise.
|
||||||
|
template<typename T>
|
||||||
|
bool unparkInternal(const std::string& hook_name, T parked_object) {
|
||||||
|
return (ServerHooks::getServerHooks().
|
||||||
|
getParkingLotPtr(hook_name)->unpark(parked_object, true));
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @brief Removes parked object without calling a callback.
|
||||||
|
///
|
||||||
|
/// @tparam T type of the parked object.
|
||||||
|
/// @param hook_name name of the hook point for which the packet is parked.
|
||||||
|
/// @param parked_object parked object to be removed.
|
||||||
|
/// @return true if the specified object has been found false otherwise.
|
||||||
|
template<typename T>
|
||||||
|
static bool dropInternal(const std::string& hook_name, T parked_object) {
|
||||||
|
return (ServerHooks::getServerHooks().
|
||||||
|
getParkingLotPtr(hook_name)->drop(parked_object));
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @brief Increases reference counter for the parked object.
|
||||||
|
///
|
||||||
|
/// @tparam Type of the parked object.
|
||||||
|
/// @param hook_name name of the hook point for which the packet is parked.
|
||||||
|
/// @param parked_object parked object for which reference counter should
|
||||||
|
/// be increased.
|
||||||
|
template<typename T>
|
||||||
|
void referenceInternal(const std::string& hook_name, T parked_object) {
|
||||||
|
ServerHooks::getServerHooks().
|
||||||
|
getParkingLotPtr(hook_name)->reference(parked_object);
|
||||||
|
}
|
||||||
|
|
||||||
//@{
|
//@{
|
||||||
/// The following methods correspond to similarly-named static methods,
|
/// The following methods correspond to similarly-named static methods,
|
||||||
/// but actually do the work on the singleton instance of the HooksManager.
|
/// but actually do the work on the singleton instance of the HooksManager.
|
||||||
|
333
src/lib/hooks/parking_lots.h
Normal file
333
src/lib/hooks/parking_lots.h
Normal file
@ -0,0 +1,333 @@
|
|||||||
|
// Copyright (C) 2018 Internet Systems Consortium, Inc. ("ISC")
|
||||||
|
//
|
||||||
|
// This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
|
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||||
|
|
||||||
|
#ifndef PARKING_LOTS_H
|
||||||
|
#define PARKING_LOTS_H
|
||||||
|
|
||||||
|
#include <exceptions/exceptions.h>
|
||||||
|
#include <boost/any.hpp>
|
||||||
|
#include <boost/make_shared.hpp>
|
||||||
|
#include <boost/shared_ptr.hpp>
|
||||||
|
#include <functional>
|
||||||
|
#include <list>
|
||||||
|
#include <map>
|
||||||
|
|
||||||
|
#include <iostream>
|
||||||
|
|
||||||
|
namespace isc {
|
||||||
|
namespace hooks {
|
||||||
|
|
||||||
|
/// @brief Parking lot for objects, e.g. packets, for a hook point.
|
||||||
|
///
|
||||||
|
/// Callouts may instruct the servers to "park" processed packets, i.e. suspend
|
||||||
|
/// their processing until explicitly unparked. This is useful in cases when
|
||||||
|
/// callouts need to perform asynchronous operations related to the packet
|
||||||
|
/// processing and the packet must not be further processed until the
|
||||||
|
/// asynchronous operations are completed. While the packet is parked, the
|
||||||
|
/// new packets can be processed, so the server remains responsive to the
|
||||||
|
/// new requests.
|
||||||
|
///
|
||||||
|
/// Parking lots are created per hook point, so the callouts installed on the
|
||||||
|
/// particular hook point only have access to the parking lots dedicated to
|
||||||
|
/// them.
|
||||||
|
///
|
||||||
|
/// The parking lot object supports 3 actions: "park", "unpark" and "reference".
|
||||||
|
/// In the typical case, the server parks the object and the callouts unpark and
|
||||||
|
/// reference the objects. Therefore, the @ref ParkingLot object is not passed
|
||||||
|
/// directly to the callouts. Instead, a ParkingLotHandle object is provided
|
||||||
|
/// to the callout, which only provides access to "unpark" and "reference"
|
||||||
|
/// operations.
|
||||||
|
///
|
||||||
|
/// Referencing an object is performed by the callouts before the
|
||||||
|
/// @c CalloutHandle::NEXT_STEP_PARK is returned to the server and before the
|
||||||
|
/// server parks the object. Trying to park unreferenced object will result
|
||||||
|
/// in error. Referencing (reference counting) as an important part of the
|
||||||
|
/// parking mechanism, which allows multiple callouts, installed on the same
|
||||||
|
/// hook point, to perform asynchronous operations and guarantees that the
|
||||||
|
/// object remains parked until all those asynchronous operations complete.
|
||||||
|
/// Each such callout must call @c unpark() when it desires the object to
|
||||||
|
/// be unparked, but the object will be unparked when all callouts call this
|
||||||
|
/// function, i.e. when all callouts signal completion of their respective
|
||||||
|
/// asynchronous operations.
|
||||||
|
///
|
||||||
|
/// The types of the parked objects provided as T parameter of respective
|
||||||
|
/// functions are most often shared pointers. One should not use references
|
||||||
|
/// to parked objects nor references to shared pointers to avoid premature
|
||||||
|
/// destruction of the parked objects.
|
||||||
|
class ParkingLot {
|
||||||
|
public:
|
||||||
|
|
||||||
|
/// @brief Parks an object.
|
||||||
|
///
|
||||||
|
/// @tparam Type of the parked object.
|
||||||
|
/// @param parked_object object to be parked, e.g. pointer to a packet.
|
||||||
|
/// @param unpark_callback callback function to be invoked when the object
|
||||||
|
/// is unparked.
|
||||||
|
/// @throw InvalidOperation if the @c reference() wasn't called prior to
|
||||||
|
/// parking the object.
|
||||||
|
template<typename T>
|
||||||
|
void park(T parked_object, std::function<void()> unpark_callback) {
|
||||||
|
auto it = find(parked_object);
|
||||||
|
if (it == parking_.end() || it->refcount_ <= 0) {
|
||||||
|
isc_throw(InvalidOperation, "unable to park an object because"
|
||||||
|
" reference count for this object hasn't been increased."
|
||||||
|
" Call ParkingLot::reference() first");
|
||||||
|
} else {
|
||||||
|
it->update(parked_object, unpark_callback);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @brief Increases reference counter for the parked object.
|
||||||
|
///
|
||||||
|
/// This method is called by the callouts to increase a reference count
|
||||||
|
/// on the object to be parked. It must be called before the object is
|
||||||
|
/// actually parked.
|
||||||
|
///
|
||||||
|
/// @tparam Type of the parked object.
|
||||||
|
/// @param parked_object object which will be parked.
|
||||||
|
template<typename T>
|
||||||
|
void reference(T parked_object) {
|
||||||
|
auto it = find(parked_object);
|
||||||
|
if (it == parking_.end()) {
|
||||||
|
ParkingInfo parking_info(parked_object);
|
||||||
|
parking_.push_back(parking_info);
|
||||||
|
|
||||||
|
} else {
|
||||||
|
++it->refcount_;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @brief Signals that the object should be unparked.
|
||||||
|
///
|
||||||
|
/// If the specified object is parked in this parking lot, the reference
|
||||||
|
/// count is decreased as a result of this method. If the reference count
|
||||||
|
/// is 0, the object is unparked and the callback is invoked. Typically, the
|
||||||
|
/// callback points to a function which resumes processing of a packet.
|
||||||
|
///
|
||||||
|
/// @tparam Type of the parked object.
|
||||||
|
/// @param parked_object parked object to be unparked.
|
||||||
|
/// @param force boolean value indicating if the reference counting should
|
||||||
|
/// be ignored and the object should be unparked immediately.
|
||||||
|
/// @return false if the object couldn't be unparked because there is
|
||||||
|
/// no such object, true otherwise.
|
||||||
|
template<typename T>
|
||||||
|
bool unpark(T parked_object, bool force = false) {
|
||||||
|
auto it = find(parked_object);
|
||||||
|
if (it != parking_.end()) {
|
||||||
|
if (force) {
|
||||||
|
it->refcount_ = 0;
|
||||||
|
|
||||||
|
} else {
|
||||||
|
--it->refcount_;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (it->refcount_ <= 0) {
|
||||||
|
// Unpark the packet and invoke the callback.
|
||||||
|
std::function<void()> cb = it->unpark_callback_;
|
||||||
|
parking_.erase(it);
|
||||||
|
cb();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Parked object found, so return true to indicate that the
|
||||||
|
// operation was successful. It doesn't necessarily mean
|
||||||
|
// that the object was unparked, but at least the reference
|
||||||
|
// count was decreased.
|
||||||
|
return (true);
|
||||||
|
}
|
||||||
|
|
||||||
|
// No such parked object.
|
||||||
|
return (false);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @brief Removes parked object without calling a callback.
|
||||||
|
///
|
||||||
|
/// @tparam Type of the parked object.
|
||||||
|
/// @param parked_object parked object to be removed.
|
||||||
|
/// @return false if the object couldn't be removed because there is
|
||||||
|
/// no such object, true otherwise.
|
||||||
|
template<typename T>
|
||||||
|
bool drop(T parked_object) {
|
||||||
|
auto it = find(parked_object);
|
||||||
|
if (it != parking_.end()) {
|
||||||
|
// Parked object found.
|
||||||
|
parking_.erase(it);
|
||||||
|
return (true);
|
||||||
|
}
|
||||||
|
|
||||||
|
// No such object.
|
||||||
|
return (false);
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
|
||||||
|
/// @brief Holds information about parked object.
|
||||||
|
struct ParkingInfo {
|
||||||
|
boost::any parked_object_; ///< parked object
|
||||||
|
std::function<void()> unpark_callback_; ///< pointer to the callback
|
||||||
|
int refcount_; ///< current reference count
|
||||||
|
|
||||||
|
/// @brief Constructor.
|
||||||
|
///
|
||||||
|
/// @param parked_object object being parked.
|
||||||
|
/// @param callback pointer to the callback.
|
||||||
|
ParkingInfo(const boost::any& parked_object,
|
||||||
|
std::function<void()> callback = 0)
|
||||||
|
: parked_object_(parked_object), unpark_callback_(callback),
|
||||||
|
refcount_(1) {
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @brief Update parking information.
|
||||||
|
///
|
||||||
|
/// @param parked_object parked object.
|
||||||
|
/// @param callback pointer to the callback.
|
||||||
|
void update(const boost::any& parked_object,
|
||||||
|
std::function<void()> callback) {
|
||||||
|
parked_object_ = parked_object;
|
||||||
|
unpark_callback_ = callback;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/// @brief Type of list of parked objects.
|
||||||
|
typedef std::list<ParkingInfo> ParkingInfoList;
|
||||||
|
/// @brief Type of the iterator in the list of parked objects.
|
||||||
|
typedef ParkingInfoList::iterator ParkingInfoListIterator;
|
||||||
|
|
||||||
|
/// @brief Container holding parked objects for this parking lot.
|
||||||
|
ParkingInfoList parking_;
|
||||||
|
|
||||||
|
/// @brief Search for the information about the parked object.
|
||||||
|
///
|
||||||
|
/// @tparam T parked object type.
|
||||||
|
/// @param parked_object object for which the information should be found.
|
||||||
|
/// @return Iterator pointing to the parked object, or @c parking_.end() if
|
||||||
|
/// no such object found.
|
||||||
|
template<typename T>
|
||||||
|
ParkingInfoListIterator find(T parked_object) {
|
||||||
|
for (auto it = parking_.begin(); it != parking_.end(); ++it) {
|
||||||
|
if (boost::any_cast<T>(it->parked_object_) == parked_object) {
|
||||||
|
return (it);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return (parking_.end());
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/// @brief Type of the pointer to the parking lot.
|
||||||
|
typedef boost::shared_ptr<ParkingLot> ParkingLotPtr;
|
||||||
|
|
||||||
|
/// @brief Provides a limited view to the @c ParkingLot.
|
||||||
|
///
|
||||||
|
/// The handle is provided to the callouts which can reference and unpark
|
||||||
|
/// parked objects. The callouts should not park objects, therefore this
|
||||||
|
/// operation is not available.
|
||||||
|
///
|
||||||
|
/// The types of the parked objects provided as T parameter of respective
|
||||||
|
/// functions are most often shared pointers. One should not use references
|
||||||
|
/// to parked objects nor references to shared pointers to avoid premature
|
||||||
|
/// destruction of the parked objects.
|
||||||
|
class ParkingLotHandle {
|
||||||
|
public:
|
||||||
|
|
||||||
|
/// @brief Constructor.
|
||||||
|
///
|
||||||
|
/// @param parking_lot pointer to the parking lot for which the handle is
|
||||||
|
/// created.
|
||||||
|
ParkingLotHandle(const ParkingLotPtr& parking_lot)
|
||||||
|
: parking_lot_(parking_lot) {
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @brief Increases reference counter for the parked object.
|
||||||
|
///
|
||||||
|
/// This method is called by the callouts to increase a reference count
|
||||||
|
/// on the object to be parked. It must be called before the object is
|
||||||
|
/// actually parked.
|
||||||
|
///
|
||||||
|
/// @tparam Type of the parked object.
|
||||||
|
/// @param parked_object object which will be parked.
|
||||||
|
template<typename T>
|
||||||
|
void reference(T parked_object) {
|
||||||
|
parking_lot_->reference(parked_object);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @brief Signals that the object should be unparked.
|
||||||
|
///
|
||||||
|
/// If the specified object is parked in this parking lot, the reference
|
||||||
|
/// count is decreased as a result of this method. If the reference count
|
||||||
|
/// is 0, the object is unparked and the callback is invoked. Typically, the
|
||||||
|
/// callback points to a function which resumes processing of a packet.
|
||||||
|
///
|
||||||
|
/// @tparam Type of the parked object.
|
||||||
|
/// @param parked_object parked object to be unparked.
|
||||||
|
/// @return false if the object couldn't be unparked because there is
|
||||||
|
/// no such object, true otherwise.
|
||||||
|
template<typename T>
|
||||||
|
bool unpark(T parked_object) {
|
||||||
|
return (parking_lot_->unpark(parked_object));
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @brief Removes parked object without calling a callback.
|
||||||
|
///
|
||||||
|
/// It ignores any reference counts on the parked object.
|
||||||
|
///
|
||||||
|
/// @tparam Type of the parked object.
|
||||||
|
/// @param parked_object parked object to be removed.
|
||||||
|
/// @return false if the object couldn't be removed because there is
|
||||||
|
/// no such object, true otherwise.
|
||||||
|
template<typename T>
|
||||||
|
bool drop(T parked_object) {
|
||||||
|
return (parking_lot_->drop(parked_object));
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
|
||||||
|
/// @brief Parking lot to which this handle points.
|
||||||
|
ParkingLotPtr parking_lot_;
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
/// @brief Pointer to the parking lot handle.
|
||||||
|
typedef boost::shared_ptr<ParkingLotHandle> ParkingLotHandlePtr;
|
||||||
|
|
||||||
|
/// @brief Collection of parking lots for various hook points.
|
||||||
|
class ParkingLots {
|
||||||
|
public:
|
||||||
|
|
||||||
|
/// @brief Removes all parked objects.
|
||||||
|
///
|
||||||
|
/// It doesn't invoke callbacks associated with the removed objects.
|
||||||
|
void clear() {
|
||||||
|
parking_lots_.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @brief Returns pointer to the parking lot for a hook points.
|
||||||
|
///
|
||||||
|
/// If the parking lot for the specified hook point doesn't exist, it is
|
||||||
|
/// created.
|
||||||
|
///
|
||||||
|
/// @param hook_index index of the hook point with which the parking
|
||||||
|
/// lot is associated.
|
||||||
|
/// @return Pointer to the parking lot.
|
||||||
|
ParkingLotPtr getParkingLotPtr(const int hook_index) {
|
||||||
|
if (parking_lots_.count(hook_index) == 0) {
|
||||||
|
parking_lots_[hook_index] = boost::make_shared<ParkingLot>();
|
||||||
|
}
|
||||||
|
return (parking_lots_[hook_index]);
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
|
||||||
|
/// @brief Container holding parking lots for various hook points.
|
||||||
|
std::map<int, ParkingLotPtr> parking_lots_;
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
/// @brief Type of the pointer to the parking lots.
|
||||||
|
typedef boost::shared_ptr<ParkingLots> ParkingLotsPtr;
|
||||||
|
|
||||||
|
} // end of namespace hooks
|
||||||
|
} // end of namespace isc
|
||||||
|
|
||||||
|
#endif
|
@ -84,6 +84,7 @@ ServerHooks::initialize() {
|
|||||||
// Clear out the name->index and index->name maps.
|
// Clear out the name->index and index->name maps.
|
||||||
hooks_.clear();
|
hooks_.clear();
|
||||||
inverse_hooks_.clear();
|
inverse_hooks_.clear();
|
||||||
|
parking_lots_.reset(new ParkingLots());
|
||||||
|
|
||||||
// Register the pre-defined hooks.
|
// Register the pre-defined hooks.
|
||||||
int create = registerHook("context_create");
|
int create = registerHook("context_create");
|
||||||
@ -175,6 +176,21 @@ ServerHooks::getServerHooksPtr() {
|
|||||||
return (hooks);
|
return (hooks);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ParkingLotsPtr
|
||||||
|
ServerHooks::getParkingLotsPtr() const {
|
||||||
|
return (parking_lots_);
|
||||||
|
}
|
||||||
|
|
||||||
|
ParkingLotPtr
|
||||||
|
ServerHooks::getParkingLotPtr(const int hook_index) {
|
||||||
|
return (parking_lots_->getParkingLotPtr(hook_index));
|
||||||
|
}
|
||||||
|
|
||||||
|
ParkingLotPtr
|
||||||
|
ServerHooks::getParkingLotPtr(const std::string& hook_name) {
|
||||||
|
return (parking_lots_->getParkingLotPtr(getServerHooks().getIndex(hook_name)));
|
||||||
|
}
|
||||||
|
|
||||||
std::string
|
std::string
|
||||||
ServerHooks::commandToHookName(const std::string& command_name) {
|
ServerHooks::commandToHookName(const std::string& command_name) {
|
||||||
// Prefix the command name with a dollar sign.
|
// Prefix the command name with a dollar sign.
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
// Copyright (C) 2013-2017 Internet Systems Consortium, Inc. ("ISC")
|
// Copyright (C) 2013-2018 Internet Systems Consortium, Inc. ("ISC")
|
||||||
//
|
//
|
||||||
// This Source Code Form is subject to the terms of the Mozilla Public
|
// This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
@ -8,6 +8,7 @@
|
|||||||
#define SERVER_HOOKS_H
|
#define SERVER_HOOKS_H
|
||||||
|
|
||||||
#include <exceptions/exceptions.h>
|
#include <exceptions/exceptions.h>
|
||||||
|
#include <hooks/parking_lots.h>
|
||||||
|
|
||||||
#include <boost/noncopyable.hpp>
|
#include <boost/noncopyable.hpp>
|
||||||
#include <boost/shared_ptr.hpp>
|
#include <boost/shared_ptr.hpp>
|
||||||
@ -152,6 +153,25 @@ public:
|
|||||||
/// @return Pointer to the global ServerHooks object.
|
/// @return Pointer to the global ServerHooks object.
|
||||||
static ServerHooksPtr getServerHooksPtr();
|
static ServerHooksPtr getServerHooksPtr();
|
||||||
|
|
||||||
|
/// @brief Returns pointer to all parking lots.
|
||||||
|
///
|
||||||
|
/// @return pointer to all parking lots.
|
||||||
|
ParkingLotsPtr getParkingLotsPtr() const;
|
||||||
|
|
||||||
|
/// @brief Returns pointer to the ParkingLot for the specified hook index.
|
||||||
|
///
|
||||||
|
/// @param hook_index index of the hook point for which the parking lot
|
||||||
|
/// should be returned.
|
||||||
|
/// @return Pointer to the ParkingLot object.
|
||||||
|
ParkingLotPtr getParkingLotPtr(const int hook_index);
|
||||||
|
|
||||||
|
/// @brief Returns pointer to the ParkingLot for the specified hook name.
|
||||||
|
///
|
||||||
|
/// @param hook_name name of the hook point for which the parking lot
|
||||||
|
/// should be returned.
|
||||||
|
/// @return Pointer to the ParkingLot object.
|
||||||
|
ParkingLotPtr getParkingLotPtr(const std::string& hook_name);
|
||||||
|
|
||||||
/// @brief Generates hook point name for the given control command name.
|
/// @brief Generates hook point name for the given control command name.
|
||||||
///
|
///
|
||||||
/// This function is called to generate the name of the hook point
|
/// This function is called to generate the name of the hook point
|
||||||
@ -216,6 +236,8 @@ private:
|
|||||||
/// simpler than using a multi-indexed container.)
|
/// simpler than using a multi-indexed container.)
|
||||||
HookCollection hooks_; ///< Hook name/index collection
|
HookCollection hooks_; ///< Hook name/index collection
|
||||||
InverseHookCollection inverse_hooks_; ///< Hook index/name collection
|
InverseHookCollection inverse_hooks_; ///< Hook index/name collection
|
||||||
|
|
||||||
|
ParkingLotsPtr parking_lots_;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace util
|
} // namespace util
|
||||||
|
@ -40,7 +40,7 @@ if HAVE_GTEST
|
|||||||
# ignored for unit tests built here.
|
# ignored for unit tests built here.
|
||||||
|
|
||||||
noinst_LTLIBRARIES = libnvl.la libivl.la libfxl.la libbcl.la liblcl.la \
|
noinst_LTLIBRARIES = libnvl.la libivl.la libfxl.la libbcl.la liblcl.la \
|
||||||
liblecl.la libucl.la libfcl.la libpcl.la
|
liblecl.la libucl.la libfcl.la libpcl.la libacl.la
|
||||||
|
|
||||||
# -rpath /nowhere is a hack to trigger libtool to not create a
|
# -rpath /nowhere is a hack to trigger libtool to not create a
|
||||||
# convenience archive, resulting in shared modules
|
# convenience archive, resulting in shared modules
|
||||||
@ -102,6 +102,12 @@ libpcl_la_CPPFLAGS = $(AM_CPPFLAGS)
|
|||||||
libpcl_la_LDFLAGS = -avoid-version -export-dynamic -module -rpath /nowhere
|
libpcl_la_LDFLAGS = -avoid-version -export-dynamic -module -rpath /nowhere
|
||||||
libpcl_la_LDFLAGS += $(top_builddir)/src/lib/util/libkea-util.la
|
libpcl_la_LDFLAGS += $(top_builddir)/src/lib/util/libkea-util.la
|
||||||
|
|
||||||
|
# The async callout library - parks object for asynchronous task
|
||||||
|
libacl_la_SOURCES = async_callout_library.cc
|
||||||
|
libacl_la_CXXFLAGS = $(AM_CXXFLAGS)
|
||||||
|
libacl_la_CPPFLAGS = $(AM_CPPFLAGS)
|
||||||
|
libacl_la_LDFLAGS = -avoid-version -export-dynamic -module -rpath /nowhere
|
||||||
|
|
||||||
TESTS += run_unittests
|
TESTS += run_unittests
|
||||||
run_unittests_SOURCES = run_unittests.cc
|
run_unittests_SOURCES = run_unittests.cc
|
||||||
run_unittests_SOURCES += callout_handle_unittest.cc
|
run_unittests_SOURCES += callout_handle_unittest.cc
|
||||||
@ -111,6 +117,7 @@ run_unittests_SOURCES += handles_unittest.cc
|
|||||||
run_unittests_SOURCES += hooks_manager_unittest.cc
|
run_unittests_SOURCES += hooks_manager_unittest.cc
|
||||||
run_unittests_SOURCES += library_manager_collection_unittest.cc
|
run_unittests_SOURCES += library_manager_collection_unittest.cc
|
||||||
run_unittests_SOURCES += library_manager_unittest.cc
|
run_unittests_SOURCES += library_manager_unittest.cc
|
||||||
|
run_unittests_SOURCES += parking_lots_unittest.cc
|
||||||
run_unittests_SOURCES += server_hooks_unittest.cc
|
run_unittests_SOURCES += server_hooks_unittest.cc
|
||||||
|
|
||||||
nodist_run_unittests_SOURCES = marker_file.h
|
nodist_run_unittests_SOURCES = marker_file.h
|
||||||
|
153
src/lib/hooks/tests/async_callout_library.cc
Normal file
153
src/lib/hooks/tests/async_callout_library.cc
Normal file
@ -0,0 +1,153 @@
|
|||||||
|
// Copyright (C) 2018 Internet Systems Consortium, Inc. ("ISC")
|
||||||
|
//
|
||||||
|
// This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
|
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||||
|
|
||||||
|
/// @file
|
||||||
|
/// @brief Async callout library
|
||||||
|
///
|
||||||
|
/// This is source of a test library for testing a "parking" feature, i.e.
|
||||||
|
/// the callouts can schedule asynchronous operation and indicate that the
|
||||||
|
/// packet should be parked until the asynchronous operation completes and
|
||||||
|
/// the hooks library indicates that packet processing should be resumed.
|
||||||
|
|
||||||
|
#include <config.h>
|
||||||
|
#include <hooks/hooks.h>
|
||||||
|
#include <hooks/parking_lots.h>
|
||||||
|
#include <log/logger.h>
|
||||||
|
#include <log/macros.h>
|
||||||
|
#include <log/message_initializer.h>
|
||||||
|
#include <algorithm>
|
||||||
|
#include <functional>
|
||||||
|
#include <sstream>
|
||||||
|
#include <string>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
using namespace isc::hooks;
|
||||||
|
using namespace isc::log;
|
||||||
|
using namespace std;
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
|
||||||
|
/// @brief Logger used by the library.
|
||||||
|
isc::log::Logger logger("acl");
|
||||||
|
|
||||||
|
/// @brief Log messages.
|
||||||
|
const char* log_messages[] = {
|
||||||
|
"ACL_LOAD_START", "async callout load %1",
|
||||||
|
"ACL_LOAD_END", "async callout load end",
|
||||||
|
"ACL_LOAD_END", "duplicate of async callout load end",
|
||||||
|
NULL
|
||||||
|
};
|
||||||
|
|
||||||
|
/// @brief Initializer for log messages.
|
||||||
|
const MessageInitializer message_initializer(log_messages);
|
||||||
|
|
||||||
|
/// @brief Simple callback which unparks parked object.
|
||||||
|
///
|
||||||
|
/// @param parking_lot parking lot where the object is parked.
|
||||||
|
/// @param parked_object parked object.
|
||||||
|
void unpark(ParkingLotHandlePtr parking_lot, const std::string& parked_object) {
|
||||||
|
parking_lot->unpark(parked_object);
|
||||||
|
}
|
||||||
|
|
||||||
|
} // end of anonymous namespace
|
||||||
|
|
||||||
|
extern "C" {
|
||||||
|
|
||||||
|
/// @brief Callout scheduling object parking and providing function to unpark
|
||||||
|
/// it.
|
||||||
|
///
|
||||||
|
/// This callout is crafted to test the following scenario. The callout returns
|
||||||
|
/// status "park" to indicate that the packet should be parked. The callout
|
||||||
|
/// performs asynchronous operation and indicates that the packet should be
|
||||||
|
/// unparked when this operation completes. Unparking the packet triggers a
|
||||||
|
/// function associated with the parked packet, e.g. a function which continues
|
||||||
|
/// processing of this packet.
|
||||||
|
///
|
||||||
|
/// This test callout "parks" a string object instead of a packet. It assumes
|
||||||
|
/// that there might be multiple callouts installed on this hook point, which
|
||||||
|
/// all trigger asynchronous operation. The object must be unparked when the
|
||||||
|
/// last asynchronous operation completes. Therefore, it calls the @c reference
|
||||||
|
/// function on the parking lot object to increase the reference count. The
|
||||||
|
/// object remains parked as long as the reference counter is greater than
|
||||||
|
/// 0.
|
||||||
|
///
|
||||||
|
/// The callout returns 1 or more pointers to the functions which should be
|
||||||
|
/// called by the unit tests to simulate completion of the asynchronous tasks.
|
||||||
|
/// When the test calls those functions, @c unpark function is called, which
|
||||||
|
/// decreases reference count on the parked object, or actually unparks the
|
||||||
|
/// object when the reference count reaches 0.
|
||||||
|
///
|
||||||
|
/// @param handle Reference to callout handle used to set/get arguments.
|
||||||
|
int
|
||||||
|
hookpt_one(CalloutHandle& handle) {
|
||||||
|
// Using a string as "parked" object.
|
||||||
|
std::string parked_object;
|
||||||
|
handle.getArgument("parked_object", parked_object);
|
||||||
|
|
||||||
|
// Retrieve the parking lot handle for this hook point. It allows for
|
||||||
|
// increasing a reference count on the parked object and also for
|
||||||
|
// scheduling packet unparking.
|
||||||
|
ParkingLotHandlePtr parking_lot = handle.getParkingLotHandlePtr();
|
||||||
|
|
||||||
|
// Increase the reference count to indicate that this callout needs the
|
||||||
|
// object to remain parked until the asynchronous operation completes.
|
||||||
|
// Otherwise, other callouts could potentially call unpark and cause the
|
||||||
|
// packet processing to continue before the asynchronous operation
|
||||||
|
// completes.
|
||||||
|
parking_lot->reference(parked_object);
|
||||||
|
|
||||||
|
// Create pointer to the function that the test should call to simulate
|
||||||
|
// completion of the asynchronous operation scheduled by this callout.
|
||||||
|
std::function<void()> unpark_trigger_func =
|
||||||
|
std::bind(unpark, parking_lot, parked_object);
|
||||||
|
|
||||||
|
// Every callout (if multiple callouts installed on this hook point) should
|
||||||
|
// return the function pointer under unique name. The base name is
|
||||||
|
// "unpark_trigger" and the callouts append consecutive numbers to this
|
||||||
|
// base name, e.g. "unpark_trigger1", "unpark_trigger2" etc.
|
||||||
|
|
||||||
|
std::string fun_name;
|
||||||
|
std::vector<std::string> args = handle.getArgumentNames();
|
||||||
|
unsigned i = 1;
|
||||||
|
do {
|
||||||
|
std::ostringstream candidate_name;
|
||||||
|
candidate_name << "unpark_trigger" << i;
|
||||||
|
if (std::find(args.begin(), args.end(), candidate_name.str()) ==
|
||||||
|
args.end()) {
|
||||||
|
fun_name = candidate_name.str();
|
||||||
|
|
||||||
|
} else {
|
||||||
|
++i;
|
||||||
|
}
|
||||||
|
} while (fun_name.empty());
|
||||||
|
|
||||||
|
handle.setArgument(fun_name, unpark_trigger_func);
|
||||||
|
|
||||||
|
handle.setStatus(CalloutHandle::NEXT_STEP_PARK);
|
||||||
|
|
||||||
|
return (0);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Framework functions.
|
||||||
|
|
||||||
|
int
|
||||||
|
version() {
|
||||||
|
return (KEA_HOOKS_VERSION);
|
||||||
|
}
|
||||||
|
|
||||||
|
// load() initializes the user library if the main image was statically linked.
|
||||||
|
int
|
||||||
|
load(isc::hooks::LibraryHandle&) {
|
||||||
|
#ifdef USE_STATIC_LINK
|
||||||
|
hooksStaticLinkInit();
|
||||||
|
#endif
|
||||||
|
LOG_INFO(logger, "ACL_LOAD_START").arg("argument");
|
||||||
|
LOG_INFO(logger, "ACL_LOAD_END");
|
||||||
|
return (0);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -1,4 +1,4 @@
|
|||||||
// Copyright (C) 2013-2015 Internet Systems Consortium, Inc. ("ISC")
|
// Copyright (C) 2013-2018 Internet Systems Consortium, Inc. ("ISC")
|
||||||
//
|
//
|
||||||
// This Source Code Form is subject to the terms of the Mozilla Public
|
// This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
@ -750,6 +750,7 @@ calloutPrintSkip(CalloutHandle& handle) {
|
|||||||
static const std::string YES("Y");
|
static const std::string YES("Y");
|
||||||
static const std::string NO("N");
|
static const std::string NO("N");
|
||||||
static const std::string DROP("D");
|
static const std::string DROP("D");
|
||||||
|
static const std::string PARK("P");
|
||||||
|
|
||||||
switch (handle.getStatus()) {
|
switch (handle.getStatus()) {
|
||||||
case CalloutHandle::NEXT_STEP_CONTINUE:
|
case CalloutHandle::NEXT_STEP_CONTINUE:
|
||||||
@ -761,6 +762,9 @@ calloutPrintSkip(CalloutHandle& handle) {
|
|||||||
case CalloutHandle::NEXT_STEP_DROP:
|
case CalloutHandle::NEXT_STEP_DROP:
|
||||||
HandlesTest::common_string_ += DROP; // drop
|
HandlesTest::common_string_ += DROP; // drop
|
||||||
break;
|
break;
|
||||||
|
case CalloutHandle::NEXT_STEP_PARK:
|
||||||
|
HandlesTest::common_string_ += PARK; // park
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
return (0);
|
return (0);
|
||||||
}
|
}
|
||||||
@ -893,6 +897,11 @@ calloutSetArgumentDrop(CalloutHandle& handle) {
|
|||||||
return (calloutSetArgumentCommon(handle, "D"));
|
return (calloutSetArgumentCommon(handle, "D"));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
calloutSetArgumentPark(CalloutHandle& handle) {
|
||||||
|
return (calloutSetArgumentCommon(handle, "P"));
|
||||||
|
}
|
||||||
|
|
||||||
// ... and a callout to just copy the argument to the "common_string_" variable
|
// ... and a callout to just copy the argument to the "common_string_" variable
|
||||||
// but otherwise not alter it.
|
// but otherwise not alter it.
|
||||||
|
|
||||||
@ -920,7 +929,9 @@ TEST_F(HandlesTest, CheckModifiedArgument) {
|
|||||||
getCalloutManager()->setLibraryIndex(2);
|
getCalloutManager()->setLibraryIndex(2);
|
||||||
getCalloutManager()->registerCallout("alpha", calloutSetArgumentSkip);
|
getCalloutManager()->registerCallout("alpha", calloutSetArgumentSkip);
|
||||||
getCalloutManager()->registerCallout("alpha", calloutSetArgumentContinue);
|
getCalloutManager()->registerCallout("alpha", calloutSetArgumentContinue);
|
||||||
|
getCalloutManager()->registerCallout("alpha", calloutSetArgumentPark);
|
||||||
getCalloutManager()->registerCallout("alpha", calloutSetArgumentSkip);
|
getCalloutManager()->registerCallout("alpha", calloutSetArgumentSkip);
|
||||||
|
getCalloutManager()->registerCallout("alpha", calloutSetArgumentPark);
|
||||||
|
|
||||||
// Create the argument with an initial empty string value. Then call the
|
// Create the argument with an initial empty string value. Then call the
|
||||||
// sequence of callouts above.
|
// sequence of callouts above.
|
||||||
@ -935,7 +946,7 @@ TEST_F(HandlesTest, CheckModifiedArgument) {
|
|||||||
EXPECT_EQ(std::string("SCC" "SD"), common_string_);
|
EXPECT_EQ(std::string("SCC" "SD"), common_string_);
|
||||||
|
|
||||||
callout_handle.getArgument(MODIFIED_ARG, modified_arg);
|
callout_handle.getArgument(MODIFIED_ARG, modified_arg);
|
||||||
EXPECT_EQ(std::string("SCC" "SDDC" "SCS"), modified_arg);
|
EXPECT_EQ(std::string("SCC" "SDDC" "SCPSP"), modified_arg);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Test that the CalloutHandle provides the name of the hook to which the
|
// Test that the CalloutHandle provides the name of the hook to which the
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
// Copyright (C) 2013-2017 Internet Systems Consortium, Inc. ("ISC")
|
// Copyright (C) 2013-2018 Internet Systems Consortium, Inc. ("ISC")
|
||||||
//
|
//
|
||||||
// This Source Code Form is subject to the terms of the Mozilla Public
|
// This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
@ -755,5 +755,205 @@ TEST_F(HooksManagerTest, LibraryParameters) {
|
|||||||
EXPECT_NO_THROW(HooksManager::unloadLibraries());
|
EXPECT_NO_THROW(HooksManager::unloadLibraries());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// This test verifies that an object can be parked in two different
|
||||||
|
// callouts and that it is unparked when the last callout calls the
|
||||||
|
// unpark function.
|
||||||
|
TEST_F(HooksManagerTest, Parking) {
|
||||||
|
// Load the same library twice. Both installed callouts will trigger
|
||||||
|
// asynchronous operation.
|
||||||
|
HookLibsCollection library_names;
|
||||||
|
library_names.push_back(make_pair(std::string(ASYNC_CALLOUT_LIBRARY),
|
||||||
|
data::ConstElementPtr()));
|
||||||
|
library_names.push_back(make_pair(std::string(ASYNC_CALLOUT_LIBRARY),
|
||||||
|
data::ConstElementPtr()));
|
||||||
|
|
||||||
|
// Load the libraries.
|
||||||
|
EXPECT_TRUE(HooksManager::loadLibraries(library_names));
|
||||||
|
|
||||||
|
CalloutHandlePtr handle = HooksManager::createCalloutHandle();
|
||||||
|
|
||||||
|
// We could be parked any object. Typically it will be a pointer to the
|
||||||
|
// packet. In this case, however, it is simpler to just use a string.
|
||||||
|
std::string parked_object = "foo";
|
||||||
|
handle->setArgument("parked_object", parked_object);
|
||||||
|
|
||||||
|
// Call both installed callouts.
|
||||||
|
HooksManager::callCallouts(hookpt_one_index_, *handle);
|
||||||
|
|
||||||
|
// This boolean value will be set to true when the packet gets unparked.
|
||||||
|
bool unparked = false;
|
||||||
|
|
||||||
|
// The callouts instruct us to park the object. We associated the callback
|
||||||
|
// function with the parked object, which sets "unparked" flag to true. We
|
||||||
|
// can later test the value of this flag to verify when exactly the packet
|
||||||
|
// got unparked.
|
||||||
|
ASSERT_NO_THROW(
|
||||||
|
HooksManager::park<std::string>("hookpt_one", "foo",
|
||||||
|
[&unparked] {
|
||||||
|
unparked = true;
|
||||||
|
})
|
||||||
|
);
|
||||||
|
|
||||||
|
// We have two callouts which should have returned pointers to the
|
||||||
|
// functions which we can call to siumulate completion of asynchronous
|
||||||
|
// tasks.
|
||||||
|
std::function<void()> unpark_trigger_func1;
|
||||||
|
handle->getArgument("unpark_trigger1", unpark_trigger_func1);
|
||||||
|
// Call the first function. It should cause the hook library to call the
|
||||||
|
// "unpark" function. However, the object should not be unparked yet,
|
||||||
|
// because the other callout hasn't completed its scheduled asynchronous
|
||||||
|
// operation (keeps a reference on the parked object).
|
||||||
|
unpark_trigger_func1();
|
||||||
|
EXPECT_FALSE(unparked);
|
||||||
|
|
||||||
|
// Call the second function. This should decrease the reference count to
|
||||||
|
// 0 and the packet should be unparked.
|
||||||
|
std::function<void()> unpark_trigger_func2;
|
||||||
|
handle->getArgument("unpark_trigger2", unpark_trigger_func2);
|
||||||
|
unpark_trigger_func2();
|
||||||
|
EXPECT_TRUE(unparked);
|
||||||
|
|
||||||
|
// Try unloading the libraries.
|
||||||
|
EXPECT_NO_THROW(HooksManager::unloadLibraries());
|
||||||
|
}
|
||||||
|
|
||||||
|
// This test verifies that the server can also unpark the packet.
|
||||||
|
TEST_F(HooksManagerTest, ServerUnpark) {
|
||||||
|
// Load the same library twice. Both installed callouts will trigger
|
||||||
|
// asynchronous operation.
|
||||||
|
HookLibsCollection library_names;
|
||||||
|
library_names.push_back(make_pair(std::string(ASYNC_CALLOUT_LIBRARY),
|
||||||
|
data::ConstElementPtr()));
|
||||||
|
library_names.push_back(make_pair(std::string(ASYNC_CALLOUT_LIBRARY),
|
||||||
|
data::ConstElementPtr()));
|
||||||
|
// Load libraries.
|
||||||
|
EXPECT_TRUE(HooksManager::loadLibraries(library_names));
|
||||||
|
|
||||||
|
CalloutHandlePtr handle = HooksManager::createCalloutHandle();
|
||||||
|
|
||||||
|
// We could be parked any object. Typically it will be a pointer to the
|
||||||
|
// packet. In this case, however, it is simpler to just use a string.
|
||||||
|
std::string parked_object = "foo";
|
||||||
|
handle->setArgument("parked_object", parked_object);
|
||||||
|
|
||||||
|
// Call installed callout.
|
||||||
|
HooksManager::callCallouts(hookpt_one_index_, *handle);
|
||||||
|
|
||||||
|
// This boolean value will be set to true when the packet gets unparked.
|
||||||
|
bool unparked = false;
|
||||||
|
|
||||||
|
// It should be possible for the server to increase reference counter.
|
||||||
|
ASSERT_NO_THROW(HooksManager::reference<std::string>("hookpt_one", "foo"));
|
||||||
|
|
||||||
|
// The callouts instruct us to park the object. We associated the callback
|
||||||
|
// function with the parked object, which sets "unparked" flag to true. We
|
||||||
|
// can later test the value of this flag to verify when exactly the packet
|
||||||
|
// got unparked.
|
||||||
|
HooksManager::park<std::string>("hookpt_one", "foo",
|
||||||
|
[&unparked] {
|
||||||
|
unparked = true;
|
||||||
|
});
|
||||||
|
|
||||||
|
// Server can force unparking the object.
|
||||||
|
EXPECT_TRUE(HooksManager::unpark<std::string>("hookpt_one", "foo"));
|
||||||
|
|
||||||
|
EXPECT_TRUE(unparked);
|
||||||
|
|
||||||
|
// Try unloading the libraries.
|
||||||
|
EXPECT_NO_THROW(HooksManager::unloadLibraries());
|
||||||
|
}
|
||||||
|
|
||||||
|
// This test verifies that the server can drop parked packet.
|
||||||
|
TEST_F(HooksManagerTest, ServerDropParked) {
|
||||||
|
// Load library.
|
||||||
|
HookLibsCollection library_names;
|
||||||
|
library_names.push_back(make_pair(std::string(ASYNC_CALLOUT_LIBRARY),
|
||||||
|
data::ConstElementPtr()));
|
||||||
|
EXPECT_TRUE(HooksManager::loadLibraries(library_names));
|
||||||
|
|
||||||
|
CalloutHandlePtr handle = HooksManager::createCalloutHandle();
|
||||||
|
|
||||||
|
// We could be parked any object. Typically it will be a pointer to the
|
||||||
|
// packet. In this case, however, it is simpler to just use a string.
|
||||||
|
std::string parked_object = "foo";
|
||||||
|
handle->setArgument("parked_object", parked_object);
|
||||||
|
|
||||||
|
// Call installed callout.
|
||||||
|
HooksManager::callCallouts(hookpt_one_index_, *handle);
|
||||||
|
|
||||||
|
// This boolean value will be set to true when the packet gets unparked.
|
||||||
|
bool unparked = false;
|
||||||
|
|
||||||
|
// It should be possible for the server to increase reference counter.
|
||||||
|
ASSERT_NO_THROW(HooksManager::reference<std::string>("hookpt_one", "foo"));
|
||||||
|
|
||||||
|
// The callouts instruct us to park the object. We associated the callback
|
||||||
|
// function with the parked object, which sets "unparked" flag to true. We
|
||||||
|
// can later test the value of this flag to verify when exactly the packet
|
||||||
|
// got unparked.
|
||||||
|
HooksManager::park<std::string>("hookpt_one", "foo",
|
||||||
|
[&unparked] {
|
||||||
|
unparked = true;
|
||||||
|
});
|
||||||
|
|
||||||
|
// Drop the parked packet. The callback should not be called.
|
||||||
|
EXPECT_TRUE(HooksManager::drop<std::string>("hookpt_one", "foo"));
|
||||||
|
|
||||||
|
EXPECT_FALSE(unparked);
|
||||||
|
|
||||||
|
// An attempt to unpark the packet should return false, as this packet
|
||||||
|
// is not parked anymore.
|
||||||
|
EXPECT_FALSE(HooksManager::unpark<std::string>("hookpt_one", "foo"));
|
||||||
|
|
||||||
|
// Try unloading the libraries.
|
||||||
|
EXPECT_NO_THROW(HooksManager::unloadLibraries());
|
||||||
|
}
|
||||||
|
|
||||||
|
// This test verifies that parked objects are removed when libraries are
|
||||||
|
// unloaded.
|
||||||
|
TEST_F(HooksManagerTest, UnloadBeforeUnpark) {
|
||||||
|
// Load the same library twice. Both installed callouts will trigger
|
||||||
|
// asynchronous operation.
|
||||||
|
HookLibsCollection library_names;
|
||||||
|
library_names.push_back(make_pair(std::string(ASYNC_CALLOUT_LIBRARY),
|
||||||
|
data::ConstElementPtr()));
|
||||||
|
library_names.push_back(make_pair(std::string(ASYNC_CALLOUT_LIBRARY),
|
||||||
|
data::ConstElementPtr()));
|
||||||
|
// Load libraries.
|
||||||
|
EXPECT_TRUE(HooksManager::loadLibraries(library_names));
|
||||||
|
|
||||||
|
CalloutHandlePtr handle = HooksManager::createCalloutHandle();
|
||||||
|
|
||||||
|
// We could be parked any object. Typically it will be a pointer to the
|
||||||
|
// packet. In this case, however, it is simpler to just use a string.
|
||||||
|
std::string parked_object = "foo";
|
||||||
|
handle->setArgument("parked_object", parked_object);
|
||||||
|
|
||||||
|
// Call installed callout.
|
||||||
|
HooksManager::callCallouts(hookpt_one_index_, *handle);
|
||||||
|
|
||||||
|
// This boolean value will be set to true when the packet gets unparked.
|
||||||
|
bool unparked = false;
|
||||||
|
|
||||||
|
// The callouts instruct us to park the object. We associated the callback
|
||||||
|
// function with the parked object, which sets "unparked" flag to true. We
|
||||||
|
// can later test the value of this flag to verify when exactly the packet
|
||||||
|
// got unparked.
|
||||||
|
HooksManager::park<std::string>("hookpt_one", "foo",
|
||||||
|
[&unparked] {
|
||||||
|
unparked = true;
|
||||||
|
});
|
||||||
|
|
||||||
|
// Try reloading the libraries.
|
||||||
|
EXPECT_NO_THROW(HooksManager::unloadLibraries());
|
||||||
|
EXPECT_TRUE(HooksManager::loadLibraries(library_names));
|
||||||
|
|
||||||
|
// Parked object should have been removed.
|
||||||
|
EXPECT_FALSE(HooksManager::unpark<std::string>("hookpt_one", "foo"));
|
||||||
|
|
||||||
|
// Callback should not be called.
|
||||||
|
EXPECT_FALSE(unparked);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
} // Anonymous namespace
|
} // Anonymous namespace
|
||||||
|
112
src/lib/hooks/tests/parking_lots_unittest.cc
Normal file
112
src/lib/hooks/tests/parking_lots_unittest.cc
Normal file
@ -0,0 +1,112 @@
|
|||||||
|
// Copyright (C) 2018 Internet Systems Consortium, Inc. ("ISC")
|
||||||
|
//
|
||||||
|
// This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
|
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||||
|
|
||||||
|
#include <exceptions/exceptions.h>
|
||||||
|
#include <hooks/parking_lots.h>
|
||||||
|
#include <gtest/gtest.h>
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
using namespace isc;
|
||||||
|
using namespace isc::hooks;
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
|
||||||
|
// Test that it is possible to create and retrieve parking lots for
|
||||||
|
// specified hook points.
|
||||||
|
TEST(ParkingLotsTest, createGetParkingLot) {
|
||||||
|
ParkingLots parking_lots;
|
||||||
|
|
||||||
|
ParkingLotPtr parking_lot0 = parking_lots.getParkingLotPtr(1);
|
||||||
|
ParkingLotPtr parking_lot1 = parking_lots.getParkingLotPtr(2);
|
||||||
|
ParkingLotPtr parking_lot2 = parking_lots.getParkingLotPtr(1);
|
||||||
|
|
||||||
|
ASSERT_TRUE(parking_lot0);
|
||||||
|
ASSERT_TRUE(parking_lot1);
|
||||||
|
ASSERT_TRUE(parking_lot2);
|
||||||
|
|
||||||
|
EXPECT_FALSE(parking_lot0 == parking_lot1);
|
||||||
|
EXPECT_TRUE(parking_lot0 == parking_lot2);
|
||||||
|
|
||||||
|
ASSERT_NO_THROW(parking_lots.clear());
|
||||||
|
|
||||||
|
ParkingLotPtr parking_lot3 = parking_lots.getParkingLotPtr(1);
|
||||||
|
ASSERT_TRUE(parking_lot3);
|
||||||
|
|
||||||
|
EXPECT_FALSE(parking_lot3 == parking_lot0);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Test that object can't be parked if it hasn't been referenced on the
|
||||||
|
// parking lot.
|
||||||
|
TEST(ParkingLotTest, parkWithoutReferencing) {
|
||||||
|
ParkingLot parking_lot;
|
||||||
|
std::string parked_object = "foo";
|
||||||
|
EXPECT_THROW(parking_lot.park(parked_object, [] {
|
||||||
|
}), InvalidOperation);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Test that object can be parked and then unparked.
|
||||||
|
TEST(ParkingLotTest, unpark) {
|
||||||
|
ParkingLotPtr parking_lot = boost::make_shared<ParkingLot>();
|
||||||
|
ParkingLotHandlePtr parking_lot_handle =
|
||||||
|
boost::make_shared<ParkingLotHandle>(parking_lot);
|
||||||
|
|
||||||
|
std::string parked_object = "foo";
|
||||||
|
|
||||||
|
// Reference the parked object twice because we're going to test that
|
||||||
|
// reference counting works fine.
|
||||||
|
ASSERT_NO_THROW(parking_lot_handle->reference(parked_object));
|
||||||
|
ASSERT_NO_THROW(parking_lot_handle->reference(parked_object));
|
||||||
|
|
||||||
|
// This flag will indicate if the callback has been called.
|
||||||
|
bool unparked = false;
|
||||||
|
ASSERT_NO_THROW(parking_lot->park(parked_object, [&unparked] {
|
||||||
|
unparked = true;
|
||||||
|
}));
|
||||||
|
|
||||||
|
// Try to unpark the object. It should decrease the reference count, but not
|
||||||
|
// unpark the packet yet.
|
||||||
|
EXPECT_TRUE(parking_lot_handle->unpark(parked_object));
|
||||||
|
EXPECT_FALSE(unparked);
|
||||||
|
|
||||||
|
// Try to unpark the object. This time it should be successful, because the
|
||||||
|
// reference count goes to 0.
|
||||||
|
EXPECT_TRUE(parking_lot_handle->unpark(parked_object));
|
||||||
|
EXPECT_TRUE(unparked);
|
||||||
|
|
||||||
|
// Calling unpark again should return false to indicate that the object is
|
||||||
|
// not parked.
|
||||||
|
EXPECT_FALSE(parking_lot_handle->unpark(parked_object));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Test that parked object can be dropped.
|
||||||
|
TEST(ParkingLotTest, drop) {
|
||||||
|
ParkingLotPtr parking_lot = boost::make_shared<ParkingLot>();
|
||||||
|
ParkingLotHandlePtr parking_lot_handle =
|
||||||
|
boost::make_shared<ParkingLotHandle>(parking_lot);
|
||||||
|
|
||||||
|
std::string parked_object = "foo";
|
||||||
|
|
||||||
|
// Reference object twice to test that dropping the packet ignores
|
||||||
|
// reference counting.
|
||||||
|
ASSERT_NO_THROW(parking_lot_handle->reference(parked_object));
|
||||||
|
ASSERT_NO_THROW(parking_lot_handle->reference(parked_object));
|
||||||
|
|
||||||
|
// This flag will indicate if the callback has been called.
|
||||||
|
bool unparked = false;
|
||||||
|
ASSERT_NO_THROW(parking_lot->park(parked_object, [&unparked] {
|
||||||
|
unparked = true;
|
||||||
|
}));
|
||||||
|
|
||||||
|
// Drop parked object. The callback should not be invoked.
|
||||||
|
EXPECT_TRUE(parking_lot_handle->drop(parked_object));
|
||||||
|
EXPECT_FALSE(unparked);
|
||||||
|
|
||||||
|
// Expect that an attempt to unpark return false, as the object
|
||||||
|
// has been dropped.
|
||||||
|
EXPECT_FALSE(parking_lot_handle->unpark(parked_object));
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -1,4 +1,4 @@
|
|||||||
// Copyright (C) 2013-2017 Internet Systems Consortium, Inc. ("ISC")
|
// Copyright (C) 2013-2018 Internet Systems Consortium, Inc. ("ISC")
|
||||||
//
|
//
|
||||||
// This Source Code Form is subject to the terms of the Mozilla Public
|
// This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
@ -219,4 +219,14 @@ TEST(ServerHooksTest, HookToCommandName) {
|
|||||||
EXPECT_TRUE(ServerHooks::hookToCommandName("abc").empty());
|
EXPECT_TRUE(ServerHooks::hookToCommandName("abc").empty());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST(ServerHooksTest, getParkingLots) {
|
||||||
|
ServerHooks& hooks = ServerHooks::getServerHooks();
|
||||||
|
hooks.reset();
|
||||||
|
int alpha_hook = hooks.registerHook("alpha");
|
||||||
|
|
||||||
|
ASSERT_TRUE(hooks.getParkingLotsPtr());
|
||||||
|
ASSERT_TRUE(hooks.getParkingLotPtr(alpha_hook));
|
||||||
|
ASSERT_TRUE(hooks.getParkingLotPtr("alpha"));
|
||||||
|
}
|
||||||
|
|
||||||
} // Anonymous namespace
|
} // Anonymous namespace
|
||||||
|
@ -49,6 +49,10 @@ static const char* UNLOAD_CALLOUT_LIBRARY = "@abs_builddir@/.libs/libucl.so";
|
|||||||
// Library where parameters are checked.
|
// Library where parameters are checked.
|
||||||
static const char* CALLOUT_PARAMS_LIBRARY = "@abs_builddir@/.libs/libpcl.so";
|
static const char* CALLOUT_PARAMS_LIBRARY = "@abs_builddir@/.libs/libpcl.so";
|
||||||
|
|
||||||
|
// Library which tests objects parking.
|
||||||
|
static const char* ASYNC_CALLOUT_LIBRARY = "@abs_builddir@/.libs/libacl.so";
|
||||||
|
|
||||||
|
|
||||||
} // anonymous namespace
|
} // anonymous namespace
|
||||||
|
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user