Commit 0677750e authored by Marcin Siodelski's avatar Marcin Siodelski

[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.
parent 814a6cea
......@@ -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
......
......@@ -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::
......
......@@ -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.
......
// 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
// 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
......@@ -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
......
......@@ -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:
......
// 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
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment