// Also pass the corresponding query packet as argument
callout_handle->setArgument("query6", query);
+ // Pass the response packet as an argument.
+ ScopedEnableOptionsCopy<Pkt6> rsp6_options_copy(rsp);
+ callout_handle->setArgument("response6", rsp);
+
Lease6CollectionPtr new_leases(new Lease6Collection());
if (!ctx.new_leases_.empty()) {
// Filter out reused leases as they were not committed.
// Check if all expected parameters were really received
vector<string> expected_argument_names;
expected_argument_names.push_back("query6");
+ expected_argument_names.push_back("response6");
expected_argument_names.push_back("deleted_leases6");
expected_argument_names.push_back("leases6");
// Check if all expected parameters were really received
vector<string> expected_argument_names;
expected_argument_names.push_back("query6");
+ expected_argument_names.push_back("response6");
expected_argument_names.push_back("deleted_leases6");
expected_argument_names.push_back("leases6");
// Check if all expected parameters were really received
vector<string> expected_argument_names;
expected_argument_names.push_back("query6");
+ expected_argument_names.push_back("response6");
expected_argument_names.push_back("deleted_leases6");
expected_argument_names.push_back("leases6");
// Check if all expected parameters were really received
vector<string> expected_argument_names;
expected_argument_names.push_back("query6");
+ expected_argument_names.push_back("response6");
expected_argument_names.push_back("deleted_leases6");
expected_argument_names.push_back("leases6");
// Check if all expected parameters were really received
vector<string> expected_argument_names;
expected_argument_names.push_back("query6");
+ expected_argument_names.push_back("response6");
expected_argument_names.push_back("deleted_leases6");
expected_argument_names.push_back("leases6");
// Check if all expected parameters were really received
vector<string> expected_argument_names;
expected_argument_names.push_back("query6");
+ expected_argument_names.push_back("response6");
expected_argument_names.push_back("deleted_leases6");
expected_argument_names.push_back("leases6");
// Check if all expected parameters were really received
vector<string> expected_argument_names;
expected_argument_names.push_back("query6");
+ expected_argument_names.push_back("response6");
expected_argument_names.push_back("deleted_leases6");
expected_argument_names.push_back("leases6");
// Check if all expected parameters were really received
vector<string> expected_argument_names;
expected_argument_names.push_back("query6");
+ expected_argument_names.push_back("response6");
expected_argument_names.push_back("deleted_leases6");
expected_argument_names.push_back("leases6");
// Check if all expected parameters were really received
vector<string> expected_argument_names;
expected_argument_names.push_back("query6");
+ expected_argument_names.push_back("response6");
expected_argument_names.push_back("deleted_leases6");
expected_argument_names.push_back("leases6");
// Check if all expected parameters were really received
vector<string> expected_argument_names;
expected_argument_names.push_back("query6");
+ expected_argument_names.push_back("response6");
expected_argument_names.push_back("deleted_leases6");
expected_argument_names.push_back("leases6");
// Check if all expected parameters were really received
vector<string> expected_argument_names;
expected_argument_names.push_back("query6");
+ expected_argument_names.push_back("response6");
expected_argument_names.push_back("deleted_leases6");
expected_argument_names.push_back("leases6");
// Check if all expected parameters were really received
vector<string> expected_argument_names;
expected_argument_names.push_back("query6");
+ expected_argument_names.push_back("response6");
expected_argument_names.push_back("deleted_leases6");
expected_argument_names.push_back("leases6");
// Check if all expected parameters were really received
vector<string> expected_argument_names;
expected_argument_names.push_back("query6");
+ expected_argument_names.push_back("response6");
expected_argument_names.push_back("deleted_leases6");
expected_argument_names.push_back("leases6");
// Check if all expected parameters were really received
vector<string> expected_argument_names;
expected_argument_names.push_back("query6");
+ expected_argument_names.push_back("response6");
expected_argument_names.push_back("deleted_leases6");
expected_argument_names.push_back("leases6");
// Check if all expected parameters were really received
vector<string> expected_argument_names;
expected_argument_names.push_back("query6");
+ expected_argument_names.push_back("response6");
expected_argument_names.push_back("deleted_leases6");
expected_argument_names.push_back("leases6");
// Check if all expected parameters were really received
vector<string> expected_argument_names;
expected_argument_names.push_back("query6");
+ expected_argument_names.push_back("response6");
expected_argument_names.push_back("deleted_leases6");
expected_argument_names.push_back("leases6");
// Check if all expected parameters were really received
vector<string> expected_argument_names;
expected_argument_names.push_back("query6");
+ expected_argument_names.push_back("response6");
expected_argument_names.push_back("deleted_leases6");
expected_argument_names.push_back("leases6");
/// @param handle Callout context - which is expected to contain the query4,
/// response4, and leases4 arguments.
/// @param mgr Pointer to the BindingVariableMgr singleton.
- /// @throw Unexpected if there is no active lease or a processing error
- /// occurs. LeaseCmdsConflict if the update fails because the lease
- /// no longer exists in the back end.
+ /// @throw Unexpected if a processing error occurs. LeaseCmdsConflict if the
+ /// update fails because the lease no longer exists in the back end.
static void leases4Committed(CalloutHandle& callout_handle,
BindingVariableMgrPtr mgr);
+
+ /// @brief leases6_committed hookpoint handler.
+ ///
+ /// Evaluates the binding variables (if any), and updates the leases'
+ /// user-context accordingly. This includes updating the leases in the lease
+ /// back end.
+ ///
+ /// @param handle Callout context - which is expected to contain the query6,
+ /// response6, and leases6 arguments.
+ /// @param mgr Pointer to the BindingVariableMgr singleton.
+ /// @throw Unexpected if there a processing error occurs. LeaseCmdsConflict
+ /// if the update fails because the lease no longer exists in the back end.
+ static void leases6Committed(CalloutHandle& callout_handle,
+ BindingVariableMgrPtr mgr);
};
void
}
}
+void
+LeaseCmdsImpl::leases6Committed(CalloutHandle& callout_handle,
+ BindingVariableMgrPtr mgr) {
+ Pkt6Ptr query;
+ Pkt6Ptr response;
+ Lease6CollectionPtr leases;
+
+ // Get the necessary arguments.
+ callout_handle.getArgument("query6", query);
+ callout_handle.getArgument("response6", response);
+ callout_handle.getArgument("leases6", leases);
+
+ // In some cases we may have no active leases or no response.
+ if (leases->empty() || !response) {
+ return;
+ }
+
+ for (auto lease : *leases) {
+ try {
+ /// @todo - Users might want to only update NA or PD leases.
+ /// This could be done via adding a lease type to the variable.
+ /// V4 would not use it, for V6 it would restrict a variable
+ /// to only that type of lease. If unspecified (default) do
+ /// both NA and PD leases.
+ // Only update a lease if its active.
+ if (lease->valid_lft_) {
+ if (mgr->evaluateVariables(query, response, lease)) {
+ LeaseMgrFactory::instance().updateLease6(lease);
+ }
+ }
+ /// @todo - for now if any leases fail we stop. This could lead
+ /// to inconsistencies in user-context content for leases belonging
+ /// the the same response. The lease6BulkApplyHandler() accumlates
+ /// failures but iterates over all the leases..
+ } catch (const NoSuchLease&) {
+ isc_throw(LeaseCmdsConflict, "failed to update"
+ " the lease with address " << lease->addr_ <<
+ " either because the lease has been"
+ " deleted or it has changed in the database");
+ } catch (const std::exception& ex) {
+ isc_throw(Unexpected, "evaluating binding variables failed for: "
+ << query->getLabel() << ", :" << ex.what());
+ }
+ }
+}
+
int
LeaseCmds::leaseAddHandler(CalloutHandle& handle) {
return (impl_->leaseAddHandler(handle));
impl_->leases4Committed(callout_handle, mgr);
}
+void
+LeaseCmds::leases6Committed(CalloutHandle& callout_handle,
+ BindingVariableMgrPtr mgr) {
+ impl_->leases6Committed(callout_handle, mgr);
+}
+
} // end of namespace lease_cmds
} // end of namespace isc
/// @brief leases4_committed hookpoint handler.
///
- /// Evaluates the binding variables (if any), and updates the given lease's
+ /// Evaluates the binding variables (if any), and updates the active lease's
/// user-context accordingly. This includes updating the lease in the lease
/// back end.
///
leases4Committed(hooks::CalloutHandle& callout_handle,
BindingVariableMgrPtr mgr);
+ /// @brief leases6_committed hookpoint handler.
+ ///
+ /// Evaluates the binding variables (if any), and updates the active leases'
+ /// user-context accordingly. This includes updating the leases in the lease
+ /// back end.
+ ///
+ /// @param handle Callout context - which is expected to contain the query6, response6,
+ /// and leases6 arguments.
+ /// @param mgr Pointer to the BindingVariableMgr singleton.
+ void
+ leases6Committed(hooks::CalloutHandle& callout_handle,
+ BindingVariableMgrPtr mgr);
private:
/// Pointer to the actual implementation
boost::shared_ptr<LeaseCmdsImpl> impl_;
return (0);
}
+/// @brief leases6_committed callout implementation.
+///
+/// @param handle callout handle.
+int leases6_committed(CalloutHandle& handle) {
+ CalloutHandle::CalloutNextStep status = handle.getStatus();
+ if (status == CalloutHandle::NEXT_STEP_DROP ||
+ status == CalloutHandle::NEXT_STEP_SKIP) {
+ return (0);
+ }
+
+ try {
+ LeaseCmds lease_cmds;
+ lease_cmds.leases6Committed(handle, binding_var_mgr);
+ } catch (const std::exception& ex) {
+ LOG_ERROR(lease_cmds_logger, LEASE_CMDS_LEASES6_COMMITTED_FAILED)
+ .arg(ex.what());
+ return (1);
+ }
+
+ return (0);
+}
+
+
} // end extern "C"
extern const isc::log::MessageID LEASE_CMDS_INIT_OK = "LEASE_CMDS_INIT_OK";
extern const isc::log::MessageID LEASE_CMDS_LEASE4_OFFER_FAILED = "LEASE_CMDS_LEASE4_OFFER_FAILED";
extern const isc::log::MessageID LEASE_CMDS_LEASES4_COMMITTED_FAILED = "LEASE_CMDS_LEASES4_COMMITTED_FAILED";
+extern const isc::log::MessageID LEASE_CMDS_LEASES6_COMMITTED_FAILED = "LEASE_CMDS_LEASES6_COMMITTED_FAILED";
extern const isc::log::MessageID LEASE_CMDS_RESEND_DDNS4 = "LEASE_CMDS_RESEND_DDNS4";
extern const isc::log::MessageID LEASE_CMDS_RESEND_DDNS4_FAILED = "LEASE_CMDS_RESEND_DDNS4_FAILED";
extern const isc::log::MessageID LEASE_CMDS_RESEND_DDNS6 = "LEASE_CMDS_RESEND_DDNS6";
"LEASE_CMDS_GET4_FAILED", "lease4-get command failed (parameters: %1, reason: %2)",
"LEASE_CMDS_GET6_FAILED", "lease6-get command failed (parameters: %1, reason: %2)",
"LEASE_CMDS_INIT_OK", "loading Lease Commands hooks library successful",
- "LEASE_CMDS_LEASE4_OFFER_FAILED", "processing error occurred evaluating binding variables for %1, %2",
- "LEASE_CMDS_LEASES4_COMMITTED_FAILED", "processing error occurred evaluating binding variables for %1, %2",
+ "LEASE_CMDS_LEASE4_OFFER_FAILED", "processing error occurred evaluating binding variables:%1",
+ "LEASE_CMDS_LEASES4_COMMITTED_FAILED", "processing error occurred evaluating binding variables: %1",
+ "LEASE_CMDS_LEASES6_COMMITTED_FAILED", "processing error occurred evaluating binding variables: %1",
"LEASE_CMDS_RESEND_DDNS4", "lease4-resend-ddns command successful: %1",
"LEASE_CMDS_RESEND_DDNS4_FAILED", "lease4-resend-ddns command failed: %1",
"LEASE_CMDS_RESEND_DDNS6", "lease6-resend-ddns command successful: %1",
extern const isc::log::MessageID LEASE_CMDS_INIT_OK;
extern const isc::log::MessageID LEASE_CMDS_LEASE4_OFFER_FAILED;
extern const isc::log::MessageID LEASE_CMDS_LEASES4_COMMITTED_FAILED;
+extern const isc::log::MessageID LEASE_CMDS_LEASES6_COMMITTED_FAILED;
extern const isc::log::MessageID LEASE_CMDS_RESEND_DDNS4;
extern const isc::log::MessageID LEASE_CMDS_RESEND_DDNS4_FAILED;
extern const isc::log::MessageID LEASE_CMDS_RESEND_DDNS6;
The lease6-wipe command has failed. Both the reason as well as the
parameters passed are logged.
-% LEASE_CMDS_LEASE4_OFFER_FAILED processing error occurred evaluating binding variables for %1, %2
+% LEASE_CMDS_LEASE4_OFFER_FAILED processing error occurred evaluating binding variables:%1
This debug log is emitted when an error occurs in the lease4_offer
-handler is invoked. The arguments detail the query and the error. This
-error means binding-variable values were not added/updated for the lease.
+handler is invoked. The argument provides an explanation.
-% LEASE_CMDS_LEASES4_COMMITTED_FAILED processing error occurred evaluating binding variables for %1, %2
+% LEASE_CMDS_LEASES4_COMMITTED_FAILED processing error occurred evaluating binding variables: %1
This debug log is emitted when an error occurs in the leases4_committed
-handler is invoked. The arguments detail the query and the error. This
-error means binding-variable values were not added/updated for the lease.
+handler is invoked. The argument provides an explanation.
+
+% LEASE_CMDS_LEASES6_COMMITTED_FAILED processing error occurred evaluating binding variables: %1
+This debug log is emitted when an error occurs in the leases6_committed
+handler is invoked. The argument provides an explanation.
ASSERT_NO_THROW_LOG(result = HooksManager::calloutsPresent(hook_index_lease4_offer_));
EXPECT_EQ(result, (isc::dhcp::CfgMgr::instance().getFamily() == AF_INET));
- /// @todo when v6 is ready, change to always expect true.
ASSERT_NO_THROW_LOG(result = HooksManager::calloutsPresent(hook_index_leasesX_committed_));
- EXPECT_EQ(result, (isc::dhcp::CfgMgr::instance().getFamily() == AF_INET));
+ EXPECT_EQ(result, true);
}
/// @brief Creates a set of configuration parameters valid for the library.
testCommand(txt, CONTROL_RESULT_ERROR, exp_rsp);
}
-#define SCOPED_LINE(line) \
- std::stringstream ss; \
- ss << "Scenario at line: " << line; \
- SCOPED_TRACE(ss.str());
-
void
Lease4CmdsTest::testValidLease4Offer() {
// Initialize lease manager (false = v4, true = add leases)
#include <dhcpsrv/resource_handler.h>
#include <cc/command_interpreter.h>
#include <cc/data.h>
+#include <lease_cmds.h>
#include <lease_cmds_unittest.h>
#include <stats/stats_mgr.h>
#include <testutils/user_context_utils.h>
#include <testutils/multi_threading_utils.h>
+#include <testutils/gtest_utils.h>
#include <gtest/gtest.h>
using namespace isc::asiolink;
using namespace isc::stats;
using namespace isc::test;
+using namespace isc::lease_cmds;
namespace {
/// @brief Check that lease6-write works as expected.
void testLease6Write();
+
+ /// @brief Check that leases6_committed handler works as expected with
+ /// valid inputs.
+ void testValidLeases6Committed();
+
+ /// @brief Check that leases6_committed handler does not throw or alter
+ /// leases under NOP conditions:
+ /// 1. There is no repsonse packet
+ /// 2. There are no leases
+ /// 3. There are leases but none active
+ void testNopLeases6Committed();
};
void Lease6CmdsTest::testLease6AddMissingParams() {
testCommand(txt, CONTROL_RESULT_ERROR, exp_rsp);
}
+void
+Lease6CmdsTest::testValidLeases6Committed() {
+ // Initialize lease manager (false = v4, true = add leases)
+ initLeaseMgr(true, true);
+
+ struct Scenario {
+ uint32_t line_;
+ std::string config_;
+ std::string orig_context_;
+ std::string exp_context_;
+ };
+
+ std::list<Scenario> scenarios = {
+ {
+ // No variables configured, nothing in lease context.
+ __LINE__,
+ R"({})",
+ R"({})",
+ R"({})"
+ },
+ {
+ // lease context has no binding-variables, two configured
+ __LINE__,
+ R"^({"binding-variables":[
+ {
+ "name": "duid",
+ "expression": "hexstring(option[1].hex,':')",
+ "source": "query"
+ },
+ {
+ "name": "sub-id",
+ "expression": "hexstring(option[38].hex, ':')",
+ "source": "response"
+ }]})^",
+ R"({})",
+ R"({"ISC":{
+ "binding-variables":{
+ "duid": "01:02:03:04",
+ "sub-id": "05:06:07:08"
+ }
+ }})",
+ },
+ {
+ // lease context has binding-variables, none configured
+ // Current logic leaves lease untouched.
+ __LINE__,
+ R"({})",
+ R"({"ISC":{
+ "binding-variables":{
+ "duid": "01:02:03:04",
+ "sub-id": "05:06:07:08"
+ }
+ }})",
+ R"({"ISC":{
+ "binding-variables":{
+ "duid": "01:02:03:04",
+ "sub-id": "05:06:07:08"
+ }
+ }})",
+ },
+ {
+ // Evaluated variable value is an empty string.
+ __LINE__,
+ R"^({"binding-variables":[
+ {
+ "name": "my-var",
+ "expression": "''",
+ "source": "query"
+ }]})^",
+ R"({"ISC":{
+ "binding-variables":{
+ "my-var": "pre-existing value"
+ }
+ }})",
+ R"({"ISC":{
+ "binding-variables":{
+ "my-var": ""
+ }
+ }})",
+ }
+ };
+
+ // Create packet pair and lease.
+ Pkt6Ptr query(new Pkt6(DHCPV6_REQUEST, 1234));
+ // Add the client id.
+ OptionPtr client_id(new Option(Option::V6, D6O_CLIENTID,
+ { 0x01, 0x02, 0x03, 0x04 }));
+ query->addOption(client_id);
+
+ Pkt6Ptr response(new Pkt6(DHCPV6_REPLY, 7890));
+ OptionPtr subscriber_id(new Option(Option::V6, D6O_SUBSCRIBER_ID,
+ { 0x05, 0x06, 0x07, 0x08 }));
+ response->addOption(subscriber_id);
+
+ IOAddress na_addr("2001:db8:1::1");
+
+ // Iterater over scenarios.
+ for (auto const& scenario : scenarios) {
+ SCOPED_LINE(scenario.line_);
+
+ // Create and configure the manager.
+ BindingVariableMgrPtr mgr;
+ ASSERT_NO_THROW_LOG(mgr.reset(new BindingVariableMgr(AF_INET6)));
+ ConstElementPtr config;
+ ASSERT_NO_THROW_LOG(config = Element::fromJSON(scenario.config_));
+ ASSERT_NO_THROW_LOG(mgr->configure(config));
+
+ // Fetch the lease and set its user-context to the original content.
+ Lease6Ptr orig_lease = lmptr_->getLease6(Lease::TYPE_NA, na_addr);
+ ASSERT_TRUE(orig_lease);
+ ASSERT_TRUE(orig_lease->valid_lft_ > 0);
+
+ ConstElementPtr orig_context;
+ ASSERT_NO_THROW_LOG(orig_context = Element::fromJSON(scenario.orig_context_));
+ orig_lease->setContext(orig_context);
+ ASSERT_NO_THROW_LOG(lmptr_->updateLease6(orig_lease));
+
+ Lease6CollectionPtr leases(new Lease6Collection());
+ leases->push_back(orig_lease);
+
+ // Create a callout handle and add the expected arguments.
+ CalloutHandlePtr callout_handle = HooksManager::createCalloutHandle();
+ callout_handle->setArgument("query6", query);
+ callout_handle->setArgument("response6", response);
+ callout_handle->setArgument("leases6", leases);
+
+ // Invoke the leases4Committed handler.
+ LeaseCmds cmds;
+ ASSERT_NO_THROW_LOG(cmds.leases6Committed(*callout_handle, mgr));
+
+ // Fetch the lease.
+ Lease6Ptr after_lease = lmptr_->getLease6(Lease::TYPE_NA, na_addr);
+ ASSERT_TRUE(after_lease);
+
+ // Context contents should match the expected context content.
+ ConstElementPtr exp_context;
+ ASSERT_NO_THROW_LOG(exp_context = Element::fromJSON(scenario.exp_context_));
+ ASSERT_EQ(*(after_lease->getContext()), *exp_context);
+ }
+}
+
+void
+Lease6CmdsTest::testNopLeases6Committed() {
+ // Initialize lease manager (false = v4, true = add leases)
+ initLeaseMgr(true, true);
+
+ struct Scenario {
+ uint32_t line_;
+ DHCPv6MessageType response_type_;
+ bool send_lease_;
+ uint32_t valid_lft_;
+ };
+
+ // Configure a single variable.
+ std::string config =
+ R"^({"binding-variables":[
+ {
+ "name": "duid",
+ "expression": "hexstring(option[1].hex,':')",
+ "source": "query"
+ }]})^";
+
+ // Create and configure the manager.
+ BindingVariableMgrPtr mgr;
+ ASSERT_NO_THROW_LOG(mgr.reset(new BindingVariableMgr(AF_INET6)));
+ ASSERT_NO_THROW_LOG(mgr->configure(Element::fromJSON(config)));
+
+ // Scenarios should all result in no change to the lease.
+ std::list<Scenario> scenarios = {
+ {
+ // No leases.
+ __LINE__,
+ DHCPV6_REPLY,
+ false,
+ 0
+ },
+ {
+ // No active leases.
+ __LINE__,
+ DHCPV6_REPLY,
+ true,
+ 0
+ },
+ {
+ // No response.
+ __LINE__,
+ DHCPV6_NOTYPE,
+ true,
+ 1000
+ }};
+
+ // Create query packet.
+ Pkt6Ptr query(new Pkt6(DHCPV6_REQUEST, 1234));
+ OptionPtr client_id(new Option(Option::V6, D6O_CLIENTID,
+ { 0x01, 0x02, 0x03, 0x04 }));
+ query->addOption(client_id);
+
+ IOAddress na_addr("2001:db8:1::1");
+ for (auto const& scenario : scenarios) {
+ SCOPED_LINE(scenario.line_);
+
+ Pkt6Ptr response;
+ if (scenario.response_type_ != DHCPV6_NOTYPE) {
+ response.reset(new Pkt6(scenario.response_type_, 1234));
+ }
+
+
+ // Fetch the lease and verify there is no context content.
+ Lease6Ptr orig_lease = lmptr_->getLease6(Lease::TYPE_NA, na_addr);
+ ASSERT_TRUE(orig_lease);
+ ASSERT_FALSE(orig_lease->getContext());
+ orig_lease->valid_lft_ = scenario.valid_lft_;
+
+ Lease6CollectionPtr leases(new Lease6Collection());
+ if (scenario.send_lease_) {
+ leases->push_back(orig_lease);
+ }
+
+ // Create a callout handle and add the expected arguments.
+ CalloutHandlePtr callout_handle = HooksManager::createCalloutHandle();
+ callout_handle->setArgument("query6", query);
+ callout_handle->setArgument("response6", response);
+ callout_handle->setArgument("leases6", leases);
+
+ // Invoke the leases4Committed handler.
+ LeaseCmds cmds;
+ ASSERT_NO_THROW_LOG(cmds.leases6Committed(*callout_handle, mgr));
+
+ // Fetch the lease. Context should still be empty.
+ Lease6Ptr after_lease = lmptr_->getLease6(Lease::TYPE_NA, na_addr);
+ ASSERT_TRUE(after_lease);
+ ASSERT_FALSE(after_lease->getContext());
+ }
+}
+
TEST_F(Lease6CmdsTest, lease6AddMissingParams) {
testLease6AddMissingParams();
}
testLease6Write();
}
+TEST_F(Lease6CmdsTest, validLeases6Committed) {
+ testValidLeases6Committed();
+}
+
+TEST_F(Lease6CmdsTest, validLeases6CommittedMultiThreading) {
+ MultiThreadingTest mt(true);
+ testValidLeases6Committed();
+}
+
+TEST_F(Lease6CmdsTest, nopLeases6Committed) {
+ testNopLeases6Committed();
+}
+
+TEST_F(Lease6CmdsTest, nopLeases6CommittedMultiThreading) {
+ MultiThreadingTest mt(true);
+ testNopLeases6Committed();
+}
+
} // end of anonymous namespace
namespace {
+#define SCOPED_LINE(line) \
+ std::stringstream ss; \
+ ss << "Scenario at line: " << line; \
+ SCOPED_TRACE(ss.str());
+
/// @brief High valid lifetime used for leases in the tests below.
constexpr uint32_t HIGH_VALID_LIFETIME = 0xFFFFFFFE;