Browse Source

[3874] Added DUID configuration parser.

Marcin Siodelski 9 years ago
parent
commit
a2329d1743

+ 2 - 0
src/lib/dhcpsrv/Makefile.am

@@ -139,6 +139,8 @@ libkea_dhcpsrv_la_SOURCES += parsers/dbaccess_parser.cc
 libkea_dhcpsrv_la_SOURCES += parsers/dbaccess_parser.h
 libkea_dhcpsrv_la_SOURCES += parsers/dhcp_parsers.cc
 libkea_dhcpsrv_la_SOURCES += parsers/dhcp_parsers.h
+libkea_dhcpsrv_la_SOURCES += parsers/duid_config_parser.cc
+libkea_dhcpsrv_la_SOURCES += parsers/duid_config_parser.h
 libkea_dhcpsrv_la_SOURCES += parsers/expiration_config_parser.cc
 libkea_dhcpsrv_la_SOURCES += parsers/expiration_config_parser.h
 libkea_dhcpsrv_la_SOURCES += parsers/host_reservation_parser.cc

+ 133 - 0
src/lib/dhcpsrv/parsers/duid_config_parser.cc

@@ -0,0 +1,133 @@
+// Copyright (C) 2015 Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#include <config.h>
+#include <cc/data.h>
+#include <dhcp/duid.h>
+#include <dhcpsrv/cfg_duid.h>
+#include <dhcpsrv/cfgmgr.h>
+#include <dhcpsrv/parsers/duid_config_parser.h>
+#include <exceptions/exceptions.h>
+#include <boost/foreach.hpp>
+#include <boost/lexical_cast.hpp>
+#include <limits>
+#include <string>
+
+using namespace isc::data;
+
+namespace isc {
+namespace dhcp {
+
+DUIDConfigParser::DUIDConfigParser()
+    : DhcpConfigParser() {
+}
+
+void
+DUIDConfigParser::build(isc::data::ConstElementPtr duid_configuration) {
+    bool type_present = false;
+    BOOST_FOREACH(ConfigPair element, duid_configuration->mapValue()) {
+        try {
+            if (element.first == "type") {
+                type_present = true;
+                setType(element.second->stringValue());
+            } else if (element.first == "identifier") {
+                setIdentifier(element.second->stringValue());
+            } else if (element.first == "htype") {
+                setHType(element.second->intValue());
+            } else if (element.first == "time") {
+                setTime(element.second->intValue());
+            } else if (element.first == "enterprise-id") {
+                setEnterpriseId(element.second->intValue());
+            } else {
+                isc_throw(DhcpConfigError, "unsupported configuration "
+                          "parameter '" << element.first << "'");
+            }
+        } catch (const std::exception& ex) {
+            // Append position.
+            isc_throw(DhcpConfigError, ex.what() << " ("
+                      << element.second->getPosition() << ")");
+        }
+    }
+
+    // "type" is mandatory
+    if (!type_present) {
+        isc_throw(DhcpConfigError, "mandatory parameter \"type\" not specified"
+                  " for the DUID configuration ("
+                  << duid_configuration->getPosition() << ")");
+    }
+}
+
+void
+DUIDConfigParser::setType(const std::string& duid_type) const {
+    // Map DUID type represented as text into numeric value.
+    DUID::DUIDType numeric_type = DUID::DUID_UNKNOWN;
+    if (duid_type == "LLT") {
+        numeric_type = DUID::DUID_LLT;
+    } else if (duid_type == "EN") {
+        numeric_type = DUID::DUID_EN;
+    } else if (duid_type == "LL") {
+        numeric_type = DUID::DUID_LL;
+    } else {
+        isc_throw(DhcpConfigError, "unsupported DUID type '"
+                  << duid_type << "'. Expected: LLT, EN or LL");
+    }
+
+    const CfgDUIDPtr& cfg = CfgMgr::instance().getStagingCfg()->getCfgDUID();
+    cfg->setType(static_cast<DUID::DUIDType>(numeric_type));
+}
+
+void
+DUIDConfigParser::setIdentifier(const std::string& identifier) const {
+    const CfgDUIDPtr& cfg = CfgMgr::instance().getStagingCfg()->getCfgDUID();
+    cfg->setIdentifier(identifier);
+}
+
+void
+DUIDConfigParser::setHType(const int64_t htype) const {
+    const CfgDUIDPtr& cfg = CfgMgr::instance().getStagingCfg()->getCfgDUID();
+    checkRange<uint16_t>("htype", htype);
+    cfg->setHType(static_cast<uint16_t>(htype));
+
+}
+
+void
+DUIDConfigParser::setTime(const int64_t new_time) const {
+    const CfgDUIDPtr& cfg = CfgMgr::instance().getStagingCfg()->getCfgDUID();
+    checkRange<uint32_t>("time", new_time);
+    cfg->setTime(static_cast<uint32_t>(new_time));
+}
+
+void
+DUIDConfigParser::setEnterpriseId(const int64_t enterprise_id) const {
+    const CfgDUIDPtr& cfg = CfgMgr::instance().getStagingCfg()->getCfgDUID();
+    checkRange<uint32_t>("enterprise-id", enterprise_id);
+    cfg->setEnterpriseId(static_cast<uint32_t>(enterprise_id));
+}
+
+template<typename NumericType>
+void
+DUIDConfigParser::checkRange(const std::string& parameter_name,
+                             const int64_t parameter_value) const {
+    if ((parameter_value < 0) ||
+        (parameter_value > std::numeric_limits<NumericType>::max())) {
+        isc_throw(DhcpConfigError, "out of range value '" << parameter_value
+                  << "' specified for parameter '" << parameter_name
+                  << "'; expected value in range of [0.."
+                  << std::numeric_limits<NumericType>::max() << "]");
+    }
+}
+
+
+} // end of namespace isc::dhcp
+} // end of namespace isc

