]> git.ipfire.org Git - thirdparty/kea.git/commitdiff
[4303] Implemented parser for 'host-reservation-identifiers' list.
authorMarcin Siodelski <marcin@isc.org>
Thu, 14 Apr 2016 09:35:47 +0000 (11:35 +0200)
committerMarcin Siodelski <marcin@isc.org>
Thu, 14 Apr 2016 09:35:47 +0000 (11:35 +0200)
src/lib/dhcpsrv/cfg_host_reservations.cc
src/lib/dhcpsrv/cfg_host_reservations.h
src/lib/dhcpsrv/host.h
src/lib/dhcpsrv/parsers/host_reservation_parser.cc
src/lib/dhcpsrv/parsers/host_reservation_parser.h
src/lib/dhcpsrv/srv_config.cc
src/lib/dhcpsrv/srv_config.h
src/lib/dhcpsrv/tests/cfg_host_reservations_unittest.cc
src/lib/dhcpsrv/tests/host_reservation_parser_unittest.cc

index 25bf62cd84ab4b4f65f7366be0a499c543694c4d..ffed69306bdfe078a36c6fc2a2169f22eefec124 100644 (file)
@@ -15,12 +15,29 @@ CfgHostReservations::CfgHostReservations()
     : identifier_types_() {
 }
 
+CfgHostReservationsPtr
+CfgHostReservations::createConfig4() {
+    CfgHostReservationsPtr cfg(new CfgHostReservations());
+    cfg->addIdentifierType("hw-address");
+    cfg->addIdentifierType("duid");
+    cfg->addIdentifierType("circuit-id");
+    return (cfg);
+}
+
+CfgHostReservationsPtr
+CfgHostReservations::createConfig6() {
+    CfgHostReservationsPtr cfg(new CfgHostReservations());
+    cfg->addIdentifierType("hw-address");
+    cfg->addIdentifierType("duid");
+    return (cfg);
+}
+
 void
 CfgHostReservations::addIdentifierType(const std::string& identifier_name) {
     Host::IdentifierType identifier_type = Host::getIdentifierType(identifier_name);
     if (std::find(identifier_types_.begin(), identifier_types_.end(),
                   identifier_type) != identifier_types_.end()) {
-        isc_throw(isc::BadValue, "invalid host identifier name '"
+        isc_throw(isc::BadValue, "duplicate host identifier '"
                   << identifier_name << "'");
     }
     identifier_types_.push_back(identifier_type);
index 0795eaf275f9dba469289f63c5b35e5a1beb389c..d846aaa8eb9ca764de08cb787cd421dc36dfa35c 100644 (file)
 namespace isc {
 namespace dhcp {
 
+/// @brief Forward declaration of the @ref CfgHostReservations.
+class CfgHostReservations;
+
+/// @name Pointers to the @ref CfgHostReservations objects.
+//@{
+/// @brief Pointer to the Non-const object.
+typedef boost::shared_ptr<CfgHostReservations> CfgHostReservationsPtr;
+
+/// @brief Pointer to the const object.
+typedef boost::shared_ptr<const CfgHostReservations>
+ConstCfgHostReservationsPtr;
+
+//@}
+
 /// @brief Represents global configuration for host reservations.
 ///
 /// This class represents server configuration pertaining to host
@@ -37,6 +51,15 @@ public:
     /// - no identifiers selected for host reservations searches.
     CfgHostReservations();
 
+    /// @name Factory functions for creating default configurations.
+    //@{
+    /// @brief Factory function for DHCPv4.
+    static CfgHostReservationsPtr createConfig4();
+
+    /// @brief Factory function for DHCPv6.
+    static CfgHostReservationsPtr createConfig6();
+    //@}
+
     /// @brief Adds new identifier type to a collection of identifiers
     /// to be used by the server to search for host reservations.
     ///
@@ -62,17 +85,6 @@ private:
 
 };
 
-/// @name Pointers to the @ref CfgHostReservations objects.
-//@{
-/// @brief Pointer to the Non-const object.
-typedef boost::shared_ptr<CfgHostReservations> CfgHostReservationsPtr;
-
-/// @brief Pointer to the const object.
-typedef boost::shared_ptr<const CfgHostReservations>
-ConstCfgHostReservationsPtr;
-
-//@}
-
 }
 }
 
index 8d0f4195c8faf56594f1bf19f43e4c941288467d..89fe0e0a00749b3243b74ec183e55ced9e0c5993 100644 (file)
@@ -183,6 +183,10 @@ public:
         IDENT_CIRCUIT_ID
     };
 
