#include <hooks/hooks_manager.h>
#include <dhcpsrv/callout_handle_store.h>
#include <stats/stats_mgr.h>
+#include <util/encode/hex.h>
#include <util/stopwatch.h>
#include <hooks/server_hooks.h>
#include <hooks/hooks_manager.h>
}
if (!ctx.fake_allocation_) {
+ // Add(update) the extended information on the lease.
+ updateLease6ExtendedInfo(expired, ctx);
+
// for REQUEST we do update the lease
LeaseMgrFactory::instance().updateLease6(expired);
}
if (!ctx.fake_allocation_) {
+ // Add(update) the extended information on the lease.
+ updateLease6ExtendedInfo(lease, ctx);
+
// That is a real (REQUEST) allocation
bool status = LeaseMgrFactory::instance().addLease(lease);
}
}
+ // @todo should we call storeLease6ExtendedInfo() here ?
+ updateLease6ExtendedInfo(lease, ctx);
+
// Now that the lease has been reclaimed, we can go ahead and update it
// in the lease database.
LeaseMgrFactory::instance().updateLease6(lease);
return;
}
- std::stringstream ss;
- ss << "{"
- << "\"relay-agent-info\": \""
- << rai->toHexString()
- << "\"}";
+ // Create a StringElement with the hex string for relay-agent-info.
+ ElementPtr relay_agent(new StringElement(rai->toHexString()));
- ConstElementPtr extended_info = Element::fromJSON(ss.str());
+ // Now we wrap the agent info in a map. This allows for future expansion.
+ ElementPtr extended_info = Element::createMap();
+ extended_info->set("relay-agent-info", relay_agent);
// Get a writable copy of the lease's current user context.
ElementPtr user_context;
lease->setContext(user_context);
}
+void
+AllocEngine::updateLease6ExtendedInfo(const Lease6Ptr& lease,
+ const AllocEngine::ClientContext6& ctx) const {
+
+ // If storage is not enabled then punt.
+ if (!ctx.subnet_->getStoreExtendedInfo()) {
+ return;
+ }
+
+ // If we do not have relay information, then punt.
+ if (ctx.query_->relay_info_.empty()) {
+ return;
+ }
+
+ // We need to convert the vector of RelayInfo instances in
+ // into an Element hierarchy like this:
+ // "relay-info": [
+ // {
+ // "hop": 123,
+ // "link": "2001:db8::1",
+ // "peer": "2001:db8::2",
+ // "options": "0x..."
+ // },..]
+ //
+ ElementPtr relay_list = Element::createList();
+ for (auto relay : ctx.query_->relay_info_) {
+ ElementPtr relay_elem = Element::createMap();
+ relay_elem->set("hop", ElementPtr(new IntElement(relay.hop_count_)));
+ relay_elem->set("link", ElementPtr(new StringElement(relay.linkaddr_.toText())));
+ relay_elem->set("peer", ElementPtr(new StringElement(relay.peeraddr_.toText())));
+
+ // If there are relay options, we'll pack them into a buffer and then
+ // convert that into a hex string. If there are no options, we omit
+ // then entry.
+ if (!relay.options_.empty()) {
+ OutputBuffer buf(128);
+ LibDHCP::packOptions6(buf, relay.options_);
+
+ if (buf.getLength() > 0) {
+ const uint8_t* cp = static_cast<const uint8_t*>(buf.getData());
+ std::vector<uint8_t>bytes;
+ std::stringstream ss;
+
+ bytes.assign(cp, cp + buf.getLength());
+ ss << "0x" << encode::encodeHex(bytes);
+ relay_elem->set("options", ElementPtr(new StringElement(ss.str())));
+ }
+ }
+
+ relay_list->add(relay_elem);
+ }
+
+ // Now we wrap the list of relays in a map. This allows for future expansion.
+ ElementPtr extended_info = Element::createMap();
+ extended_info->set("relays", relay_list);
+
+ // Get a writable copy of the lease's current user context.
+ ElementPtr user_context;
+ if (lease->getContext()) {
+ user_context = UserContext::toElement(lease->getContext());
+ } else {
+ user_context = Element::createMap();
+ }
+
+ // Add/replace the extended info entry.
+ user_context->set("ISC", extended_info);
+
+ // Update the lease's user_context.
+ lease->setContext(user_context);
+}
+
+
bool
AllocEngine::conditionalExtendLifetime(Lease& lease) const {
lease.cltt_ = time(NULL);
ClientContext4& ctx) const;
protected:
- /// @brief Stores additional client query parameters on the lease
+ /// @brief Stores additional client query parameters on a V4 lease
///
/// Extended features such as LeaseQuery require addtional parameters
/// to be stored for each lease, than we would otherwise retain.
void updateLease4ExtendedInfo(const Lease4Ptr& lease,
const ClientContext4& ctx) const;
+ /// @brief Stores additional client query parameters on a V6 lease
+ ///
+ /// Extended features such as LeaseQuery and Reconfigure require
+ /// addtional parameters to be stored for each lease, than we would
+ /// otherwise retain. This function adds that information to the
+ /// lease's user-context.
+ /// (Note it is protected to facilitate unit testing).
+ ///
+ /// @warning This method doesn't check if the pointer to the lease is
+ /// valid nor if the subnet to the pointer in the @c ctx is valid.
+ /// The caller is responsible for making sure that they are valid.
+ ///
+ /// @param [out] lease A pointer to the lease to be updated.
+ /// @param ctx A context containing information from the server about the
+ void updateLease6ExtendedInfo(const Lease6Ptr& lease,
+ const ClientContext6& ctx) const;
+
private:
/// @brief Extends the lease lifetime.
inheritance, "store-extended-info"));
}
- /// @brief Sets new ddns-override-no-update
+ /// @brief Sets new store-extended-info
///
- /// @param ddns_override_no_update New value to use.
+ /// @param store-extended-info New value to use.
void setStoreExtendedInfo(const util::Optional<bool>& store_extended_info) {
store_extended_info_ = store_extended_info;
}
}
void
-BaseNetworkParser::parseCommonTimers(const ConstElementPtr& network_data,
- NetworkPtr& network) {
+BaseNetworkParser::parseCommon(const ConstElementPtr& network_data,
+ NetworkPtr& network) {
if (network_data->contains("renew-timer")) {
network->setT1(getInteger(network_data, "renew-timer"));
}
}
network->setValid(parseLifetime(network_data, "valid-lifetime"));
+
+ if (network_data->contains("store-extended-info")) {
+ network->setStoreExtendedInfo(getBoolean(network_data,
+ "store-extended-info"));
+ }
}
void
const Triplet<uint32_t> parseLifetime(const data::ConstElementPtr& scope,
const std::string& name);
- /// @brief Parses common DHCP timers.
+ /// @brief Parses common parameters
///
/// The parsed parameters are:
/// - renew-timer,
/// - rebind-timer,
/// - valid-lifetime
+ /// - store-extended-info
///
/// @param network_data Data element holding shared network
/// configuration to be parsed.
/// @param [out] network Pointer to a network in which parsed data is
/// to be stored.
- void parseCommonTimers(const data::ConstElementPtr& network_data,
- NetworkPtr& network);
+ void parseCommon(const data::ConstElementPtr& network_data,
+ NetworkPtr& network);
/// @brief Parses parameters related to "percent" timers settngs.
///
subnet_id));
subnet_ = subnet4;
+ // Parse parameters common to all Network derivations.
NetworkPtr network = boost::dynamic_pointer_cast<Network>(subnet4);
- parseCommonTimers(params, network);
+ parseCommon(params, network);
stringstream s;
s << addr << "/" << static_cast<int>(len) << " with params: ";
subnet_id);
subnet_.reset(subnet6);
- // Parse timers that are common for v4 and v6.
+ // Parse parameters common to all Network derivations.
NetworkPtr network = boost::dynamic_pointer_cast<Network>(subnet_);
- parseCommonTimers(params, network);
+ parseCommon(params, network);
// Enable or disable Rapid Commit option support for the subnet.
if (!rapid_commit.unspecified()) {
std::string name = getString(shared_network_data, "name");
shared_network.reset(new SharedNetwork4(name));
- // Parse timers.
+ // Parse parameters common to all Network derivations.
NetworkPtr network = boost::dynamic_pointer_cast<Network>(shared_network);
- parseCommonTimers(shared_network_data, network);
+ parseCommon(shared_network_data, network);
// interface is an optional parameter
if (shared_network_data->contains("interface")) {
std::string name = getString(shared_network_data, "name");
shared_network.reset(new SharedNetwork6(name));
+ // Parse parameters common to all Network derivations.
NetworkPtr network = boost::dynamic_pointer_cast<Network>(shared_network);
- parseCommonTimers(shared_network_data, network);
+ parseCommon(shared_network_data, network);
// preferred-lifetime
shared_network->setPreferred(parseLifetime(shared_network_data,
"max-valid-lifetime",
"calculate-tee-times",
"t1-percent",
- "t2-percent"
+ "t2-percent",
+ "store-extended-info"
};
/// @brief This table defines all pool parameters.
{ "ddns-override-client-update", Element::boolean },
{ "ddns-replace-client-name", Element::string },
{ "ddns-generated-prefix", Element::string },
- { "ddns-qualifying-suffix", Element::string }
+ { "ddns-qualifying-suffix", Element::string },
+ { "store-extended-info", Element::boolean }
};
/// @brief This table defines default global values for DHCPv6
{ "ddns-replace-client-name", Element::string, "never" },
{ "ddns-generated-prefix", Element::string, "myhost" },
{ "ddns-qualifying-suffix", Element::string, "" },
- { "hostname-char-set", Element::string, "[^A-Za-z0-9.-]" },
- { "hostname-char-replacement", Element::string, "" }
+ { "hostname-char-set", Element::string, "[^A-Za-z0-9.-]" },
+ { "hostname-char-replacement", Element::string, "" },
+ { "store-extended-info", Element::boolean, "false" }
+
};
/// @brief This table defines all option definition parameters.
{ "ddns-qualifying-suffix", Element::string },
{ "hostname-char-set", Element::string },
{ "hostname-char-replacement", Element::string },
+ { "store-extended-info", Element::boolean },
{ "metadata", Element::map }
};
"max-valid-lifetime",
"calculate-tee-times",
"t1-percent",
- "t2-percent"
+ "t2-percent",
+ "store-extended-info"
};
/// @brief This table defines all pool parameters.
{ "ddns-qualifying-suffix", Element::string },
{ "hostname-char-set", Element::string },
{ "hostname-char-replacement", Element::string },
+ { "store-extended-info", Element::boolean },
{ "metadata", Element::map }
};
// Test scenarios.
std::vector<Scenario> scenarios {
- {
+ {
"no context, no rai",
"",
"",
""
- },
- {
+ },
+ {
"some original context, no rai",
"{\"foo\": 123}",
"",
"{\"foo\": 123}"
- },
- {
+ },
+ {
"no original context, rai",
"",
"0x52050104aabbccdd",
"{ \"ISC\": { \"relay-agent-info\": \"0x52050104AABBCCDD\" } }",
- },
- {
+ },
+ {
"some original context, rai",
"{\"foo\": 123}",
"0x52050104aabbccdd",
"{ \"ISC\": { \"relay-agent-info\": \"0x52050104AABBCCDD\" }, \"foo\": 123 }"
- },
- {
+ },
+ {
"original rai context, no rai",
"{ \"ISC\": { \"relay-agent-info\": \"0x52050104AABBCCDD\" } }",
"",
"{ \"ISC\": { \"relay-agent-info\": \"0x52050104AABBCCDD\" } }",
- },
- {
+ },
+ {
"original rai context, different rai",
"{ \"ISC\": { \"relay-agent-info\": \"0x52050104AABBCCDD\" } }",
"0x52050104ddeeffaa",
"{ \"ISC\": { \"relay-agent-info\": \"0x52050104DDEEFFAA\" } }",
- },
- };
+ }};
// Create the allocation engine, context and lease.
NakedAllocEngine engine(AllocEngine::ALLOC_ITERATIVE, 0, false);
// Test scenarios.
std::vector<Scenario> scenarios {
- {
- "create client one without rai",
- mac1,
- "",
- "",
- mac1_addr
- },
- {
- "renew client one without rai",
- {},
- "",
- "",
- mac1_addr
- },
- {
- "create client two with rai",
- mac2,
- "0x52050104a1b1c1d1",
- "{ \"ISC\": { \"relay-agent-info\": \"0x52050104A1B1C1D1\" } }",
- mac2_addr
- },
- {
- "renew client two without rai",
- {},
- "",
- "{ \"ISC\": { \"relay-agent-info\": \"0x52050104A1B1C1D1\" } }",
- mac2_addr
- },
- };
+ {
+ "create client one without rai",
+ mac1,
+ "",
+ "",
+ mac1_addr
+ },
+ {
+ "renew client one without rai",
+ {},
+ "",
+ "",
+ mac1_addr
+ },
+ {
+ "create client two with rai",
+ mac2,
+ "0x52050104a1b1c1d1",
+ "{ \"ISC\": { \"relay-agent-info\": \"0x52050104A1B1C1D1\" } }",
+ mac2_addr
+ },
+ {
+ "renew client two without rai",
+ {},
+ "",
+ "{ \"ISC\": { \"relay-agent-info\": \"0x52050104A1B1C1D1\" } }",
+ mac2_addr
+ }};
// Create the allocation engine, context and lease.
NakedAllocEngine engine(AllocEngine::ALLOC_ITERATIVE, 0, false);
IOAddress::IPV4_ZERO_ADDRESS(),
false, false, "", false);
- Lease4Ptr lease;
-
// Iterate over the test scenarios.
+ Lease4Ptr lease;
for (auto scenario : scenarios) {
SCOPED_TRACE(scenario.description_);
std::vector<uint8_t> mac2 = { 0, 0xfe, 0xfe, 0xfe, 0xfe, 0x02};
std::string mac2_addr = "192.0.2.101";
- // @todo set store-extended-info = false
-
// Test scenarios.
std::vector<Scenario> scenarios {
- {
- "create client one without rai",
- mac1,
- "",
- mac1_addr
- },
- {
- "renew client one without rai",
- {},
- "",
- mac1_addr
- },
- {
- "create client two with rai",
- mac2,
- "0x52050104a1b1c1d1",
- mac2_addr
- },
- {
- "renew client two with rai",
- {},
- "0x52050104a1b1c1d1",
- mac2_addr
- },
- };
+ {
+ "create client one without rai",
+ mac1,
+ "",
+ mac1_addr
+ },
+ {
+ "renew client one without rai",
+ {},
+ "",
+ mac1_addr
+ },
+ {
+ "create client two with rai",
+ mac2,
+ "0x52050104a1b1c1d1",
+ mac2_addr
+ },
+ {
+ "renew client two with rai",
+ {},
+ "0x52050104a1b1c1d1",
+ mac2_addr
+ }};
// Create the allocation engine, context and lease.
NakedAllocEngine engine(AllocEngine::ALLOC_ITERATIVE, 0, false);
#include <dhcpsrv/tests/alloc_engine_utils.h>
#include <dhcpsrv/tests/test_utils.h>
#include <stats/stats_mgr.h>
+#include <testutils/gtest_utils.h>
using namespace std;
using namespace isc::hooks;
using namespace isc::asiolink;
using namespace isc::stats;
+using namespace isc::data;
namespace isc {
namespace dhcp {
<< "Lease lifetime was not extended, but it should";
}
+/// @brief Test fixture class for testing storage of extended lease data.
+/// It primarily creates several configuration items common to the
+/// extended info tests.
+class AllocEngine6ExtendedInfoTest : public AllocEngine6Test {
+public:
+ /// @brief Constructor
+ AllocEngine6ExtendedInfoTest()
+ : engine_(AllocEngine::ALLOC_ITERATIVE, 100, true), duid1_(), duid2_(),
+ relay1_(), relay2_(), duid1_addr_("::"), duid2_addr_("::") {
+ duid1_.reset(new DUID(std::vector<uint8_t>(8, 0x84)));
+ duid2_.reset(new DUID(std::vector<uint8_t>(8, 0x74)));
+
+ relay1_.msg_type_ = DHCPV6_RELAY_FORW;
+ relay1_.hop_count_ = 33;
+ relay1_.linkaddr_ = IOAddress("2001:db8::1");
+ relay1_.peeraddr_ = IOAddress("2001:db8::2");
+ relay1_.relay_msg_len_ = 0;
+
+ uint8_t relay_opt_data[] = { 1, 2, 3, 4, 5, 6, 7, 8};
+ vector<uint8_t> relay_data(relay_opt_data,
+ relay_opt_data + sizeof(relay_opt_data));
+ OptionPtr optRelay1(new Option(Option::V6, 200, relay_data));
+
+ relay1_.options_.insert(make_pair(optRelay1->getType(), optRelay1));
+
+ relay2_.msg_type_ = DHCPV6_RELAY_FORW;
+ relay2_.hop_count_ = 77;
+ relay2_.linkaddr_ = IOAddress("2001:db8::3");
+ relay2_.peeraddr_ = IOAddress("2001:db8::4");
+ relay2_.relay_msg_len_ = 0;
+
+ duid1_addr_ = IOAddress("2001:db8:1::10");
+ duid2_addr_ = IOAddress("2001:db8:1::11");
+
+ // Create the allocation engine, context and lease.
+ NakedAllocEngine engine(AllocEngine::ALLOC_ITERATIVE, 100, true);
+ }
+
+ /// @brief Destructor
+ virtual ~AllocEngine6ExtendedInfoTest(){};
+
+ /// Configuration elements. These are initialized in the constructor
+ /// and are used throughout the tests.
+ NakedAllocEngine engine_;
+ DuidPtr duid1_;
+ DuidPtr duid2_;
+ Pkt6::RelayInfo relay1_;
+ Pkt6::RelayInfo relay2_;
+ IOAddress duid1_addr_;
+ IOAddress duid2_addr_;
+};
+
+
+// Exercises AllocEnginer6Test::updateExtendedInfo6() through various
+// permutations of client packet content.
+TEST_F(AllocEngine6ExtendedInfoTest, updateExtendedInfo6) {
+ // Structure that defines a test scenario.
+ struct Scenario {
+ std::string description_; // test description
+ std::string orig_context_json_; // user context the lease begins with
+ std::vector<Pkt6::RelayInfo> relays_; // vector of relays from pkt
+ std::string exp_context_json_; // expected user context on the lease
+ };
+
+ // Test scenarios.
+ std::vector<Scenario> scenarios {
+ {
+ "no context, no relay",
+ "",
+ {},
+ ""
+ },
+ {
+ "some original context, no relay",
+ "{\"foo\": 123}",
+ {},
+ "{\"foo\": 123}"
+ },
+ {
+ "no original context, one relay",
+ "",
+ { relay1_ },
+ "{ \"ISC\": { \"relays\": [ { \"hop\": 33, \"link\": \"2001:db8::1\","
+ " \"options\": \"0x00C800080102030405060708\", \"peer\": \"2001:db8::2\" } ] } }"
+ },
+ {
+ "some original context, one relay",
+ "{\"foo\": 123}",
+ { relay1_ },
+ "{ \"ISC\": { \"relays\": [ { \"hop\": 33, \"link\": \"2001:db8::1\","
+ " \"options\": \"0x00C800080102030405060708\", \"peer\": \"2001:db8::2\" } ] },"
+ " \"foo\": 123 }"
+ },
+ {
+ "no original context, two relays",
+ "",
+ { relay1_, relay2_ },
+ "{ \"ISC\": { \"relays\": [ { \"hop\": 33, \"link\": \"2001:db8::1\","
+ " \"options\": \"0x00C800080102030405060708\", \"peer\": \"2001:db8::2\" },"
+ " {\"hop\": 77, \"link\": \"2001:db8::3\", \"peer\": \"2001:db8::4\" } ] } }"
+ },
+ {
+ "original relay context, no relay",
+ "{ \"ISC\": { \"relays\": [ { \"hop\": 33, \"link\": \"2001:db8::1\","
+ " \"options\": \"0x00C800080102030405060708\", \"peer\": \"2001:db8::2\" } ] } }",
+ {},
+ "{ \"ISC\": { \"relays\": [ { \"hop\": 33, \"link\": \"2001:db8::1\","
+ " \"options\": \"0x00C800080102030405060708\", \"peer\": \"2001:db8::2\" } ] } }"
+ },
+ {
+ "original relay context, different relay",
+ "{ \"ISC\": { \"relays\": [ { \"hop\": 33, \"link\": \"2001:db8::1\","
+ " \"options\": \"0x00C800080102030405060708\", \"peer\": \"2001:db8::2\" } ] } }",
+ { relay2_ },
+ "{ \"ISC\": { \"relays\": [ { \"hop\": 77, \"link\": \"2001:db8::3\","
+ " \"peer\": \"2001:db8::4\" } ] } }"
+ }};
+
+ // Allocate a lease.
+ Lease6Ptr lease;
+ AllocEngine::ClientContext6 ctx(subnet_, duid_, false, false, "", false,
+ Pkt6Ptr(new Pkt6(DHCPV6_REQUEST, 1234)));
+ ASSERT_NO_THROW(lease = expectOneLease(engine_.allocateLeases6(ctx)));
+ ASSERT_TRUE(lease);
+
+ // All scenarios require storage to be enabled.
+ ctx.subnet_->setStoreExtendedInfo(true);
+
+ // Verify that the lease begins with no user context.
+ ConstElementPtr user_context = lease->getContext();
+ ASSERT_FALSE(user_context);
+
+ // Iterate over the test scenarios.
+ ElementPtr orig_context;
+ ElementPtr exp_context;
+ for (auto scenario : scenarios) {
+ SCOPED_TRACE(scenario.description_);
+
+ // Create the original user context from JSON.
+ if (scenario.orig_context_json_.empty()) {
+ orig_context.reset();
+ } else {
+ ASSERT_NO_THROW(orig_context = Element::fromJSON(scenario.orig_context_json_))
+ << "invalid orig_context_json_, test is broken";
+ }
+
+ // Create the expected user context from JSON.
+ if (scenario.exp_context_json_.empty()) {
+ exp_context.reset();
+ } else {
+ ASSERT_NO_THROW(exp_context = Element::fromJSON(scenario.exp_context_json_))
+ << "invalid exp_context_json_, test is broken";
+ }
+
+ // Initialize lease's user context.
+ lease->setContext(orig_context);
+ if (!orig_context) {
+ ASSERT_FALSE(lease->getContext());
+ }
+ else {
+ ASSERT_TRUE(lease->getContext());
+ ASSERT_TRUE(orig_context->equals(*(lease->getContext())));
+ }
+
+ // Set the client packet relay vector from the scenario.
+ ctx.query_->relay_info_ = scenario.relays_;
+
+ // Call AllocEngine::updateLease6ExtendeInfo().
+ ASSERT_NO_THROW_LOG(engine_.callUpdateLease6ExtendedInfo(lease, ctx));
+
+ // Verify the lease has the expected user context content.
+ if (!exp_context) {
+ ASSERT_FALSE(lease->getContext());
+ }
+ else {
+ ASSERT_TRUE(lease->getContext());
+ ASSERT_TRUE(exp_context->equals(*(lease->getContext())))
+ << "expected: " << *(exp_context) << std::endl
+ << " actual: " << *(lease->getContext()) << std::endl;
+ }
+ }
+}
+
+// Verifies that the extended data (RelayInfos for now) is
+// added to a V6 lease when leases are created and/or renewed,
+// when store-extended-info is true.
+TEST_F(AllocEngine6ExtendedInfoTest, storeExtendedInfoEnabled6) {
+ // Structure that defines a test scenario.
+ struct Scenario {
+ std::string description_; // test description
+ DuidPtr duid_; // client DUID
+ std::vector<Pkt6::RelayInfo> relays_; // vector of relays from pkt
+ std::string exp_context_json_; // expected user context on the lease
+ IOAddress exp_address_; // expected lease address
+ };
+
+ // Test scenarios.
+ std::vector<Scenario> scenarios {
+ {
+ "create client one without relays",
+ duid1_,
+ {},
+ "",
+ duid1_addr_
+ },
+ {
+ "renew client one without relays",
+ DuidPtr(),
+ {},
+ "",
+ duid1_addr_
+ },
+ {
+ "create client two with relays",
+ duid2_,
+ { relay1_, relay2_ },
+ "{ \"ISC\": { \"relays\": [ { \"hop\": 33, \"link\": \"2001:db8::1\","
+ " \"options\": \"0x00C800080102030405060708\", \"peer\": \"2001:db8::2\" },"
+ " { \"hop\": 77, \"link\": \"2001:db8::3\", \"peer\": \"2001:db8::4\" } ] } }",
+ duid2_addr_
+ },
+ {
+ "renew client two without rai",
+ DuidPtr(),
+ {},
+ "{ \"ISC\": { \"relays\": [ { \"hop\": 33, \"link\": \"2001:db8::1\","
+ " \"options\": \"0x00C800080102030405060708\", \"peer\": \"2001:db8::2\" },"
+ " { \"hop\": 77, \"link\": \"2001:db8::3\", \"peer\": \"2001:db8::4\" } ] } }",
+ duid2_addr_
+ }};
+
+ // All of the scenarios require storage to be enabled.
+ subnet_->setStoreExtendedInfo(true);
+
+ // Iterate over the test scenarios.
+ DuidPtr current_duid;
+ for (auto scenario : scenarios) {
+ SCOPED_TRACE(scenario.description_);
+
+ ElementPtr exp_context;
+ // Create the expected user context from JSON.
+ if (!scenario.exp_context_json_.empty()) {
+ ASSERT_NO_THROW(exp_context = Element::fromJSON(scenario.exp_context_json_))
+ << "invalid exp_context_json_, test is broken";
+ }
+
+ Pkt6Ptr pkt;
+ if (scenario.duid_) {
+ current_duid = scenario.duid_;
+ pkt.reset(new Pkt6(DHCPV6_REQUEST, 1234));
+ } else {
+ pkt.reset(new Pkt6(DHCPV6_RENEW, 1234));
+ }
+
+ // Set packet relay vector from the scenario.
+ pkt->relay_info_ = scenario.relays_;
+
+ // Create the context;
+ AllocEngine::ClientContext6 ctx(subnet_, current_duid, false, false, "", false, pkt);
+
+ // Create or renew the lease.
+ Lease6Ptr lease;
+ ASSERT_NO_THROW(lease = expectOneLease(engine_.allocateLeases6(ctx)));
+ ASSERT_TRUE(lease);
+
+ EXPECT_EQ(scenario.exp_address_, lease->addr_);
+
+ // Verify the lease has the expected user context content.
+ if (!exp_context) {
+ ASSERT_FALSE(lease->getContext());
+ }
+ else {
+ ASSERT_TRUE(lease->getContext());
+ ASSERT_TRUE(exp_context->equals(*(lease->getContext())))
+ << "expected: " << *(exp_context) << std::endl
+ << " actual: " << *(lease->getContext()) << std::endl;
+ }
+ }
+}
+
+// Verifies that the extended data (RelayInfos for now) is
+// not added to a V6 lease when leases are created and/or renewed,
+// when store-extended-info is false.
+TEST_F(AllocEngine6ExtendedInfoTest, storeExtendedInfoDisabled6) {
+ // Structure that defines a test scenario.
+ struct Scenario {
+ std::string description_; // test description
+ DuidPtr duid_; // client DUID
+ std::vector<Pkt6::RelayInfo> relays_; // vector of relays from pkt
+ IOAddress exp_address_; // expected lease address
+ };
+
+ // Test scenarios.
+ std::vector<Scenario> scenarios {
+ {
+ "create client one without relays",
+ duid1_,
+ {},
+ duid1_addr_
+ },
+ {
+ "renew client one without relays",
+ DuidPtr(),
+ {},
+ duid1_addr_
+ },
+ {
+ "create client two with relays",
+ duid2_,
+ { relay1_, relay2_ },
+ duid2_addr_
+ },
+ {
+ "renew client two with relays",
+ DuidPtr(),
+ { relay1_, relay2_ },
+ duid2_addr_
+ }
+ };
+
+ // All of the scenarios require storage to be disabled.
+ subnet_->setStoreExtendedInfo(false);
+
+ // Iterate over the test scenarios.
+ DuidPtr current_duid;
+ for (auto scenario : scenarios) {
+ SCOPED_TRACE(scenario.description_);
+
+ Pkt6Ptr pkt;
+ if (scenario.duid_) {
+ current_duid = scenario.duid_;
+ pkt.reset(new Pkt6(DHCPV6_REQUEST, 1234));
+ } else {
+ pkt.reset(new Pkt6(DHCPV6_RENEW, 1234));
+ }
+
+ // Set packet relay vector from the scenario.
+ pkt->relay_info_ = scenario.relays_;
+
+ // Create the context;
+ AllocEngine::ClientContext6 ctx(subnet_, current_duid, false, false, "", false, pkt);
+
+ // Create or renew the lease.
+ Lease6Ptr lease;
+ ASSERT_NO_THROW(lease = expectOneLease(engine_.allocateLeases6(ctx)));
+ ASSERT_TRUE(lease);
+
+ EXPECT_EQ(scenario.exp_address_, lease->addr_);
+
+ // Verify the lease had no user context content.
+ ASSERT_FALSE(lease->getContext());
+ }
+}
+
+// Verifies that the extended data (RelayInfos for now) is
+// added to a V6 lease when an expired lease is reused and
+// store-extended-info is true. We don't bother testing the
+// disabled case as this is tested thoroughly elsewhere.
+TEST_F(AllocEngine6ExtendedInfoTest, reuseExpiredLease6) {
+ // Create one subnet with a pool holding one address.
+ IOAddress addr("2001:db8:1::ad");
+ initSubnet(IOAddress("2001:db8:1::"), addr, addr);
+ subnet_->setPreferred(Triplet<uint32_t>(200, 300, 400));
+ subnet_->setValid(Triplet<uint32_t>(300, 400, 500));
+
+ // Create an expired lease for duid1_.
+ Lease6Ptr lease(new Lease6(Lease::TYPE_NA, addr, duid1_, 1234,
+ 501, 502, subnet_->getID(),
+ HWAddrPtr(), 0));
+ lease->cltt_ = time(NULL) - 500; // Allocated 500 seconds ago
+ lease->valid_lft_ = 495; // Lease was valid for 495 seconds
+ ASSERT_TRUE(LeaseMgrFactory::instance().addLease(lease));
+
+ // Make sure that we really created expired lease
+ ASSERT_TRUE(lease->expired());
+
+ // Asking specifically for this address with zero lifetimes
+ AllocEngine::ClientContext6 ctx(subnet_, duid2_, false, false, "", false,
+ Pkt6Ptr(new Pkt6(DHCPV6_REQUEST, 5678)));
+ ctx.currentIA().iaid_ = iaid_;
+ ctx.currentIA().addHint(addr, 128, 0, 0);
+
+ // Add a relay to the packet relay vector.
+ ctx.query_->relay_info_.push_back(relay1_);
+
+ // Enable extended info storage.
+ subnet_->setStoreExtendedInfo(true);
+
+ // Reuse the expired lease.
+ EXPECT_NO_THROW(lease = expectOneLease(engine_.allocateLeases6(ctx)));
+
+ // Check that we got that single lease
+ ASSERT_TRUE(lease);
+ EXPECT_EQ(addr, lease->addr_);
+
+ // Now let's verify that the extended info is in the user-context.
+ ASSERT_TRUE(lease->getContext());
+ std::string exp_content_json =
+ "{ \"ISC\": { \"relays\": [ { \"hop\": 33, \"link\": \"2001:db8::1\","
+ " \"options\": \"0x00C800080102030405060708\", \"peer\": \"2001:db8::2\" } ] } }";
+ ConstElementPtr exp_context;
+ ASSERT_NO_THROW(exp_context = Element::fromJSON(exp_content_json))
+ << "invalid exp_context_json_, test is broken";
+ ASSERT_TRUE(exp_context->equals(*(lease->getContext())))
+ << "expected: " << *(exp_context) << std::endl
+ << " actual: " << *(lease->getContext()) << std::endl;
+}
+
} // namespace test
} // namespace dhcp
} // namespace isc
using AllocEngine::IterativeAllocator::increasePrefix;
};
+ /// @brief Wrapper method for invoking AllocEngine4::updateLease4ExtendedInfo().
+ /// @param lease lease to update
+ /// @param ctx current packet processing context
void callUpdateLease4ExtendedInfo(const Lease4Ptr& lease,
AllocEngine::ClientContext4& ctx) const {
updateLease4ExtendedInfo(lease,ctx);
}
+
+ /// @brief Wrapper method for invoking AllocEngine6::updateLease6ExtendedInfo().
+ /// @param lease lease to update
+ /// @param ctx current packet processing context
+ void callUpdateLease6ExtendedInfo(const Lease6Ptr& lease,
+ AllocEngine::ClientContext6& ctx) const {
+ updateLease6ExtendedInfo(lease,ctx);
+ }
+
};
/// @brief Used in Allocation Engine tests for IPv6
network2->setPreferred(Triplet<uint32_t>(200));
network2->setValid(Triplet<uint32_t>(300));
network2->setDdnsSendUpdates(false);
+ network2->setStoreExtendedInfo(true);
network3->setIface("eth2");
network3->setPreferred(Triplet<uint32_t>(100,200,300));
" \"renew-timer\": 100,\n"
" \"subnet6\": [ ],\n"
" \"preferred-lifetime\": 200,\n"
- " \"valid-lifetime\": 300\n"
+ " \"valid-lifetime\": 300\n,"
+ " \"store-extended-info\": true\n"
" },\n"
" {\n"
" \"calculate-tee-times\": true,\n"
subnet2->addRelayAddress(IOAddress("2001:db8:ff::2"));
subnet2->setValid(Triplet<uint32_t>(200));
subnet2->setPreferred(Triplet<uint32_t>(100));
+ subnet2->setStoreExtendedInfo(true);
subnet3->setIface("eth1");
subnet3->requireClientClass("foo");
" \"user-context\": { },\n"
" \"pools\": [ ],\n"
" \"pd-pools\": [ ],\n"
- " \"option-data\": [ ]\n"
+ " \"option-data\": [ ],\n"
+ " \"store-extended-info\": true\n"
"},{\n"
" \"id\": 125,\n"
" \"subnet\": \"2001:db8:3::/48\",\n"
EXPECT_TRUE(subnet->getHostnameCharReplacement().unspecified());
EXPECT_TRUE(subnet->getHostnameCharReplacement().empty());
+
+ EXPECT_TRUE(subnet->getStoreExtendedInfo().unspecified());
+ EXPECT_FALSE(subnet->getStoreExtendedInfo().get());
}
// This test verifies that it is possible to parse an IPv6 subnet for which
EXPECT_TRUE(subnet->getHostnameCharReplacement().unspecified());
EXPECT_TRUE(subnet->getHostnameCharReplacement().empty());
+
+ EXPECT_TRUE(subnet->getStoreExtendedInfo().unspecified());
+ EXPECT_FALSE(subnet->getStoreExtendedInfo().get());
}
// This test verifies that it is possible to parse an IPv4 shared network
EXPECT_TRUE(network->getDdnsQualifyingSuffix().unspecified());
EXPECT_TRUE(network->getDdnsQualifyingSuffix().empty());
+
+ EXPECT_TRUE(network->getStoreExtendedInfo().unspecified());
+ EXPECT_FALSE(network->getStoreExtendedInfo().get());
}
// This test verifies that it is possible to parse an IPv6 shared network
EXPECT_TRUE(network->getDdnsQualifyingSuffix().unspecified());
EXPECT_TRUE(network->getDdnsQualifyingSuffix().empty());
+
+ EXPECT_TRUE(network->getStoreExtendedInfo().unspecified());
+ EXPECT_FALSE(network->getStoreExtendedInfo().get());
}
// There's no test for ControlSocketParser, as it is tested in the DHCPv4 code
globals_->set("ddns-qualifying-suffix", Element::create("gs"));
globals_->set("hostname-char-set", Element::create("gc"));
globals_->set("hostname-char-replacement", Element::create("gr"));
+ globals_->set("store-extended-info", Element::create(true));
// For each parameter for which inheritance is supported run
// the test that checks if the values are inherited properly.
&Network6::setHostnameCharReplacement,
"nr", "gr");
}
+ {
+ SCOPED_TRACE("store-extended-info");
+ testNetworkInheritance<TestNetwork4>(&Network6::getStoreExtendedInfo,
+ &Network6::setStoreExtendedInfo,
+ false, true);
+ }
// Interface-id requires special type of test.
boost::shared_ptr<TestNetwork6> net_child(new TestNetwork6());
#include <dhcp/tests/iface_mgr_test_config.h>
#include <dhcpsrv/cfg_option.h>
#include <dhcpsrv/parsers/shared_network_parser.h>
+#include <testutils/gtest_utils.h>
#include <gtest/gtest.h>
#include <string>
SharedNetwork4Parser parser;
SharedNetwork4Ptr network;
- ASSERT_NO_THROW(network = parser.parse(config_element));
+ ASSERT_NO_THROW_LOG(network = parser.parse(config_element));
ASSERT_TRUE(network);
// Check basic parameters.
EXPECT_EQ("example.com.", network->getDdnsQualifyingSuffix().get());
EXPECT_EQ("[^A-Z]", network->getHostnameCharSet().get());
EXPECT_EQ("x", network->getHostnameCharReplacement().get());
+ EXPECT_TRUE(network->getStoreExtendedInfo().get());
// Relay information.
auto relay_info = network->getRelayInfo();
" \"ddns-qualifying-suffix\": \"example.com.\","
" \"hostname-char-set\": \"[^A-Z]\","
" \"hostname-char-replacement\": \"x\","
+ " \"store-extended-info\": true,"
" \"option-data\": ["
" {"
" \"name\": \"dns-servers\","
EXPECT_EQ("example.com.", network->getDdnsQualifyingSuffix().get());
EXPECT_EQ("[^A-Z]", network->getHostnameCharSet().get());
EXPECT_EQ("x", network->getHostnameCharReplacement().get());
+ EXPECT_TRUE(network->getStoreExtendedInfo().get());
// Relay information.
auto relay_info = network->getRelayInfo();