+ 87 - 0
src/lib/dhcpsrv/parsers/duid_config_parser.h

@@ -0,0 +1,87 @@
+// Copyright (C) 2015 Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#ifndef DUID_CONFIG_PARSER_H
+#define DUID_CONFIG_PARSER_H
+
+#include <cc/data.h>
+#include <dhcpsrv/parsers/dhcp_config_parser.h>
+#include <stdint.h>
+#include <string>
+
+namespace isc {
+namespace dhcp {
+
+/// @brief Parser for a single host reservation entry.
+class DUIDConfigParser : public DhcpConfigParser {
+public:
+
+    /// @brief Constructor.
+    DUIDConfigParser();
+
+    /// @brief Parses DUID configuration.
+    ///
+    /// @param duid_configuration Data element holding a map representing
+    /// DUID configuration.
+    ///
+    /// @throw DhcpConfigError If the configuration is invalid.
+    virtual void build(isc::data::ConstElementPtr duid_configuration);
+
+    /// @brief Commit, unused.
+    virtual void commit() { }
+
+private:
+
+    /// @brief Validate and set DUID type.
+    ///
+    /// @param duid_type DUID type in textfual format.
+    void setType(const std::string& duid_type) const;
+
+    /// @brief Validate and set identifier.
+    ///
+    /// @param identifier Identifier.
+    void setIdentifier(const std::string& identifier) const;
+
+    /// @brief Validate and set hardware type.
+    ///
+    /// @param htype Hardware type.
+    void setHType(const int64_t htype) const;
+
+    /// @brief Validate and set time value.
+    ///
+    /// @param new_time Time value to be used for DUID.
+    void setTime(const int64_t new_time) const;
+
+    /// @brief Validate and set enterprise id.
+    ///
+    /// @param enterprise_id Enterprise id.
+    void setEnterpriseId(const int64_t enterprise_id) const;
+
+    /// @brief Verifies if the specified parameter is in range.
+    ///
+    /// Each numeric value must be in range of [0 .. max_value], where
+    /// max_value is a maximum value for the numeric type used for this
+    /// parameter.
+    ///
+    /// @param parameter_name Parameter name.
+    /// @tparam Numeric type of the specified parameter.
+    template<typename NumericType>
+    void checkRange(const std::string& parameter_name,
+                    const int64_t parameter_value) const;
+};
+
+}
+} // end of namespace isc
+
+#endif // DUID_CONFIG_PARSER_H

+ 1 - 0
src/lib/dhcpsrv/tests/Makefile.am

@@ -84,6 +84,7 @@ libdhcpsrv_unittests_SOURCES += d2_udp_unittest.cc
 libdhcpsrv_unittests_SOURCES += daemon_unittest.cc
 libdhcpsrv_unittests_SOURCES += database_connection_unittest.cc
 libdhcpsrv_unittests_SOURCES += dbaccess_parser_unittest.cc
