Browse Source

[3604] Created a configuration parser for interface-config map.

The interface-config map will hold the "interfaces" list which used to
be a global parameter. The new configuration layout is incompatible with
a previous layout.
Marcin Siodelski 10 years ago
parent
commit
0677750e48

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

@@ -115,6 +115,8 @@ libkea_dhcpsrv_la_SOURCES += parsers/dhcp_parsers.h
 libkea_dhcpsrv_la_SOURCES += parsers/host_reservation_parser.cc
 libkea_dhcpsrv_la_SOURCES += parsers/host_reservation_parser.h
 libkea_dhcpsrv_la_SOURCES += parsers/host_reservations_list_parser.h
+libkea_dhcpsrv_la_SOURCES += parsers/ifaces_config_parser.cc
+libkea_dhcpsrv_la_SOURCES += parsers/ifaces_config_parser.h
 
 
 nodist_libkea_dhcpsrv_la_SOURCES = dhcpsrv_messages.h dhcpsrv_messages.cc

+ 0 - 35
src/lib/dhcpsrv/parsers/dhcp_parsers.cc

@@ -173,41 +173,6 @@ template <> void ValueParser<std::string>::build(ConstElementPtr value) {
     boost::erase_all(value_, "\"");
 }
 
-// ******************** InterfaceListConfigParser *************************
-
-InterfaceListConfigParser::
-InterfaceListConfigParser(const std::string& param_name,
-                          ParserContextPtr global_context)
-    : param_name_(param_name), global_context_(global_context) {
-    if (param_name_ != "interfaces") {
-        isc_throw(BadValue, "Internal error. Interface configuration "
-            "parser called for the wrong parameter: " << param_name);
-    }
-}
-
-void
-InterfaceListConfigParser::build(ConstElementPtr value) {
-    CfgIface cfg_iface;
-
-    BOOST_FOREACH(ConstElementPtr iface, value->listValue()) {
-        std::string iface_name = iface->stringValue();
-        try {
-            cfg_iface.use(global_context_->universe_ == Option::V4 ?
-                          AF_INET : AF_INET6, iface_name);
-
-        } catch (const std::exception& ex) {
-            isc_throw(DhcpConfigError, "Failed to select interface: "
-                      << ex.what() << " (" << value->getPosition() << ")");
-        }
-    }
-    CfgMgr::instance().getStagingCfg()->setCfgIface(cfg_iface);
-}
-
-void
-InterfaceListConfigParser::commit() {
-    // Nothing to do.
-}
-
 // ******************** MACSourcesListConfigParser *************************
 
 MACSourcesListConfigParser::

+ 0 - 41
src/lib/dhcpsrv/parsers/dhcp_parsers.h

@@ -380,47 +380,6 @@ private:
 
 };
 
-/// @brief parser for interface list definition
-///
-/// This parser handles Dhcp4/interfaces and Dhcp6/interfaces entries.
-/// It contains a list of network interfaces that the server listens on.
-/// In particular, it can contain an entry called "all" or "any" that
-/// designates all interfaces.
-class InterfaceListConfigParser : public DhcpConfigParser {
-public:
-
-    /// @brief constructor
-    ///
-    /// As this is a dedicated parser, it must be used to parse
-    /// "interface" parameter only. All other types will throw exception.
-    ///
-    /// @param param_name name of the configuration parameter being parsed
-    /// @param global_context Global parser context.
-    /// @throw BadValue if supplied parameter name is not "interface"
-    InterfaceListConfigParser(const std::string& param_name,
-                              ParserContextPtr global_context);
-
-    /// @brief parses parameters value
-    ///
-    /// Parses configuration entry (list of parameters) and adds each element
-    /// to the interfaces list.
-    ///
-    /// @param value pointer to the content of parsed values
-    virtual void build(isc::data::ConstElementPtr value);
-
-    /// @brief Does nothing.
-    virtual void commit();
-
-private:
-
-    // Parsed parameter name
-    std::string param_name_;
-
-    /// Global parser context.
-    ParserContextPtr global_context_;
-};
-
-
 /// @brief parser for MAC/hardware aquisition sources
 ///
 /// This parser handles Dhcp6/mac-sources entry.

