]> git.ipfire.org Git - thirdparty/kea.git/commitdiff
[#1010] Modified AllocateEngine and parsers to store extended v6 lease info
authorThomas Markwalder <tmark@isc.org>
Tue, 31 Mar 2020 20:17:43 +0000 (16:17 -0400)
committerFrancis Dupont <fdupont@isc.org>
Thu, 2 Apr 2020 19:08:09 +0000 (21:08 +0200)
src/lib/dhcpsrv/alloc_engine.*
    AllocEngine::reuseExpiredLease()
    AllocEngine::createLease6()
    AllocEngine::extendLease6() - added call to AllocEngine::updateLase6ExtendedInfo()

    AllocEngine::updateLease4ExtendedInfo() - create Elements directly

    AllocEngine::updateLease6ExtendedInfo() - new method

src/lib/dhcpsrv/tests/alloc_engine6_unittest.cc
    TEST_F(AllocEngine6ExtendedInfoTest, updateExtendedInfo6)
    TEST_F(AllocEngine6ExtendedInfoTest, storeExtendedInfoEnabled6)
    TEST_F(AllocEngine6ExtendedInfoTest, storeExtendedInfoDisabled6)
    TEST_F(AllocEngine6ExtendedInfoTest, reuseExpiredLease6)
    - new tests

src/lib/dhcpsrv/parsers/dhcp_parsers.cc
src/lib/dhcpsrv/parsers/shared_network_parser.cc
src/lib/dhcpsrv/parsers/base_network_parser.*
    Renamed BaseNetworkParser::parseCommonTimer() to parseCommon() and
    added handling of store-extended-info.

src/lib/dhcpsrv/parsers/simple_parser6.cc
    Added store-extended-info

src/lib/dhcpsrv/tests/cfg_shared_networks6_unittest.cc
src/lib/dhcpsrv/tests/cfg_subnets6_unittest.cc
src/lib/dhcpsrv/tests/dhcp_parsers_unittest.cc
src/lib/dhcpsrv/tests/network_unittest.cc
src/lib/dhcpsrv/tests/shared_network_parser_unittest.cc
    updated tests

17 files changed:
src/lib/dhcpsrv/alloc_engine.cc
src/lib/dhcpsrv/alloc_engine.h
src/lib/dhcpsrv/network.h
src/lib/dhcpsrv/parsers/base_network_parser.cc
src/lib/dhcpsrv/parsers/base_network_parser.h
src/lib/dhcpsrv/parsers/dhcp_parsers.cc
src/lib/dhcpsrv/parsers/shared_network_parser.cc
src/lib/dhcpsrv/parsers/simple_parser4.cc
src/lib/dhcpsrv/parsers/simple_parser6.cc
src/lib/dhcpsrv/tests/alloc_engine4_unittest.cc
src/lib/dhcpsrv/tests/alloc_engine6_unittest.cc
src/lib/dhcpsrv/tests/alloc_engine_utils.h
src/lib/dhcpsrv/tests/cfg_shared_networks6_unittest.cc
src/lib/dhcpsrv/tests/cfg_subnets6_unittest.cc
src/lib/dhcpsrv/tests/dhcp_parsers_unittest.cc
src/lib/dhcpsrv/tests/network_unittest.cc
src/lib/dhcpsrv/tests/shared_network_parser_unittest.cc

index 8cc0f446d21cc04647dbb1f505c6b585706b7dfc..07004cfcac7ef2a79fddb07ea43c21ec75f35d0a 100644 (file)
@@ -25,6 +25,7 @@
 #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>
@@ -1743,6 +1744,9 @@ AllocEngine::reuseExpiredLease(Lease6Ptr& expired, ClientContext6& ctx,
     }
 
     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);
 
@@ -1839,6 +1843,9 @@ Lease6Ptr AllocEngine::createLease6(ClientContext6& ctx,
     }
 
     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);
 