+libdhcpsrv_unittests_SOURCES += duid_config_parser_unittest.cc
 libdhcpsrv_unittests_SOURCES += expiration_config_parser_unittest.cc
 libdhcpsrv_unittests_SOURCES += host_mgr_unittest.cc
 libdhcpsrv_unittests_SOURCES += host_unittest.cc

+ 2 - 0
src/lib/dhcpsrv/tests/cfg_duid_unittest.cc

@@ -93,6 +93,8 @@ TEST(CfgDUIDTest, setIdentifier) {
         << toString(cfg_duid.getIdentifier());
 }
 
+// This test verifies that the invalid identifier is rejected and
+// exception is thrown.
 TEST(CfgDUIDTest, setInvalidIdentifier) {
     CfgDUID cfg_duid;
     // Check that hexadecimal characters may be lower case.

+ 214 - 0
src/lib/dhcpsrv/tests/duid_config_parser_unittest.cc

@@ -0,0 +1,214 @@
+// Copyright (C) 2015 Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#include <config.h>
+#include <cc/data.h>
+#include <dhcpsrv/cfgmgr.h>
+#include <dhcpsrv/cfg_duid.h>
+#include <dhcpsrv/parsers/duid_config_parser.h>
+#include <dhcpsrv/testutils/config_result_check.h>
+#include <util/encode/hex.h>
+#include <gtest/gtest.h>
+#include <limits>
+#include <sstream>
+#include <string>
+
+using namespace isc;
+using namespace isc::data;
+using namespace isc::dhcp;
+
+namespace {
+
+/// @brief Test fixture class for @c DUIDConfigParser
+class DUIDConfigParserTest : public ::testing::Test {
+public:
+
+    /// @brief Creates simple configuration with DUID type only.
+    ///
+    /// @param duid_type DUID type in the textual format.
+    std::string createConfigWithType(const std::string& duid_type) const;
+
+    /// @brief Creates simple configuration with DUID type and one
+    /// numeric parameter.
+    ///
+    /// @param name Parameter name.
+    /// @param value Parameter value.
+    std::string createConfigWithInteger(const std::string& name,
+                                        const int64_t value) const;
+
+    /// @brief Parse configuration.
+    ///
+    /// @param config String representing DUID configuration.
+    void build(const std::string& config) const;
+
+    /// @brief Test that only a DUID type can be specified.
+    ///
+    /// @param duid_type DUID type in numeric format.
+    /// @param duid_type_text DUID type in textual format.
+    void testTypeOnly(const DUID::DUIDType& duid_type,
+                      const std::string duid_type_text) const;
+
+    /// @brief Test that invalid configuration is rejected.
+    ///
+    /// @param config Holds JSON configuration to be used.
+    void testInvalidConfig(const std::string& config) const;
+
+    /// @brief Test out of range numeric values.
+    ///
+    /// @param param_name Parameter name.
+    /// @tparam Type of the numeric parameter.
+    template<typename NumericType>
+    void testOutOfRange(const std::string& param_name) {
+        // Obtain maximum value for the specified numeric type.
+        const uint64_t max_value = std::numeric_limits<NumericType>::max();
+
+        // Negative values are not allowed.
+        EXPECT_THROW(build(createConfigWithInteger(param_name, -1)),
+                     DhcpConfigError);
+        // Zero is allowed.
+        EXPECT_NO_THROW(build(createConfigWithInteger(param_name, 0)));
+        // Maximum value.
+        EXPECT_NO_THROW(build(createConfigWithInteger(param_name, max_value)));
+        // Value greater than maximum should result in exception.
+        EXPECT_THROW(build(createConfigWithInteger(param_name, max_value + 1)),
+                     DhcpConfigError);
+    }
+
+    /// @brief Converts vector to string of hexadecimal digits.
+    ///
+    /// @param vec Input vector.
+    /// @return String of hexadecimal digits converted from vector.
+    std::string toString(const std::vector<uint8_t>& vec) const;
+};
+
+std::string 
+DUIDConfigParserTest::createConfigWithType(const std::string& duid_type) const {
+    std::ostringstream s;
+    s << "{ \"type\": \"" << duid_type << "\" }";
+    return (s.str());
+}
+
+std::string
+DUIDConfigParserTest::createConfigWithInteger(const std::string& name,
+                                              const int64_t value) const {
+    std::ostringstream s;
+    s << "{ \"type\": \"LLT\", \"" << name << "\": " << value << " }";
+    return (s.str());
+}
+
+void
+DUIDConfigParserTest::build(const std::string& config) const {
+    ElementPtr config_element = Element::fromJSON(config);
+    DUIDConfigParser parser;
+    parser.build(config_element);
+}
+
+
+void
+DUIDConfigParserTest::testTypeOnly(const DUID::DUIDType& duid_type,
+                                   const std::string duid_type_text) const {
+    // Use DUID configuration with only a "type".
+    ASSERT_NO_THROW(build(createConfigWithType(duid_type_text)));
+
+    // Make sure that the type is correct and that other parameters are set
+    // to their defaults.
+    CfgDUIDPtr cfg_duid = CfgMgr::instance().getStagingCfg()->getCfgDUID();
+    EXPECT_EQ(duid_type, cfg_duid->getType());
+    EXPECT_TRUE(cfg_duid->getIdentifier().empty());
+    EXPECT_EQ(0, cfg_duid->getHType());
+    EXPECT_EQ(0, cfg_duid->getTime());
+    EXPECT_EQ(0, cfg_duid->getEnterpriseId());
+}
+
+void
+DUIDConfigParserTest::testInvalidConfig(const std::string& config) const {
+    EXPECT_THROW(build(config), DhcpConfigError);
+}
+
+std::string
+DUIDConfigParserTest::toString(const std::vector<uint8_t>& vec) const {
+    try {
+        return (util::encode::encodeHex(vec));
+    } catch (...) {
+        ADD_FAILURE() << "toString: unable to encode vector to"
+            " hexadecimal string";
+    }
+    return ("");
+}
+
+// This test verifies that it is allowed to specify a DUID-LLT type.
+TEST_F(DUIDConfigParserTest, typeOnlyLLT) {
+    testTypeOnly(DUID::DUID_LLT, "LLT");
+}
+
+// This test verifies that it is allowed to specify a DUID-EN type.
+TEST_F(DUIDConfigParserTest, typeOnlyEN) {
+    testTypeOnly(DUID::DUID_EN, "EN");
+}
+
+// This test verifies that it is allowed to specify a DUID-LL type.
+TEST_F(DUIDConfigParserTest, typeOnlyLL) {
+    testTypeOnly(DUID::DUID_LL, "LL");
+}
+
+// This test verifies that using unsupported DUID type will result in
+// configuration error.
+TEST_F(DUIDConfigParserTest, typeInvalid) {
+    testInvalidConfig(createConfigWithType("WRONG"));
+}
+
+// This test verifies that DUID type is required.
+TEST_F(DUIDConfigParserTest, noType) {
+    // First check that the configuration with DUID type specified is
+    // accepted.
+    ASSERT_NO_THROW(build("{ \"type\": \"LLT\", \"time\": 1 }"));
+    // Now remove the type and expect an error.
+    testInvalidConfig("{ \"time\": 1 }");
+}
+
+// This test verifies that all parameters can be set.
+TEST_F(DUIDConfigParserTest, allParameters) {
+    // Set all parameters.
+    ASSERT_NO_THROW(build("{ \"type\": \"EN\","
+                          "  \"identifier\": \"ABCDEF\","
+                          "  \"time\": 100,"
+                          "  \"htype\": 8,"
+                          "  \"enterprise-id\": 2024"
+                          "}"));
+
+    // Verify that parameters have been set correctly.
+    CfgDUIDPtr cfg_duid = CfgMgr::instance().getStagingCfg()->getCfgDUID();
+    EXPECT_EQ(DUID::DUID_EN, cfg_duid->getType());
+    EXPECT_EQ("ABCDEF", toString(cfg_duid->getIdentifier()));
+    EXPECT_EQ(8, cfg_duid->getHType());
+    EXPECT_EQ(100, cfg_duid->getTime());
+    EXPECT_EQ(2024, cfg_duid->getEnterpriseId());
+}
+
+// Test out of range values for time.
+TEST_F(DUIDConfigParserTest, timeOutOfRange) {
+    testOutOfRange<uint32_t>("time");
+}
+
+// Test out of range values for hardware type.
+TEST_F(DUIDConfigParserTest, htypeOutOfRange) {
+    testOutOfRange<uint16_t>("htype");
+}
+
+// Test out of range values for enterprise id.
+TEST_F(DUIDConfigParserTest, enterpriseIdOutOfRange) {
+    testOutOfRange<uint32_t>("enterprise-id");
+}
+
+} // end of anonymous namespace