+ 131 - 0
src/lib/dhcpsrv/parsers/ifaces_config_parser.cc

@@ -0,0 +1,131 @@
+// 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 <cc/data.h>
+#include <dhcpsrv/cfgmgr.h>
+#include <dhcpsrv/parsers/ifaces_config_parser.h>
+#include <boost/foreach.hpp>
+#include <string>
+#include <sys/types.h>
+
+using namespace isc::data;
+
+namespace isc {
+namespace dhcp {
+
+InterfaceListConfigParser::InterfaceListConfigParser(const int protocol)
+    : protocol_(protocol) {
+}
+
+void
+InterfaceListConfigParser::build(ConstElementPtr value) {
+    CfgIface cfg_iface;
+
+    BOOST_FOREACH(ConstElementPtr iface, value->listValue()) {
+        std::string iface_name = iface->stringValue();
+        try {
+            cfg_iface.use(protocol_, iface_name);
+
+        } catch (const std::exception& ex) {
+            isc_throw(DhcpConfigError, "Failed to select interface: "
+                      << ex.what() << " (" << value->getPosition() << ")");
+        }
+    }
+    CfgMgr::instance().getStagingCfg()->setCfgIface(cfg_iface);
+}
+
+void
+InterfaceListConfigParser::commit() {
+    // Nothing to do.
+}
+
+IfacesConfigParser::IfacesConfigParser(const int protocol)
+    : protocol_(protocol) {
+}
+
+void
+IfacesConfigParser::build(isc::data::ConstElementPtr ifaces_config) {
+    BOOST_FOREACH(ConfigPair element, ifaces_config->mapValue()) {
+        try {
+            if (element.first == "interfaces") {
+                InterfaceListConfigParser parser(protocol_);
+                parser.build(element.second);
+
+            } 
+
+        } catch (const std::exception& ex) {
+            // Append line number where the error occurred.
+            isc_throw(DhcpConfigError, ex.what() << " ("
+                      << element.second->getPosition() << ")");
+        }
+    }
+}
+
+bool
+IfacesConfigParser::isGenericParameter(const std::string& parameter) const {
+    // Currently, the "interfaces" is the only common parameter for
+    // DHCPv4 and DHCPv6.
+    return (parameter == "interfaces");
+}
+
+IfacesConfigParser4::IfacesConfigParser4()
+    : IfacesConfigParser(AF_INET) {
+}
+
+void
+IfacesConfigParser4::build(isc::data::ConstElementPtr ifaces_config) {
+    IfacesConfigParser::build(ifaces_config);
+    BOOST_FOREACH(ConfigPair element, ifaces_config->mapValue()) {
+        try {
+            if (element.first == "socket-type") {
+                /// @todo set socket-type
+
+            } else if (!isGenericParameter(element.first)) {
+                isc_throw(DhcpConfigError, "usupported parameter '"
+                          << element.first << "'");
+            }
+
+        } catch (const std::exception& ex) {
+            // Append line number where the error occurred.
+            isc_throw(DhcpConfigError, ex.what() << " ("
+                      << element.second->getPosition() << ")");
+        }
+    }
+}
+
+IfacesConfigParser6::IfacesConfigParser6()
+    : IfacesConfigParser(AF_INET6) {
+}
+
+void
+IfacesConfigParser6::build(isc::data::ConstElementPtr ifaces_config) {
+    IfacesConfigParser::build(ifaces_config);
+
+    BOOST_FOREACH(ConfigPair element, ifaces_config->mapValue()) {
+        try {
+            if (!isGenericParameter(element.first)) {
+                isc_throw(DhcpConfigError, "usupported parameter '"
+                          << element.first << "'");
+            }
+
+        } catch (const std::exception& ex) {
+            // Append line number where the error occurred.
+            isc_throw(DhcpConfigError, ex.what() << " ("
+                      << element.second->getPosition() << ")");
+        }
+    }
+}
+
+} // end of namespace isc::dhcp
+} // end of namespace isc