@@ -2119,6 +2126,9 @@ AllocEngine::extendLease6(ClientContext6& ctx, Lease6Ptr 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);
@@ -4054,13 +4064,12 @@ AllocEngine::updateLease4ExtendedInfo(const Lease4Ptr& 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;
@@ -4077,6 +4086,78 @@ AllocEngine::updateLease4ExtendedInfo(const Lease4Ptr& lease,
     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);
index 509f8d36cd627fb87af79827026b2a7031bef5bd..3fdcbe46bd07814f748d8a4d8d287356723f78f2 100644 (file)
@@ -1786,7 +1786,7 @@ private:
                                  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.
@@ -1802,6 +1802,23 @@ protected:
     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.
index 08a7ea477c3dad7477a6d72cb7296c1b2e8360d8..f73064c35f1f8b35f9016dd90064bd94c370646b 100644 (file)
@@ -691,9 +691,9 @@ public:
                                      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;
     }
index aac2a5de1f1c5ad01de81de41baf4d8c79837937..2f22e007166ffddab8216d71c933fbaf436782d3 100644 (file)
@@ -98,8 +98,8 @@ BaseNetworkParser::parseLifetime(const ConstElementPtr& scope,
 }
 
 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"));
     }
@@ -109,6 +109,11 @@ BaseNetworkParser::parseCommonTimers(const ConstElementPtr& network_data,
     }
 
     network->setValid(parseLifetime(network_data, "valid-lifetime"));
+
+    if (network_data->contains("store-extended-info")) {
+        network->setStoreExtendedInfo(getBoolean(network_data,
+                                                 "store-extended-info"));
+    }
 }
 
 void
index ac4196db8d84be10d79b543044085786abb42cc9..eb8ff8e4bd1751a42b27c0513a3d9358cc16a6bc 100644 (file)
@@ -30,19 +30,20 @@ protected:
     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.
     ///
