CalloutHandlePtr callout_handle = HooksManager::createCalloutHandle();
callout_handle->setArgument("io_context", srv->getIOService());
+ // callout_handle->setArgument("network_state", srv->getNetworkState());
callout_handle->setArgument("json_config", config);
callout_handle->setArgument("server_config", CfgMgr::instance().getStagingCfg());
that are not specific to packet processing (e.g. lease expiration) will be added
to the end of this list.
+ @subsection dhcp6HooksDhcpv6SrvConfigured dhcp6_srv_configured
+ - @b Arguments:
+ - name: @b io_context, type: isc::asiolink::IOServicePtr, direction: <b>in</b>
+ - name: @b network_state, type: isc::dhcp::NetworkStatePtr, 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. The NetworkState object
+ provides access to the DHCP service state of the server and allows for
+ enabling and disabling the DHCP service from the hooks libraries.
+
+ - <b>Next step status</b>: Status codes retured by the callouts installed on
+ this hook point are ignored.
+
@subsection dhcpv6HooksBuffer6Receive buffer6_receive
- @b Arguments:
remain in the database until it expires. However, the server will send out
the response back to the client as if it did.
+@subsection dhcpv6Leases6Committed leases6_committed
+
+ - @b Arguments:
+ - name: @b query6, type: isc::dhcp::Pkt6Ptr, direction: <b>in</b>
+ - name: @b leases6, type: isc::dhcp::Leases6CollectionPtr, direction: <b>in</b>
+ - name: @b deleted_leases6, type: isc::dhcp::Leases6CollectionPtr, 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. REQUEST, RENEW, REBIND, RELEASE and
+ DECLINE. This callout is not executed for SOLICIT, CONFIRM and
+ INFORMATION REQUEST. If the callouts are executed as a result of
+ PREQUEST or RENEW 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_leases6" object will hold a previous lease instance and
+ the "leases6" 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 RELEASE or DECLINE, the callout
+ will typically receive only one lease (being released) in the
+ "deleted_leases6" 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 "leases6_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 dhcpv6HooksPkt6Send pkt6_send
- @b Arguments:
will continue processing the packet. In particular, it will send a REPLY message
as if the decline actually took place.
+% DHCP6_HOOK_LEASES6_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 leases6_committed
+hook point sets the next step to DROP.
+
+% DHCP6_HOOK_LEASES6_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 lease6_committed
+hook point sets the next step to PARK.
+
% DHCP6_HOOK_LEASE6_RELEASE_NA_SKIP %1: DHCPv6 address lease was not released because a callout set the next step to SKIP
This debug message is printed when a callout installed on the
lease6_release hook point set the next step to SKIP. For this particular hook
#include <dhcp6/dhcp6to4_ipc.h>
#include <dhcp6/dhcp6_log.h>
#include <dhcp6/dhcp6_srv.h>
-#include <dhcpsrv/callout_handle_store.h>
#include <dhcpsrv/cfg_host_operations.h>
#include <dhcpsrv/cfgmgr.h>
#include <dhcpsrv/lease_mgr.h>
/// Structure that holds registered hook indexes
struct Dhcp6Hooks {
- int hook_index_buffer6_receive_;///< index for "buffer6_receive" hook point
- int hook_index_pkt6_receive_; ///< index for "pkt6_receive" hook point
- int hook_index_subnet6_select_; ///< index for "subnet6_select" hook point
- int hook_index_lease6_release_; ///< index for "lease6_release" hook point
- int hook_index_pkt6_send_; ///< index for "pkt6_send" hook point
- int hook_index_buffer6_send_; ///< index for "buffer6_send" hook point
- int hook_index_lease6_decline_; ///< index for "lease6_decline" hook point
- int hook_index_host6_identifier_;///< index for "host6_identifier" hook point
+ int hook_index_buffer6_receive_; ///< index for "buffer6_receive" hook point
+ int hook_index_pkt6_receive_; ///< index for "pkt6_receive" hook point
+ int hook_index_subnet6_select_; ///< index for "subnet6_select" hook point
+ int hook_index_leases6_committed_;///< index for "leases6_committed" hook point
+ int hook_index_lease6_release_; ///< index for "lease6_release" hook point
+ int hook_index_pkt6_send_; ///< index for "pkt6_send" hook point
+ int hook_index_buffer6_send_; ///< index for "buffer6_send" hook point
+ int hook_index_lease6_decline_; ///< index for "lease6_decline" hook point
+ int hook_index_host6_identifier_; ///< index for "host6_identifier" hook point
/// Constructor that registers hook points for DHCPv6 engine
Dhcp6Hooks() {
- hook_index_buffer6_receive_ = HooksManager::registerHook("buffer6_receive");
- hook_index_pkt6_receive_ = HooksManager::registerHook("pkt6_receive");
- hook_index_subnet6_select_ = HooksManager::registerHook("subnet6_select");
- hook_index_lease6_release_ = HooksManager::registerHook("lease6_release");
- hook_index_pkt6_send_ = HooksManager::registerHook("pkt6_send");
- hook_index_buffer6_send_ = HooksManager::registerHook("buffer6_send");
- hook_index_lease6_decline_ = HooksManager::registerHook("lease6_decline");
- hook_index_host6_identifier_= HooksManager::registerHook("host6_identifier");
+ hook_index_buffer6_receive_ = HooksManager::registerHook("buffer6_receive");
+ hook_index_pkt6_receive_ = HooksManager::registerHook("pkt6_receive");
+ hook_index_subnet6_select_ = HooksManager::registerHook("subnet6_select");
+ hook_index_leases6_committed_ = HooksManager::registerHook("leases6_committed");
+ hook_index_lease6_release_ = HooksManager::registerHook("lease6_release");
+ hook_index_pkt6_send_ = HooksManager::registerHook("pkt6_send");
+ hook_index_buffer6_send_ = HooksManager::registerHook("buffer6_send");
+ hook_index_lease6_decline_ = HooksManager::registerHook("lease6_decline");
+ hook_index_host6_identifier_ = HooksManager::registerHook("host6_identifier");
}
};
+} // end of anonymous namespace
+
// 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.
Dhcp6Hooks Hooks;
+namespace {
+
/// @brief Creates instance of the Status Code option.
///
/// This variant of the function is used when the Status Code option
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 buffer6_send
- if (HooksManager::calloutsPresent(Hooks.hook_index_buffer6_send_)) {
- CalloutHandlePtr callout_handle = getCalloutHandle(query);
-
- // Delete previously set arguments
- callout_handle->deleteAllArguments();
-
- // Enable copying options from the packet within hook library.
- ScopedEnableOptionsCopy<Pkt6> response6_options_copy(rsp);
-
- // Pass incoming packet as argument
- callout_handle->setArgument("response6", rsp);
-
- // Call callouts
- HooksManager::callCallouts(Hooks.hook_index_buffer6_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_DHCP6_HOOKS, DHCP6_HOOK_BUFFER_SEND_SKIP)
- .arg(rsp->getLabel());
- return;
- }
-
- callout_handle->getArgument("response6", rsp);
- }
-
- LOG_DEBUG(packet6_logger, DBG_DHCP6_DETAIL_DATA, DHCP6_RESPONSE_DATA)
- .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(packet6_logger, DHCP6_PACKET_SEND_FAIL).arg(e.what());
- }
+ CalloutHandlePtr callout_handle = getCalloutHandle(query);
+ processPacketBufferSend(callout_handle, rsp);
}
void
-Dhcpv6Srv::processPacket(Pkt6Ptr& query, Pkt6Ptr& rsp) {
+Dhcpv6Srv::processPacket(Pkt6Ptr& query, Pkt6Ptr& rsp, bool allow_packet_park) {
bool skip_unpack = false;
// The packet has just been received so contains the uninterpreted wire
return;
}
+ // Assign this packet to a class, if possible
+ classifyPacket(query);
+
LOG_DEBUG(packet6_logger, DBG_DHCP6_BASIC_DATA, DHCP6_PACKET_RECEIVED)
.arg(query->getLabel())
.arg(query->getName())
callout_handle->getArgument("query6", query);
}
- // Assign this packet to a class, if possible
- classifyPacket(query);
+ AllocEngine::ClientContext6Ptr ctx;
try {
NameChangeRequestPtr ncr;
break;
case DHCPV6_REQUEST:
- rsp = processRequest(query);
+ rsp = processRequest(query, ctx);
break;
case DHCPV6_RENEW:
- rsp = processRenew(query);
+ rsp = processRenew(query, ctx);
break;
case DHCPV6_REBIND:
- rsp = processRebind(query);
+ rsp = processRebind(query, ctx);
break;
case DHCPV6_CONFIRM:
break;
case DHCPV6_RELEASE:
- rsp = processRelease(query);
+ rsp = processRelease(query, ctx);
break;
case DHCPV6_DECLINE:
- rsp = processDecline(query);
+ rsp = processDecline(query, ctx);
break;
case DHCPV6_INFORMATION_REQUEST:
rsp->setIndex(query->getIndex());
rsp->setIface(query->getIface());
+ bool packet_park = false;
+
+ if (ctx && HooksManager::calloutsPresent(Hooks.hook_index_leases6_committed_)) {
+ CalloutHandlePtr callout_handle = getCalloutHandle(query);
+
+ // Delete all previous arguments
+ callout_handle->deleteAllArguments();
+
+ // Clear skip flag if it was set in previous callouts
+ callout_handle->setStatus(CalloutHandle::NEXT_STEP_CONTINUE);
+
+ ScopedEnableOptionsCopy<Pkt6> query6_options_copy(query);
+
+ // Also pass the corresponding query packet as argument
+ callout_handle->setArgument("query6", query);
+
+ Lease6CollectionPtr new_leases(new Lease6Collection());
+ if (ctx->new_lease_) {
+ new_leases->push_back(ctx->new_lease_);
+ }
+ callout_handle->setArgument("leases6", new_leases);
+
+ Lease6CollectionPtr deleted_leases(new Lease6Collection());
+ 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_leases6", deleted_leases);
+
+ // Call all installed callouts
+ HooksManager::callCallouts(Hooks.hook_index_leases6_committed_,
+ *callout_handle);
+
+ if (callout_handle->getStatus() == CalloutHandle::NEXT_STEP_DROP) {
+ LOG_DEBUG(hooks_logger, DBG_DHCP6_HOOKS,
+ DHCP6_HOOK_LEASES6_COMMITTED_DROP)
+ .arg(query->getLabel());
+ rsp.reset();
+
+ } else if ((callout_handle->getStatus() == CalloutHandle::NEXT_STEP_PARK)
+ && allow_packet_park) {
+ packet_park = true;
+ }
+ }
+
+ if (!rsp) {
+ return;
+ }
+
+ // PARKING SPOT after leases6_committed hook point.
+ CalloutHandlePtr callout_handle = getCalloutHandle(query);
+ if (packet_park) {
+ LOG_DEBUG(hooks_logger, DBG_DHCP6_HOOKS,
+ DHCP6_HOOK_LEASES6_COMMITTED_PARK)
+ .arg(query->getLabel());
+
+ // Park the packet. The function we bind here will be executed when the hook
+ // library unparks the packet.
+ HooksManager::park("leases6_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
+Dhcpv6Srv::processPacketPktSend(hooks::CalloutHandlePtr& callout_handle,
+ Pkt6Ptr& query, Pkt6Ptr& rsp) {
+ if (!rsp) {
+ return;
+ }
+
// Specifies if server should do the packing
bool skip_pack = false;
// output wire data has not been prepared yet.
// Execute all callouts registered for packet6_send
if (HooksManager::calloutsPresent(Hooks.hook_index_pkt6_send_)) {
- CalloutHandlePtr callout_handle = getCalloutHandle(query);
// Enable copying options from the packets within hook library.
ScopedEnableOptionsCopy<Pkt6> query_resp_options_copy(query, rsp);
}
}
+void
+Dhcpv6Srv::processPacketBufferSend(CalloutHandlePtr& callout_handle,
+ Pkt6Ptr& 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 buffer6_send
+ if (HooksManager::calloutsPresent(Hooks.hook_index_buffer6_send_)) {
+
+ // Delete previously set arguments
+ callout_handle->deleteAllArguments();
+
+ // Enable copying options from the packet within hook library.
+ ScopedEnableOptionsCopy<Pkt6> response6_options_copy(rsp);
+
+ // Pass incoming packet as argument
+ callout_handle->setArgument("response6", rsp);
+
+ // Call callouts
+ HooksManager::callCallouts(Hooks.hook_index_buffer6_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_DHCP6_HOOKS,
+ DHCP6_HOOK_BUFFER_SEND_SKIP)
+ .arg(rsp->getLabel());
+ return;
+ }
+
+ callout_handle->getArgument("response6", rsp);
+ }
+
+ LOG_DEBUG(packet6_logger, DBG_DHCP6_DETAIL_DATA, DHCP6_RESPONSE_DATA)
+ .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(packet6_logger, DHCP6_PACKET_SEND_FAIL).arg(e.what());
+ }
+}
+
std::string
Dhcpv6Srv::duidToString(const OptionPtr& opt) {
stringstream tmp;
}
-
Pkt6Ptr
Dhcpv6Srv::processSolicit(const Pkt6Ptr& solicit) {
}
Pkt6Ptr
-Dhcpv6Srv::processRequest(const Pkt6Ptr& request) {
+Dhcpv6Srv::processRequest(const Pkt6Ptr& request,
+ AllocEngine::ClientContext6Ptr& context) {
sanityCheck(request, MANDATORY, MANDATORY);
// Let's create a simplified client context here.
- AllocEngine::ClientContext6 ctx;
+ context.reset(new AllocEngine::ClientContext6());
+ AllocEngine::ClientContext6& ctx = *context;
bool drop = false;
initContext(request, ctx, drop);
// Stop here if initContext decided to drop the packet.
if (drop) {
+ context.reset();
return (Pkt6Ptr());
}
}
Pkt6Ptr
-Dhcpv6Srv::processRenew(const Pkt6Ptr& renew) {
+Dhcpv6Srv::processRenew(const Pkt6Ptr& renew,
+ AllocEngine::ClientContext6Ptr& context) {
sanityCheck(renew, MANDATORY, MANDATORY);
// Let's create a simplified client context here.
- AllocEngine::ClientContext6 ctx;
+ context.reset(new AllocEngine::ClientContext6());
+ AllocEngine::ClientContext6& ctx = *context;
bool drop = false;
initContext(renew, ctx, drop);
// Stop here if initContext decided to drop the packet.
if (drop) {
+ context.reset();
return (Pkt6Ptr());
}
}
Pkt6Ptr
-Dhcpv6Srv::processRebind(const Pkt6Ptr& rebind) {
+Dhcpv6Srv::processRebind(const Pkt6Ptr& rebind,
+ AllocEngine::ClientContext6Ptr& context) {
sanityCheck(rebind, MANDATORY, FORBIDDEN);
// Let's create a simplified client context here.
- AllocEngine::ClientContext6 ctx;
+ context.reset(new AllocEngine::ClientContext6());
+ AllocEngine::ClientContext6& ctx = *context;
bool drop = false;
initContext(rebind, ctx, drop);
// Stop here if initContext decided to drop the packet.
if (drop) {
+ context.reset();
return (Pkt6Ptr());
}
}
Pkt6Ptr
-Dhcpv6Srv::processRelease(const Pkt6Ptr& release) {
+Dhcpv6Srv::processRelease(const Pkt6Ptr& release,
+ AllocEngine::ClientContext6Ptr& context) {
sanityCheck(release, MANDATORY, MANDATORY);
// Let's create a simplified client context here.
- AllocEngine::ClientContext6 ctx;
+ context.reset(new AllocEngine::ClientContext6());
+ AllocEngine::ClientContext6& ctx = *context;
bool drop = false;
initContext(release, ctx, drop);
// Stop here if initContext decided to drop the packet.
if (drop) {
+ context.reset();
return (Pkt6Ptr());
}
}
Pkt6Ptr
-Dhcpv6Srv::processDecline(const Pkt6Ptr& decline) {
+Dhcpv6Srv::processDecline(const Pkt6Ptr& decline,
+ AllocEngine::ClientContext6Ptr& context) {
// Do sanity check.
sanityCheck(decline, MANDATORY, MANDATORY);
- // Create an empty Reply message.
- Pkt6Ptr reply(new Pkt6(DHCPV6_REPLY, decline->getTransid()));
-
// Let's create a simplified client context here.
- AllocEngine::ClientContext6 ctx;
+ context.reset(new AllocEngine::ClientContext6());
+ AllocEngine::ClientContext6& ctx = *context;
bool drop = false;
initContext(decline, ctx, drop);
// Stop here if initContext decided to drop the packet.
if (drop) {
+ context.reset();
return (Pkt6Ptr());
}
setReservedClientClasses(decline, ctx);
requiredClassify(decline, ctx);
+ // Create an empty Reply message.
+ Pkt6Ptr reply(new Pkt6(DHCPV6_REPLY, decline->getTransid()));
+
// Copy client options (client-id, also relay information if present)
copyClientOptions(decline, reply);
// declineLeases returns false only if the hooks set the next step
// status to DROP. We'll just doing as requested.
+ context.reset();
return (Pkt6Ptr());
}
}
}
// Ok, all is good. Decline this lease.
- if (!declineLease(decline, lease, ia_rsp)) {
+ if (!declineLease(decline, lease, ia_rsp, ctx)) {
// declineLease returns false only when hook callouts set the next
// step status to drop. We just propagate the bad news here.
return (OptionPtr());
bool
Dhcpv6Srv::declineLease(const Pkt6Ptr& decline, const Lease6Ptr lease,
- boost::shared_ptr<Option6IA> ia_rsp) {
+ boost::shared_ptr<Option6IA> ia_rsp,
+ AllocEngine::ClientContext6& ctx) {
// We do not want to decrease the assigned-nas at this time. While
// technically a declined address is no longer allocated, the primary usage
// of the assigned-addresses statistic is to monitor pool utilization. Most
lease->decline(CfgMgr::instance().getCurrentCfg()->getDeclinePeriod());
LeaseMgrFactory::instance().updateLease6(lease);
+ ctx.new_lease_ = lease;
+
LOG_INFO(lease6_logger, DHCP6_DECLINE_LEASE).arg(decline->getLabel())
.arg(lease->addr_.toText()).arg(lease->valid_lft_);
#include <dhcp/option_definition.h>
#include <dhcp/pkt6.h>
#include <dhcpsrv/alloc_engine.h>
+#include <dhcpsrv/callout_handle_store.h>
#include <dhcpsrv/cfg_option.h>
#include <dhcpsrv/d2_client_mgr.h>
#include <dhcpsrv/network_state.h>
#include <hooks/callout_handle.h>
#include <dhcpsrv/daemon.h>
+#include <functional>
#include <iostream>
#include <queue>
///
/// @param query A pointer to the packet to be processed.
/// @param rsp A pointer to the response
- void processPacket(Pkt6Ptr& query, Pkt6Ptr& rsp);
+ /// @param allow_packet_park Indicates if parking a packet is allowed.
+ void processPacket(Pkt6Ptr& query, Pkt6Ptr& rsp,
+ bool allow_packet_park = true);
/// @brief Instructs the server to shut down.
void shutdown();
/// leases.
///
/// @param request a message received from client
+ /// @param [out] context pointer to the client context where allocated
+ /// and deleted leases are stored.
///
/// @return REPLY message or NULL
- Pkt6Ptr processRequest(const Pkt6Ptr& request);
+ Pkt6Ptr processRequest(const Pkt6Ptr& request,
+ AllocEngine::ClientContext6Ptr& context);
/// @brief Processes incoming Renew message.
///
/// @param renew message received from the client
+ /// @param [out] context pointer to the client context where allocated
+ /// and deleted leases are stored.
/// @return Reply message to be sent to the client.
- Pkt6Ptr processRenew(const Pkt6Ptr& renew);
+ Pkt6Ptr processRenew(const Pkt6Ptr& renew,
+ AllocEngine::ClientContext6Ptr& context);
/// @brief Processes incoming Rebind message.
///
/// now.
///
/// @param rebind message received from the client.
+ /// @param [out] context pointer to the client context where allocated
+ /// and deleted leases are stored.
/// @return Reply message to be sent to the client.
- Pkt6Ptr processRebind(const Pkt6Ptr& rebind);
+ Pkt6Ptr processRebind(const Pkt6Ptr& rebind,
+ AllocEngine::ClientContext6Ptr& context);
/// @brief Processes incoming Confirm message and returns Reply.
///
/// @brief Process incoming Release message.
///
/// @param release message received from client
+ /// @param [out] context pointer to the client context where released
+ /// leases are stored.
/// @return Reply message to be sent to the client.
- Pkt6Ptr processRelease(const Pkt6Ptr& release);
+ Pkt6Ptr processRelease(const Pkt6Ptr& release,
+ AllocEngine::ClientContext6Ptr& context);
/// @brief Process incoming Decline message.
///
/// options)
///
/// @param decline message received from client
- Pkt6Ptr processDecline(const Pkt6Ptr& decline);
+ /// @param [out] context pointer to the client context where declined
+ /// leases are stored.
+ Pkt6Ptr processDecline(const Pkt6Ptr& decline,
+ AllocEngine::ClientContext6Ptr& context);
/// @brief Processes incoming Information-request message.
///
/// @return the index of the buffer6_send hook
static int getHookIndexBuffer6Send();
+ /// @brief Executes buffer6_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,
+ Pkt6Ptr& rsp);
+
protected:
/// Server DUID (to be sent in server-identifier option)
/// initiate server shutdown procedure.
volatile bool shutdown_;
+ /// @brief Executes pkt6_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,
+ Pkt6Ptr& query, Pkt6Ptr& rsp);
+
/// @brief Allocation Engine.
/// Pointer to the allocation engine that we are currently using
/// It must be a pointer, because we will support changing engines
// Can't call the pkt6_send callout because we don't have the query
- // Copied from Dhcpv6Srv::run_one() sending part
-
- try {
- // Let's execute all callouts registered for buffer6_send
- if (HooksManager::calloutsPresent(Dhcpv6Srv::getHookIndexBuffer6Send())) {
- CalloutHandlePtr callout_handle = getCalloutHandle(pkt);
-
- // Delete previously set arguments
- callout_handle->deleteAllArguments();
-
- // Enable copying options from the packet within hook library.
- ScopedEnableOptionsCopy<Pkt6> response6_options_copy(pkt);
-
- // Pass incoming packet as argument
- callout_handle->setArgument("response6", pkt);
-
- // Call callouts
- HooksManager::callCallouts(Dhcpv6Srv::getHookIndexBuffer6Send(),
- *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_DHCP6_HOOKS, DHCP6_HOOK_BUFFER_SEND_SKIP)
- .arg(pkt->getLabel());
- return;
- }
-
- callout_handle->getArgument("response6", pkt);
- }
-
- LOG_DEBUG(packet6_logger, DBG_DHCP6_DETAIL_DATA, DHCP6_RESPONSE_DATA)
- .arg(static_cast<int>(pkt->getType())).arg(pkt->toText());
-
-
- // Forward packet to the client.
- IfaceMgr::instance().send(pkt);
-
- // Update statistics accordingly for sent packet.
- Dhcpv6Srv::processStatsSent(pkt);
-
- } catch (const std::exception& e) {
- LOG_ERROR(packet6_logger, DHCP6_DHCP4O6_SEND_FAIL).arg(e.what());
- }
+ ControlledDhcpv4Srv::getInstance()->
+ processPacketBufferSend(getCalloutHandle(pkt), pkt);
}
}; // namespace dhcp
# to unexpected errors. For this reason, the --enable-static-link option is
# 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
# convenience archive, resulting in shared modules
libco2_la_CPPFLAGS = $(AM_CPPFLAGS)
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 += dhcp6_unittests
dhcp6_unittests_SOURCES = dhcp6_unittests.cc
dhcp6_unittests_SOURCES += dhcp6_srv_unittest.cc
-// 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
// License, v. 2.0. If a copy of the MPL was not distributed with this
static const int LIBRARY_NUMBER = 1;
#include <config.h>
-#include "callout_library_common.h"
+#include <dhcp6/tests/callout_library_common.h>
-// 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
// License, v. 2.0. If a copy of the MPL was not distributed with this
static const int LIBRARY_NUMBER = 2;
#include <config.h>
-#include "callout_library_common.h"
+#include <dhcp6/tests/callout_library_common.h>
--- /dev/null
+// 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 dhcp6_srv_configured
+/// hook point.
+///
+static const int LIBRARY_NUMBER = 3;
+#include <dhcp6/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 dhcp6_srv_configured callout.
+///
+/// @param handle callout handle passed to the callout.
+///
+/// @return 0 on success, 1 otherwise.
+int
+dhcp6_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);
+}
+
+}
-// 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
// License, v. 2.0. If a copy of the MPL was not distributed with this
/// can determine whether the load/unload functions have been run and, if so,
/// in what order.
///
+/// The additional marker file is created for the dhcp6_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
/// by itself - it is included into each callout library which specifies the
/// missing constant LIBRARY_NUMBER before the inclusion.
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
int
version() {
return (msg);
}
+void
+Dhcp6Client::receiveResponse() {
+ context_.response_ = receiveOneMsg();
+ // If the server has responded, store the configuration received.
+ if (context_.response_) {
+ config_.clear();
+ applyRcvdConfiguration(context_.response_);
+ }
+}
+
void
Dhcp6Client::sendMsg(const Pkt6Ptr& msg) {
srv_->shutdown_ = false;
}
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 DHCPv6 server does.
+ }
}
void
/// is incomplete. Please extend it when needed.
void printConfiguration() const;
+ /// @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();
+
private:
/// @brief Applies the new leases for the client.
-// 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
// License, v. 2.0. If a copy of the MPL was not distributed with this
isc::dhcp::LeaseMgrFactory::destroy();
}
+ /// @brief Processes incoming Request message.
+ ///
+ /// @param request a message received from client
+ /// @return REPLY message or NULL
+ Pkt6Ptr processRequest(const Pkt6Ptr& request) {
+ AllocEngine::ClientContext6Ptr context(new AllocEngine::ClientContext6());
+ return (processRequest(request, context));
+ }
+
+ /// @brief Processes incoming Renew message.
+ ///
+ /// @param renew a message received from client
+ /// @return REPLY message or NULL
+ Pkt6Ptr processRenew(const Pkt6Ptr& renew) {
+ AllocEngine::ClientContext6Ptr context(new AllocEngine::ClientContext6());
+ return (processRenew(renew, context));
+ }
+
+ /// @brief Processes incoming Rebind message.
+ ///
+ /// @param rebind a message received from client
+ /// @return REPLY message or NULL
+ Pkt6Ptr processRebind(const Pkt6Ptr& rebind) {
+ AllocEngine::ClientContext6Ptr context(new AllocEngine::ClientContext6());
+ return (processRebind(rebind, context));
+ }
+
+ /// @brief Processes incoming Release message.
+ ///
+ /// @param release a message received from client
+ /// @return REPLY message or NULL
+ Pkt6Ptr processRelease(const Pkt6Ptr& release) {
+ AllocEngine::ClientContext6Ptr context(new AllocEngine::ClientContext6());
+ return (processRelease(release, context));
+ }
+
+ /// @brief Processes incoming Decline message.
+ ///
+ /// @param decline a message received from client
+ /// @return REPLY message or NULL
+ Pkt6Ptr processDecline(const Pkt6Ptr& decline) {
+ AllocEngine::ClientContext6Ptr context(new AllocEngine::ClientContext6());
+ return (processDecline(decline, context));
+ }
+
using Dhcpv6Srv::processSolicit;
using Dhcpv6Srv::processRequest;
using Dhcpv6Srv::processRenew;
-// 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
// License, v. 2.0. If a copy of the MPL was not distributed with this
#include <config.h>
#include <asiolink/io_address.h>
+#include <asiolink/io_service.h>
#include <dhcp/dhcp6.h>
#include <dhcp/duid.h>
#include <dhcp6/json_config_parser.h>
#include <dhcp6/tests/dhcp6_test_utils.h>
#include <dhcp6/tests/dhcp6_client.h>
+#include <dhcp6/ctrl_dhcp6_srv.h>
#include <dhcp/tests/iface_mgr_test_config.h>
#include <dhcp/tests/pkt_captures.h>
#include <cc/command_interpreter.h>
NakedDhcpv6Srv srv(0);
// check if appropriate hooks are registered
- int hook_index_buffer6_receive = -1;
- int hook_index_buffer6_send = -1;
- int hook_index_lease6_renew = -1;
- int hook_index_lease6_release = -1;
- int hook_index_lease6_rebind = -1;
- int hook_index_lease6_decline = -1;
- int hook_index_pkt6_received = -1;
- int hook_index_select_subnet = -1;
- int hook_index_pkt6_send = -1;
- int hook_index_host6_identifier = -1;
+ int hook_index_buffer6_receive = -1;
+ int hook_index_buffer6_send = -1;
+ int hook_index_lease6_renew = -1;
+ int hook_index_lease6_release = -1;
+ int hook_index_lease6_rebind = -1;
+ int hook_index_lease6_decline = -1;
+ int hook_index_pkt6_received = -1;
+ int hook_index_select_subnet = -1;
+ int hook_index_leases6_committed = -1;
+ int hook_index_pkt6_send = -1;
+ int hook_index_host6_identifier = -1;
// check if appropriate indexes are set
EXPECT_NO_THROW(hook_index_buffer6_receive = ServerHooks::getServerHooks()
.getIndex("pkt6_receive"));
EXPECT_NO_THROW(hook_index_select_subnet = ServerHooks::getServerHooks()
.getIndex("subnet6_select"));
- EXPECT_NO_THROW(hook_index_pkt6_send = ServerHooks::getServerHooks()
+ EXPECT_NO_THROW(hook_index_leases6_committed = ServerHooks::getServerHooks()
+ .getIndex("leases6_committed"));
+ EXPECT_NO_THROW(hook_index_pkt6_send = ServerHooks::getServerHooks()
.getIndex("pkt6_send"));
EXPECT_NO_THROW(hook_index_host6_identifier = ServerHooks::getServerHooks()
.getIndex("host6_identifier"));
- EXPECT_TRUE(hook_index_pkt6_received > 0);
- EXPECT_TRUE(hook_index_select_subnet > 0);
- EXPECT_TRUE(hook_index_pkt6_send > 0);
- EXPECT_TRUE(hook_index_buffer6_receive > 0);
- EXPECT_TRUE(hook_index_buffer6_send > 0);
- EXPECT_TRUE(hook_index_lease6_renew > 0);
- EXPECT_TRUE(hook_index_lease6_release > 0);
- EXPECT_TRUE(hook_index_lease6_rebind > 0);
- EXPECT_TRUE(hook_index_lease6_decline > 0);
- EXPECT_TRUE(hook_index_host6_identifier > 0);
+ EXPECT_TRUE(hook_index_pkt6_received > 0);
+ EXPECT_TRUE(hook_index_select_subnet > 0);
+ EXPECT_TRUE(hook_index_leases6_committed > 0);
+ EXPECT_TRUE(hook_index_pkt6_send > 0);
+ EXPECT_TRUE(hook_index_buffer6_receive > 0);
+ EXPECT_TRUE(hook_index_buffer6_send > 0);
+ EXPECT_TRUE(hook_index_lease6_renew > 0);
+ EXPECT_TRUE(hook_index_lease6_release > 0);
+ EXPECT_TRUE(hook_index_lease6_rebind > 0);
+ EXPECT_TRUE(hook_index_lease6_decline > 0);
+ EXPECT_TRUE(hook_index_host6_identifier > 0);
}
/// @brief a class dedicated to Hooks testing in DHCPv6 server
// Clear static buffers
resetCalloutBuffers();
+ io_service_ = boost::make_shared<IOService>();
+
// Reset the hook system in its original state
HooksManager::unloadLibraries();
}
return (lease6_decline_callout(callout_handle));
}
+ /// Test callback that stores values passed to leases6_committed.
+ static int
+ leases6_committed_callout(CalloutHandle& callout_handle) {
+ callback_name_ = string("leases6_committed");
+
+ callout_handle.getArgument("query6", callback_qry_pkt6_);
+
+ Lease6CollectionPtr leases6;
+ callout_handle.getArgument("leases6", leases6);
+ if (leases6->size() > 0) {
+ callback_lease6_ = leases6->at(0);
+ }
+
+ Lease6CollectionPtr deleted_leases6;
+ callout_handle.getArgument("deleted_leases6", deleted_leases6);
+ if (deleted_leases6->size() > 0) {
+ callback_deleted_lease6_ = deleted_leases6->at(0);
+ }
+
+ callback_argument_names_ = callout_handle.getArgumentNames();
+ sort(callback_argument_names_.begin(), callback_argument_names_.end());
+
+ if (callback_qry_pkt6_) {
+ callback_qry_options_copy_ = callback_qry_pkt6_->isCopyRetrievedOptions();
+ }
+
+ return (0);
+ }
+
+ static void
+ leases6_committed_unpark(ParkingLotHandlePtr parking_lot, Pkt6Ptr query) {
+ parking_lot->unpark(query);
+ }
+
+ /// Test callback which asks the server to park the packet.
+ static int
+ leases6_committed_park_callout(CalloutHandle& callout_handle) {
+ callback_name_ = string("leases6_committed");
+
+ callout_handle.getArgument("query6", callback_qry_pkt6_);
+
+ io_service_->post(boost::bind(&HooksDhcpv6SrvTest::leases6_committed_unpark,
+ callout_handle.getParkingLotHandlePtr(),
+ callback_qry_pkt6_));
+
+ callout_handle.getParkingLotHandlePtr()->reference(callback_qry_pkt6_);
+ callout_handle.setStatus(CalloutHandle::NEXT_STEP_PARK);
+
+ Lease6CollectionPtr leases6;
+ callout_handle.getArgument("leases6", leases6);
+ if (leases6->size() > 0) {
+ callback_lease6_ = leases6->at(0);
+ }
+
+ Lease6CollectionPtr deleted_leases6;
+ callout_handle.getArgument("deleted_leases6", deleted_leases6);
+ if (deleted_leases6->size() > 0) {
+ callback_deleted_lease6_ = deleted_leases6->at(0);
+ }
+
+ callback_argument_names_ = callout_handle.getArgumentNames();
+ sort(callback_argument_names_.begin(), callback_argument_names_.end());
+
+ if (callback_qry_pkt6_) {
+ callback_qry_options_copy_ = callback_qry_pkt6_->isCopyRetrievedOptions();
+ }
+
+ return (0);
+ }
+
/// @brief Test host6_identifier by setting identifier to "foo"
///
/// @param callout_handle handle passed by the hooks framework
return (0);
}
- /// @brief Test host4_identifier callout by setting identifier to hwaddr
+ /// @brief Test host6_identifier callout by setting identifier to hwaddr
///
/// This callout always returns fixed HWADDR: 00:01:02:03:04:05
///
callback_resp_pkt6_.reset();
callback_subnet6_.reset();
callback_lease6_.reset();
+ callback_deleted_lease6_.reset();
callback_ia_na_.reset();
callback_subnet6collection_ = NULL;
callback_argument_names_.clear();
/// Pointer to Dhcpv6Srv that is used in tests
boost::scoped_ptr<NakedDhcpv6Srv> srv_;
+ /// Pointer to the IO service used in the tests.
+ static IOServicePtr io_service_;
+
// The following fields are used in testing pkt6_receive_callout
/// String name of the received callout
/// Server's response Pkt6 structure returned in the callout
static Pkt6Ptr callback_resp_pkt6_;
- /// Pointer to lease6
+ /// Pointer to lease6 structure returned in the leases8_committed callout
static Lease6Ptr callback_lease6_;
+ /// Pointer to lease6 structure returned in the leases8_committed callout
+ static Lease6Ptr callback_deleted_lease6_;
+
/// Pointer to IA_NA option being renewed or rebound
static boost::shared_ptr<Option6IA> callback_ia_na_;
// The following fields are used in testing pkt6_receive_callout.
// See fields description in the class for details
+IOServicePtr HooksDhcpv4SrvTest::io_service_;
string HooksDhcpv6SrvTest::callback_name_;
Pkt6Ptr HooksDhcpv6SrvTest::callback_qry_pkt6_;
Pkt6Ptr HooksDhcpv6SrvTest::callback_resp_pkt6_;
const Subnet6Collection* HooksDhcpv6SrvTest::callback_subnet6collection_;
vector<string> HooksDhcpv6SrvTest::callback_argument_names_;
Lease6Ptr HooksDhcpv6SrvTest::callback_lease6_;
+Lease6Ptr HooksDhcpv6SrvTest::callback_deleted_lease6_;
boost::shared_ptr<Option6IA> HooksDhcpv6SrvTest::callback_ia_na_;
bool HooksDhcpv6SrvTest::callback_qry_options_copy_;
bool HooksDhcpv6SrvTest::callback_resp_options_copy_;
// Get rid of any marker files.
static_cast<void>(remove(LOAD_MARKER_FILE));
static_cast<void>(remove(UNLOAD_MARKER_FILE));
+ static_cast<void>(remove(SRV_CONFIG_MARKER_FILE));
CfgMgr::instance().clear();
}
};
+
// Checks if callouts installed on buffer6_receive are indeed called and the
// all necessary parameters are passed.
//
-// 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
// License, v. 2.0. If a copy of the MPL was not distributed with this
namespace {
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 SRV_CONFIG_MARKER_FILE = "@abs_builddir@/srv_config_marker_file.txt";
}
namespace isc {
// operation.
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_3 = "@abs_builddir@/.libs/libco3.so";
// Name of a library which is not present.
const char* const NOT_PRESENT_LIBRARY = "@abs_builddir@/.libs/libnothere.so";