+ 167 - 0
src/lib/dhcpsrv/parsers/ifaces_config_parser.h

@@ -0,0 +1,167 @@
+// 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 IFACES_CONFIG_PARSER_H
+#define IFACES_CONFIG_PARSER_H
+
+#include <cc/data.h>
+#include <dhcpsrv/parsers/dhcp_config_parser.h>
+#include <dhcpsrv/parsers/dhcp_parsers.h>
+
+namespace isc {
+namespace dhcp {
+
+/// @brief Parser for interface list definition.
+///
+/// This parser handles Dhcp4/interface-config/interfaces and
+/// Dhcp6/interface-config/interfaces entries.
+/// It contains a list of network interfaces that the server listens on.
+/// In particular, it can contain an "*" that designates all interfaces.
+class InterfaceListConfigParser : public DhcpConfigParser {
+public:
+
+    /// @brief Constructor
+    ///
+    /// @param protocol AF_INET for DHCPv4 and AF_INET6 for DHCPv6.
+    ///
+    /// @throw BadValue if supplied parameter name is not "interface"
+    InterfaceListConfigParser(const int protocol);
+
+    /// @brief Parses a list of interface names.
+    ///
+    /// This method parses a list of interface/address tuples in a text
+    /// format. The tuples specify the IP addresses and corresponding
+    /// interface names on which the server should listen to the DHCP
+    /// messages. The address is optional in each tuple and, if not
+    /// specified, the interface name (without slash character) should
+    /// be present.
+    ///
+    /// @param value pointer to the content of parsed values
+    ///
+    /// @throw DhcpConfigError if the interface names and/or addresses
+    /// are invalid.
+    virtual void build(isc::data::ConstElementPtr value);
+
+    /// @brief Does nothing.
+    virtual void commit();
+
+private:
+
+    /// @brief AF_INET for DHCPv4 and AF_INET6 for DHCPv6.
+    int protocol_;
+
+};
+
+
+/// @brief Parser for the configuration of interfaces.
+///
+/// This parser parses the "interface-config" parameter which holds the
+/// full configuration of the DHCP server with respect to the use of
+/// interfaces, sockets and alike.
+///
+/// This parser uses the @c InterfaceListConfigParser to parse the
+/// list of interfaces on which the server should listen. It handles
+/// remaining parameters internally.
+///
+/// This parser is used as a base for the DHCPv4 and DHCPv6 specific
+/// parsers and should not be used directly.
+class IfacesConfigParser : public DhcpConfigParser {
+public:
+
+    /// @brief Constructor
+    ///
+    /// @param protocol AF_INET for DHCPv4 and AF_INET6 for DHCPv6.
+    IfacesConfigParser(const int protocol);
+
+    /// @brief Parses generic parameters in "interface-config".
+    ///
+    /// The generic parameters in the "interface-config" map are
+    /// the ones that are common for DHCPv4 and DHCPv6.
+    ///
+    /// @param ifaces_config A data element holding configuration of
+    /// interfaces.
+    virtual void build(isc::data::ConstElementPtr ifaces_config);
+
+    /// @brief Commit, unused.
+    virtual void commit() { }
+
+    /// @brief Checks if the specified parameter is a common parameter
+    /// for DHCPv4 and DHCPv6 interface configuration.
+    ///
+    /// This method is invoked by the derived classes to check if the
+    /// particular parameter is supported.
+    ///
+    /// @param parameter A name of the parameter.
+    ///
+    /// @return true if the specified parameter is a common parameter
+    /// for DHCPv4 and DHCPv6 server.
+    bool isGenericParameter(const std::string& parameter) const;
+
+private:
+
+    /// @brief AF_INET for DHCPv4 and AF_INET6 for DHCPv6.
+    int protocol_;
+
+};
+
+
+/// @brief Parser for the "interface-config" parameter of the DHCPv4 server.
+class IfacesConfigParser4 : public IfacesConfigParser {
+public:
+
+    /// @brief Constructor.
+    ///
+    /// Sets the protocol to AF_INET.
+    IfacesConfigParser4();
+
+    /// @brief Parses DHCPv4 specific parameters.
+    ///
+    /// Internally it invokes the @c InterfaceConfigParser::build to parse
+    /// generic parameters. In addition, it parses the following parameters:
+    /// - socket-type
+    ///
+    /// @param ifaces_config A data element holding configuration of
+    /// interfaces.
+    ///
+    /// @throw DhcpConfigError if unsupported parameters is specified.
+    virtual void build(isc::data::ConstElementPtr ifaces_config);
+
+};
+
+/// @brief Parser for the "interface-config" parameter of the DHCPv4 server.
+class IfacesConfigParser6 : public IfacesConfigParser {
+public:
+
+    /// @brief Constructor.
+    ///
+    /// Sets the protocol to AF_INET6.
+    IfacesConfigParser6();
+
+    /// @brief Parses DHCPv6 specific parameters.
+    ///
+    /// Internally it invokes the @c InterfaceConfigParser::build to parse
+    /// generic parameters. Currently it doesn't parse any other parameters.
+    ///
+    /// @param ifaces_config A data element holding configuration of
+    /// interfaces.
+    ///
+    /// @throw DhcpConfigError if unsupported parameters is specified.
+    virtual void build(isc::data::ConstElementPtr ifaces_config);
+
+};
+
+}
+} // end of namespace isc
+
+#endif // IFACES_CONFIG_PARSER_H

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