index 275138ac6425012af1d58fdfb29a0e2402340805..b0c5bdb85a9d53cae83ef51309084cd3ab12a0e6 100644 (file)
@@ -742,8 +742,9 @@ Subnet4ConfigParser::initSubnet(data::ConstElementPtr params,
                                    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: ";
@@ -1206,9 +1207,9 @@ Subnet6ConfigParser::initSubnet(data::ConstElementPtr 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()) {
index a423cb26d251a8552e766d98e55e06b3ad750d27..84d348d0ecec655921477b3fdae3f06a3fb257ce 100644 (file)
@@ -44,9 +44,9 @@ SharedNetwork4Parser::parse(const data::ConstElementPtr& shared_network_data) {
         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")) {
@@ -228,8 +228,9 @@ SharedNetwork6Parser::parse(const data::ConstElementPtr& shared_network_data) {
         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,
index facc4f4fde9a220410bc6d259771e34b9ea8f0b0..5e7e4189dfd879991264986d277319ff8e1ccefc 100644 (file)
@@ -267,7 +267,8 @@ const ParamsList SimpleParser4::INHERIT_TO_SUBNET4 = {
     "max-valid-lifetime",
     "calculate-tee-times",
     "t1-percent",
-    "t2-percent"
+    "t2-percent",
+    "store-extended-info"
 };
 
 /// @brief This table defines all pool parameters.
index c53a14437c028a2cfdf28d254240238e22b25689..c2a66735dc0233308233f2271cf91c6d94fab0b2 100644 (file)
@@ -82,7 +82,8 @@ const SimpleKeywords SimpleParser6::GLOBAL6_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
@@ -106,8 +107,10 @@ const SimpleDefaults SimpleParser6::GLOBAL6_DEFAULTS = {
     { "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.
@@ -207,6 +210,7 @@ const SimpleKeywords SimpleParser6::SUBNET6_PARAMETERS = {
     { "ddns-qualifying-suffix",         Element::string },
     { "hostname-char-set",              Element::string },
     { "hostname-char-replacement",      Element::string },
+    { "store-extended-info",            Element::boolean },
     { "metadata",                       Element::map }
 };
 
@@ -248,7 +252,8 @@ const ParamsList SimpleParser6::INHERIT_TO_SUBNET6 = {
     "max-valid-lifetime",
     "calculate-tee-times",
     "t1-percent",
-    "t2-percent"
+    "t2-percent",
+    "store-extended-info"
 };
 
 /// @brief This table defines all pool parameters.
@@ -322,6 +327,7 @@ const SimpleKeywords SimpleParser6::SHARED_NETWORK6_PARAMETERS = {
     { "ddns-qualifying-suffix",         Element::string },
     { "hostname-char-set",              Element::string },
     { "hostname-char-replacement",      Element::string },
+    { "store-extended-info",            Element::boolean },
     { "metadata",                       Element::map }
 };
 
index 097fad1dff94f6ae8f54cc40b8dfedacf798a00e..d717fb6e5b95366779052958d5005f5ba580d51b 100644 (file)
@@ -3212,43 +3212,42 @@ TEST_F(AllocEngine4Test, updateExtendedInfo4) {
 
     // 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);
@@ -3354,35 +3353,34 @@ TEST_F(AllocEngine4Test, storeExtendedInfoEnabled4) {
 
     // 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);
@@ -3394,9 +3392,8 @@ TEST_F(AllocEngine4Test, storeExtendedInfoEnabled4) {
                                     IOAddress::IPV4_ZERO_ADDRESS(),
                                     false, false, "", false);
 
-    Lease4Ptr lease;
-
     // Iterate over the test scenarios.
+    Lease4Ptr lease;
     for (auto scenario : scenarios) {
         SCOPED_TRACE(scenario.description_);
 
@@ -3464,35 +3461,32 @@ TEST_F(AllocEngine4Test, storeExtendedInfoDisabled4) {
     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);
index 207f65561dd3789989c23fd63b587d92465748a7..f68679c5bae724fa01da88568e83b5a2b67cebb7 100644 (file)
 #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 {
@@ -3442,6 +3444,414 @@ TEST_F(AllocEngine6Test, globalHostReservedPrefix) {
         << "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
index 552f1688361695ce00214a11df8436021c95a902..4a198f39dcab8edac87e2f3c27a88024c6c4b67c 100644 (file)
@@ -79,10 +79,22 @@ public:
         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
index b2f71ebe80d95e4e0302c48263eaaee5b0750c74..d03cad31f8ea98b7b11539f9172783f598c8e629 100644 (file)
@@ -215,6 +215,7 @@ TEST(CfgSharedNetworks6Test, unparse) {
     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));
@@ -250,7 +251,8 @@ TEST(CfgSharedNetworks6Test, unparse) {
         "    \"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"
index b379d0ebe5e35b87c3fc205e9f399c115b7d7d64..0161d6f130f661dda2072d1f49e013474eca3b01 100644 (file)
@@ -624,6 +624,7 @@ TEST(CfgSubnets6Test, unparseSubnet) {
     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");
@@ -683,7 +684,8 @@ TEST(CfgSubnets6Test, unparseSubnet) {
         "    \"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"
index c94938041a8fee6f8004e6800c4fd2cbf636342d..1e57188f55c514d4c0cc37efdca9edc4609a7b69 100644 (file)
@@ -2787,6 +2787,9 @@ TEST_F(ParseConfigTest, defaultSubnet4) {
 
     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
@@ -2864,6 +2867,9 @@ TEST_F(ParseConfigTest, defaultSubnet6) {
 
     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
@@ -2936,6 +2942,9 @@ TEST_F(ParseConfigTest, defaultSharedNetwork4) {
 
     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
@@ -3008,6 +3017,9 @@ TEST_F(ParseConfigTest, defaultSharedNetwork6) {
 
     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
index 28695b9a8528cdafb6e8bdc2a6675ea67461bf23..3b8c38b36c76fd701cb1feab2964db90751d1e32 100644 (file)
@@ -340,6 +340,7 @@ TEST_F(NetworkTest, inheritanceSupport6) {
     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.
@@ -406,6 +407,12 @@ TEST_F(NetworkTest, inheritanceSupport6) {
                                              &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());
index 055ebc272541a6087fe54b28fe82f6c6d8db797e..629132a5fa59c61ff998351f6d7931be12f5b39e 100644 (file)
@@ -15,6 +15,7 @@
 #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>
 
@@ -234,7 +235,7 @@ TEST_F(SharedNetwork4ParserTest, parse) {
     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.
@@ -262,6 +263,7 @@ TEST_F(SharedNetwork4ParserTest, parse) {
     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();
@@ -510,6 +512,7 @@ public:
                 "    \"ddns-qualifying-suffix\": \"example.com.\","
                 "    \"hostname-char-set\": \"[^A-Z]\","
                 "    \"hostname-char-replacement\": \"x\","
+                "    \"store-extended-info\": true,"
                 "    \"option-data\": ["
                 "        {"
                 "            \"name\": \"dns-servers\","
@@ -613,6 +616,7 @@ TEST_F(SharedNetwork6ParserTest, parse) {
     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();