@subsection dhcpv4HooksPkt4Send pkt4_send
- @b Arguments:
- - name: @b response4, type: isc::dhcp::Pkt4Ptr, direction: <b>in/out</b>
- name: @b query4, type: isc::dhcp::Pkt4Ptr, direction: <b>in</b>
+ - name: @b response4, type: isc::dhcp::Pkt4Ptr, direction: <b>in/out</b>
+ - name: @b subnet4, type: isc::dhcp::Subnet4Ptr, direction: <b>in</b>
- @b Description: this callout is executed when server's response
- is about to be sent back to the client. The sole argument "response4"
+ is about to be sent back to the client. The argument "response4"
contains a pointer to an isc::dhcp::Pkt4 object carrying the
packet, with source and destination addresses set, interface over which
it will be sent, and a list of all options and relay information. All fields
The argument query4 contains a pointer to the corresponding query packet
(allowing to perform correlation between response and query). This object
cannot be modified.
+ The argument subnet4 contains a pointer to the selected subnet (if one).
+ This object cannot be modified.
- <b>Next step action</b>: if any callout installed on the "pkt4_send" hook
sets the next step action to SKIP, the server will not construct the raw
return;
} else {
if (MultiThreadingMgr::instance().getMode()) {
+ query->addPktEvent("mt_queued");
typedef function<void()> CallBack;
boost::shared_ptr<CallBack> call_back =
boost::make_shared<CallBack>(std::bind(&Dhcpv4Srv::processPacketAndSendResponseNoThrow,
}
CalloutHandlePtr callout_handle = getCalloutHandle(query);
+
processPacketBufferSend(callout_handle, rsp);
}
void
Dhcpv4Srv::processPacket(Pkt4Ptr& query, Pkt4Ptr& rsp, bool allow_packet_park) {
+ query->addPktEvent("process_started");
+
// All packets belong to ALL.
query->addClass("ALL");
typedef function<void()> CallBack;
boost::shared_ptr<CallBack> call_back = boost::make_shared<CallBack>(
std::bind(&Dhcpv4Srv::sendResponseNoThrow, this, callout_handle,
- query, rsp));
+ query, rsp, ctx->subnet_));
callout_handle_state->on_completion_ = [call_back]() {
MultiThreadingMgr::instance().getThreadPool().add(call_back);
};
} else {
- processPacketPktSend(callout_handle, query, rsp);
+ processPacketPktSend(callout_handle, query, rsp, ctx->subnet_);
processPacketBufferSend(callout_handle, rsp);
}
});
// If we have a response prep it for shipment.
if (rsp) {
- processPacketPktSend(callout_handle, query, rsp);
+ Subnet4Ptr subnet = (ctx ? ctx->subnet_ : Subnet4Ptr());
+ processPacketPktSend(callout_handle, query, rsp, subnet);
}
}
void
Dhcpv4Srv::sendResponseNoThrow(hooks::CalloutHandlePtr& callout_handle,
- Pkt4Ptr& query, Pkt4Ptr& rsp) {
+ Pkt4Ptr& query, Pkt4Ptr& rsp, Subnet4Ptr& subnet) {
try {
- processPacketPktSend(callout_handle, query, rsp);
+ processPacketPktSend(callout_handle, query, rsp, subnet);
processPacketBufferSend(callout_handle, rsp);
} catch (const std::exception& e) {
LOG_ERROR(packet4_logger, DHCP4_PACKET_PROCESS_STD_EXCEPTION)
void
Dhcpv4Srv::processPacketPktSend(hooks::CalloutHandlePtr& callout_handle,
- Pkt4Ptr& query, Pkt4Ptr& rsp) {
+ Pkt4Ptr& query, Pkt4Ptr& rsp, Subnet4Ptr& subnet) {
+ query->addPktEvent("process_completed");
if (!rsp) {
return;
}
// Set our response
callout_handle->setArgument("response4", rsp);
+ // Pass in the selected subnet.
+ callout_handle->setArgument("subnet4", subnet);
+
// Call all installed callouts
HooksManager::callCallouts(Hooks.hook_index_pkt4_send_,
*callout_handle);
/// @param callout_handle pointer to the callout handle.
/// @param query A pointer to the packet to be processed.
/// @param rsp A pointer to the response.
+ /// @param subnet A pointer to selected subnet.
void sendResponseNoThrow(hooks::CalloutHandlePtr& callout_handle,
- Pkt4Ptr& query, Pkt4Ptr& rsp);
+ Pkt4Ptr& query, Pkt4Ptr& rsp, Subnet4Ptr& subnet);
/// @brief Process a single incoming DHCPv4 packet.
///
/// @param callout_handle pointer to the callout handle.
/// @param query Pointer to a query.
/// @param rsp Pointer to a response.
+ /// @param subnet A pointer to selected subnet.
void processPacketPktSend(hooks::CalloutHandlePtr& callout_handle,
- Pkt4Ptr& query, Pkt4Ptr& rsp);
+ Pkt4Ptr& query, Pkt4Ptr& rsp, Subnet4Ptr& subnet);
/// @brief Executes buffer4_send callout and sends the response.
///
using namespace isc::asiolink;
using namespace isc::data;
using namespace isc::util;
+using namespace boost::posix_time;
namespace isc {
namespace dhcp {
}
Dhcpv4SrvTest::Dhcpv4SrvTest()
- : rcode_(-1), srv_(0), multi_threading_(false) {
+ : rcode_(-1), srv_(0), multi_threading_(false), start_time_(PktEvent::now()) {
// Wipe any existing statistics
isc::stats::StatsMgr::instance().removeAll();
EXPECT_EQ(1, tested_stat->getInteger().first);
}
+void
+Dhcpv4SrvTest::checkPktEvents(const PktPtr& msg,
+ std::list<std::string> expected_events) {
+ ASSERT_NE(start_time_, PktEvent::EMPTY_TIME());
+ auto events = msg->getPktEvents();
+ ASSERT_EQ(events.size(), expected_events.size());
+ ptime prev_time = start_time_;
+ auto expected_event = expected_events.begin();
+ for (const auto& event : events) {
+ ASSERT_EQ(event.label_, *expected_event);
+ EXPECT_GE(event.timestamp_, prev_time);
+ ++expected_event;
+ }
+}
+
} // end of isc::dhcp::test namespace
} // end of isc::dhcp namespace
} // end of isc namespace
///
/// See fake_received_ field for description
void fakeReceive(const Pkt4Ptr& pkt) {
+ pkt->addPktEvent(PktEvent::SOCKET_RECEIVED);
+ pkt->addPktEvent(PktEvent::BUFFER_READ);
fake_received_.push_back(pkt);
}
multi_threading_ = enabled;
}
+ /// @brief Checks the contents of a packet's event stack agains a list
+ /// of expected events.
+ ///
+ /// @param msg pointer to the packet under test.
+ /// @param start_time system time prior to or equal to the timestamp
+ /// of the stack's first event (i.e. before packet was sent or received)
+ /// @param expected_events a list of the event labels in the order they
+ /// are expected to occur in the stack.
+ void checkPktEvents(const PktPtr& msg, std::list<std::string> expected_events);
+
/// @brief A subnet used in most tests.
Subnet4Ptr subnet_;
/// @brief The multi-threading flag.
bool multi_threading_;
+
+ /// @brief Time the test started (UTC/microseconds)
+ boost::posix_time::ptime start_time_;
};
/// @brief Patch the server config to add interface-config/re-detect=false
vector<string> expected_argument_names;
expected_argument_names.push_back(string("query4"));
expected_argument_names.push_back(string("response4"));
+ expected_argument_names.push_back(string("subnet4"));
sort(callback_argument_names_.begin(), callback_argument_names_.end());
sort(expected_argument_names.begin(), expected_argument_names.end());
EXPECT_TRUE(expected_argument_names == callback_argument_names_);
EXPECT_TRUE(callback_qry_options_copy_);
EXPECT_TRUE(callback_resp_options_copy_);
+ // Verify that packet sent to callout had the expected packet events.
+ std::list<std::string> expected_events;
+ expected_events.push_back(PktEvent::SOCKET_RECEIVED);
+ expected_events.push_back(PktEvent::BUFFER_READ);
+ expected_events.push_back("process_started");
+ expected_events.push_back("process_completed");
+ checkPktEvents(callback_qry_pkt4_, expected_events);
+
// Check if the callout handle state was reset after the callout.
checkCalloutHandleReset(discover);
}
- @b Arguments:
- name: @b query6, type: isc::dhcp::Pkt6Ptr, direction: <b>in</b>
- name: @b response6, type: isc::dhcp::Pkt6Ptr, direction: <b>in/out</b>
+ - name: @b subnet6, type: isc::dhcp::Subnet6Ptr, direction: <b>in/out</b>
- @b Description: This callout is executed when server's response
- is about to be send back to the client. The sole argument "response6"
+ is about to be send back to the client. The argument "response6"
contains a pointer to an @c isc::dhcp::Pkt6 object that contains the
packet, with set source and destination addresses, interface over which
it will be send, list of all options and relay information. All fields
noted that unless the callout sets the skip flag (see below), anything
placed in the @c buffer_out_ field will be overwritten when the callout
returns. (buffer_out_ is scratch space used for constructing the packet.)
+ The argument query6 contains a pointer to the corresponding query packet
+ (allowing to perform correlation between response and query). This object
+ cannot be modified.
+ The argument subnet6 contains a pointer to the selected subnet (if one).
+ This object cannot be modified.
+
- <b>Next step status</b>: If any callout sets the status to SKIP, the server
will assume that the callout did pack the "transaction-id", "message type"
return;
} else {
if (MultiThreadingMgr::instance().getMode()) {
+ query->addPktEvent("mt_queued");
typedef function<void()> CallBack;
boost::shared_ptr<CallBack> call_back =
boost::make_shared<CallBack>(std::bind(&Dhcpv6Srv::processPacketAndSendResponseNoThrow,
Pkt6Ptr
Dhcpv6Srv::processPacket(Pkt6Ptr query) {
+ query->addPktEvent("process_started");
+
// All packets belong to ALL.
query->addClass("ALL");
// the callback (i.e. drop) unless the callout status is set to
// NEXT_STEP_PARK. Otherwise the callback we bind here will be
// executed when the hook library unparks the packet.
+ Subnet6Ptr subnet = ctx.subnet_;
HooksManager::park("leases6_committed", query,
- [this, callout_handle, query, rsp, callout_handle_state]() mutable {
+ [this, callout_handle, query, rsp, callout_handle_state, subnet]() mutable {
if (MultiThreadingMgr::instance().getMode()) {
typedef function<void()> CallBack;
boost::shared_ptr<CallBack> call_back =
boost::make_shared<CallBack>(std::bind(&Dhcpv6Srv::sendResponseNoThrow,
- this, callout_handle, query, rsp));
+ this, callout_handle, query, rsp, subnet));
callout_handle_state->on_completion_ = [call_back]() {
MultiThreadingMgr::instance().getThreadPool().add(call_back);
};
} else {
- processPacketPktSend(callout_handle, query, rsp);
+ processPacketPktSend(callout_handle, query, rsp, subnet);
processPacketBufferSend(callout_handle, rsp);
}
});
// If we have a response prep it for shipment.
if (rsp) {
- processPacketPktSend(callout_handle, query, rsp);
+ processPacketPktSend(callout_handle, query, rsp, ctx.subnet_);
}
return (rsp);
void
Dhcpv6Srv::sendResponseNoThrow(hooks::CalloutHandlePtr& callout_handle,
- Pkt6Ptr query, Pkt6Ptr& rsp) {
+ Pkt6Ptr query, Pkt6Ptr& rsp, Subnet6Ptr& subnet) {
try {
- processPacketPktSend(callout_handle, query, rsp);
+ processPacketPktSend(callout_handle, query, rsp, subnet);
processPacketBufferSend(callout_handle, rsp);
} catch (const std::exception& e) {
LOG_ERROR(packet6_logger, DHCP6_PACKET_PROCESS_STD_EXCEPTION)
void
Dhcpv6Srv::processPacketPktSend(hooks::CalloutHandlePtr& callout_handle,
- Pkt6Ptr& query, Pkt6Ptr& rsp) {
+ Pkt6Ptr& query, Pkt6Ptr& rsp, Subnet6Ptr& subnet) {
+ query->addPktEvent("process_completed");
if (!rsp) {
return;
}
// Set our response
callout_handle->setArgument("response6", rsp);
+ // Pass the selected subnet as an argument.
+ callout_handle->setArgument("subnet6", subnet);
+
// Call all installed callouts
HooksManager::callCallouts(Hooks.hook_index_pkt6_send_, *callout_handle);
/// @param callout_handle pointer to the callout handle.
/// @param query A pointer to the packet to be processed.
/// @param rsp A pointer to the response.
+ /// @param subnet A pointer to the selected subnet.
void sendResponseNoThrow(hooks::CalloutHandlePtr& callout_handle,
- Pkt6Ptr query, Pkt6Ptr& rsp);
+ Pkt6Ptr query, Pkt6Ptr& rsp, Subnet6Ptr& subnet);
/// @brief Process a single incoming DHCPv6 packet.
///
/// @param callout_handle pointer to the callout handle.
/// @param query Pointer to a query.
/// @param rsp Pointer to a response.
+ /// @param subnet A pointer to the selected subnet.
void processPacketPktSend(hooks::CalloutHandlePtr& callout_handle,
- Pkt6Ptr& query, Pkt6Ptr& rsp);
+ Pkt6Ptr& query, Pkt6Ptr& rsp, Subnet6Ptr& subnet);
/// @brief Allocation Engine.
/// Pointer to the allocation engine that we are currently using
using namespace isc::asiolink;
using namespace isc::stats;
using namespace isc::util;
+using namespace boost::posix_time;
namespace isc {
namespace dhcp {
}
NakedDhcpv6SrvTest::NakedDhcpv6SrvTest()
- : rcode_(-1) {
+ : rcode_(-1), start_time_(PktEvent::now()) {
// it's ok if that fails. There should not be such a file anyway
static_cast<void>(remove(DUID_FILE));
return (ia);
}
+void
+NakedDhcpv6SrvTest::checkPktEvents(const PktPtr& msg,
+ std::list<std::string> expected_events) {
+ ASSERT_NE(start_time_, PktEvent::EMPTY_TIME());
+ auto events = msg->getPktEvents();
+ ASSERT_EQ(events.size(), expected_events.size());
+ ptime prev_time = start_time_;
+ auto expected_event = expected_events.begin();
+ for (const auto& event : events) {
+ ASSERT_EQ(event.label_, *expected_event);
+ EXPECT_GE(event.timestamp_, prev_time);
+ ++expected_event;
+ }
+}
+
bool
Dhcpv6SrvTest::compareOptions(const isc::dhcp::OptionPtr& option1,
const isc::dhcp::OptionPtr& option2) {
///
/// See fake_received_ field for description
void fakeReceive(const isc::dhcp::Pkt6Ptr& pkt) {
+ // Add packet events normally set by PktFilter.
+ pkt->addPktEvent(PktEvent::SOCKET_RECEIVED);
+ pkt->addPktEvent(PktEvent::BUFFER_READ);
fake_received_.push_back(pkt);
}
EXPECT_EQ(expected_transid, rsp->getTransid());
}
+ /// @brief Checks the contents of a packet's event stack agains a list
+ /// of expected events.
+ ///
+ /// @param msg pointer to the packet under test.
+ /// @param start_time system time prior to or equal to the timestamp
+ /// of the stack's first event (i.e. before packet was sent or received)
+ /// @param expected_events a list of the event labels in the order they
+ /// are expected to occur in the stack.
+ void checkPktEvents(const PktPtr& msg, std::list<std::string> expected_events);
+
virtual ~NakedDhcpv6SrvTest();
// A DUID used in most tests (typically as client-id)
// Index of a valid network interface
unsigned int valid_ifindex_;
+
+ /// @brief Time the test started (UTC/microseconds)
+ boost::posix_time::ptime start_time_;
};
// We need to pass one reference to the Dhcp6Client, which is defined in
callout_handle.getArgument("query6", callback_qry_pkt6_);
+ callout_handle.getArgument("subnet6", callback_subnet6_);
+
callback_argument_names_ = callout_handle.getArgumentNames();
if (callback_qry_pkt6_) {
vector<string> expected_argument_names;
expected_argument_names.push_back(string("query6"));
expected_argument_names.push_back(string("response6"));
+ expected_argument_names.push_back(string("subnet6"));
EXPECT_TRUE(expected_argument_names == callback_argument_names_);
// Pkt passed to a callout must be configured to copy retrieved options.
EXPECT_TRUE(callback_qry_options_copy_);
EXPECT_TRUE(callback_resp_options_copy_);
+ // Verify that packet sent to callout has the expected packet events.
+ std::list<std::string> expected_events;
+ expected_events.push_back(PktEvent::SOCKET_RECEIVED);
+ expected_events.push_back(PktEvent::BUFFER_READ);
+ expected_events.push_back("process_started");
+ expected_events.push_back("process_completed");
+ checkPktEvents(callback_qry_pkt6_, expected_events);
+
// Check if the callout handle state was reset after the callout.
checkCalloutHandleReset(sol);
}