@@ -73,6 +73,7 @@ libdhcpsrv_unittests_SOURCES += host_mgr_unittest.cc
 libdhcpsrv_unittests_SOURCES += host_unittest.cc
 libdhcpsrv_unittests_SOURCES += host_reservation_parser_unittest.cc
 libdhcpsrv_unittests_SOURCES += host_reservations_list_parser_unittest.cc
+libdhcpsrv_unittests_SOURCES += ifaces_config_parser_unittest.cc
 libdhcpsrv_unittests_SOURCES += lease_file_io.cc lease_file_io.h
 libdhcpsrv_unittests_SOURCES += lease_file_loader_unittest.cc
 libdhcpsrv_unittests_SOURCES += lease_unittest.cc

+ 0 - 59
src/lib/dhcpsrv/tests/dhcp_parsers_unittest.cc

@@ -213,65 +213,6 @@ TEST_F(DhcpParserTest, uint32ParserTest) {
     EXPECT_EQ(test_value, actual_value);
 }
 
-/// @brief Check InterfaceListConfigParser  basic functionality
-///
-/// Verifies that the parser:
-/// 1. Does not allow empty for storage.
-/// 2. Does not allow name other than "interfaces"
-/// 3. Parses list of interfaces and adds them to CfgMgr
-/// 4. Parses wildcard interface name and sets a CfgMgr flag which indicates
-/// that server will listen on all interfaces.
-TEST_F(DhcpParserTest, interfaceListParserTest) {
-    IfaceMgrTestConfig test_config(true);
-
-    const std::string name = "interfaces";
-
-    ParserContextPtr parser_context(new ParserContext(Option::V4));
-
-    // Verify that parser constructor fails if parameter name isn't "interface"
-    EXPECT_THROW(InterfaceListConfigParser("bogus_name", parser_context),
-                 isc::BadValue);
-
-    boost::scoped_ptr<InterfaceListConfigParser>
-        parser(new InterfaceListConfigParser(name, parser_context));
-    ElementPtr list_element = Element::createList();
-    list_element->add(Element::create("eth0"));
-
-    // This should parse the configuration and add eth0 and eth1 to the list
-    // of interfaces that server should listen on.
-    parser->build(list_element);
-    parser->commit();
-
-    // Use CfgMgr instance to check if eth0 and eth1 was added, and that
-    // eth2 was not added.
-    SrvConfigPtr cfg = CfgMgr::instance().getStagingCfg();
-    ASSERT_TRUE(cfg);
-    ASSERT_NO_THROW(cfg->getCfgIface().openSockets(AF_INET, 10000));
-
-    EXPECT_TRUE(test_config.socketOpen("eth0", AF_INET));
-    EXPECT_FALSE(test_config.socketOpen("eth1", AF_INET));
-
-    // Add keyword all to the configuration. This should activate all
-    // interfaces, including eth2, even though it has not been explicitly
-    // added.
-    list_element->add(Element::create("*"));
-
-    // Reset parser and configuration.
-    parser.reset(new InterfaceListConfigParser(name, parser_context));
-    cfg->getCfgIface().closeSockets();
-    CfgMgr::instance().clear();
-
-    parser->build(list_element);
-    parser->commit();
-
-    cfg = CfgMgr::instance().getStagingCfg();
-    ASSERT_NO_THROW(cfg->getCfgIface().openSockets(AF_INET, 10000));
-
-    EXPECT_TRUE(test_config.socketOpen("eth0", AF_INET));
-    EXPECT_TRUE(test_config.socketOpen("eth1", AF_INET));
-}
-
-
 /// @brief Check MACSourcesListConfigParser  basic functionality
 ///
 /// Verifies that the parser:

