summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--src/lib/dhcpsrv/cfg_subnets4.cc14
-rw-r--r--src/lib/dhcpsrv/cfg_subnets4.h36
-rw-r--r--src/lib/dhcpsrv/cfg_subnets6.cc14
-rw-r--r--src/lib/dhcpsrv/cfg_subnets6.h38
-rw-r--r--src/lib/dhcpsrv/subnet.h93
-rw-r--r--src/lib/dhcpsrv/tests/alloc_engine_utils.cc2
-rw-r--r--src/lib/dhcpsrv/tests/cfg_subnets4_unittest.cc51
-rw-r--r--src/lib/dhcpsrv/tests/cfg_subnets6_unittest.cc51
-rw-r--r--src/lib/dhcpsrv/tests/srv_config_unittest.cc3
9 files changed, 289 insertions, 13 deletions
diff --git a/src/lib/dhcpsrv/cfg_subnets4.cc b/src/lib/dhcpsrv/cfg_subnets4.cc
index e86580e351..26b079b2c9 100644
--- a/src/lib/dhcpsrv/cfg_subnets4.cc
+++ b/src/lib/dhcpsrv/cfg_subnets4.cc
@@ -34,6 +34,20 @@ CfgSubnets4::add(const Subnet4Ptr& subnet) {
subnets_.push_back(subnet);
}
+ConstSubnet4Ptr
+CfgSubnets4::getBySubnetId(const SubnetID& subnet_id) const {
+ const auto& index = subnets_.get<SubnetIdIndexTag>();
+ auto subnet_it = index.find(subnet_id);
+ return ((subnet_it != index.cend()) ? (*subnet_it) : ConstSubnet4Ptr());
+}
+
+ConstSubnet4Ptr
+CfgSubnets4::getByPrefix(const std::string& subnet_text) const {
+ const auto& index = subnets_.get<SubnetPrefixIndexTag>();
+ auto subnet_it = index.find(subnet_text);
+ return ((subnet_it != index.cend()) ? (*subnet_it) : ConstSubnet4Ptr());
+}
+
Subnet4Ptr
CfgSubnets4::selectSubnet4o6(const SubnetSelector& selector) const {
diff --git a/src/lib/dhcpsrv/cfg_subnets4.h b/src/lib/dhcpsrv/cfg_subnets4.h
index a065ddc7a5..5c0d31bcff 100644
--- a/src/lib/dhcpsrv/cfg_subnets4.h
+++ b/src/lib/dhcpsrv/cfg_subnets4.h
@@ -10,8 +10,10 @@
#include <asiolink/io_address.h>
#include <cc/cfg_to_element.h>
#include <dhcpsrv/subnet.h>
+#include <dhcpsrv/subnet_id.h>
#include <dhcpsrv/subnet_selector.h>
#include <boost/shared_ptr.hpp>
+#include <string>
namespace isc {
namespace dhcp {
@@ -48,6 +50,40 @@ public:
return (&subnets_);
}
+ /// @brief Returns const pointer to a subnet identified by the specified
+ /// subnet identifier.
+ ///
+ /// The const pointer is returned by this method to prevent a caller from
+ /// modifying the subnet configuration. Modifications to subnet configuration
+ /// is dangerous and must be done carefully. The subnets' configruation is
+ /// held in the multi index container and any modifications to the subnet
+ /// id or subnet prefix must trigger re-indexing of multi index container.
+ /// There is no possibility to enforce this when the non-const pointer is
+ /// returned.
+ ///
+ /// @param subnet_id Subnet identifier.
+ ///
+ /// @return Pointer to the @c Subnet4 object or null pointer if such
+ /// subnet doesn't exist.
+ ConstSubnet4Ptr getBySubnetId(const SubnetID& subnet_id) const;
+
+ /// @brief Returns const pointer to a subnet which matches the specified
+ /// prefix in the canonical form.
+ ///
+ /// The const pointer is returned by this method to prevent a caller from
+ /// modifying the subnet configuration. Modifications to subnet configuration
+ /// is dangerous and must be done carefully. The subnets' configruation is
+ /// held in the multi index container and any modifications to the subnet
+ /// id or subnet prefix must trigger re-indexing of multi index container.
+ /// There is no possibility to enforce this when the non-const pointer is
+ /// returned.
+ ///
+ /// @param subnet_prefix Subnet prefix, e.g. 10.2.3.0/24
+ ///
+ /// @return Pointer to the @c Subnet4 object or null pointer if such
+ /// subnet doesn't exist.
+ ConstSubnet4Ptr getByPrefix(const std::string& subnet_prefix) const;
+
/// @brief Returns a pointer to the selected subnet.
///
/// This method tries to retrieve the subnet for the client using various
diff --git a/src/lib/dhcpsrv/cfg_subnets6.cc b/src/lib/dhcpsrv/cfg_subnets6.cc
index 439bcbf616..491fdb227e 100644
--- a/src/lib/dhcpsrv/cfg_subnets6.cc
+++ b/src/lib/dhcpsrv/cfg_subnets6.cc
@@ -33,6 +33,20 @@ CfgSubnets6::add(const Subnet6Ptr& subnet) {
subnets_.push_back(subnet);
}
+ConstSubnet6Ptr
+CfgSubnets6::getBySubnetId(const SubnetID& subnet_id) const {
+ const auto& index = subnets_.get<SubnetIdIndexTag>();
+ auto subnet_it = index.find(subnet_id);
+ return ((subnet_it != index.cend()) ? (*subnet_it) : ConstSubnet6Ptr());
+}
+
+ConstSubnet6Ptr
+CfgSubnets6::getByPrefix(const std::string& subnet_text) const {
+ const auto& index = subnets_.get<SubnetPrefixIndexTag>();
+ auto subnet_it = index.find(subnet_text);
+ return ((subnet_it != index.cend()) ? (*subnet_it) : ConstSubnet6Ptr());
+}
+
Subnet6Ptr
CfgSubnets6::selectSubnet(const SubnetSelector& selector) const {
Subnet6Ptr subnet;
diff --git a/src/lib/dhcpsrv/cfg_subnets6.h b/src/lib/dhcpsrv/cfg_subnets6.h
index a027dd7d31..8ea9151888 100644
--- a/src/lib/dhcpsrv/cfg_subnets6.h
+++ b/src/lib/dhcpsrv/cfg_subnets6.h
@@ -1,4 +1,4 @@
-// Copyright (C) 2014-2015,2017 Internet Systems Consortium, Inc. ("ISC")
+// Copyright (C) 2014-2017 Internet Systems Consortium, Inc. ("ISC")
//
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
@@ -11,9 +11,11 @@
#include <dhcp/option.h>
#include <cc/cfg_to_element.h>
#include <dhcpsrv/subnet.h>
+#include <dhcpsrv/subnet_id.h>
#include <dhcpsrv/subnet_selector.h>
#include <util/optional_value.h>
#include <boost/shared_ptr.hpp>
+#include <string>
namespace isc {
namespace dhcp {
@@ -49,6 +51,40 @@ public:
return (&subnets_);
}
+ /// @brief Returns const pointer to a subnet identified by the specified
+ /// subnet identifier.
+ ///
+ /// The const pointer is returned by this method to prevent a caller from
+ /// modifying the subnet configuration. Modifications to subnet configuration
+ /// is dangerous and must be done carefully. The subnets' configruation is
+ /// held in the multi index container and any modifications to the subnet
+ /// id or subnet prefix must trigger re-indexing of multi index container.
+ /// There is no possibility to enforce this when the non-const pointer is
+ /// returned.
+ ///
+ /// @param subnet_id Subnet identifier.
+ ///
+ /// @return Pointer to the @c Subnet6 object or null pointer if such
+ /// subnet doesn't exist.
+ ConstSubnet6Ptr getBySubnetId(const SubnetID& subnet_id) const;
+
+ /// @brief Returns const pointer to a subnet which matches the specified
+ /// prefix in the canonical form.
+ ///
+ /// The const pointer is returned by this method to prevent a caller from
+ /// modifying the subnet configuration. Modifications to subnet configuration
+ /// is dangerous and must be done carefully. The subnets' configruation is
+ /// held in the multi index container and any modifications to the subnet
+ /// id or subnet prefix must trigger re-indexing of multi index container.
+ /// There is no possibility to enforce this when the non-const pointer is
+ /// returned.
+ ///
+ /// @param subnet_prefix Subnet prefix, e.g. 2001:db8:1::/64
+ ///
+ /// @return Pointer to the @c Subnet6 object or null pointer if such
+ /// subnet doesn't exist.
+ ConstSubnet6Ptr getByPrefix(const std::string& subnet_prefix) const;
+
/// @brief Selects a subnet using parameters specified in the selector.
///
/// This method tries to retrieve the subnet for the client using various
diff --git a/src/lib/dhcpsrv/subnet.h b/src/lib/dhcpsrv/subnet.h
index e736fb6e89..c03925244d 100644
--- a/src/lib/dhcpsrv/subnet.h
+++ b/src/lib/dhcpsrv/subnet.h
@@ -19,6 +19,11 @@
#include <dhcpsrv/subnet_id.h>
#include <dhcpsrv/triplet.h>
+#include <boost/multi_index/mem_fun.hpp>
+#include <boost/multi_index/indexed_by.hpp>
+#include <boost/multi_index/ordered_index.hpp>
+#include <boost/multi_index/random_access_index.hpp>
+#include <boost/multi_index_container.hpp>
#include <boost/shared_ptr.hpp>
namespace isc {
@@ -623,16 +628,12 @@ private:
Cfg4o6 dhcp4o6_;
};
-/// @brief A pointer to a @c Subnet4 object
+/// @brief A const pointer to a @c Subnet4 object.
+typedef boost::shared_ptr<const Subnet4> ConstSubnet4Ptr;
+
+/// @brief A pointer to a @c Subnet4 object.
typedef boost::shared_ptr<Subnet4> Subnet4Ptr;
-/// @brief A collection of @c Subnet4 objects
-///
-/// That is a simple vector of pointers. It does not make much sense to
-/// optimize access time (e.g. using a map), because typical search
-/// pattern will use calling inRange() method on each subnet until
-/// a match is found.
-typedef std::vector<Subnet4Ptr> Subnet4Collection;
/// @brief A configuration holder for IPv6 subnet.
///
@@ -731,11 +732,83 @@ private:
};
+/// @brief A const pointer to a @c Subnet6 object.
+typedef boost::shared_ptr<const Subnet6> ConstSubnet6Ptr;
+
/// @brief A pointer to a Subnet6 object
typedef boost::shared_ptr<Subnet6> Subnet6Ptr;
-/// @brief A collection of Subnet6 objects
-typedef std::vector<Subnet6Ptr> Subnet6Collection;
+/// @name Definition of the multi index container holding subnet information
+///
+//@{
+
+/// @brief Tag for the random access index.
+struct SubnetRandomAccessIndexTag { };
+
+/// @brief Tag for the index for searching by subnet identifier.
+struct SubnetIdIndexTag { };
+
+/// @brief Tag for the index for searching by subnet prefix.
+struct SubnetPrefixIndexTag { };
+
+/// @brief Multi index container holding subnets.
+///
+/// This multi index container can hold pointers to @ref Subnet4 or
+/// @ref Subnet6 objects representing subnets. It provides indexes for
+/// subnet lookups using subnet properties such as: subnet identifier
+/// or subnet prefix. It also provides a random access index which
+/// allows for using the container like a vector.
+///
+/// The random access index is used by the DHCP servers which perform
+/// a full scan on subnets to find the one that matches some specific
+/// criteria for subnet selection.
+///
+/// The remaining indexes are used for searching for a specific subnet
+/// as a result of receiving a command over the control API, e.g.
+/// when 'subnet-get' command is received.
+///
+/// @todo We should consider optimizing subnet selection by leveraging
+/// the indexing capabilities of this container, e.g. searching for
+/// a subnet by interface name, relay address etc.
+///
+/// @tparam SubnetType Type of the subnet: @ref Subnet4 or @ref Subnet6.
+template<typename SubnetType>
+using SubnetCollection = boost::multi_index_container<
+ // Multi index container holds pointers to the subnets.
+ boost::shared_ptr<SubnetType>,
+ // The following holds all indexes.
+ boost::multi_index::indexed_by<
+ // First is the random access index allowing for accessing
+ // objects just like we'd do with a vector.
+ boost::multi_index::random_access<
+ boost::multi_index::tag<SubnetRandomAccessIndexTag>
+ >,
+ // Second index allows for searching using subnet identifier.
+ boost::multi_index::ordered_unique<
+ boost::multi_index::tag<SubnetIdIndexTag>,
+ boost::multi_index::const_mem_fun<Subnet, SubnetID, &Subnet::getID>
+ >,
+ // Third index allows for searching using an output from toText function.
+ boost::multi_index::ordered_unique<
+ boost::multi_index::tag<SubnetTextIndexTag>,
+ boost::multi_index::const_mem_fun<Subnet, std::string, &Subnet::toText>
+ >
+ >
+>;
+
+/// @brief A collection of @c Subnet4 objects
+///
+/// This container provides a set of indexes which can be used to retrieve
+/// subnets by various properties.
+typedef SubnetCollection<Subnet4> Subnet4Collection;
+
+/// @brief A collection of @c Subnet6 objects
+///
+/// This container provides a set of indexes which can be used to retrieve
+/// subnets by various properties.
+typedef SubnetCollection<Subnet6> Subnet6Collection;
+
+//@}
} // end of isc::dhcp namespace
} // end of isc namespace
diff --git a/src/lib/dhcpsrv/tests/alloc_engine_utils.cc b/src/lib/dhcpsrv/tests/alloc_engine_utils.cc
index 063c4b7daa..dc495a2e3e 100644
--- a/src/lib/dhcpsrv/tests/alloc_engine_utils.cc
+++ b/src/lib/dhcpsrv/tests/alloc_engine_utils.cc
@@ -529,6 +529,8 @@ AllocEngine4Test::initSubnet(const asiolink::IOAddress& pool_start,
AllocEngine4Test::AllocEngine4Test() {
+ CfgMgr::instance().clear();
+
// This lease mgr needs to exist to before configuration commits.
factory_.create("type=memfile universe=4 persist=false");
diff --git a/src/lib/dhcpsrv/tests/cfg_subnets4_unittest.cc b/src/lib/dhcpsrv/tests/cfg_subnets4_unittest.cc
index d7433f2d8c..dc6dd1dae6 100644
--- a/src/lib/dhcpsrv/tests/cfg_subnets4_unittest.cc
+++ b/src/lib/dhcpsrv/tests/cfg_subnets4_unittest.cc
@@ -23,6 +23,57 @@ using namespace isc::test;
namespace {
+// This test verifies that specific subnet can be retrieved by specifying
+// subnet identifier or subnet prefix.
+TEST(CfgSubnets4Test, getSpecificSubnet) {
+ CfgSubnets4 cfg;
+
+ // Create 3 subnets.
+ Subnet4Ptr subnet1(new Subnet4(IOAddress("192.0.2.0"),
+ 26, 1, 2, 3, SubnetID(5)));
+ Subnet4Ptr subnet2(new Subnet4(IOAddress("192.0.2.64"),
+ 26, 1, 2, 3, SubnetID(8)));
+ Subnet4Ptr subnet3(new Subnet4(IOAddress("192.0.2.128"),
+ 26, 1, 2, 3, SubnetID(10)));
+
+ // Store the subnets in a vector to make it possible to loop over
+ // all configured subnets.
+ std::vector<Subnet4Ptr> subnets;
+ subnets.push_back(subnet1);
+ subnets.push_back(subnet2);
+ subnets.push_back(subnet3);
+
+ // Add all subnets to the configuration.
+ for (auto subnet = subnets.cbegin(); subnet != subnets.cend(); ++subnet) {
+ ASSERT_NO_THROW(cfg.add(*subnet)) << "failed to add subnet with id: "
+ << (*subnet)->getID();
+ }
+
+ // Iterate over all subnets and make sure they can be retrieved by
+ // subnet identifier.
+ for (auto subnet = subnets.rbegin(); subnet != subnets.rend(); ++subnet) {
+ ConstSubnet4Ptr subnet_returned = cfg.getBySubnetId((*subnet)->getID());
+ ASSERT_TRUE(subnet_returned) << "failed to return subnet with id: "
+ << (*subnet)->getID();
+ EXPECT_EQ((*subnet)->getID(), subnet_returned->getID());
+ EXPECT_EQ((*subnet)->toText(), subnet_returned->toText());
+ }
+
+ // Repeat the previous test, but this time retrieve subnets by their
+ // prefixes.
+ for (auto subnet = subnets.rbegin(); subnet != subnets.rend(); ++subnet) {
+ ConstSubnet4Ptr subnet_returned = cfg.getByPrefix((*subnet)->toText());
+ ASSERT_TRUE(subnet_returned) << "failed to return subnet with id: "
+ << (*subnet)->getID();
+ EXPECT_EQ((*subnet)->getID(), subnet_returned->getID());
+ EXPECT_EQ((*subnet)->toText(), subnet_returned->toText());
+ }
+
+ // Make sure that null pointers are returned for non-existing subnets.
+ EXPECT_FALSE(cfg.getBySubnetId(SubnetID(123)));
+ EXPECT_FALSE(cfg.getByPrefix("10.20.30.0/29"));
+}
+
// This test verifies that it is possible to retrieve a subnet using an
// IP address.
TEST(CfgSubnets4Test, selectSubnetByCiaddr) {
diff --git a/src/lib/dhcpsrv/tests/cfg_subnets6_unittest.cc b/src/lib/dhcpsrv/tests/cfg_subnets6_unittest.cc
index 5df42700a6..822cf6b892 100644
--- a/src/lib/dhcpsrv/tests/cfg_subnets6_unittest.cc
+++ b/src/lib/dhcpsrv/tests/cfg_subnets6_unittest.cc
@@ -32,6 +32,57 @@ generateInterfaceId(const std::string& text) {
return OptionPtr(new Option(Option::V6, D6O_INTERFACE_ID, buffer));
}
+// This test verifies that specific subnet can be retrieved by specifying
+// subnet identifier or subnet prefix.
+TEST(CfgSubnets6Test, getSpecificSubnet) {
+ CfgSubnets6 cfg;
+
+ // Create 3 subnets.
+ Subnet6Ptr subnet1(new Subnet6(IOAddress("2001:db8:1::"), 48, 1, 2, 3, 4,
+ SubnetID(5)));
+ Subnet6Ptr subnet2(new Subnet6(IOAddress("2001:db8:2::"), 48, 1, 2, 3, 4,
+ SubnetID(8)));
+ Subnet6Ptr subnet3(new Subnet6(IOAddress("2001:db8:3::"), 48, 1, 2, 3, 4,
+ SubnetID(10)));
+
+ // Store the subnets in a vector to make it possible to loop over
+ // all configured subnets.
+ std::vector<Subnet6Ptr> subnets;
+ subnets.push_back(subnet1);
+ subnets.push_back(subnet2);
+ subnets.push_back(subnet3);
+
+ // Add all subnets to the configuration.
+ for (auto subnet = subnets.cbegin(); subnet != subnets.cend(); ++subnet) {
+ ASSERT_NO_THROW(cfg.add(*subnet)) << "failed to add subnet with id: "
+ << (*subnet)->getID();
+ }
+
+ // Iterate over all subnets and make sure they can be retrieved by
+ // subnet identifier.
+ for (auto subnet = subnets.rbegin(); subnet != subnets.rend(); ++subnet) {
+ ConstSubnet6Ptr subnet_returned = cfg.getBySubnetId((*subnet)->getID());
+ ASSERT_TRUE(subnet_returned) << "failed to return subnet with id: "
+ << (*subnet)->getID();
+ EXPECT_EQ((*subnet)->getID(), subnet_returned->getID());
+ EXPECT_EQ((*subnet)->toText(), subnet_returned->toText());
+ }
+
+ // Repeat the previous test, but this time retrieve subnets by their
+ // prefixes.
+ for (auto subnet = subnets.rbegin(); subnet != subnets.rend(); ++subnet) {
+ ConstSubnet6Ptr subnet_returned = cfg.getByPrefix((*subnet)->toText());
+ ASSERT_TRUE(subnet_returned) << "failed to return subnet with id: "
+ << (*subnet)->getID();
+ EXPECT_EQ((*subnet)->getID(), subnet_returned->getID());
+ EXPECT_EQ((*subnet)->toText(), subnet_returned->toText());
+ }
+
+ // Make sure that null pointers are returned for non-existing subnets.
+ EXPECT_FALSE(cfg.getBySubnetId(SubnetID(123)));
+ EXPECT_FALSE(cfg.getByPrefix("3000::/16"));
+}
+
// This test checks that the subnet can be selected using a relay agent's
// link address.
TEST(CfgSubnets6Test, selectSubnetByRelayAddress) {
diff --git a/src/lib/dhcpsrv/tests/srv_config_unittest.cc b/src/lib/dhcpsrv/tests/srv_config_unittest.cc
index 1a185dc7b8..fa465e7962 100644
--- a/src/lib/dhcpsrv/tests/srv_config_unittest.cc
+++ b/src/lib/dhcpsrv/tests/srv_config_unittest.cc
@@ -51,10 +51,10 @@ public:
test_subnets4_.push_back(subnet);
}
// Create IPv6 subnets.
+ IOAddress prefix("2001:db8:1::0");
for (int i = 0; i < TEST_SUBNETS_NUM; ++i) {
// This is a base prefix. All other prefixes will be created by
// modifying this one.
- IOAddress prefix("2001:db8:1::0");
std::vector<uint8_t> prefix_bytes = prefix.toBytes();
// Modify 5th byte of the prefix, so 2001:db8:1::0 becomes
// 2001:db8:2::0 etc.
@@ -239,7 +239,6 @@ TEST_F(SrvConfigTest, summarySubnets) {
addSubnet6(1);
EXPECT_EQ("added IPv4 subnets: 2; added IPv6 subnets: 2",
conf_.getConfigSummary(SrvConfig::CFGSEL_SUBNET));
-
}
// Verifies that we can get and set the client class dictionary