+    /// @brief Constant pointing to the last identifier of the
+    /// @ref IdentifierType enumeration.
+    static const IdentifierType LAST_IDENTIFIER_TYPE = IDENT_CIRCUIT_ID;
+
     /// @brief Constructor.
     ///
     /// Creates a @c Host object using an identifier in a binary format. This
index 78dbf204090189cc51fafbd994bb6270f7716b44..ed5cd45ce65ab5783a2b7288a593d4c190b6482e 100644 (file)
@@ -11,6 +11,7 @@
 #include <dhcpsrv/parsers/host_reservation_parser.h>
 #include <boost/foreach.hpp>
 #include <boost/lexical_cast.hpp>
+#include <algorithm>
 #include <sys/socket.h>
 #include <sstream>
 #include <string>
@@ -37,8 +38,8 @@ getSupportedParams4(const bool identifiers_only = false) {
     // If this is first execution of this function, we need
     // to initialize the set.
     if (identifiers_set.empty()) {
-        identifiers_set.insert("duid");
         identifiers_set.insert("hw-address");
+        identifiers_set.insert("duid");
         identifiers_set.insert("circuit-id");
     }
     // Copy identifiers and add all other parameters.
@@ -68,8 +69,8 @@ getSupportedParams6(const bool identifiers_only = false) {
     // If this is first execution of this function, we need
     // to initialize the set.
     if (identifiers_set.empty()) {
-        identifiers_set.insert("duid");
         identifiers_set.insert("hw-address");
+        identifiers_set.insert("duid");
     }
     // Copy identifiers and add all other parameters.
     if (params_set.empty()) {
@@ -317,5 +318,82 @@ HostReservationParser6::getSupportedParameters(const bool identifiers_only) cons
     return (getSupportedParams6(identifiers_only));
 }
 
+HostReservationIdsParser::HostReservationIdsParser()
+    : staging_cfg_() {
+}
+
+void
+HostReservationIdsParser::build(isc::data::ConstElementPtr ids_list) {
+    // Remove any default configuration.
+    staging_cfg_->clear();
+
+    BOOST_FOREACH(ConstElementPtr element, ids_list->listValue()) {
+        std::string id_name = element->stringValue();
+        try {
+            if (id_name != "auto") {
+                if (!isSupportedIdentifier(id_name)) {
+                    isc_throw(isc::BadValue, "unsupported identifier '"
+                              << id_name << "'");
+                }
+                staging_cfg_->addIdentifierType(id_name);
+
+            } else {
+                // 'auto' is mutually exclusive with other values. If there
+                // are any values in the configuration already it means that
+                // some other values have already been specified.
+                if (!staging_cfg_->getIdentifierTypes().empty()) {
+                    isc_throw(isc::BadValue, "if 'auto' keyword is used,"
+                              " no other values can be specified within '"
+                              "host-reservation-identifiers' list");
+                }
+                // Iterate over all identifier types and for those supported
+                // in a given context (DHCPv4 or DHCPv6) add the identifier type
+                // to the configuration.
+                for (unsigned int i = 0;
+                     i <= static_cast<unsigned int>(Host::LAST_IDENTIFIER_TYPE);
+                     ++i) {
+                    std::string supported_id_name =
+                        Host::getIdentifierName(static_cast<Host::IdentifierType>(i));
+                    if (isSupportedIdentifier(supported_id_name)) {
+                        staging_cfg_->addIdentifierType(supported_id_name);
+                    }
+                }
+            }
+
+        } catch (const std::exception& ex) {
+            // Append line number where the error occurred.
+            isc_throw(DhcpConfigError, ex.what() << " ("
+                      << element->getPosition() << ")");
+        }
+    }
+
+    // The parsed list must not be empty.
+    if (staging_cfg_->getIdentifierTypes().empty()) {
+        isc_throw(DhcpConfigError, "'host-reservation-identifiers' list must not"
+                  " be empty (" << ids_list->getPosition() << ")");
+    }
+
+}
+
+HostReservationIdsParser4::HostReservationIdsParser4()
+    : HostReservationIdsParser() {
+    staging_cfg_ = CfgMgr::instance().getStagingCfg()->getCfgHostReservations4();
+}
+
+bool
+HostReservationIdsParser4::isSupportedIdentifier(const std::string& id_name) const {
+    return (getSupportedParams4(true).count(id_name) > 0);
+}
+
+HostReservationIdsParser6::HostReservationIdsParser6()
+    : HostReservationIdsParser() {
+    staging_cfg_ = CfgMgr::instance().getStagingCfg()->getCfgHostReservations6();
+}
+
+bool
+HostReservationIdsParser6::isSupportedIdentifier(const std::string& id_name) const {
+    return (getSupportedParams6(true).count(id_name) > 0);
+}
+
 } // end of namespace isc::dhcp
 } // end of namespace isc
