diff options
-rw-r--r-- | src/lib/dhcpsrv/cfg_subnets4.cc | 14 | ||||
-rw-r--r-- | src/lib/dhcpsrv/cfg_subnets4.h | 36 | ||||
-rw-r--r-- | src/lib/dhcpsrv/cfg_subnets6.cc | 14 | ||||
-rw-r--r-- | src/lib/dhcpsrv/cfg_subnets6.h | 38 | ||||
-rw-r--r-- | src/lib/dhcpsrv/subnet.h | 93 | ||||
-rw-r--r-- | src/lib/dhcpsrv/tests/alloc_engine_utils.cc | 2 | ||||
-rw-r--r-- | src/lib/dhcpsrv/tests/cfg_subnets4_unittest.cc | 51 | ||||
-rw-r--r-- | src/lib/dhcpsrv/tests/cfg_subnets6_unittest.cc | 51 | ||||
-rw-r--r-- | src/lib/dhcpsrv/tests/srv_config_unittest.cc | 3 |
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 |