+ 103 - 0
src/lib/dhcpsrv/tests/ifaces_config_parser_unittest.cc

@@ -0,0 +1,103 @@
+// 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/tests/iface_mgr_test_config.h>
+#include <dhcpsrv/cfgmgr.h>
+#include <dhcpsrv/parsers/ifaces_config_parser.h>
+#include <gtest/gtest.h>
+
+using namespace isc::data;
+using namespace isc::dhcp;
+using namespace isc::dhcp::test;
+
+namespace {
+
+/// @brief Test fixture class for @c IfacesConfigParser
+class IfacesConfigParserTest : public ::testing::Test {
+protected:
+
+    /// @brief Setup for each test.
+    ///
+    /// Clears the configuration in the @c CfgMgr.
+    virtual void SetUp();
+
+    /// @brief Cleans up after each test.
+    ///
+    /// Clears the configuration in the @c CfgMgr.
+    virtual void TearDown();
+
+};
+
+void
+IfacesConfigParserTest::SetUp() {
+    CfgMgr::instance().clear();
+}
+
+void
+IfacesConfigParserTest::TearDown() {
+    CfgMgr::instance().clear();
+}
+
+// This test checks that the parser correctly parses the list of interfaces
+// on which the server should listen.
+TEST_F(IfacesConfigParserTest, interfaces) {
+    // Creates fake interfaces with fake addresses.
+    IfaceMgrTestConfig test_config(true);
+
+    // Configuration with one interface.
+    std::string config = "{"
+        "\"interfaces\": [ \"eth0\" ],"
+        "\"socket-type\": \"raw\" }";
+
+    ElementPtr config_element = Element::fromJSON(config);
+
+    // Parse the configuration.
+    IfacesConfigParser4 parser;
+    ASSERT_NO_THROW(parser.build(config_element));
+
+    // Open sockets according to the parsed configuration.
+    SrvConfigPtr cfg = CfgMgr::instance().getStagingCfg();
+    ASSERT_TRUE(cfg);
+    ASSERT_NO_THROW(cfg->getCfgIface().openSockets(AF_INET, 10000));
+
+    // Only eth0 should have an open socket.
+    EXPECT_TRUE(test_config.socketOpen("eth0", AF_INET));
+    EXPECT_FALSE(test_config.socketOpen("eth1", AF_INET));
+
+    // Reset configuration.
+    cfg->getCfgIface().closeSockets();
+    CfgMgr::instance().clear();
+
+    // Try similar configuration but this time add a wildcard interface
+    // to see if sockets will open on all interfaces.
+    config = "{"
+        "\"interfaces\": [ \"eth0\", \"*\" ],"
+        "\"socket-type\": \"raw\" }";
+
+    config_element = Element::fromJSON(config);
+
+    ASSERT_NO_THROW(parser.build(config_element));
+
+    cfg = CfgMgr::instance().getStagingCfg();
+    ASSERT_NO_THROW(cfg->getCfgIface().openSockets(AF_INET, 10000));
+
+    EXPECT_TRUE(test_config.socketOpen("eth0", AF_INET));
+    EXPECT_TRUE(test_config.socketOpen("eth1", AF_INET));
+}
+
+
+} // end of anonymous namespace