index d6eaa2bb1870b0784235ee3cc3f665d14e758ad5..992d9accb483ace1d7e245882853c67860f06311 100644 (file)
@@ -146,6 +146,87 @@ protected:
 
 };
 
+/// @brief Parser for a list of host identifiers.
+///
+/// This is a parent parser class for parsing "host-reservation-identifiers"
+/// global configuration parmeter. The DHCPv4 and DHCPv6 specific parsers
+/// derive from this class.
+class HostReservationIdsParser : public DhcpConfigParser {
+public:
+
+    /// @brief Constructor.
+    HostReservationIdsParser();
+
+    /// @brief Parses a list of host identifiers.
+    ///
+    /// @param ids_list Data element pointing to an ordered list of host
+    /// identifier names.
+    ///
+    /// @throw DhcpConfigError If specified configuration is invalid.
+    virtual void build(isc::data::ConstElementPtr ids_list);
+
+    /// @brief Commit, unused.
+    virtual void commit() { }
+
+protected:
+
+    /// @brief Checks if specified identifier name is supported in the
+    /// context of the parser.
+    ///
+    /// This is abstract method which must be implemented in the derived
+    /// parser classes for DHCPv4 and DHCPv6.
+    ///
+    /// @param id_name Identifier name.
+    /// @return true if the specified identifier is supported, false
+    /// otherwise.
+    virtual bool isSupportedIdentifier(const std::string& id_name) const = 0;
+
+    /// @brief Pointer to the object holding configuration.
+    CfgHostReservationsPtr staging_cfg_;
+
+};
+
+/// @brief Parser for a list of host identifiers for DHCPv4.
+class HostReservationIdsParser4 : public HostReservationIdsParser {
+public:
+
+    /// @brief Constructor.
+    ///
+    /// Initializes staging configuration pointer to the one used for DHCPv4
+    /// configuration.
+    HostReservationIdsParser4();
+
+protected:
+
+    /// @brief Checks if specified identifier name is supported for DHCPv4.
+    ///
+    /// @param id_name Identifier name.
+    /// @return true if the specified identifier is supported, false
+    /// otherwise.
+    virtual bool isSupportedIdentifier(const std::string& id_name) const;
+
+};
+
+/// @brief Parser for a list of host identifiers for DHCPv6.
+class HostReservationIdsParser6 : public HostReservationIdsParser {
+public:
+
+    /// @brief Constructor.
+    ///
+    /// Initializes staging configuration pointer to the one used for DHCPv6
+    /// configuration.
+    HostReservationIdsParser6();
+
+protected:
+
+    /// @brief Checks if specified identifier name is supported for DHCPv6.
+    ///
+    /// @param id_name Identifier name.
+    /// @return true if the specified identifier is supported, false
+    /// otherwise.
+    virtual bool isSupportedIdentifier(const std::string& id_name) const;
+};
+
 
 }
 } // end of namespace isc
index c9ad06db6b53d2a7a7ae4e268c6ae56acd32c0b0..07c5729a0f37af0d9862ba16683985d64eaca5ef 100644 (file)
@@ -25,6 +25,8 @@ SrvConfig::SrvConfig()
       cfg_hosts_(new CfgHosts()), cfg_rsoo_(new CfgRSOO()),
       cfg_expiration_(new CfgExpiration()), cfg_duid_(new CfgDUID()),
       cfg_db_access_(new CfgDbAccess()),
