From: Marcin Siodelski Date: Fri, 6 Feb 2015 18:56:39 +0000 (+0100) Subject: [3604] Created a configuration parser for interface-config map. X-Git-Tag: trac3712_base~9^2~12 X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=0677750e48abe7d362aa32e911d8f1a62f691981;p=thirdparty%2Fkea.git [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. --- diff --git a/src/lib/dhcpsrv/Makefile.am b/src/lib/dhcpsrv/Makefile.am index bef01c9594..282b041205 100644 --- a/src/lib/dhcpsrv/Makefile.am +++ b/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 diff --git a/src/lib/dhcpsrv/parsers/dhcp_parsers.cc b/src/lib/dhcpsrv/parsers/dhcp_parsers.cc index bf9a47731f..068216fdbd 100644 --- a/src/lib/dhcpsrv/parsers/dhcp_parsers.cc +++ b/src/lib/dhcpsrv/parsers/dhcp_parsers.cc @@ -173,41 +173,6 @@ template <> void ValueParser::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:: diff --git a/src/lib/dhcpsrv/parsers/dhcp_parsers.h b/src/lib/dhcpsrv/parsers/dhcp_parsers.h index 46e1f7f865..53bf505ee3 100644 --- a/src/lib/dhcpsrv/parsers/dhcp_parsers.h +++ b/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. diff --git a/src/lib/dhcpsrv/parsers/ifaces_config_parser.cc b/src/lib/dhcpsrv/parsers/ifaces_config_parser.cc new file mode 100644 index 0000000000..5c9fa34b9a --- /dev/null +++ b/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 +#include +#include +#include +#include +#include + +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 diff --git a/src/lib/dhcpsrv/parsers/ifaces_config_parser.h b/src/lib/dhcpsrv/parsers/ifaces_config_parser.h new file mode 100644 index 0000000000..6f2037e866 --- /dev/null +++ b/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 +#include +#include + +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 diff --git a/src/lib/dhcpsrv/tests/Makefile.am b/src/lib/dhcpsrv/tests/Makefile.am index 5e0f4e0dbf..cbd0865f48 100644 --- a/src/lib/dhcpsrv/tests/Makefile.am +++ b/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 diff --git a/src/lib/dhcpsrv/tests/dhcp_parsers_unittest.cc b/src/lib/dhcpsrv/tests/dhcp_parsers_unittest.cc index 61b8e9d080..cbd9034286 100644 --- a/src/lib/dhcpsrv/tests/dhcp_parsers_unittest.cc +++ b/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 - 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: diff --git a/src/lib/dhcpsrv/tests/ifaces_config_parser_unittest.cc b/src/lib/dhcpsrv/tests/ifaces_config_parser_unittest.cc new file mode 100644 index 0000000000..d3ad960364 --- /dev/null +++ b/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 + +#include +#include +#include +#include +#include + +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