+      cfg_host_reservations4_(CfgHostReservations::createConfig4()),
+      cfg_host_reservations6_(CfgHostReservations::createConfig6()),
       class_dictionary_(new ClientClassDictionary()),
       decline_timer_(0) {
 }
@@ -36,6 +38,8 @@ SrvConfig::SrvConfig(const uint32_t sequence)
       cfg_hosts_(new CfgHosts()), cfg_rsoo_(new CfgRSOO()),
       cfg_expiration_(new CfgExpiration()), cfg_duid_(new CfgDUID()),
       cfg_db_access_(new CfgDbAccess()),
+      cfg_host_reservations4_(CfgHostReservations::createConfig4()),
+      cfg_host_reservations6_(CfgHostReservations::createConfig6()),
       class_dictionary_(new ClientClassDictionary()),
       decline_timer_(0) {
 }
index ba0bc33cf2f941ec77d32ec6d9ebba84dab7d89b..b07b7b21c2cf4f366a89865bd18258191d29fd50 100644 (file)
@@ -10,6 +10,7 @@
 #include <dhcpsrv/cfg_db_access.h>
 #include <dhcpsrv/cfg_duid.h>
 #include <dhcpsrv/cfg_expiration.h>
+#include <dhcpsrv/cfg_host_reservations.h>
 #include <dhcpsrv/cfg_hosts.h>
 #include <dhcpsrv/cfg_iface.h>
 #include <dhcpsrv/cfg_option.h>
@@ -291,6 +292,30 @@ public:
         return (cfg_db_access_);
     }
 
+    /// @brief Returns pointer to the object holding general configuration
+    /// for host reservations in DHCPv4.
+    CfgHostReservationsPtr getCfgHostReservations4() {
+        return (cfg_host_reservations4_);
+    }
+
+    /// @brief Returns const pointer to the object holding general
+    /// configuration for host reservations in DHCPv4
+    ConstCfgHostReservationsPtr getCfgHostReservations4() const {
+        return (cfg_host_reservations4_);
+    }
+
+    /// @brief Returns pointer to the object holding general configuration
+    /// for host reservations in DHCPv6.
+    CfgHostReservationsPtr getCfgHostReservations6() {
+        return (cfg_host_reservations6_);
+    }
+
+    /// @brief Returns const pointer to the object holding general
+    /// configuration for host reservations in DHCPv6
+    ConstCfgHostReservationsPtr getCfgHostReservations6() const {
+        return (cfg_host_reservations6_);
+    }
+
     //@}
 
     /// @brief Returns non-const reference to an array that stores
@@ -502,6 +527,14 @@ private:
     /// connection parameters.
     CfgDbAccessPtr cfg_db_access_;
 
+    /// @brief Pointer to the general configuration for host reservations in
+    /// DHCPv4.
+    CfgHostReservationsPtr cfg_host_reservations4_;
+
+    /// @brief Pointer to the general configuration for host reservations in
+    /// DHCPv6.
+    CfgHostReservationsPtr cfg_host_reservations6_;
+
     /// @brief Pointer to the control-socket information
     isc::data::ConstElementPtr control_socket_;
 
index 9b1528825543d4b93d9a156b191a196e3702df77..df59dcb20750c71aad9d4e92be66ddcb6c2505af 100644 (file)
@@ -60,4 +60,24 @@ TEST(CfgHostReservationsTest, addIdentifier) {
     EXPECT_TRUE(cfg.getIdentifierTypes().empty());
 }
 
+// This test verfies that the default DHCPv4 configuration is created
+// as expected.
+TEST(CfgHostReservationsTest, createConfig4) {
+    CfgHostReservationsPtr cfg = CfgHostReservations::createConfig4();
+
+    EXPECT_TRUE(identifierAdded(*cfg, Host::IDENT_HWADDR));
+    EXPECT_TRUE(identifierAdded(*cfg, Host::IDENT_DUID));
+    EXPECT_TRUE(identifierAdded(*cfg, Host::IDENT_CIRCUIT_ID));
+}
+
+// This test verfies that the default DHCPv6 configuration is created
+// as expected.
+TEST(CfgHostReservationsTest, createConfig6) {
+    CfgHostReservationsPtr cfg = CfgHostReservations::createConfig6();
+
+    EXPECT_TRUE(identifierAdded(*cfg, Host::IDENT_HWADDR));
+    EXPECT_TRUE(identifierAdded(*cfg, Host::IDENT_DUID));
+    EXPECT_FALSE(identifierAdded(*cfg, Host::IDENT_CIRCUIT_ID));
+}
+
 } // end of anonymous namespace
index c0990ceda856916bb425157a96eef1c1bd45224d..54637d355a51e26c232322247b77b3b15ad91fe4 100644 (file)
@@ -783,5 +783,175 @@ TEST_F(HostReservationParserTest, mutuallyExclusiveIdentifiers6) {
     }
 }
 
+/// @brief Test fixture class for @ref HostReservationIdsParser.
+class HostReservationIdsParserTest : public ::testing::Test {
+public:
+
+    /// @brief Constructor.
+    ///
+    /// Clears current configuration.
+    HostReservationIdsParserTest() {
+        CfgMgr::instance().clear();
+    }
+
+    /// @brief Destructor.
+    ///
+    /// Clears current configuration.
+    virtual ~HostReservationIdsParserTest() {
+        CfgMgr::instance().clear();
+    }
+
+    /// @brief Test verifies that invalid configuration causes an error.
+    ///
+    /// @param config Configuration string.
+    /// @tparam ParserType @ref HostReservationIdsParser4 or
+    /// @ref HostReservationIdsParser6
+    template<typename ParserType>
+    void testInvalidConfig(const std::string& config) const {
+        ElementPtr config_element = Element::fromJSON(config);
+        ParserType parser;
+        EXPECT_THROW(parser.build(config_element), DhcpConfigError);
+    }
+
+};
+
+// Test that list of supported DHCPv4 identifiers list is correctly
+// parsed.
+TEST_F(HostReservationIdsParserTest, dhcp4Identifiers) {
+    std::string config = "[ \"circuit-id\", \"duid\", \"hw-address\" ]";
+
+    ElementPtr config_element = Element::fromJSON(config);
+
+    HostReservationIdsParser4 parser;
+    ASSERT_NO_THROW(parser.build(config_element));
+
+    ConstCfgHostReservationsPtr cfg = CfgMgr::instance().getStagingCfg()->
+        getCfgHostReservations4();
+    const CfgHostReservations::IdentifierTypes& ids = cfg->getIdentifierTypes();
+    ASSERT_EQ(3, ids.size());
+
+    CfgHostReservations::IdentifierTypes::const_iterator id = ids.begin();
+    EXPECT_EQ(*id++, Host::IDENT_CIRCUIT_ID);
+    EXPECT_EQ(*id++, Host::IDENT_DUID);
+    EXPECT_EQ(*id++, Host::IDENT_HWADDR);
+}
+
+// Test that list of supported DHCPv6 identifiers list is correctly
+// parsed.
+TEST_F(HostReservationIdsParserTest, dhcp6Identifiers) {
+    std::string config = "[ \"duid\", \"hw-address\" ]";
+
+    ElementPtr config_element = Element::fromJSON(config);
+
+    HostReservationIdsParser6 parser;
+    ASSERT_NO_THROW(parser.build(config_element));
+
+    ConstCfgHostReservationsPtr cfg = CfgMgr::instance().getStagingCfg()->
+        getCfgHostReservations6();
+    const CfgHostReservations::IdentifierTypes& ids = cfg->getIdentifierTypes();
+    ASSERT_EQ(2, ids.size());
+
+    CfgHostReservations::IdentifierTypes::const_iterator id = ids.begin();
+    EXPECT_EQ(*id++, Host::IDENT_DUID);
+    EXPECT_EQ(*id++, Host::IDENT_HWADDR);
+}
+
+// Test that invalid DHCPv4 identifier causes error.
+TEST_F(HostReservationIdsParserTest, dhcp4InvalidIdentifier) {
+    // Create configuration including unsupported identifier.
+    std::string config = "[ \"unsupported-id\" ]";
+    testInvalidConfig<HostReservationIdsParser4>(config);
+}
+
+// Test that invalid DHCPv6 identifier causes error.
+TEST_F(HostReservationIdsParserTest, dhcp6InvalidIdentifier) {
+    // Create configuration including unsupported identifier for DHCPv6.
+    // The circuit-id is only supported in DHCPv4.
+    std::string config = "[ \"circuit-id\" ]";
+    testInvalidConfig<HostReservationIdsParser6>(config);
+}
+
+// Check that all supported identifiers are used when 'auto' keyword
+// is specified for DHCPv4 case.
+TEST_F(HostReservationIdsParserTest, dhcp4AutoIdentifiers) {
+    std::string config = "[ \"auto\" ]";
+    ElementPtr config_element = Element::fromJSON(config);
+
+    HostReservationIdsParser4 parser;
+    ASSERT_NO_THROW(parser.build(config_element));
+
+    ConstCfgHostReservationsPtr cfg = CfgMgr::instance().getStagingCfg()->
+        getCfgHostReservations4();
+    const CfgHostReservations::IdentifierTypes& ids = cfg->getIdentifierTypes();
+    ASSERT_EQ(3, ids.size());
+
+    CfgHostReservations::IdentifierTypes::const_iterator id = ids.begin();
+    EXPECT_EQ(*id++, Host::IDENT_HWADDR);
+    EXPECT_EQ(*id++, Host::IDENT_DUID);
+    EXPECT_EQ(*id++, Host::IDENT_CIRCUIT_ID);
+}
+
+// This test verifies that use of "auto" together with an explicit
+// identifier causes an error. "auto" is placed before the explicit
+// identifier.
+TEST_F(HostReservationIdsParserTest, dhcp4AutoBeforeIdentifier) {
+    std::string config = "[ \"auto\", \"duid\" ]";
+    testInvalidConfig<HostReservationIdsParser4>(config);
+}
+
+// This test verifies that use of "auto" together with an explicit
+// identifier causes an error. "auto" is placed after the explicit
+// identifier.
+TEST_F(HostReservationIdsParserTest, dhcp4AutoAfterIdentifier) {
+    std::string config = "[ \"duid\", \"auto\" ]";
+    testInvalidConfig<HostReservationIdsParser4>(config);
+}
+
+// Test that empty list of identifier types is not allowed.
+TEST_F(HostReservationIdsParserTest, dhcp4EmptyList) {
+    std::string config = "[ ]";
+    testInvalidConfig<HostReservationIdsParser4>(config);
+}
+
+// Check that all supported identifiers are used when 'auto' keyword
+// is specified for DHCPv6 case.
+TEST_F(HostReservationIdsParserTest, dhcp6AutoIdentifiers) {
+    std::string config = "[ \"auto\" ]";
+    ElementPtr config_element = Element::fromJSON(config);
+
+    HostReservationIdsParser6 parser;
+    ASSERT_NO_THROW(parser.build(config_element));
+
+    ConstCfgHostReservationsPtr cfg = CfgMgr::instance().getStagingCfg()->
+        getCfgHostReservations6();
+    const CfgHostReservations::IdentifierTypes& ids = cfg->getIdentifierTypes();
+    ASSERT_EQ(2, ids.size());
+
+    CfgHostReservations::IdentifierTypes::const_iterator id = ids.begin();
+    EXPECT_EQ(*id++, Host::IDENT_HWADDR);
+    EXPECT_EQ(*id++, Host::IDENT_DUID);
+}
+
+// This test verifies that use of "auto" together with an explicit
+// identifier causes an error. "auto" is placed before the explicit
+// identifier.
+TEST_F(HostReservationIdsParserTest, dhcp6AutoBeforeIdentifier) {
+    std::string config = "[ \"auto\", \"duid\" ]";
+    testInvalidConfig<HostReservationIdsParser6>(config);
+}
+
+// This test verifies that use of "auto" together with an explicit
+// identifier causes an error. "auto" is placed after the explicit
+// identifier.
+TEST_F(HostReservationIdsParserTest, dhcp6AutoAfterIdentifier) {
+    std::string config = "[ \"duid\", \"auto\" ]";
+    testInvalidConfig<HostReservationIdsParser6>(config);
+}
+
+// Test that empty list of identifier types is not allowed.
+TEST_F(HostReservationIdsParserTest, dhcp6EmptyList) {
+    std::string config = "[ ]";
+    testInvalidConfig<HostReservationIdsParser6>(config);
+}
 
 } // end of anonymous namespace