BIND 10 master, updated. 066c983e091831645218810fb921986a659ccb1b [master] Merge branch 'trac2355'
BIND 10 source code commits
bind10-changes at lists.isc.org
Tue May 14 18:42:48 UTC 2013
The branch, master has been updated
via 066c983e091831645218810fb921986a659ccb1b (commit)
via 2df5585f42f1af03b89204c7cc22569720a26ae4 (commit)
via 36f7b7fa7bddd07da90c9346d5d62dbed77c2a49 (commit)
via aedbd11c6780fb36fbaa8c6c8c4521caf24fa5b4 (commit)
via c519aa88c8cf4edb25a53ec761364b076a6ae497 (commit)
via f18722f2e329c939584ff1b45936eed172fb16e3 (commit)
via f3259cf183a8bea65acad690b50982057f24abe5 (commit)
via 36d8374051ef3df79035debd0bcdf63016d0aa85 (commit)
via 702512a6bbb48421042b69fba312bb41296eab61 (commit)
via 53ceb71e36d310a2a4cd5fef7ae9fa5d410f078a (commit)
from 95c6f676d504950bab0964fe9297893f36d4fac6 (commit)
Those revisions listed above that are new to this repository have
not appeared on any other notification email; so we list those
revisions in full, below.
- Log -----------------------------------------------------------------
commit 066c983e091831645218810fb921986a659ccb1b
Merge: 95c6f67 2df5585
Author: Thomas Markwalder <tmark at isc.org>
Date: Tue May 14 14:38:45 2013 -0400
[master] Merge branch 'trac2355'
-----------------------------------------------------------------------
Summary of changes:
src/bin/dhcp4/config_parser.cc | 1749 +++--------------------
src/bin/dhcp4/config_parser.h | 25 +-
src/bin/dhcp4/tests/config_parser_unittest.cc | 5 +-
src/bin/dhcp6/config_parser.cc | 1813 ++++--------------------
src/bin/dhcp6/config_parser.h | 13 +-
src/lib/dhcpsrv/Makefile.am | 2 +
src/lib/dhcpsrv/dhcp_config_parser.h | 73 -
src/lib/dhcpsrv/dhcp_parsers.cc | 957 +++++++++++++
src/lib/dhcpsrv/dhcp_parsers.h | 765 ++++++++++
src/lib/dhcpsrv/subnet.cc | 65 +-
src/lib/dhcpsrv/subnet.h | 25 +-
src/lib/dhcpsrv/tests/Makefile.am | 1 +
src/lib/dhcpsrv/tests/cfgmgr_unittest.cc | 3 +-
src/lib/dhcpsrv/tests/dhcp_parsers_unittest.cc | 519 +++++++
14 files changed, 2766 insertions(+), 3249 deletions(-)
create mode 100644 src/lib/dhcpsrv/dhcp_parsers.cc
create mode 100644 src/lib/dhcpsrv/dhcp_parsers.h
create mode 100644 src/lib/dhcpsrv/tests/dhcp_parsers_unittest.cc
-----------------------------------------------------------------------
diff --git a/src/bin/dhcp4/config_parser.cc b/src/bin/dhcp4/config_parser.cc
index 3a54c28..f30ff21 100644
--- a/src/bin/dhcp4/config_parser.cc
+++ b/src/bin/dhcp4/config_parser.cc
@@ -13,19 +13,21 @@
// PERFORMANCE OF THIS SOFTWARE.
#include <config/ccsession.h>
-#include <dhcp4/config_parser.h>
#include <dhcp4/dhcp4_log.h>
#include <dhcp/libdhcp++.h>
#include <dhcp/option_definition.h>
#include <dhcpsrv/cfgmgr.h>
+#include <dhcp4/config_parser.h>
#include <dhcpsrv/dbaccess_parser.h>
-#include <dhcpsrv/dhcp_config_parser.h>
+#include <dhcpsrv/dhcp_parsers.h>
#include <dhcpsrv/option_space_container.h>
#include <util/encode/hex.h>
#include <util/strutil.h>
+
#include <boost/foreach.hpp>
#include <boost/lexical_cast.hpp>
#include <boost/algorithm/string.hpp>
+
#include <limits>
#include <iostream>
#include <vector>
@@ -39,1410 +41,213 @@ using namespace isc::asiolink;
namespace {
-// Forward declarations of some of the parser classes.
-// They are used to define pointer types for these classes.
-class BooleanParser;
-class StringParser;
-class Uint32Parser;
-
-// Pointers to various parser objects.
-typedef boost::shared_ptr<BooleanParser> BooleanParserPtr;
-typedef boost::shared_ptr<StringParser> StringParserPtr;
-typedef boost::shared_ptr<Uint32Parser> Uint32ParserPtr;
-
-/// @brief a factory method that will create a parser for a given element name
-typedef isc::dhcp::DhcpConfigParser* ParserFactory(const std::string& config_id);
-
-/// @brief a collection of factories that creates parsers for specified element names
-typedef std::map<std::string, ParserFactory*> FactoryMap;
-
-/// @brief Storage for option definitions.
-typedef OptionSpaceContainer<OptionDefContainer,
- OptionDefinitionPtr> OptionDefStorage;
-
-/// @brief a collection of pools
-///
-/// That type is used as intermediate storage, when pools are parsed, but there is
-/// no subnet object created yet to store them.
-typedef std::vector<Pool4Ptr> PoolStorage;
-
-/// Collection of containers holding option spaces. Each container within
-/// a particular option space holds so-called option descriptors.
-typedef OptionSpaceContainer<Subnet::OptionContainer,
- Subnet::OptionDescriptor> OptionStorage;
-
-/// @brief Global uint32 parameters that will be used as defaults.
-Uint32Storage uint32_defaults;
-
-/// @brief global string parameters that will be used as defaults.
-StringStorage string_defaults;
-
-/// @brief Global storage for options that will be used as defaults.
-OptionStorage option_defaults;
-
-/// @brief Global storage for option definitions.
-OptionDefStorage option_def_intermediate;
-
-/// @brief a dummy configuration parser
-///
-/// It is a debugging parser. It does not configure anything,
-/// will accept any configuration and will just print it out
-/// on commit. Useful for debugging existing configurations and
-/// adding new ones.
-class DebugParser : public DhcpConfigParser {
-public:
-
- /// @brief Constructor
- ///
- /// See @ref DhcpConfigParser class for details.
- ///
- /// @param param_name name of the parsed parameter
- DebugParser(const std::string& param_name)
- :param_name_(param_name) {
- }
-
- /// @brief builds parameter value
- ///
- /// See @ref DhcpConfigParser class for details.
- ///
- /// @param new_config pointer to the new configuration
- virtual void build(ConstElementPtr new_config) {
- std::cout << "Build for token: [" << param_name_ << "] = ["
- << value_->str() << "]" << std::endl;
- value_ = new_config;
- }
-
- /// @brief pretends to apply the configuration
- ///
- /// This is a method required by base class. It pretends to apply the
- /// configuration, but in fact it only prints the parameter out.
- ///
- /// See @ref DhcpConfigParser class for details.
- virtual void commit() {
- // Debug message. The whole DebugParser class is used only for parser
- // debugging, and is not used in production code. It is very convenient
- // to keep it around. Please do not turn this cout into logger calls.
- std::cout << "Commit for token: [" << param_name_ << "] = ["
- << value_->str() << "]" << std::endl;
- }
-
- /// @brief factory that constructs DebugParser objects
- ///
- /// @param param_name name of the parameter to be parsed
- static DhcpConfigParser* Factory(const std::string& param_name) {
- return (new DebugParser(param_name));
- }
-
-private:
- /// name of the parsed parameter
- std::string param_name_;
-
- /// pointer to the actual value of the parameter
- ConstElementPtr value_;
-};
-
-/// @brief A boolean value parser.
-///
-/// This parser handles configuration values of the boolean type.
-/// Parsed values are stored in a provided storage. If no storage
-/// is provided then the build function throws an exception.
-class BooleanParser : public DhcpConfigParser {
-public:
- /// @brief Constructor.
- ///
- /// @param param_name name of the parameter.
- BooleanParser(const std::string& param_name)
- : storage_(NULL),
- param_name_(param_name),
- value_(false) {
- // Empty parameter name is invalid.
- if (param_name_.empty()) {
- isc_throw(isc::dhcp::DhcpConfigError, "parser logic error:"
- << "empty parameter name provided");
- }
- }
-
- /// @brief Parse a boolean value.
- ///
- /// @param value a value to be parsed.
- ///
- /// @throw isc::InvalidOperation if a storage has not been set
- /// prior to calling this function
- /// @throw isc::dhcp::DhcpConfigError if a provided parameter's
- /// name is empty.
- virtual void build(ConstElementPtr value) {
- if (storage_ == NULL) {
- isc_throw(isc::InvalidOperation, "parser logic error:"
- << " storage for the " << param_name_
- << " value has not been set");
- } else if (param_name_.empty()) {
- isc_throw(isc::dhcp::DhcpConfigError, "parser logic error:"
- << "empty parameter name provided");
- }
- // The Config Manager checks if user specified a
- // valid value for a boolean parameter: True or False.
- // It is then ok to assume that if str() does not return
- // 'true' the value is 'false'.
- value_ = (value->str() == "true") ? true : false;
- }
-
- /// @brief Put a parsed value to the storage.
- virtual void commit() {
- if (storage_ != NULL && !param_name_.empty()) {
- storage_->setParam(param_name_, value_);
- }
- }
-
- /// @brief Create an instance of the boolean parser.
- ///
- /// @param param_name name of the parameter for which the
- /// parser is created.
- static DhcpConfigParser* factory(const std::string& param_name) {
- return (new BooleanParser(param_name));
- }
-
- /// @brief Set the storage for parsed value.
- ///
- /// This function must be called prior to calling build.
- ///
- /// @param storage a pointer to the storage where parsed data
- /// is to be stored.
- void setStorage(BooleanStorage* storage) {
- storage_ = storage;
- }
-
-private:
- /// Pointer to the storage where parsed value is stored.
- BooleanStorage* storage_;
- /// Name of the parameter which value is parsed with this parser.
- std::string param_name_;
- /// Parsed value.
- bool value_;
-};
-
-/// @brief Configuration parser for uint32 parameters
-///
-/// This class is a generic parser that is able to handle any uint32 integer
-/// type. By default it stores the value in external global container
-/// (uint32_defaults). If used in smaller scopes (e.g. to parse parameters
-/// in subnet config), it can be pointed to a different storage, using
-/// setStorage() method. This class follows the parser interface, laid out
-/// in its base class, @ref DhcpConfigParser.
-///
-/// For overview of usability of this generic purpose parser, see
-/// @ref dhcpv4ConfigInherit page.
-class Uint32Parser : public DhcpConfigParser {
-public:
-
- /// @brief constructor for Uint32Parser
- /// @param param_name name of the configuration parameter being parsed
- Uint32Parser(const std::string& param_name)
- : storage_(&uint32_defaults),
- param_name_(param_name) {
- // Empty parameter name is invalid.
- if (param_name_.empty()) {
- isc_throw(DhcpConfigError, "parser logic error:"
- << "empty parameter name provided");
- }
- }
-
- /// @brief Parses configuration configuration parameter as uint32_t.
- ///
- /// @param value pointer to the content of parsed values
- /// @throw BadValue if supplied value could not be base to uint32_t
- /// or the parameter name is empty.
- virtual void build(ConstElementPtr value) {
- if (param_name_.empty()) {
- isc_throw(DhcpConfigError, "parser logic error:"
- << "empty parameter name provided");
- }
-
- int64_t check;
- string x = value->str();
- try {
- check = boost::lexical_cast<int64_t>(x);
- } catch (const boost::bad_lexical_cast &) {
- isc_throw(BadValue, "Failed to parse value " << value->str()
- << " as unsigned 32-bit integer.");
- }
- if (check > std::numeric_limits<uint32_t>::max()) {
- isc_throw(BadValue, "Value " << value->str() << "is too large"
- << " for unsigned 32-bit integer.");
- }
- if (check < 0) {
- isc_throw(BadValue, "Value " << value->str() << "is negative."
- << " Only 0 or larger are allowed for unsigned 32-bit integer.");
- }
-
- // value is small enough to fit
- value_ = static_cast<uint32_t>(check);
- }
-
- /// @brief Stores the parsed uint32_t value in a storage.
- virtual void commit() {
- if (storage_ != NULL && !param_name_.empty()) {
- // If a given parameter already exists in the storage we override
- // its value. If it doesn't we insert a new element.
- storage_->setParam(param_name_, value_);
- }
- }
-
- /// @brief factory that constructs Uint32Parser objects
- ///
- /// @param param_name name of the parameter to be parsed
- static DhcpConfigParser* factory(const std::string& param_name) {
- return (new Uint32Parser(param_name));
- }
-
- /// @brief sets storage for value of this parameter
- ///
- /// See @ref dhcpv4ConfigInherit for details.
- ///
- /// @param storage pointer to the storage container
- void setStorage(Uint32Storage* storage) {
- storage_ = storage;
- }
-
-private:
- /// pointer to the storage, where parsed value will be stored
- Uint32Storage* storage_;
-
- /// name of the parameter to be parsed
- std::string param_name_;
-
- /// the actual parsed value
- uint32_t value_;
-};
-
-/// @brief Configuration parser for string parameters
-///
-/// This class is a generic parser that is able to handle any string
-/// parameter. By default it stores the value in external global container
-/// (string_defaults). If used in smaller scopes (e.g. to parse parameters
-/// in subnet config), it can be pointed to a different storage, using
-/// setStorage() method. This class follows the parser interface, laid out
-/// in its base class, @ref DhcpConfigParser.
-///
-/// For overview of usability of this generic purpose parser, see
-/// @ref dhcpv4ConfigInherit page.
-class StringParser : public DhcpConfigParser {
-public:
-
- /// @brief constructor for StringParser
- /// @param param_name name of the configuration parameter being parsed
- StringParser(const std::string& param_name)
- :storage_(&string_defaults), param_name_(param_name) {
- // Empty parameter name is invalid.
- if (param_name_.empty()) {
- isc_throw(DhcpConfigError, "parser logic error:"
- << "empty parameter name provided");
- }
- }
-
- /// @brief parses parameter value
- ///
- /// Parses configuration entry and stores it in storage. See
- /// @ref setStorage() for details.
- ///
- /// @param value pointer to the content of parsed values
- virtual void build(ConstElementPtr value) {
- value_ = value->str();
- boost::erase_all(value_, "\"");
- }
-
- /// @brief Stores the parsed value in a storage.
- virtual void commit() {
- if (storage_ != NULL && !param_name_.empty()) {
- // If a given parameter already exists in the storage we override
- // its value. If it doesn't we insert a new element.
- storage_->setParam(param_name_, value_);
- }
- }
-
- /// @brief factory that constructs StringParser objects
- ///
- /// @param param_name name of the parameter to be parsed
- static DhcpConfigParser* factory(const std::string& param_name) {
- return (new StringParser(param_name));
- }
-
- /// @brief sets storage for value of this parameter
- ///
- /// See \ref dhcpv4ConfigInherit for details.
- ///
- /// @param storage pointer to the storage container
- void setStorage(StringStorage* storage) {
- storage_ = storage;
- }
-
-private:
- /// pointer to the storage, where parsed value will be stored
- StringStorage* storage_;
-
- /// name of the parameter to be parsed
- std::string param_name_;
-
- /// the actual parsed value
- std::string value_;
-};
-
-
-/// @brief parser for interface list definition
-///
-/// This parser handles Dhcp4/interface entry.
-/// 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.
-///
-/// It is useful for parsing Dhcp4/interface parameter.
-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
- /// @throw BadValue if supplied parameter name is not "interface"
- InterfaceListConfigParser(const std::string& param_name) {
- if (param_name != "interface") {
- isc_throw(BadValue, "Internal error. Interface configuration "
- "parser called for the wrong parameter: " << param_name);
- }
- }
-
- /// @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(ConstElementPtr value) {
- BOOST_FOREACH(ConstElementPtr iface, value->listValue()) {
- interfaces_.push_back(iface->str());
- }
- }
-
- /// @brief commits interfaces list configuration
- virtual void commit() {
- /// @todo: Implement per interface listening. Currently always listening
- /// on all interfaces.
- }
-
- /// @brief factory that constructs InterfaceListConfigParser objects
- ///
- /// @param param_name name of the parameter to be parsed
- static DhcpConfigParser* factory(const std::string& param_name) {
- return (new InterfaceListConfigParser(param_name));
- }
-
-private:
- /// contains list of network interfaces
- vector<string> interfaces_;
-};
-
-/// @brief parser for pool definition
-///
-/// This parser handles pool definitions, i.e. a list of entries of one
-/// of two syntaxes: min-max and prefix/len. Pool4 objects are created
-/// and stored in chosen PoolStorage container.
-///
-/// As there are no default values for pool, setStorage() must be called
-/// before build(). Otherwise exception will be thrown.
-///
-/// It is useful for parsing Dhcp4/subnet4[X]/pool parameters.
-class PoolParser : public DhcpConfigParser {
-public:
-
- /// @brief constructor.
- PoolParser(const std::string& /*param_name*/)
- :pools_(NULL) {
- // ignore parameter name, it is always Dhcp4/subnet4[X]/pool
- }
-
- /// @brief parses the actual list
- ///
- /// This method parses the actual list of interfaces.
- /// No validation is done at this stage, everything is interpreted as
- /// interface name.
- /// @param pools_list list of pools defined for a subnet
- /// @throw InvalidOperation if storage was not specified (setStorage() not called)
- /// @throw DhcpConfigError when pool parsing fails
- void build(ConstElementPtr pools_list) {
- // setStorage() should have been called before build
- if (!pools_) {
- isc_throw(InvalidOperation, "Parser logic error. No pool storage set,"
- " but pool parser asked to parse pools");
- }
-
- BOOST_FOREACH(ConstElementPtr text_pool, pools_list->listValue()) {
-
- // That should be a single pool representation. It should contain
- // text is form prefix/len or first - last. Note that spaces
- // are allowed
- string txt = text_pool->stringValue();
-
- // first let's remove any whitespaces
- boost::erase_all(txt, " "); // space
- boost::erase_all(txt, "\t"); // tabulation
-
- // Is this prefix/len notation?
- size_t pos = txt.find("/");
- if (pos != string::npos) {
- IOAddress addr("::");
- uint8_t len = 0;
- try {
- addr = IOAddress(txt.substr(0, pos));
-
- // start with the first character after /
- string prefix_len = txt.substr(pos + 1);
-
- // It is lexical cast to int and then downcast to uint8_t.
- // Direct cast to uint8_t (which is really an unsigned char)
- // will result in interpreting the first digit as output
- // value and throwing exception if length is written on two
- // digits (because there are extra characters left over).
-
- // No checks for values over 128. Range correctness will
- // be checked in Pool4 constructor.
- len = boost::lexical_cast<int>(prefix_len);
- } catch (...) {
- isc_throw(DhcpConfigError, "Failed to parse pool "
- "definition: " << text_pool->stringValue());
- }
-
- Pool4Ptr pool(new Pool4(addr, len));
- local_pools_.push_back(pool);
- continue;
- }
-
- // Is this min-max notation?
- pos = txt.find("-");
- if (pos != string::npos) {
- // using min-max notation
- IOAddress min(txt.substr(0,pos));
- IOAddress max(txt.substr(pos + 1));
-
- Pool4Ptr pool(new Pool4(min, max));
-
- local_pools_.push_back(pool);
- continue;
- }
-
- isc_throw(DhcpConfigError, "Failed to parse pool definition:"
- << text_pool->stringValue() <<
- ". Does not contain - (for min-max) nor / (prefix/len)");
- }
- }
-
- /// @brief sets storage for value of this parameter
- ///
- /// See \ref dhcpv4ConfigInherit for details.
- ///
- /// @param storage pointer to the storage container
- void setStorage(PoolStorage* storage) {
- pools_ = storage;
- }
-
- /// @brief Stores the parsed values in a storage provided
- /// by an upper level parser.
- virtual void commit() {
- if (pools_) {
- // local_pools_ holds the values produced by the build function.
- // At this point parsing should have completed successfuly so
- // we can append new data to the supplied storage.
- pools_->insert(pools_->end(), local_pools_.begin(),
- local_pools_.end());
- }
- }
-
- /// @brief factory that constructs PoolParser objects
- ///
- /// @param param_name name of the parameter to be parsed
- static DhcpConfigParser* factory(const std::string& param_name) {
- return (new PoolParser(param_name));
- }
-
-private:
- /// @brief pointer to the actual Pools storage
- ///
- /// That is typically a storage somewhere in Subnet parser
- /// (an upper level parser).
- PoolStorage* pools_;
- /// A temporary storage for pools configuration. It is a
- /// storage where pools are stored by build function.
- PoolStorage local_pools_;
-};
-
-/// @brief Parser for option data value.
+/// @brief Parser for DHCP4 option data value.
///
/// This parser parses configuration entries that specify value of
-/// a single option. These entries include option name, option code
-/// and data carried by the option. The option data can be specified
-/// in one of the two available formats: binary value represented as
-/// a string of hexadecimal digits or a list of comma separated values.
-/// The format being used is controlled by csv-format configuration
-/// parameter. When setting this value to True, the latter format is
-/// used. The subsequent values in the CSV format apply to relevant
-/// option data fields in the configured option. For example the
-/// configuration: "data" : "192.168.2.0, 56, hello world" can be
-/// used to set values for the option comprising IPv4 address,
-/// integer and string data field. Note that order matters. If the
-/// order of values does not match the order of data fields within
-/// an option the configuration will not be accepted. If parsing
-/// is successful then an instance of an option is created and
-/// added to the storage provided by the calling class.
-class OptionDataParser : public DhcpConfigParser {
+/// a single option specific to DHCP4. It provides the DHCP4-specific
+/// implementation of the abstract class OptionDataParser.
+class Dhcp4OptionDataParser : public OptionDataParser {
public:
-
/// @brief Constructor.
///
- /// Class constructor.
- OptionDataParser(const std::string&)
- : options_(NULL),
- // initialize option to NULL ptr
- option_descriptor_(false) { }
-
- /// @brief Parses the single option data.
- ///
- /// This method parses the data of a single option from the configuration.
- /// The option data includes option name, option code and data being
- /// carried by this option. Eventually it creates the instance of the
- /// option.
- ///
- /// @warning setStorage must be called with valid storage pointer prior
- /// to calling this method.
- ///
- /// @param option_data_entries collection of entries that define value
- /// for a particular option.
- /// @throw DhcpConfigError if invalid parameter specified in
- /// the configuration.
- /// @throw isc::InvalidOperation if failed to set storage prior to
- /// calling build.
- virtual void build(ConstElementPtr option_data_entries) {
- if (options_ == NULL) {
- isc_throw(isc::InvalidOperation, "Parser logic error: storage must be set before "
- "parsing option data.");
- }
- BOOST_FOREACH(ConfigPair param, option_data_entries->mapValue()) {
- ParserPtr parser;
- if (param.first == "name" || param.first == "data" ||
- param.first == "space") {
- boost::shared_ptr<StringParser>
- name_parser(dynamic_cast<StringParser*>(StringParser::factory(param.first)));
- if (name_parser) {
- name_parser->setStorage(&string_values_);
- parser = name_parser;
- }
- } else if (param.first == "code") {
- boost::shared_ptr<Uint32Parser>
- code_parser(dynamic_cast<Uint32Parser*>(Uint32Parser::factory(param.first)));
- if (code_parser) {
- code_parser->setStorage(&uint32_values_);
- parser = code_parser;
- }
- } else if (param.first == "csv-format") {
- boost::shared_ptr<BooleanParser>
- value_parser(dynamic_cast<BooleanParser*>(BooleanParser::factory(param.first)));
- if (value_parser) {
- value_parser->setStorage(&boolean_values_);
- parser = value_parser;
- }
- } else {
- isc_throw(DhcpConfigError,
- "Parser error: option-data parameter not supported: "
- << param.first);
- }
- parser->build(param.second);
- // Before we can create an option we need to get the data from
- // the child parsers. The only way to do it is to invoke commit
- // on them so as they store the values in appropriate storages
- // that this class provided to them. Note that this will not
- // modify values stored in the global storages so the configuration
- // will remain consistent even parsing fails somewhere further on.
- parser->commit();
- }
- // Try to create the option instance.
- createOption();
- }
-
- /// @brief Commits option value.
- ///
- /// This function adds a new option to the storage or replaces an existing option
- /// with the same code.
- ///
- /// @throw isc::InvalidOperation if failed to set pointer to storage or failed
- /// to call build() prior to commit. If that happens data in the storage
- /// remain un-modified.
- virtual void commit() {
- if (options_ == NULL) {
- isc_throw(isc::InvalidOperation, "parser logic error: storage must be set before "
- "committing option data.");
- } else if (!option_descriptor_.option) {
- // Before we can commit the new option should be configured. If it is not
- // than somebody must have called commit() before build().
- isc_throw(isc::InvalidOperation, "parser logic error: no option has been configured and"
- " thus there is nothing to commit. Has build() been called?");
- }
- uint16_t opt_type = option_descriptor_.option->getType();
- Subnet::OptionContainerPtr options = options_->getItems(option_space_);
- // The getItems() should never return NULL pointer. If there are no
- // options configured for the particular option space a pointer
- // to an empty container should be returned.
- assert(options);
- Subnet::OptionContainerTypeIndex& idx = options->get<1>();
- // Try to find options with the particular option code in the main
- // storage. If found, remove these options because they will be
- // replaced with new one.
- Subnet::OptionContainerTypeRange range =
- idx.equal_range(opt_type);
- if (std::distance(range.first, range.second) > 0) {
- idx.erase(range.first, range.second);
- }
- // Append new option to the main storage.
- options_->addItem(option_descriptor_, option_space_);
- }
-
- /// @brief Set storage for the parser.
- ///
- /// Sets storage for the parser. This storage points to the
- /// vector of options and is used by multiple instances of
- /// OptionDataParser. Each instance creates exactly one object
- /// of dhcp::Option or derived type and appends it to this
- /// storage.
- ///
- /// @param storage pointer to the options storage
- void setStorage(OptionStorage* storage) {
- options_ = storage;
- }
-
-private:
-
- /// @brief Create option instance.
- ///
- /// Creates an instance of an option and adds it to the provided
- /// options storage. If the option data parsed by \ref build function
- /// are invalid or insufficient this function emits an exception.
- ///
- /// @warning this function does not check if options_ storage pointer
- /// is initialized but this check is not needed here because it is done
- /// in the \ref build function.
- ///
- /// @throw DhcpConfigError if parameters provided in the configuration
- /// are invalid.
- void createOption() {
- // Option code is held in the uint32_t storage but is supposed to
- // be uint16_t value. We need to check that value in the configuration
- // does not exceed range of uint8_t and is not zero.
- uint32_t option_code = uint32_values_.getParam("code");
- if (option_code == 0) {
- isc_throw(DhcpConfigError, "option code must not be zero."
- << " Option code '0' is reserved in DHCPv4.");
- } else if (option_code > std::numeric_limits<uint8_t>::max()) {
- isc_throw(DhcpConfigError, "invalid option code '" << option_code
- << "', it must not exceed '"
- << std::numeric_limits<uint8_t>::max() << "'");
- }
-
- // Check that the option name has been specified, is non-empty and does not
- // contain spaces
- std::string option_name = string_values_.getParam("name");
- if (option_name.empty()) {
- isc_throw(DhcpConfigError, "name of the option with code '"
- << option_code << "' is empty");
- } else if (option_name.find(" ") != std::string::npos) {
- isc_throw(DhcpConfigError, "invalid option name '" << option_name
- << "', space character is not allowed");
- }
-
- std::string option_space = string_values_.getParam("space");
- if (!OptionSpace::validateName(option_space)) {
- isc_throw(DhcpConfigError, "invalid option space name '"
- << option_space << "' specified for option '"
- << option_name << "' (code '" << option_code
- << "')");
- }
-
+ /// @param dummy first param, option names are always "Dhcp4/option-data[n]"
+ /// @param options is the option storage in which to store the parsed option
+ /// upon "commit".
+ /// @param global_context is a pointer to the global context which
+ /// stores global scope parameters, options, option defintions.
+ Dhcp4OptionDataParser(const std::string&,
+ OptionStoragePtr options, ParserContextPtr global_context)
+ :OptionDataParser("", options, global_context) {
+ }
+
+ /// @brief static factory method for instantiating Dhcp4OptionDataParsers
+ ///
+ /// @param param_name name of the parameter to be parsed.
+ /// @param options storage where the parameter value is to be stored.
+ /// @param global_context is a pointer to the global context which
+ /// stores global scope parameters, options, option defintions.
+ /// @return returns a pointer to a new OptionDataParser. Caller is
+ /// is responsible for deleting it when it is no longer needed.
+ static OptionDataParser* factory(const std::string& param_name,
+ OptionStoragePtr options, ParserContextPtr global_context) {
+ return (new Dhcp4OptionDataParser(param_name, options, global_context));
+ }
+
+protected:
+ /// @brief Finds an option definition within the server's option space
+ ///
+ /// Given an option space and an option code, find the correpsonding
+ /// option defintion within the server's option defintion storage.
+ ///
+ /// @param option_space name of the parameter option space
+ /// @param option_code numeric value of the parameter to find
+ /// @return OptionDefintionPtr of the option defintion or an
+ /// empty OptionDefinitionPtr if not found.
+ /// @throw DhcpConfigError if the option space requested is not valid
+ /// for this server.
+ virtual OptionDefinitionPtr findServerSpaceOptionDefinition (
+ std::string& option_space, uint32_t option_code) {
OptionDefinitionPtr def;
if (option_space == "dhcp4" &&
LibDHCP::isStandardOption(Option::V4, option_code)) {
def = LibDHCP::getOptionDef(Option::V4, option_code);
-
} else if (option_space == "dhcp6") {
isc_throw(DhcpConfigError, "'dhcp6' option space name is reserved"
- << " for DHCPv6 server");
- } else {
- // If we are not dealing with a standard option then we
- // need to search for its definition among user-configured
- // options. They are expected to be in the global storage
- // already.
- OptionDefContainerPtr defs = option_def_intermediate.getItems(option_space);
- // The getItems() should never return the NULL pointer. If there are
- // no option definitions for the particular option space a pointer
- // to an empty container should be returned.
- assert(defs);
- const OptionDefContainerTypeIndex& idx = defs->get<1>();
- OptionDefContainerTypeRange range = idx.equal_range(option_code);
- if (std::distance(range.first, range.second) > 0) {
- def = *range.first;
- }
- if (!def) {
- isc_throw(DhcpConfigError, "definition for the option '"
- << option_space << "." << option_name
- << "' having code '" << option_code
- << "' does not exist");
- }
-
+ << " for DHCPv6 server");
}
- // Get option data from the configuration database ('data' field).
- const std::string option_data = string_values_.getParam("data");
- const bool csv_format = boolean_values_.getParam("csv-format");
-
- // Transform string of hexadecimal digits into binary format.
- std::vector<uint8_t> binary;
- std::vector<std::string> data_tokens;
-
- if (csv_format) {
- // If the option data is specified as a string of comma
- // separated values then we need to split this string into
- // individual values - each value will be used to initialize
- // one data field of an option.
- data_tokens = isc::util::str::tokens(option_data, ",");
- } else {
- // Otherwise, the option data is specified as a string of
- // hexadecimal digits that we have to turn into binary format.
- try {
- util::encode::decodeHex(option_data, binary);
- } catch (...) {
- isc_throw(DhcpConfigError, "option data is not a valid"
- << " string of hexadecimal digits: " << option_data);
- }
- }
-
- OptionPtr option;
- if (!def) {
- if (csv_format) {
- isc_throw(DhcpConfigError, "the CSV option data format can be"
- " used to specify values for an option that has a"
- " definition. The option with code " << option_code
- << " does not have a definition.");
- }
-
- // @todo We have a limited set of option definitions initialized at the moment.
- // In the future we want to initialize option definitions for all options.
- // Consequently an error will be issued if an option definition does not exist
- // for a particular option code. For now it is ok to create generic option
- // if definition does not exist.
- OptionPtr option(new Option(Option::V4, static_cast<uint16_t>(option_code),
- binary));
- // The created option is stored in option_descriptor_ class member until the
- // commit stage when it is inserted into the main storage. If an option with the
- // same code exists in main storage already the old option is replaced.
- option_descriptor_.option = option;
- option_descriptor_.persistent = false;
- } else {
-
- // Option name should match the definition. The option name
- // may seem to be redundant but in the future we may want
- // to reference options and definitions using their names
- // and/or option codes so keeping the option name in the
- // definition of option value makes sense.
- if (def->getName() != option_name) {
- isc_throw(DhcpConfigError, "specified option name '"
- << option_name << "' does not match the "
- << "option definition: '" << option_space
- << "." << def->getName() << "'");
- }
-
- // Option definition has been found so let's use it to create
- // an instance of our option.
- try {
- OptionPtr option = csv_format ?
- def->optionFactory(Option::V4, option_code, data_tokens) :
- def->optionFactory(Option::V4, option_code, binary);
- Subnet::OptionDescriptor desc(option, false);
- option_descriptor_.option = option;
- option_descriptor_.persistent = false;
- } catch (const isc::Exception& ex) {
- isc_throw(DhcpConfigError, "option data does not match"
- << " option definition (space: " << option_space
- << ", code: " << option_code << "): "
- << ex.what());
- }
-
- }
- // All went good, so we can set the option space name.
- option_space_ = option_space;
+ return (def);
}
-
- /// Storage for uint32 values (e.g. option code).
- Uint32Storage uint32_values_;
- /// Storage for string values (e.g. option name or data).
- StringStorage string_values_;
- /// Storage for boolean values.
- BooleanStorage boolean_values_;
- /// Pointer to options storage. This storage is provided by
- /// the calling class and is shared by all OptionDataParser objects.
- OptionStorage* options_;
- /// Option descriptor holds newly configured option.
- Subnet::OptionDescriptor option_descriptor_;
- /// Option space name where the option belongs to.
- std::string option_space_;
};
-/// @brief Parser for option data values within a subnet.
+/// @brief Parser for IPv4 pool definitions.
+///
+/// This is the IPv4 derivation of the PoolParser class and handles pool
+/// definitions, i.e. a list of entries of one of two syntaxes: min-max and
+/// prefix/len for IPv4 pools. Pool4 objects are created and stored in chosen
+/// PoolStorage container.
///
-/// This parser iterates over all entries that define options
-/// data for a particular subnet and creates a collection of options.
-/// If parsing is successful, all these options are added to the Subnet
-/// object.
-class OptionDataListParser : public DhcpConfigParser {
+/// It is useful for parsing Dhcp4/subnet4[X]/pool parameters.
+class Pool4Parser : public PoolParser {
public:
/// @brief Constructor.
///
- /// Unless otherwise specified, parsed options will be stored in
- /// a global option container (option_default). That storage location
- /// is overriden on a subnet basis.
- OptionDataListParser(const std::string&)
- : options_(&option_defaults), local_options_() { }
-
- /// @brief Parses entries that define options' data for a subnet.
- ///
- /// This method iterates over all entries that define option data
- /// for options within a single subnet and creates options' instances.
- ///
- /// @param option_data_list pointer to a list of options' data sets.
- /// @throw DhcpConfigError if option parsing failed.
- void build(ConstElementPtr option_data_list) {
- BOOST_FOREACH(ConstElementPtr option_value, option_data_list->listValue()) {
- boost::shared_ptr<OptionDataParser> parser(new OptionDataParser("option-data"));
- // options_ member will hold instances of all options thus
- // each OptionDataParser takes it as a storage.
- parser->setStorage(&local_options_);
- // Build the instance of a single option.
- parser->build(option_value);
- // Store a parser as it will be used to commit.
- parsers_.push_back(parser);
- }
- }
-
- /// @brief Set storage for option instances.
- ///
- /// @param storage pointer to options storage.
- void setStorage(OptionStorage* storage) {
- options_ = storage;
+ /// @param param_name name of the parameter. Note, it is passed through
+ /// but unused, parameter is currently always "Dhcp4/subnet4[X]/pool"
+ /// @param pools storage container in which to store the parsed pool
+ /// upon "commit"
+ Pool4Parser(const std::string& param_name, PoolStoragePtr pools)
+ :PoolParser(param_name, pools) {
}
-
- /// @brief Commit all option values.
+protected:
+ /// @brief Creates a Pool4 object given a IPv4 prefix and the prefix length.
///
- /// This function invokes commit for all option values.
- void commit() {
- BOOST_FOREACH(ParserPtr parser, parsers_) {
- parser->commit();
- }
- // Parsing was successful and we have all configured
- // options in local storage. We can now replace old values
- // with new values.
- std::swap(local_options_, *options_);
+ /// @param addr is the IPv4 prefix of the pool.
+ /// @param len is the prefix length.
+ /// @param ignored dummy parameter to provide symmetry between the
+ /// PoolParser derivations. The V6 derivation requires a third value.
+ /// @return returns a PoolPtr to the new Pool4 object.
+ PoolPtr poolMaker (IOAddress &addr, uint32_t len, int32_t) {
+ return (PoolPtr(new Pool4(addr, len)));
}
- /// @brief Create DhcpDataListParser object
- ///
- /// @param param_name param name.
+ /// @brief Creates a Pool4 object given starting and ending IPv4 addresses.
///
- /// @return DhcpConfigParser object.
- static DhcpConfigParser* factory(const std::string& param_name) {
- return (new OptionDataListParser(param_name));
+ /// @param min is the first IPv4 address in the pool.
+ /// @param max is the last IPv4 address in the pool.
+ /// @param ignored dummy parameter to provide symmetry between the
+ /// PoolParser derivations. The V6 derivation requires a third value.
+ /// @return returns a PoolPtr to the new Pool4 object.
+ PoolPtr poolMaker (IOAddress &min, IOAddress &max, int32_t) {
+ return (PoolPtr(new Pool4(min, max)));
}
-
- /// Pointer to options instances storage.
- OptionStorage* options_;
- /// Intermediate option storage. This storage is used by
- /// lower level parsers to add new options. Values held
- /// in this storage are assigned to main storage (options_)
- /// if overall parsing was successful.
- OptionStorage local_options_;
- /// Collection of parsers;
- ParserCollection parsers_;
};
-/// @brief Parser for a single option definition.
+/// @brief This class parses a single IPv4 subnet.
///
-/// This parser creates an instance of a single option definition.
-class OptionDefParser : public DhcpConfigParser {
+/// This is the IPv4 derivation of the SubnetConfigParser class and it parses
+/// the whole subnet definition. It creates parsersfor received configuration
+/// parameters as needed.
+class Subnet4ConfigParser : public SubnetConfigParser {
public:
-
- /// @brief Constructor.
- ///
- /// This constructor sets the pointer to the option definitions
- /// storage to NULL. It must be set to point to the actual storage
- /// before \ref build is called.
- OptionDefParser(const std::string&)
- : storage_(NULL) {
- }
-
- /// @brief Parses an entry that describes single option definition.
- ///
- /// @param option_def a configuration entry to be parsed.
+ /// @brief Constructor
///
- /// @throw DhcpConfigError if parsing was unsuccessful.
- void build(ConstElementPtr option_def) {
- if (storage_ == NULL) {
- isc_throw(DhcpConfigError, "parser logic error: storage must be set"
- " before parsing option definition data");
- }
- // Parse the elements that make up the option definition.
- BOOST_FOREACH(ConfigPair param, option_def->mapValue()) {
- std::string entry(param.first);
- ParserPtr parser;
- if (entry == "name" || entry == "type" ||
- entry == "record-types" || entry == "space" ||
- entry == "encapsulate") {
- StringParserPtr
- str_parser(dynamic_cast<StringParser*>(StringParser::factory(entry)));
- if (str_parser) {
- str_parser->setStorage(&string_values_);
- parser = str_parser;
- }
- } else if (entry == "code") {
- Uint32ParserPtr
- code_parser(dynamic_cast<Uint32Parser*>(Uint32Parser::factory(entry)));
- if (code_parser) {
- code_parser->setStorage(&uint32_values_);
- parser = code_parser;
- }
- } else if (entry == "array") {
- BooleanParserPtr
- array_parser(dynamic_cast<BooleanParser*>(BooleanParser::factory(entry)));
- if (array_parser) {
- array_parser->setStorage(&boolean_values_);
- parser = array_parser;
- }
- } else {
- isc_throw(DhcpConfigError, "invalid parameter: " << entry);
- }
-
- parser->build(param.second);
- parser->commit();
- }
+ /// @param ignored first parameter
+ /// stores global scope parameters, options, option defintions.
+ Subnet4ConfigParser(const std::string&)
+ :SubnetConfigParser("", globalContext()) {
+ }
- // Create an instance of option definition.
- createOptionDef();
-
- // Get all items we collected so far for the particular option space.
- OptionDefContainerPtr defs = storage_->getItems(option_space_name_);
- // Check if there are any items with option code the same as the
- // one specified for the definition we are now creating.
- const OptionDefContainerTypeIndex& idx = defs->get<1>();
- const OptionDefContainerTypeRange& range =
- idx.equal_range(option_definition_->getCode());
- // If there are any items with this option code already we need
- // to issue an error because we don't allow duplicates for
- // option definitions within an option space.
- if (std::distance(range.first, range.second) > 0) {
- isc_throw(DhcpConfigError, "duplicated option definition for"
- << " code '" << option_definition_->getCode() << "'");
- }
- }
-
- /// @brief Stores the parsed option definition in a storage.
+ /// @brief Adds the created subnet to a server's configuration.
+ /// @throw throws Unexpected if dynamic cast fails.
void commit() {
- if (storage_ && option_definition_ &&
- OptionSpace::validateName(option_space_name_)) {
- storage_->addItem(option_definition_, option_space_name_);
- }
- }
-
- /// @brief Sets a pointer to the data store.
- ///
- /// The newly created instance of an option definition will be
- /// added to the data store given by the argument.
- ///
- /// @param storage pointer to the data store where the option definition
- /// will be added to.
- void setStorage(OptionDefStorage* storage) {
- storage_ = storage;
- }
-
-private:
-
- /// @brief Create option definition from the parsed parameters.
- void createOptionDef() {
-
- // Get the option space name and validate it.
- std::string space = string_values_.getParam("space");
- if (!OptionSpace::validateName(space)) {
- isc_throw(DhcpConfigError, "invalid option space name '"
- << space << "'");
- }
-
- // Get other parameters that are needed to create the
- // option definition.
- std::string name = string_values_.getParam("name");
- uint32_t code = uint32_values_.getParam("code");
- std::string type = string_values_.getParam("type");
- bool array_type = boolean_values_.getParam("array");
- std::string encapsulates = string_values_.getParam("encapsulate");
-
- // Create option definition.
- OptionDefinitionPtr def;
- // We need to check if user has set encapsulated option space
- // name. If so, different constructor will be used.
- if (!encapsulates.empty()) {
- // Arrays can't be used together with sub-options.
- if (array_type) {
- isc_throw(DhcpConfigError, "option '" << space << "."
- << "name" << "', comprising an array of data"
- << " fields may not encapsulate any option space");
-
- } else if (encapsulates == space) {
- isc_throw(DhcpConfigError, "option must not encapsulate"
- << " an option space it belongs to: '"
- << space << "." << name << "' is set to"
- << " encapsulate '" << space << "'");
-
- } else {
- def.reset(new OptionDefinition(name, code, type,
- encapsulates.c_str()));
- }
-
- } else {
- def.reset(new OptionDefinition(name, code, type, array_type));
-
- }
- // The record-types field may carry a list of comma separated names
- // of data types that form a record.
- std::string record_types = string_values_.getParam("record-types");
-
- // Split the list of record types into tokens.
- std::vector<std::string> record_tokens =
- isc::util::str::tokens(record_types, ",");
- // Iterate over each token and add a record type into
- // option definition.
- BOOST_FOREACH(std::string record_type, record_tokens) {
- try {
- boost::trim(record_type);
- if (!record_type.empty()) {
- def->addRecordField(record_type);
- }
- } catch (const Exception& ex) {
- isc_throw(DhcpConfigError, "invalid record type values"
- << " specified for the option definition: "
- << ex.what());
+ if (subnet_) {
+ Subnet4Ptr sub4ptr = boost::dynamic_pointer_cast<Subnet4>(subnet_);
+ if (!sub4ptr) {
+ // If we hit this, it is a programming error.
+ isc_throw(Unexpected,
+ "Invalid cast in Subnet4ConfigParser::commit");
}
- }
- // Check the option definition parameters are valid.
- try {
- def->validate();
- } catch (const isc::Exception& ex) {
- isc_throw(DhcpConfigError, "invalid option definition"
- << " parameters: " << ex.what());
+ isc::dhcp::CfgMgr::instance().addSubnet4(sub4ptr);
}
- // Option definition has been created successfully.
- option_space_name_ = space;
- option_definition_ = def;
}
- /// Instance of option definition being created by this parser.
- OptionDefinitionPtr option_definition_;
- /// Name of the space the option definition belongs to.
- std::string option_space_name_;
-
- /// Pointer to a storage where the option definition will be
- /// added when \ref commit is called.
- OptionDefStorage* storage_;
-
- /// Storage for boolean values.
- BooleanStorage boolean_values_;
- /// Storage for string values.
- StringStorage string_values_;
- /// Storage for uint32 values.
- Uint32Storage uint32_values_;
-};
+protected:
-/// @brief Parser for a list of option definitions.
-///
-/// This parser iterates over all configuration entries that define
-/// option definitions and creates instances of these definitions.
-/// If the parsing is successful, the collection of created definitions
-/// is put into the provided storage.
-class OptionDefListParser : DhcpConfigParser {
-public:
-
- /// @brief Constructor.
- ///
- /// This constructor initializes the pointer to option definitions
- /// storage to NULL value. This pointer has to be set to point to
- /// the actual storage before the \ref build function is called.
- OptionDefListParser(const std::string&) {
- }
-
- /// @brief Parse configuration entries.
+ /// @brief Creates parsers for entries in subnet definition.
///
- /// This function parses configuration entries and creates instances
- /// of option definitions.
+ /// @param config_id name of the entry
///
- /// @param option_def_list pointer to an element that holds entries
- /// that define option definitions.
- /// @throw DhcpConfigError if configuration parsing fails.
- void build(ConstElementPtr option_def_list) {
- // Clear existing items in the global storage.
- // We are going to replace all of them.
- option_def_intermediate.clearItems();
-
- if (!option_def_list) {
- isc_throw(DhcpConfigError, "parser error: a pointer to a list of"
- << " option definitions is NULL");
- }
-
- BOOST_FOREACH(ConstElementPtr option_def, option_def_list->listValue()) {
- boost::shared_ptr<OptionDefParser>
- parser(new OptionDefParser("single-option-def"));
- parser->setStorage(&option_def_intermediate);
- parser->build(option_def);
- parser->commit();
- }
- }
-
- /// @brief Stores option definitions in the CfgMgr.
- void commit() {
-
- CfgMgr& cfg_mgr = CfgMgr::instance();
-
- cfg_mgr.deleteOptionDefs();
-
- // We need to move option definitions from the temporary
- // storage to the global storage.
- std::list<std::string> space_names =
- option_def_intermediate.getOptionSpaceNames();
- BOOST_FOREACH(std::string space_name, space_names) {
-
- BOOST_FOREACH(OptionDefinitionPtr def,
- *option_def_intermediate.getItems(space_name)) {
- // All option definitions should be initialized to non-NULL
- // values. The validation is expected to be made by the
- // OptionDefParser when creating an option definition.
- assert(def);
- cfg_mgr.addOptionDef(def, space_name);
- }
+ /// @return parser object for specified entry name. Note the caller is
+ /// responsible for deleting the parser created.
+ /// @throw isc::dhcp::DhcpConfigError if trying to create a parser
+ /// for unknown config element
+ DhcpConfigParser* createSubnetConfigParser(const std::string& config_id) {
+ DhcpConfigParser* parser = NULL;
+ if ((config_id.compare("valid-lifetime") == 0) ||
+ (config_id.compare("renew-timer") == 0) ||
+ (config_id.compare("rebind-timer") == 0)) {
+ parser = new Uint32Parser(config_id, uint32_values_);
+ } else if ((config_id.compare("subnet") == 0) ||
+ (config_id.compare("interface") == 0)) {
+ parser = new StringParser(config_id, string_values_);
+ } else if (config_id.compare("pool") == 0) {
+ parser = new Pool4Parser(config_id, pools_);
+ } else if (config_id.compare("option-data") == 0) {
+ parser = new OptionDataListParser(config_id, options_,
+ global_context_,
+ Dhcp4OptionDataParser::factory);
+ } else {
+ isc_throw(NotImplemented,
+ "parser error: Subnet4 parameter not supported: " << config_id);
}
- }
-
- /// @brief Create an OptionDefListParser object.
- ///
- /// @param param_name configuration entry holding option definitions.
- ///
- /// @return OptionDefListParser object.
- static DhcpConfigParser* factory(const std::string& param_name) {
- return (new OptionDefListParser(param_name));
- }
-
-};
-
-/// @brief this class parses a single subnet
-///
-/// This class parses the whole subnet definition. It creates parsers
-/// for received configuration parameters as needed.
-class Subnet4ConfigParser : public DhcpConfigParser {
-public:
- /// @brief constructor
- Subnet4ConfigParser(const std::string& ) {
- // The parameter should always be "subnet", but we don't check here
- // against it in case someone wants to reuse this parser somewhere.
+ return (parser);
}
- /// @brief parses parameter value
- ///
- /// @param subnet pointer to the content of subnet definition
- void build(ConstElementPtr subnet) {
-
- BOOST_FOREACH(ConfigPair param, subnet->mapValue()) {
- ParserPtr parser(createSubnet4ConfigParser(param.first));
- // The actual type of the parser is unknown here. We have to discover
- // the parser type here to invoke the corresponding setStorage function
- // on it. We discover parser type by trying to cast the parser to various
- // parser types and checking which one was successful. For this one
- // a setStorage and build methods are invoked.
-
- // Try uint32 type parser.
- if (!buildParser<Uint32Parser, Uint32Storage >(parser, uint32_values_,
- param.second) &&
- // Try string type parser.
- !buildParser<StringParser, StringStorage >(parser, string_values_,
- param.second) &&
- // Try pool parser.
- !buildParser<PoolParser, PoolStorage >(parser, pools_,
- param.second) &&
- // Try option data parser.
- !buildParser<OptionDataListParser, OptionStorage >(parser, options_,
- param.second)) {
- // Appropriate parsers are created in the createSubnet6ConfigParser
- // and they should be limited to those that we check here for. Thus,
- // if we fail to find a matching parser here it is a programming error.
- isc_throw(DhcpConfigError, "failed to find suitable parser");
- }
- }
- // In order to create new subnet we need to get the data out
- // of the child parsers first. The only way to do it is to
- // invoke commit on them because it will make them write
- // parsed data into storages we have supplied.
- // Note that triggering commits on child parsers does not
- // affect global data because we supplied pointers to storages
- // local to this object. Thus, even if this method fails
- // later on, the configuration remains consistent.
- BOOST_FOREACH(ParserPtr parser, parsers_) {
- parser->commit();
- }
-
- // Create a subnet.
- createSubnet();
- }
- /// @brief commits received configuration.
+ /// @brief Determines if the given option space name and code describe
+ /// a standard option for the DCHP4 server.
///
- /// This method does most of the configuration. Many other parsers are just
- /// storing the values that are actually consumed here. Pool definitions
- /// created in other parsers are used here and added to newly created Subnet4
- /// objects. Subnet4 are then added to DHCP CfgMgr.
- /// @throw DhcpConfigError if there are any issues encountered during commit
- void commit() {
- if (subnet_) {
- CfgMgr::instance().addSubnet4(subnet_);
- }
+ /// @param option_space is the name of the option space to consider
+ /// @param code is the numeric option code to consider
+ /// @return returns true if the space and code are part of the server's
+ /// standard options.
+ bool isServerStdOption(std::string option_space, uint32_t code) {
+ return ((option_space.compare("dhcp4") == 0)
+ && LibDHCP::isStandardOption(Option::V4, code));
}
-private:
-
- /// @brief Set storage for a parser and invoke build.
- ///
- /// This helper method casts the provided parser pointer to the specified
- /// type. If the cast is successful it sets the corresponding storage for
- /// this parser, invokes build on it and saves the parser.
- ///
- /// @tparam T parser type to which parser argument should be cast.
- /// @tparam Y storage type for the specified parser type.
- /// @param parser parser on which build must be invoked.
- /// @param storage reference to a storage that will be set for a parser.
- /// @param subnet subnet element read from the configuration and being parsed.
- /// @return true if parser pointer was successfully cast to specialized
- /// parser type provided as Y.
- template<typename T, typename Y>
- bool buildParser(const ParserPtr& parser, Y& storage, const ConstElementPtr& subnet) {
- // We need to cast to T in order to set storage for the parser.
- boost::shared_ptr<T> cast_parser = boost::dynamic_pointer_cast<T>(parser);
- // It is common that this cast is not successful because we try to cast to all
- // supported parser types as we don't know the type of a parser in advance.
- if (cast_parser) {
- // Cast, successful so we go ahead with setting storage and actual parse.
- cast_parser->setStorage(&storage);
- parser->build(subnet);
- parsers_.push_back(parser);
- // We indicate that cast was successful so as the calling function
- // may skip attempts to cast to other parser types and proceed to
- // next element.
- return (true);
- }
- // It was not successful. Indicate that another parser type
- // should be tried.
- return (false);
+ /// @brief Returns the option definition for a given option code from
+ /// the DHCP4 server's standard set of options.
+ /// @param code is the numeric option code of the desired option definition.
+ /// @return returns a pointer the option definition
+ OptionDefinitionPtr getServerStdOptionDefinition (uint32_t code) {
+ return (LibDHCP::getOptionDef(Option::V4, code));
}
- /// @brief Append sub-options to an option.
- ///
- /// @param option_space a name of the encapsulated option space.
- /// @param option option instance to append sub-options to.
- void appendSubOptions(const std::string& option_space, OptionPtr& option) {
- // Only non-NULL options are stored in option container.
- // If this option pointer is NULL this is a serious error.
- assert(option);
-
- OptionDefinitionPtr def;
- if (option_space == "dhcp4" &&
- LibDHCP::isStandardOption(Option::V4, option->getType())) {
- def = LibDHCP::getOptionDef(Option::V4, option->getType());
- // Definitions for some of the standard options hasn't been
- // implemented so it is ok to leave here.
- if (!def) {
- return;
- }
- } else {
- const OptionDefContainerPtr defs =
- option_def_intermediate.getItems(option_space);
- const OptionDefContainerTypeIndex& idx = defs->get<1>();
- const OptionDefContainerTypeRange& range =
- idx.equal_range(option->getType());
- // There is no definition so we have to leave.
- if (std::distance(range.first, range.second) == 0) {
- return;
- }
-
- def = *range.first;
-
- // If the definition exists, it must be non-NULL.
- // Otherwise it is a programming error.
- assert(def);
- }
-
- // We need to get option definition for the particular option space
- // and code. This definition holds the information whether our
- // option encapsulates any option space.
- // Get the encapsulated option space name.
- std::string encapsulated_space = def->getEncapsulatedSpace();
- // If option space name is empty it means that our option does not
- // encapsulate any option space (does not include sub-options).
- if (!encapsulated_space.empty()) {
- // Get the sub-options that belong to the encapsulated
- // option space.
- const Subnet::OptionContainerPtr sub_opts =
- option_defaults.getItems(encapsulated_space);
- // Append sub-options to the option.
- BOOST_FOREACH(Subnet::OptionDescriptor desc, *sub_opts) {
- if (desc.option) {
- option->addOption(desc.option);
- }
- }
- }
+ /// @brief Issues a DHCP4 server specific warning regarding duplicate subnet
+ /// options.
+ ///
+ /// @param code is the numeric option code of the duplicate option
+ /// @param addr is the subnet address
+ /// @todo a means to know the correct logger and perhaps a common
+ /// message would allow this method to be emitted by the base class.
+ virtual void duplicate_option_warning(uint32_t code,
+ isc::asiolink::IOAddress& addr) {
+ LOG_WARN(dhcp4_logger, DHCP4_CONFIG_OPTION_DUPLICATE)
+ .arg(code).arg(addr.toText());
}
- /// @brief Create a new subnet using a data from child parsers.
- ///
- /// @throw isc::dhcp::DhcpConfigError if subnet configuration parsing failed.
- void createSubnet() {
- std::string subnet_txt;
- try {
- subnet_txt = string_values_.getParam("subnet");
- } catch (DhcpConfigError) {
- // Rethrow with precise error.
- isc_throw(DhcpConfigError,
- "Mandatory subnet definition in subnet missing");
- }
-
- // Remove any spaces or tabs.
- boost::erase_all(subnet_txt, " ");
- boost::erase_all(subnet_txt, "\t");
-
- // The subnet format is prefix/len. We are going to extract
- // the prefix portion of a subnet string to create IOAddress
- // object from it. IOAddress will be passed to the Subnet's
- // constructor later on. In order to extract the prefix we
- // need to get all characters preceding "/".
- size_t pos = subnet_txt.find("/");
- if (pos == string::npos) {
- isc_throw(DhcpConfigError,
- "Invalid subnet syntax (prefix/len expected):" << subnet_txt);
- }
-
- // Try to create the address object. It also validates that
- // the address syntax is ok.
- IOAddress addr(subnet_txt.substr(0, pos));
- uint8_t len = boost::lexical_cast<unsigned int>(subnet_txt.substr(pos + 1));
-
+ /// @brief Instantiates the IPv4 Subnet based on a given IPv4 address
+ /// and prefix length.
+ ///
+ /// @param addr is IPv4 address of the subnet.
+ /// @param len is the prefix length
+ void initSubnet(isc::asiolink::IOAddress addr, uint8_t len) {
// Get all 'time' parameters using inheritance.
// If the subnet-specific value is defined then use it, else
// use the global value. The global value must always be
@@ -1460,148 +265,10 @@ private:
LOG_INFO(dhcp4_logger, DHCP4_CONFIG_NEW_SUBNET).arg(tmp.str());
subnet_.reset(new Subnet4(addr, len, t1, t2, valid));
-
- for (PoolStorage::iterator it = pools_.begin(); it != pools_.end(); ++it) {
- subnet_->addPool(*it);
- }
-
- // We are going to move configured options to the Subnet object.
- // Configured options reside in the container where options
- // are grouped by space names. Thus we need to get all space names
- // and iterate over all options that belong to them.
- std::list<std::string> space_names = options_.getOptionSpaceNames();
- BOOST_FOREACH(std::string option_space, space_names) {
- // Get all options within a particular option space.
- BOOST_FOREACH(Subnet::OptionDescriptor desc,
- *options_.getItems(option_space)) {
- // The pointer should be non-NULL. The validation is expected
- // to be performed by the OptionDataParser before adding an
- // option descriptor to the container.
- assert(desc.option);
- // We want to check whether an option with the particular
- // option code has been already added. If so, we want
- // to issue a warning.
- Subnet::OptionDescriptor existing_desc =
- subnet_->getOptionDescriptor("option_space",
- desc.option->getType());
- if (existing_desc.option) {
- LOG_WARN(dhcp4_logger, DHCP4_CONFIG_OPTION_DUPLICATE)
- .arg(desc.option->getType()).arg(addr.toText());
- }
- // Add sub-options (if any).
- appendSubOptions(option_space, desc.option);
- // In any case, we add the option to the subnet.
- subnet_->addOption(desc.option, false, option_space);
- }
- }
-
- // Check all global options and add them to the subnet object if
- // they have been configured in the global scope. If they have been
- // configured in the subnet scope we don't add global option because
- // the one configured in the subnet scope always takes precedence.
- space_names = option_defaults.getOptionSpaceNames();
- BOOST_FOREACH(std::string option_space, space_names) {
- // Get all global options for the particular option space.
- BOOST_FOREACH(Subnet::OptionDescriptor desc,
- *option_defaults.getItems(option_space)) {
- // The pointer should be non-NULL. The validation is expected
- // to be performed by the OptionDataParser before adding an
- // option descriptor to the container.
- assert(desc.option);
- // Check if the particular option has been already added.
- // This would mean that it has been configured in the
- // subnet scope. Since option values configured in the
- // subnet scope take precedence over globally configured
- // values we don't add option from the global storage
- // if there is one already.
- Subnet::OptionDescriptor existing_desc =
- subnet_->getOptionDescriptor(option_space, desc.option->getType());
- if (!existing_desc.option) {
- // Add sub-options (if any).
- appendSubOptions(option_space, desc.option);
-
- subnet_->addOption(desc.option, false, option_space);
- }
- }
- }
}
-
- /// @brief creates parsers for entries in subnet definition
- ///
- /// @todo Add subnet-specific things here (e.g. subnet-specific options)
- ///
- /// @param config_id name od the entry
- /// @return parser object for specified entry name
- /// @throw NotImplemented if trying to create a parser for unknown config element
- DhcpConfigParser* createSubnet4ConfigParser(const std::string& config_id) {
- FactoryMap factories;
- factories["valid-lifetime"] = Uint32Parser::factory;
- factories["renew-timer"] = Uint32Parser::factory;
- factories["rebind-timer"] = Uint32Parser::factory;
- factories["subnet"] = StringParser::factory;
- factories["pool"] = PoolParser::factory;
- factories["option-data"] = OptionDataListParser::factory;
-
- FactoryMap::iterator f = factories.find(config_id);
- if (f == factories.end()) {
- // Used for debugging only.
- // return new DebugParser(config_id);
-
- isc_throw(NotImplemented,
- "parser error: Subnet4 parameter not supported: "
- << config_id);
- }
- return (f->second(config_id));
- }
-
- /// @brief Returns value for a given parameter (after using inheritance)
- ///
- /// This method implements inheritance. For a given parameter name, it first
- /// checks if there is a global value for it and overwrites it with specific
- /// value if such value was defined in subnet.
- ///
- /// @param name name of the parameter
- /// @return triplet with the parameter name
- /// @throw DhcpConfigError when requested parameter is not present
- Triplet<uint32_t> getParam(const std::string& name) {
- uint32_t value = 0;
- try {
- // look for local value
- value = uint32_values_.getParam(name);
- } catch (DhcpConfigError) {
- try {
- // no local, use global value
- value = uint32_defaults.getParam(name);
- } catch (DhcpConfigError) {
- isc_throw(DhcpConfigError, "Mandatory parameter " << name
- << " missing (no global default and no subnet-"
- << "specific value)");
- }
- }
-
- return (Triplet<uint32_t>(value));
- }
-
- /// storage for subnet-specific uint32 values
- Uint32Storage uint32_values_;
-
- /// storage for subnet-specific integer values
- StringStorage string_values_;
-
- /// storage for pools belonging to this subnet
- PoolStorage pools_;
-
- /// storage for options belonging to this subnet
- OptionStorage options_;
-
- /// parsers are stored here
- ParserCollection parsers_;
-
- /// @brief Pointer to the created subnet object.
- isc::dhcp::Subnet4Ptr subnet_;
};
-/// @brief this class parses list of subnets
+/// @brief this class parses list of DHCP4 subnets
///
/// This is a wrapper parser that handles the whole list of Subnet4
/// definitions. It iterates over all entries and creates Subnet4ConfigParser
@@ -1611,8 +278,9 @@ public:
/// @brief constructor
///
+ /// @param dummy first argument, always ignored. All parsers accept a
+ /// string parameter "name" as their first argument.
Subnets4ListConfigParser(const std::string&) {
- /// parameter name is ignored
}
/// @brief parses contents of the list
@@ -1622,22 +290,17 @@ public:
///
/// @param subnets_list pointer to a list of IPv4 subnets
void build(ConstElementPtr subnets_list) {
-
- // No need to define FactoryMap here. There's only one type
- // used: Subnet4ConfigParser
-
BOOST_FOREACH(ConstElementPtr subnet, subnets_list->listValue()) {
ParserPtr parser(new Subnet4ConfigParser("subnet"));
parser->build(subnet);
subnets_.push_back(parser);
}
-
}
/// @brief commits subnets definitions.
///
- /// Iterates over all Subnet4 parsers. Each parser contains definitions
- /// of a single subnet and its parameters and commits each subnet separately.
+ /// Iterates over all Subnet4 parsers. Each parser contains definitions of
+ /// a single subnet and its parameters and commits each subnet separately.
void commit() {
// @todo: Implement more subtle reconfiguration than toss
// the old one and replace with the new one.
@@ -1675,44 +338,54 @@ namespace dhcp {
///
/// @param config_id pointer to received global configuration entry
/// @return parser for specified global DHCPv4 parameter
-/// @throw NotImplemented if trying to create a parser for unknown config element
+/// @throw NotImplemented if trying to create a parser for unknown
+/// config element
DhcpConfigParser* createGlobalDhcp4ConfigParser(const std::string& config_id) {
- FactoryMap factories;
-
- factories["valid-lifetime"] = Uint32Parser::factory;
- factories["renew-timer"] = Uint32Parser::factory;
- factories["rebind-timer"] = Uint32Parser::factory;
- factories["interface"] = InterfaceListConfigParser::factory;
- factories["subnet4"] = Subnets4ListConfigParser::factory;
- factories["option-data"] = OptionDataListParser::factory;
- factories["option-def"] = OptionDefListParser::factory;
- factories["version"] = StringParser::factory;
- factories["lease-database"] = DbAccessParser::factory;
-
- FactoryMap::iterator f = factories.find(config_id);
- if (f == factories.end()) {
- // Used for debugging only.
- // return new DebugParser(config_id);
-
+ DhcpConfigParser* parser = NULL;
+ if ((config_id.compare("valid-lifetime") == 0) ||
+ (config_id.compare("renew-timer") == 0) ||
+ (config_id.compare("rebind-timer") == 0)) {
+ parser = new Uint32Parser(config_id,
+ globalContext()->uint32_values_);
+ } else if (config_id.compare("interface") == 0) {
+ parser = new InterfaceListConfigParser(config_id);
+ } else if (config_id.compare("subnet4") == 0) {
+ parser = new Subnets4ListConfigParser(config_id);
+ } else if (config_id.compare("option-data") == 0) {
+ parser = new OptionDataListParser(config_id,
+ globalContext()->options_,
+ globalContext(),
+ Dhcp4OptionDataParser::factory);
+ } else if (config_id.compare("option-def") == 0) {
+ parser = new OptionDefListParser(config_id,
+ globalContext()->option_defs_);
+ } else if (config_id.compare("version") == 0) {
+ parser = new StringParser(config_id,
+ globalContext()->string_values_);
+ } else if (config_id.compare("lease-database") == 0) {
+ parser = new DbAccessParser(config_id);
+ } else {
isc_throw(NotImplemented,
- "Parser error: Global configuration parameter not supported: "
- << config_id);
+ "Parser error: Global configuration parameter not supported: "
+ << config_id);
}
- return (f->second(config_id));
+
+ return (parser);
}
isc::data::ConstElementPtr
-configureDhcp4Server(Dhcpv4Srv&, ConstElementPtr config_set) {
+configureDhcp4Server(Dhcpv4Srv&, isc::data::ConstElementPtr config_set) {
if (!config_set) {
ConstElementPtr answer = isc::config::createAnswer(1,
string("Can't parse NULL config"));
return (answer);
}
- /// @todo: append most essential info here (like "2 new subnets configured")
+ /// @todo: Append most essential info here (like "2 new subnets configured")
string config_details;
- LOG_DEBUG(dhcp4_logger, DBG_DHCP4_COMMAND, DHCP4_CONFIG_START).arg(config_set->str());
+ LOG_DEBUG(dhcp4_logger, DBG_DHCP4_COMMAND,
+ DHCP4_CONFIG_START).arg(config_set->str());
// Some of the values specified in the configuration depend on
// other values. Typically, the values in the subnet4 structure
@@ -1732,14 +405,11 @@ configureDhcp4Server(Dhcpv4Srv&, ConstElementPtr config_set) {
// parsing operation fails after the global storage has been
// modified. We need to preserve the original global data here
// so as we can rollback changes when an error occurs.
- Uint32Storage uint32_local(uint32_defaults);
- StringStorage string_local(string_defaults);
- OptionStorage option_local(option_defaults);
- OptionDefStorage option_def_local(option_def_intermediate);
+ ParserContext original_context(*globalContext());
- // answer will hold the result.
+ // Answer will hold the result.
ConstElementPtr answer;
- // rollback informs whether error occured and original data
+ // Rollback informs whether error occured and original data
// have to be restored to global storages.
bool rollback = false;
// config_pair holds the details of the current parser when iterating over
@@ -1749,17 +419,15 @@ configureDhcp4Server(Dhcpv4Srv&, ConstElementPtr config_set) {
try {
// Make parsers grouping.
const std::map<std::string, ConstElementPtr>& values_map =
- config_set->mapValue();
+ config_set->mapValue();
BOOST_FOREACH(config_pair, values_map) {
ParserPtr parser(createGlobalDhcp4ConfigParser(config_pair.first));
LOG_DEBUG(dhcp4_logger, DBG_DHCP4_DETAIL, DHCP4_PARSER_CREATED)
.arg(config_pair.first);
if (config_pair.first == "subnet4") {
subnet_parser = parser;
-
} else if (config_pair.first == "option-data") {
option_parser = parser;
-
} else {
// Those parsers should be started before other
// parsers so we can call build straight away.
@@ -1797,7 +465,7 @@ configureDhcp4Server(Dhcpv4Srv&, ConstElementPtr config_set) {
rollback = true;
} catch (...) {
- // for things like bad_cast in boost::lexical_cast
+ // For things like bad_cast in boost::lexical_cast
LOG_ERROR(dhcp4_logger, DHCP4_PARSER_EXCEPTION).arg(config_pair.first);
answer = isc::config::createAnswer(1,
string("Configuration parsing failed"));
@@ -1821,23 +489,18 @@ configureDhcp4Server(Dhcpv4Srv&, ConstElementPtr config_set) {
answer = isc::config::createAnswer(2,
string("Configuration commit failed: ") + ex.what());
rollback = true;
-
} catch (...) {
- // for things like bad_cast in boost::lexical_cast
+ // For things like bad_cast in boost::lexical_cast
LOG_ERROR(dhcp4_logger, DHCP4_PARSER_COMMIT_EXCEPTION);
answer = isc::config::createAnswer(2,
string("Configuration commit failed"));
rollback = true;
-
}
}
// Rollback changes as the configuration parsing failed.
if (rollback) {
- std::swap(uint32_defaults, uint32_local);
- std::swap(string_defaults, string_local);
- std::swap(option_defaults, option_local);
- std::swap(option_def_intermediate, option_def_local);
+ globalContext().reset(new ParserContext(original_context));
return (answer);
}
@@ -1848,9 +511,13 @@ configureDhcp4Server(Dhcpv4Srv&, ConstElementPtr config_set) {
return (answer);
}
-const Uint32Storage& getUint32Defaults() {
- return (uint32_defaults);
+ParserContextPtr& globalContext() {
+ static ParserContextPtr global_context_ptr(new ParserContext(Option::V4));
+ return (global_context_ptr);
}
+
+
}; // end of isc::dhcp namespace
}; // end of isc namespace
+
diff --git a/src/bin/dhcp4/config_parser.h b/src/bin/dhcp4/config_parser.h
index 51a7556..ea84cc6 100644
--- a/src/bin/dhcp4/config_parser.h
+++ b/src/bin/dhcp4/config_parser.h
@@ -12,9 +12,10 @@
// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
// PERFORMANCE OF THIS SOFTWARE.
-#include <exceptions/exceptions.h>
-#include <dhcpsrv/dhcp_config_parser.h>
#include <cc/data.h>
+#include <exceptions/exceptions.h>
+#include <dhcpsrv/dhcp_parsers.h>
+
#include <stdint.h>
#include <string>
@@ -29,7 +30,8 @@ namespace dhcp {
class Dhcpv4Srv;
-/// @brief Configure DHCPv4 server (@c Dhcpv4Srv) with a set of configuration values.
+/// @brief Configure DHCPv4 server (@c Dhcpv4Srv) with a set of configuration
+/// values.
///
/// This function parses configuration information stored in @c config_set
/// and configures the @c server by applying the configuration to it.
@@ -42,9 +44,9 @@ class Dhcpv4Srv;
/// (such as malformed configuration or invalid configuration parameter),
/// this function returns appropriate error code.
///
-/// This function is called every time a new configuration is received. The extra
-/// parameter is a reference to DHCPv4 server component. It is currently not used
-/// and CfgMgr::instance() is accessed instead.
+/// This function is called every time a new configuration is received. The
+/// extra parameter is a reference to DHCPv4 server component. It is currently
+/// not used and CfgMgr::instance() is accessed instead.
///
/// This method does not throw. It catches all exceptions and returns them as
/// reconfiguration statuses. It may return the following response codes:
@@ -59,15 +61,10 @@ isc::data::ConstElementPtr
configureDhcp4Server(Dhcpv4Srv&,
isc::data::ConstElementPtr config_set);
-
-/// @brief Returns the global uint32_t values storage.
-///
-/// This function must be only used by unit tests that need
-/// to access uint32_t global storage to verify that the
-/// Uint32Parser works as expected.
+/// @brief Returns the global context
///
-/// @return a reference to a global uint32 values storage.
-const Uint32Storage& getUint32Defaults();
+/// @return a reference to the global context
+ParserContextPtr& globalContext();
}; // end of isc::dhcp namespace
}; // end of isc namespace
diff --git a/src/bin/dhcp4/tests/config_parser_unittest.cc b/src/bin/dhcp4/tests/config_parser_unittest.cc
index 61e6b61..32770b3 100644
--- a/src/bin/dhcp4/tests/config_parser_unittest.cc
+++ b/src/bin/dhcp4/tests/config_parser_unittest.cc
@@ -55,9 +55,10 @@ public:
// Checks if global parameter of name have expected_value
void checkGlobalUint32(string name, uint32_t expected_value) {
- const Uint32Storage& uint32_defaults = getUint32Defaults();
+ const Uint32StoragePtr uint32_defaults =
+ globalContext()->uint32_values_;
try {
- uint32_t actual_value = uint32_defaults.getParam(name);
+ uint32_t actual_value = uint32_defaults->getParam(name);
EXPECT_EQ(expected_value, actual_value);
} catch (DhcpConfigError) {
ADD_FAILURE() << "Expected uint32 with name " << name
diff --git a/src/bin/dhcp6/config_parser.cc b/src/bin/dhcp6/config_parser.cc
index f386243..74012ac 100644
--- a/src/bin/dhcp6/config_parser.cc
+++ b/src/bin/dhcp6/config_parser.cc
@@ -22,6 +22,7 @@
#include <dhcpsrv/cfgmgr.h>
#include <dhcpsrv/dbaccess_parser.h>
#include <dhcpsrv/dhcp_config_parser.h>
+#include <dhcpsrv/dhcp_parsers.h>
#include <dhcpsrv/pool.h>
#include <dhcpsrv/subnet.h>
#include <dhcpsrv/triplet.h>
@@ -49,1423 +50,227 @@ using namespace isc::asiolink;
namespace {
-// Forward declarations of some of the parser classes.
-// They are used to define pointer types for these classes.
-class BooleanParser;
-class StringParser;
-class Uint32Parser;
-
// Pointers to various parser objects.
typedef boost::shared_ptr<BooleanParser> BooleanParserPtr;
typedef boost::shared_ptr<StringParser> StringParserPtr;
typedef boost::shared_ptr<Uint32Parser> Uint32ParserPtr;
-/// @brief Factory method that will create a parser for a given element name
-typedef isc::dhcp::DhcpConfigParser* ParserFactory(const std::string& config_id);
-
-/// @brief Collection of factories that create parsers for specified element names
-typedef std::map<std::string, ParserFactory*> FactoryMap;
-
-/// @brief Storage for option definitions.
-typedef OptionSpaceContainer<OptionDefContainer,
- OptionDefinitionPtr> OptionDefStorage;
-
-/// @brief Collection of address pools.
-///
-/// This type is used as intermediate storage, when pools are parsed, but there is
-/// no subnet object created yet to store them.
-typedef std::vector<isc::dhcp::Pool6Ptr> PoolStorage;
-
-/// Collection of containers holding option spaces. Each container within
-/// a particular option space holds so-called option descriptors.
-typedef OptionSpaceContainer<Subnet::OptionContainer,
- Subnet::OptionDescriptor> OptionStorage;
-
-/// @brief Global uint32 parameters that will be used as defaults.
-Uint32Storage uint32_defaults;
-
-/// @brief global string parameters that will be used as defaults.
-StringStorage string_defaults;
-
-/// @brief Global storage for options that will be used as defaults.
-OptionStorage option_defaults;
-
-/// @brief Global storage for option definitions.
-OptionDefStorage option_def_intermediate;
-
-/// @brief a dummy configuration parser
-///
-/// This is a debugging parser. It does not configure anything,
-/// will accept any configuration and will just print it out
-/// on commit. Useful for debugging existing configurations and
-/// adding new ones.
-class DebugParser : public DhcpConfigParser {
-public:
-
- /// @brief Constructor
- ///
- /// See @ref DhcpConfigParser class for details.
- ///
- /// @param param_name name of the parsed parameter
- DebugParser(const std::string& param_name)
- :param_name_(param_name) {
- }
-
- /// @brief builds parameter value
- ///
- /// See @ref DhcpConfigParser class for details.
- ///
- /// @param new_config pointer to the new configuration
- virtual void build(ConstElementPtr new_config) {
- std::cout << "Build for token: [" << param_name_ << "] = ["
- << value_->str() << "]" << std::endl;
- value_ = new_config;
- }
-
- /// @brief Pretends to apply the configuration.
- ///
- /// This is a method required by the base class. It pretends to apply the
- /// configuration, but in fact it only prints the parameter out.
- ///
- /// See @ref DhcpConfigParser class for details.
- virtual void commit() {
- // Debug message. The whole DebugParser class is used only for parser
- // debugging, and is not used in production code. It is very convenient
- // to keep it around. Please do not turn this cout into logger calls.
- std::cout << "Commit for token: [" << param_name_ << "] = ["
- << value_->str() << "]" << std::endl;
- }
-
- /// @brief factory that constructs DebugParser objects
- ///
- /// @param param_name name of the parameter to be parsed
- static DhcpConfigParser* factory(const std::string& param_name) {
- return (new DebugParser(param_name));
- }
-
-private:
- /// name of the parsed parameter
- std::string param_name_;
-
- /// pointer to the actual value of the parameter
- ConstElementPtr value_;
-};
-
-
-/// @brief A boolean value parser.
-///
-/// This parser handles configuration values of the boolean type.
-/// Parsed values are stored in a provided storage. If no storage
-/// is provided then the build function throws an exception.
-class BooleanParser : public DhcpConfigParser {
-public:
- /// @brief Constructor.
- ///
- /// @param param_name name of the parameter.
- BooleanParser(const std::string& param_name)
- : storage_(NULL),
- param_name_(param_name),
- value_(false) {
- // Empty parameter name is invalid.
- if (param_name_.empty()) {
- isc_throw(isc::dhcp::DhcpConfigError, "parser logic error:"
- << "empty parameter name provided");
- }
- }
-
- /// @brief Parse a boolean value.
- ///
- /// @param value a value to be parsed.
- ///
- /// @throw isc::InvalidOperation if a storage has not been set
- /// prior to calling this function
- /// @throw isc::dhcp::DhcpConfigError if a provided parameter's
- /// name is empty.
- virtual void build(ConstElementPtr value) {
- if (storage_ == NULL) {
- isc_throw(isc::InvalidOperation, "parser logic error:"
- << " storage for the " << param_name_
- << " value has not been set");
- } else if (param_name_.empty()) {
- isc_throw(isc::dhcp::DhcpConfigError, "parser logic error:"
- << "empty parameter name provided");
- }
- // The Config Manager checks if user specified a
- // valid value for a boolean parameter: True or False.
- // It is then ok to assume that if str() does not return
- // 'true' the value is 'false'.
- value_ = (value->str() == "true") ? true : false;
- }
-
- /// @brief Put a parsed value to the storage.
- virtual void commit() {
- if (storage_ != NULL && !param_name_.empty()) {
- storage_->setParam(param_name_, value_);
- }
- }
-
- /// @brief Create an instance of the boolean parser.
- ///
- /// @param param_name name of the parameter for which the
- /// parser is created.
- static DhcpConfigParser* factory(const std::string& param_name) {
- return (new BooleanParser(param_name));
- }
-
- /// @brief Set the storage for parsed value.
- ///
- /// This function must be called prior to calling build.
- ///
- /// @param storage a pointer to the storage where parsed data
- /// is to be stored.
- void setStorage(BooleanStorage* storage) {
- storage_ = storage;
- }
-
-private:
- /// Pointer to the storage where parsed value is stored.
- BooleanStorage* storage_;
- /// Name of the parameter which value is parsed with this parser.
- std::string param_name_;
- /// Parsed value.
- bool value_;
-};
-
-/// @brief Configuration parser for uint32 parameters
-///
-/// This class is a generic parser that is able to handle any uint32 integer
-/// type. By default it stores the value in external global container
-/// (uint32_defaults). If used in smaller scopes (e.g. to parse parameters
-/// in subnet config), it can be pointed to a different storage, using
-/// setStorage() method. This class follows the parser interface, laid out
-/// in its base class, @ref DhcpConfigParser.
-///
-/// For overview of usability of this generic purpose parser, see
-/// @ref dhcpv6ConfigInherit page.
-///
-/// @todo this class should be turned into the template class which
-/// will handle all uintX_types of data (see ticket #2415).
-class Uint32Parser : public DhcpConfigParser {
-public:
-
- /// @brief constructor for Uint32Parser
- ///
- /// @param param_name name of the configuration parameter being parsed
- Uint32Parser(const std::string& param_name)
- : storage_(&uint32_defaults),
- param_name_(param_name) {
- // Empty parameter name is invalid.
- if (param_name_.empty()) {
- isc_throw(DhcpConfigError, "parser logic error:"
- << "empty parameter name provided");
- }
- }
-
- /// @brief Parses configuration configuration parameter as uint32_t.
- ///
- /// @param value pointer to the content of parsed values
- /// @throw isc::dhcp::DhcpConfigError if failed to parse
- /// the configuration parameter as uint32_t value.
- virtual void build(ConstElementPtr value) {
- if (param_name_.empty()) {
- isc_throw(isc::dhcp::DhcpConfigError, "parser logic error:"
- << "empty parameter name provided");
- }
-
- bool parse_error = false;
- // Cast the provided value to int64 value to check.
- int64_t int64value = 0;
- try {
- // Parsing the value as a int64 value allows to
- // check if the provided value is within the range
- // of uint32_t (is not negative or greater than
- // maximal uint32_t value).
- int64value = boost::lexical_cast<int64_t>(value->str());
- } catch (const boost::bad_lexical_cast&) {
- parse_error = true;
- }
- if (!parse_error) {
- // Check that the value is not out of bounds.
- if ((int64value < 0) ||
- (int64value > std::numeric_limits<uint32_t>::max())) {
- parse_error = true;
- } else {
- // A value is not out of bounds so let's cast it to
- // the uint32_t type.
- value_ = static_cast<uint32_t>(int64value);
- }
-
- }
- // Invalid value provided.
- if (parse_error) {
- isc_throw(isc::dhcp::DhcpConfigError, "Failed to parse value " << value->str()
- << " as unsigned 32-bit integer.");
- }
- }
-
- /// @brief Stores the parsed uint32_t value in a storage.
- virtual void commit() {
- if (storage_ != NULL) {
- // If a given parameter already exists in the storage we override
- // its value. If it doesn't we insert a new element.
- storage_->setParam(param_name_, value_);
- }
- }
-
- /// @brief Factory that constructs Uint32Parser objects.
- ///
- /// @param param_name name of the parameter to be parsed.
- static DhcpConfigParser* factory(const std::string& param_name) {
- return (new Uint32Parser(param_name));
- }
-
- /// @brief Sets storage for value of this parameter.
- ///
- /// See @ref dhcpv6ConfigInherit for details.
- ///
- /// @param storage pointer to the storage container.
- void setStorage(Uint32Storage* storage) {
- storage_ = storage;
- }
-
-private:
- /// pointer to the storage, where parsed value will be stored
- Uint32Storage* storage_;
- /// name of the parameter to be parsed
- std::string param_name_;
- /// the actual parsed value
- uint32_t value_;
-};
-
-/// @brief Configuration parser for string parameters
-///
-/// This class is a generic parser that is able to handle any string
-/// parameter. By default it stores the value in an external global container
-/// (string_defaults). If used in smaller scopes (e.g. to parse parameters
-/// in subnet config), it can be pointed to a different storage, using the
-/// setStorage() method. This class follows the parser interface, laid out
-/// in its base class, @ref DhcpConfigParser.
-///
-/// For overview of usability of this generic purpose parser, see
-/// @ref dhcpv6ConfigInherit page.
-class StringParser : public DhcpConfigParser {
-public:
-
- /// @brief constructor for StringParser
- ///
- /// @param param_name name of the configuration parameter being parsed
- StringParser(const std::string& param_name)
- : storage_(&string_defaults),
- param_name_(param_name) {
- // Empty parameter name is invalid.
- if (param_name_.empty()) {
- isc_throw(DhcpConfigError, "parser logic error:"
- << "empty parameter name provided");
- }
- }
-
- /// @brief parses parameter value
- ///
- /// Parses configuration parameter's value as string.
- ///
- /// @param value pointer to the content of parsed values
- /// @throws DhcpConfigError if the parsed parameter's name is empty.
- virtual void build(ConstElementPtr value) {
- if (param_name_.empty()) {
- isc_throw(isc::dhcp::DhcpConfigError, "parser logic error:"
- << "empty parameter name provided");
- }
- value_ = value->str();
- boost::erase_all(value_, "\"");
- }
-
- /// @brief Stores the parsed value in a storage.
- virtual void commit() {
- if (storage_ != NULL && !param_name_.empty()) {
- // If a given parameter already exists in the storage we override
- // its value. If it doesn't we insert a new element.
- storage_->setParam(param_name_, value_);
- }
- }
-
- /// @brief Factory that constructs StringParser objects
- ///
- /// @param param_name name of the parameter to be parsed
- static DhcpConfigParser* factory(const std::string& param_name) {
- return (new StringParser(param_name));
- }
-
- /// @brief Sets storage for value of this parameter.
- ///
- /// See @ref dhcpv6ConfigInherit for details.
- ///
- /// @param storage pointer to the storage container
- void setStorage(StringStorage* storage) {
- storage_ = storage;
- }
-
-private:
- /// Pointer to the storage, where parsed value will be stored
- StringStorage* storage_;
- /// Name of the parameter to be parsed
- std::string param_name_;
- /// The actual parsed value
- std::string value_;
-};
-
-/// @brief parser for interface list definition
-///
-/// This parser handles Dhcp6/interface entry.
-/// 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.
-///
-/// It is useful for parsing Dhcp6/interface parameter.
-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
- /// @throw BadValue if supplied parameter name is not "interface"
- InterfaceListConfigParser(const std::string& param_name) {
- if (param_name != "interface") {
- isc_throw(isc::BadValue, "Internal error. Interface configuration "
- "parser called for the wrong parameter: " << param_name);
- }
- }
-
- /// @brief parses parameters value
- ///
- /// Parses configuration entry (list of parameters) and stores it in
- /// storage.
- ///
- /// @param value pointer to the content of parsed values
- virtual void build(ConstElementPtr value) {
- BOOST_FOREACH(ConstElementPtr iface, value->listValue()) {
- interfaces_.push_back(iface->str());
- }
- }
-
- /// @brief commits interfaces list configuration
- virtual void commit() {
- /// @todo: Implement per interface listening. Currently always listening
- /// on all interfaces.
- }
-
- /// @brief factory that constructs InterfaceListConfigParser objects
- ///
- /// @param param_name name of the parameter to be parsed
- static DhcpConfigParser* factory(const std::string& param_name) {
- return (new InterfaceListConfigParser(param_name));
- }
-
-private:
- /// contains list of network interfaces
- vector<string> interfaces_;
-};
-
-/// @brief parser for pool definition
-///
-/// This parser handles pool definitions, i.e. a list of entries of one
-/// of two syntaxes: min-max and prefix/len. Pool6 objects are created
-/// and stored in chosen PoolStorage container.
-///
-/// As there are no default values for pool, setStorage() must be called
-/// before build(). Otherwise an exception will be thrown.
-///
-/// It is useful for parsing Dhcp6/subnet6[X]/pool parameters.
-class PoolParser : public DhcpConfigParser {
-public:
-
- /// @brief constructor.
- PoolParser(const std::string& /*param_name*/)
- : pools_(NULL) {
- // ignore parameter name, it is always Dhcp6/subnet6[X]/pool
- }
-
- /// @brief parses the actual list
- ///
- /// This method parses the actual list of interfaces.
- /// No validation is done at this stage, everything is interpreted as
- /// interface name.
- /// @param pools_list list of pools defined for a subnet
- /// @throw isc::InvalidOperation if storage was not specified
- /// (setStorage() not called)
- void build(ConstElementPtr pools_list) {
-
- // setStorage() should have been called before build
- if (!pools_) {
- isc_throw(isc::InvalidOperation, "parser logic error: no pool storage set,"
- " but pool parser asked to parse pools");
- }
-
- BOOST_FOREACH(ConstElementPtr text_pool, pools_list->listValue()) {
-
- // That should be a single pool representation. It should contain
- // text in the form prefix/len or first - last. Note that spaces
- // are allowed
- string txt = text_pool->stringValue();
-
- // first let's remove any whitespaces
- boost::erase_all(txt, " "); // space
- boost::erase_all(txt, "\t"); // tabulation
-
- // Is this prefix/len notation?
- size_t pos = txt.find("/");
- if (pos != string::npos) {
- IOAddress addr("::");
- uint8_t len = 0;
- try {
- addr = IOAddress(txt.substr(0, pos));
-
- // start with the first character after /
- string prefix_len = txt.substr(pos + 1);
-
- // It is lexically cast to int and then downcast to uint8_t.
- // Direct cast to uint8_t (which is really an unsigned char)
- // will result in interpreting the first digit as output
- // value and throwing exception if length is written on two
- // digits (because there are extra characters left over).
-
- // No checks for values over 128. Range correctness will
- // be checked in Pool6 constructor.
- len = boost::lexical_cast<int>(prefix_len);
- } catch (...) {
- isc_throw(DhcpConfigError, "failed to parse pool "
- "definition: " << text_pool->stringValue());
- }
-
- Pool6Ptr pool(new Pool6(Pool6::TYPE_IA, addr, len));
- local_pools_.push_back(pool);
- continue;
- }
-
- // Is this min-max notation?
- pos = txt.find("-");
- if (pos != string::npos) {
- // using min-max notation
- IOAddress min(txt.substr(0, pos));
- IOAddress max(txt.substr(pos + 1));
-
- Pool6Ptr pool(new Pool6(Pool6::TYPE_IA, min, max));
-
- local_pools_.push_back(pool);
- continue;
- }
-
- isc_throw(DhcpConfigError, "failed to parse pool definition:"
- << text_pool->stringValue() <<
- ". Does not contain - (for min-max) nor / (prefix/len)");
- }
- }
-
- /// @brief sets storage for value of this parameter
- ///
- /// See @ref dhcpv6ConfigInherit for details.
- ///
- /// @param storage pointer to the storage container
- void setStorage(PoolStorage* storage) {
- pools_ = storage;
- }
-
- /// @brief Stores the parsed values in a storage provided
- /// by an upper level parser.
- virtual void commit() {
- if (pools_) {
- // local_pools_ holds the values produced by the build function.
- // At this point parsing should have completed successfuly so
- // we can append new data to the supplied storage.
- pools_->insert(pools_->end(), local_pools_.begin(),
- local_pools_.end());
- }
- }
-
- /// @brief factory that constructs PoolParser objects
- ///
- /// @param param_name name of the parameter to be parsed
- static DhcpConfigParser* factory(const std::string& param_name) {
- return (new PoolParser(param_name));
- }
-
-private:
- /// @brief pointer to the actual Pools storage
- ///
- /// This is typically a storage somewhere in Subnet parser
- /// (an upper level parser).
- PoolStorage* pools_;
- /// A temporary storage for pools configuration. It is a
- /// storage where pools are stored by build function.
- PoolStorage local_pools_;
-};
-
-
-/// @brief Parser for option data value.
+/// @brief Parser for DHCP6 option data value.
///
/// This parser parses configuration entries that specify value of
-/// a single option. These entries include option name, option code
-/// and data carried by the option. The option data can be specified
-/// in one of the two available formats: binary value represented as
-/// a string of hexadecimal digits or a list of comma separated values.
-/// The format being used is controlled by csv-format configuration
-/// parameter. When setting this value to True, the latter format is
-/// used. The subsequent values in the CSV format apply to relevant
-/// option data fields in the configured option. For example the
-/// configuration: "data" : "192.168.2.0, 56, hello world" can be
-/// used to set values for the option comprising IPv4 address,
-/// integer and string data field. Note that order matters. If the
-/// order of values does not match the order of data fields within
-/// an option the configuration will not be accepted. If parsing
-/// is successful then an instance of an option is created and
-/// added to the storage provided by the calling class.
-class OptionDataParser : public DhcpConfigParser {
+/// a single option specific to DHCP6. It provides the DHCP6-specific
+/// implementation of the abstract class OptionDataParser.
+class Dhcp6OptionDataParser : public OptionDataParser {
public:
-
/// @brief Constructor.
///
- /// Class constructor.
- OptionDataParser(const std::string&)
- : options_(NULL),
- // initialize option to NULL ptr
- option_descriptor_(false) { }
-
- /// @brief Parses the single option data.
- ///
- /// This method parses the data of a single option from the configuration.
- /// The option data includes option name, option code and data being
- /// carried by this option. Eventually it creates the instance of the
- /// option.
- ///
- /// @warning setStorage must be called with valid storage pointer prior
- /// to calling this method.
- ///
- /// @param option_data_entries collection of entries that define value
- /// for a particular option.
- /// @throw DhcpConfigError if invalid parameter specified in
- /// the configuration.
- /// @throw isc::InvalidOperation if failed to set storage prior to
- /// calling build.
- virtual void build(ConstElementPtr option_data_entries) {
-
- if (options_ == NULL) {
- isc_throw(isc::InvalidOperation, "Parser logic error: storage must be set before "
- "parsing option data.");
- }
- BOOST_FOREACH(ConfigPair param, option_data_entries->mapValue()) {
- ParserPtr parser;
- if (param.first == "name" || param.first == "data" ||
- param.first == "space") {
- boost::shared_ptr<StringParser>
- name_parser(dynamic_cast<StringParser*>(StringParser::factory(param.first)));
- if (name_parser) {
- name_parser->setStorage(&string_values_);
- parser = name_parser;
- }
- } else if (param.first == "code") {
- boost::shared_ptr<Uint32Parser>
- code_parser(dynamic_cast<Uint32Parser*>(Uint32Parser::factory(param.first)));
- if (code_parser) {
- code_parser->setStorage(&uint32_values_);
- parser = code_parser;
- }
- } else if (param.first == "csv-format") {
- boost::shared_ptr<BooleanParser>
- value_parser(dynamic_cast<BooleanParser*>(BooleanParser::factory(param.first)));
- if (value_parser) {
- value_parser->setStorage(&boolean_values_);
- parser = value_parser;
- }
- } else {
- isc_throw(DhcpConfigError,
- "parser error: option-data parameter not supported: "
- << param.first);
- }
- parser->build(param.second);
- // Before we can create an option we need to get the data from
- // the child parsers. The only way to do it is to invoke commit
- // on them so as they store the values in appropriate storages
- // that this class provided to them. Note that this will not
- // modify values stored in the global storages so the configuration
- // will remain consistent even parsing fails somewhere further on.
- parser->commit();
- }
- // Try to create the option instance.
- createOption();
+ /// @param dummy first param, option names are always "Dhcp6/option-data[n]"
+ /// @param options is the option storage in which to store the parsed option
+ /// upon "commit".
+ /// @param global_context is a pointer to the global context which
+ /// stores global scope parameters, options, option defintions.
+ Dhcp6OptionDataParser(const std::string&, OptionStoragePtr options,
+ ParserContextPtr global_context)
+ :OptionDataParser("", options, global_context) {
}
- /// @brief Commits option value.
- ///
- /// This function adds a new option to the storage or replaces an existing option
- /// with the same code.
+ /// @brief static factory method for instantiating Dhcp4OptionDataParsers
///
- /// @throw isc::InvalidOperation if failed to set pointer to storage or failed
- /// to call build() prior to commit. If that happens data in the storage
- /// remain un-modified.
- virtual void commit() {
- if (options_ == NULL) {
- isc_throw(isc::InvalidOperation, "parser logic error: storage must be set before "
- "committing option data.");
- } else if (!option_descriptor_.option) {
- // Before we can commit the new option should be configured. If it is not
- // than somebody must have called commit() before build().
- isc_throw(isc::InvalidOperation, "parser logic error: no option has been configured and"
- " thus there is nothing to commit. Has build() been called?");
- }
- uint16_t opt_type = option_descriptor_.option->getType();
- Subnet::OptionContainerPtr options = options_->getItems(option_space_);
- // The getItems() should never return NULL pointer. If there are no
- // options configured for the particular option space a pointer
- // to an empty container should be returned.
- assert(options);
- Subnet::OptionContainerTypeIndex& idx = options->get<1>();
- // Try to find options with the particular option code in the main
- // storage. If found, remove these options because they will be
- // replaced with new one.
- Subnet::OptionContainerTypeRange range =
- idx.equal_range(opt_type);
- if (std::distance(range.first, range.second) > 0) {
- idx.erase(range.first, range.second);
- }
- // Append new option to the main storage.
- options_->addItem(option_descriptor_, option_space_);
- }
-
- /// @brief Set storage for the parser.
- ///
- /// Sets storage for the parser. This storage points to the
- /// vector of options and is used by multiple instances of
- /// OptionDataParser. Each instance creates exactly one object
- /// of dhcp::Option or derived type and appends it to this
- /// storage.
- ///
- /// @param storage pointer to the options storage
- void setStorage(OptionStorage* storage) {
- options_ = storage;
- }
-
-private:
-
- /// @brief Create option instance.
- ///
- /// Creates an instance of an option and adds it to the provided
- /// options storage. If the option data parsed by \ref build function
- /// are invalid or insufficient this function emits an exception.
- ///
- /// @warning this function does not check if options_ storage pointer
- /// is initialized but this check is not needed here because it is done
- /// in the \ref build function.
- ///
- /// @throw DhcpConfigError if parameters provided in the configuration
- /// are invalid.
- void createOption() {
-
- // Option code is held in the uint32_t storage but is supposed to
- // be uint16_t value. We need to check that value in the configuration
- // does not exceed range of uint16_t and is not zero.
- uint32_t option_code = uint32_values_.getParam("code");
- if (option_code == 0) {
- isc_throw(DhcpConfigError, "option code must not be zero."
- << " Option code '0' is reserved in DHCPv6.");
- } else if (option_code > std::numeric_limits<uint16_t>::max()) {
- isc_throw(DhcpConfigError, "invalid option code '" << option_code
- << "', it must not exceed '"
- << std::numeric_limits<uint16_t>::max() << "'");
- }
- // Check that the option name has been specified, is non-empty and does not
- // contain spaces.
- std::string option_name = string_values_.getParam("name");
- if (option_name.empty()) {
- isc_throw(DhcpConfigError, "name of the option with code '"
- << option_code << "' is empty");
- } else if (option_name.find(" ") != std::string::npos) {
- isc_throw(DhcpConfigError, "invalid option name '" << option_name
- << "', space character is not allowed");
- }
-
- std::string option_space = string_values_.getParam("space");
- if (!OptionSpace::validateName(option_space)) {
- isc_throw(DhcpConfigError, "invalid option space name '"
- << option_space << "' specified for option '"
- << option_name << "' (code '" << option_code
- << "')");
- }
-
+ /// @param param_name name of the parameter to be parsed.
+ /// @param options storage where the parameter value is to be stored.
+ /// @param global_context is a pointer to the global context which
+ /// stores global scope parameters, options, option defintions.
+ /// @return returns a pointer to a new OptionDataParser. Caller is
+ /// is responsible for deleting it when it is no longer needed.
+ static OptionDataParser* factory(const std::string& param_name,
+ OptionStoragePtr options, ParserContextPtr global_context) {
+ return (new Dhcp6OptionDataParser(param_name, options, global_context));
+ }
+
+
+protected:
+ /// @brief Finds an option definition within the server's option space
+ ///
+ /// Given an option space and an option code, find the correpsonding
+ /// option defintion within the server's option defintion storage.
+ ///
+ /// @param option_space name of the parameter option space
+ /// @param option_code numeric value of the parameter to find
+ /// @return OptionDefintionPtr of the option defintion or an
+ /// empty OptionDefinitionPtr if not found.
+ /// @throw DhcpConfigError if the option space requested is not valid
+ /// for this server.
+ virtual OptionDefinitionPtr findServerSpaceOptionDefinition (
+ std::string& option_space, uint32_t option_code) {
OptionDefinitionPtr def;
if (option_space == "dhcp6" &&
LibDHCP::isStandardOption(Option::V6, option_code)) {
def = LibDHCP::getOptionDef(Option::V6, option_code);
-
} else if (option_space == "dhcp4") {
isc_throw(DhcpConfigError, "'dhcp4' option space name is reserved"
- << " for DHCPv4 server");
- } else {
- // If we are not dealing with a standard option then we
- // need to search for its definition among user-configured
- // options. They are expected to be in the global storage
- // already.
- OptionDefContainerPtr defs = option_def_intermediate.getItems(option_space);
- // The getItems() should never return the NULL pointer. If there are
- // no option definitions for the particular option space a pointer
- // to an empty container should be returned.
- assert(defs);
- const OptionDefContainerTypeIndex& idx = defs->get<1>();
- OptionDefContainerTypeRange range = idx.equal_range(option_code);
- if (std::distance(range.first, range.second) > 0) {
- def = *range.first;
- }
- if (!def) {
- isc_throw(DhcpConfigError, "definition for the option '"
- << option_space << "." << option_name
- << "' having code '" << option_code
- << "' does not exist");
- }
-
- }
-
- // Get option data from the configuration database ('data' field).
- const std::string option_data = string_values_.getParam("data");
- const bool csv_format = boolean_values_.getParam("csv-format");
-
- // Transform string of hexadecimal digits into binary format.
- std::vector<uint8_t> binary;
- std::vector<std::string> data_tokens;
-
- if (csv_format) {
- // If the option data is specified as a string of comma
- // separated values then we need to split this string into
- // individual values - each value will be used to initialize
- // one data field of an option.
- data_tokens = isc::util::str::tokens(option_data, ",");
- } else {
- // Otherwise, the option data is specified as a string of
- // hexadecimal digits that we have to turn into binary format.
- try {
- util::encode::decodeHex(option_data, binary);
- } catch (...) {
- isc_throw(DhcpConfigError, "Parser error: option data is not a valid"
- << " string of hexadecimal digits: " << option_data);
- }
+ << " for DHCPv4 server");
}
- OptionPtr option;
- if (!def) {
- if (csv_format) {
- isc_throw(DhcpConfigError, "the CSV option data format can be"
- " used to specify values for an option that has a"
- " definition. The option with code " << option_code
- << " does not have a definition.");
- }
-
- // @todo We have a limited set of option definitions initialized at the moment.
- // In the future we want to initialize option definitions for all options.
- // Consequently an error will be issued if an option definition does not exist
- // for a particular option code. For now it is ok to create generic option
- // if definition does not exist.
- OptionPtr option(new Option(Option::V6, static_cast<uint16_t>(option_code),
- binary));
- // The created option is stored in option_descriptor_ class member until the
- // commit stage when it is inserted into the main storage. If an option with the
- // same code exists in main storage already the old option is replaced.
- option_descriptor_.option = option;
- option_descriptor_.persistent = false;
- } else {
-
- // Option name should match the definition. The option name
- // may seem to be redundant but in the future we may want
- // to reference options and definitions using their names
- // and/or option codes so keeping the option name in the
- // definition of option value makes sense.
- if (def->getName() != option_name) {
- isc_throw(DhcpConfigError, "specified option name '"
- << option_name << "' does not match the "
- << "option definition: '" << option_space
- << "." << def->getName() << "'");
- }
-
- // Option definition has been found so let's use it to create
- // an instance of our option.
- try {
- OptionPtr option = csv_format ?
- def->optionFactory(Option::V6, option_code, data_tokens) :
- def->optionFactory(Option::V6, option_code, binary);
- Subnet::OptionDescriptor desc(option, false);
- option_descriptor_.option = option;
- option_descriptor_.persistent = false;
- } catch (const isc::Exception& ex) {
- isc_throw(DhcpConfigError, "option data does not match"
- << " option definition (space: " << option_space
- << ", code: " << option_code << "): "
- << ex.what());
- }
- }
- // All went good, so we can set the option space name.
- option_space_ = option_space;
+ return def;
}
-
- /// Storage for uint32 values (e.g. option code).
- Uint32Storage uint32_values_;
- /// Storage for string values (e.g. option name or data).
- StringStorage string_values_;
- /// Storage for boolean values.
- BooleanStorage boolean_values_;
- /// Pointer to options storage. This storage is provided by
- /// the calling class and is shared by all OptionDataParser objects.
- OptionStorage* options_;
- /// Option descriptor holds newly configured option.
- isc::dhcp::Subnet::OptionDescriptor option_descriptor_;
- /// Option space name where the option belongs to.
- std::string option_space_;
};
-/// @brief Parser for option data values within a subnet.
+/// @brief Parser for IPv4 pool definitions.
///
-/// This parser iterates over all entries that define options
-/// data for a particular subnet and creates a collection of options.
-/// If parsing is successful, all these options are added to the Subnet
-/// object.
-class OptionDataListParser : public DhcpConfigParser {
-public:
-
- /// @brief Constructor.
- ///
- /// Unless otherwise specified, parsed options will be stored in
- /// a global option container (option_default). That storage location
- /// is overriden on a subnet basis.
- OptionDataListParser(const std::string&)
- : options_(&option_defaults), local_options_() { }
-
- /// @brief Parses entries that define options' data for a subnet.
- ///
- /// This method iterates over all entries that define option data
- /// for options within a single subnet and creates options' instances.
- ///
- /// @param option_data_list pointer to a list of options' data sets.
- /// @throw DhcpConfigError if option parsing failed.
- void build(ConstElementPtr option_data_list) {
- BOOST_FOREACH(ConstElementPtr option_value, option_data_list->listValue()) {
- boost::shared_ptr<OptionDataParser> parser(new OptionDataParser("option-data"));
- // options_ member will hold instances of all options thus
- // each OptionDataParser takes it as a storage.
- parser->setStorage(&local_options_);
- // Build the instance of a single option.
- parser->build(option_value);
- // Store a parser as it will be used to commit.
- parsers_.push_back(parser);
- }
- }
-
- /// @brief Set storage for option instances.
- ///
- /// @param storage pointer to options storage.
- void setStorage(OptionStorage* storage) {
- options_ = storage;
- }
-
-
- /// @brief Commit all option values.
- ///
- /// This function invokes commit for all option values.
- void commit() {
- BOOST_FOREACH(ParserPtr parser, parsers_) {
- parser->commit();
- }
- // Parsing was successful and we have all configured
- // options in local storage. We can now replace old values
- // with new values.
- std::swap(local_options_, *options_);
- }
-
- /// @brief Create DhcpDataListParser object
- ///
- /// @param param_name param name.
- ///
- /// @return DhcpConfigParser object.
- static DhcpConfigParser* factory(const std::string& param_name) {
- return (new OptionDataListParser(param_name));
- }
-
- /// Pointer to options instances storage.
- OptionStorage* options_;
- /// Intermediate option storage. This storage is used by
- /// lower level parsers to add new options. Values held
- /// in this storage are assigned to main storage (options_)
- /// if overall parsing was successful.
- OptionStorage local_options_;
- /// Collection of parsers;
- ParserCollection parsers_;
-};
-
-/// @brief Parser for a single option definition.
+/// This is the IPv6 derivation of the PoolParser class and handles pool
+/// definitions, i.e. a list of entries of one of two syntaxes: min-max and
+/// prefix/len for IPv6 pools. Pool6 objects are created and stored in chosen
+/// PoolStorage container.
///
-/// This parser creates an instance of a single option definition.
-class OptionDefParser: DhcpConfigParser {
+/// It is useful for parsing Dhcp6/subnet6[X]/pool parameters.
+class Pool6Parser : public PoolParser {
public:
/// @brief Constructor.
///
- /// This constructor sets the pointer to the option definitions
- /// storage to NULL. It must be set to point to the actual storage
- /// before \ref build is called.
- OptionDefParser(const std::string&)
- : storage_(NULL) {
+ /// @param param_name name of the parameter. Note, it is passed through
+ /// but unused, parameter is currently always "Dhcp6/subnet6[X]/pool"
+ /// @param pools storage container in which to store the parsed pool
+ /// upon "commit"
+ Pool6Parser(const std::string& param_name, PoolStoragePtr pools)
+ :PoolParser(param_name, pools) {
+ }
+
+protected:
+ /// @brief Creates a Pool6 object given a IPv6 prefix and the prefix length.
+ ///
+ /// @param addr is the IPv6 prefix of the pool.
+ /// @param len is the prefix length.
+ /// @param ptype is the type of IPv6 pool (Pool6::Pool6Type). Note this is
+ /// passed in as an int32_t and cast to Pool6Type to accommodate a
+ /// polymorphic interface.
+ /// @return returns a PoolPtr to the new Pool4 object.
+ PoolPtr poolMaker (IOAddress &addr, uint32_t len, int32_t ptype)
+ {
+ return (PoolPtr(new Pool6(static_cast<isc::dhcp::Pool6::Pool6Type>
+ (ptype), addr, len)));
+ }
+
+ /// @brief Creates a Pool6 object given starting and ending IPv6 addresses.
+ ///
+ /// @param min is the first IPv6 address in the pool.
+ /// @param max is the last IPv6 address in the pool.
+ /// @param ptype is the type of IPv6 pool (Pool6::Pool6Type). Note this is
+ /// passed in as an int32_t and cast to Pool6Type to accommodate a
+ /// polymorphic interface.
+ /// @return returns a PoolPtr to the new Pool4 object.
+ PoolPtr poolMaker (IOAddress &min, IOAddress &max, int32_t ptype)
+ {
+ return (PoolPtr(new Pool6(static_cast<isc::dhcp::Pool6::Pool6Type>
+ (ptype), min, max)));
}
-
- /// @brief Parses an entry that describes single option definition.
- ///
- /// @param option_def a configuration entry to be parsed.
- ///
- /// @throw DhcpConfigError if parsing was unsuccessful.
- void build(ConstElementPtr option_def) {
- if (storage_ == NULL) {
- isc_throw(DhcpConfigError, "parser logic error: storage must be set"
- " before parsing option definition data");
- }
- // Parse the elements that make up the option definition.
- BOOST_FOREACH(ConfigPair param, option_def->mapValue()) {
- std::string entry(param.first);
- ParserPtr parser;
- if (entry == "name" || entry == "type" || entry == "record-types" ||
- entry == "space" || entry == "encapsulate") {
- StringParserPtr
- str_parser(dynamic_cast<StringParser*>(StringParser::factory(entry)));
- if (str_parser) {
- str_parser->setStorage(&string_values_);
- parser = str_parser;
- }
- } else if (entry == "code") {
- Uint32ParserPtr
- code_parser(dynamic_cast<Uint32Parser*>(Uint32Parser::factory(entry)));
- if (code_parser) {
- code_parser->setStorage(&uint32_values_);
- parser = code_parser;
- }
- } else if (entry == "array") {
- BooleanParserPtr
- array_parser(dynamic_cast<BooleanParser*>(BooleanParser::factory(entry)));
- if (array_parser) {
- array_parser->setStorage(&boolean_values_);
- parser = array_parser;
- }
- } else {
- isc_throw(DhcpConfigError, "invalid parameter: " << entry);
- }
-
- parser->build(param.second);
- parser->commit();
- }
-
- // Create an instance of option definition.
- createOptionDef();
-
- // Get all items we collected so far for the particular option space.
- OptionDefContainerPtr defs = storage_->getItems(option_space_name_);
- // Check if there are any items with option code the same as the
- // one specified for the definition we are now creating.
- const OptionDefContainerTypeIndex& idx = defs->get<1>();
- const OptionDefContainerTypeRange& range =
- idx.equal_range(option_definition_->getCode());
- // If there are any items with this option code already we need
- // to issue an error because we don't allow duplicates for
- // option definitions within an option space.
- if (std::distance(range.first, range.second) > 0) {
- isc_throw(DhcpConfigError, "duplicated option definition for"
- << " code '" << option_definition_->getCode() << "'");
- }
- }
-
- /// @brief Stores the parsed option definition in the data store.
- void commit() {
- if (storage_ && option_definition_ &&
- OptionSpace::validateName(option_space_name_)) {
- storage_->addItem(option_definition_, option_space_name_);
- }
- }
-
- /// @brief Sets a pointer to the data store.
- ///
- /// The newly created instance of an option definition will be
- /// added to the data store given by the argument.
- ///
- /// @param storage pointer to the data store where the option definition
- /// will be added to.
- void setStorage(OptionDefStorage* storage) {
- storage_ = storage;
- }
-
-private:
-
- /// @brief Create option definition from the parsed parameters.
- void createOptionDef() {
- // Get the option space name and validate it.
- std::string space = string_values_.getParam("space");
- if (!OptionSpace::validateName(space)) {
- isc_throw(DhcpConfigError, "invalid option space name '"
- << space << "'");
- }
-
- // Get other parameters that are needed to create the
- // option definition.
- std::string name = string_values_.getParam("name");
- uint32_t code = uint32_values_.getParam("code");
- std::string type = string_values_.getParam("type");
- bool array_type = boolean_values_.getParam("array");
- std::string encapsulates = string_values_.getParam("encapsulate");
-
- // Create option definition.
- OptionDefinitionPtr def;
- // We need to check if user has set encapsulated option space
- // name. If so, different constructor will be used.
- if (!encapsulates.empty()) {
- // Arrays can't be used together with sub-options.
- if (array_type) {
- isc_throw(DhcpConfigError, "option '" << space << "."
- << "name" << "', comprising an array of data"
- << " fields may not encapsulate any option space");
-
- } else if (encapsulates == space) {
- isc_throw(DhcpConfigError, "option must not encapsulate"
- << " an option space it belongs to: '"
- << space << "." << name << "' is set to"
- << " encapsulate '" << space << "'");
-
- } else {
- def.reset(new OptionDefinition(name, code, type,
- encapsulates.c_str()));
- }
-
- } else {
- def.reset(new OptionDefinition(name, code, type, array_type));
-
- }
-
- // The record-types field may carry a list of comma separated names
- // of data types that form a record.
- std::string record_types = string_values_.getParam("record-types");
- // Split the list of record types into tokens.
- std::vector<std::string> record_tokens =
- isc::util::str::tokens(record_types, ",");
- // Iterate over each token and add a record type into
- // option definition.
- BOOST_FOREACH(std::string record_type, record_tokens) {
- try {
- boost::trim(record_type);
- if (!record_type.empty()) {
- def->addRecordField(record_type);
- }
- } catch (const Exception& ex) {
- isc_throw(DhcpConfigError, "invalid record type values"
- << " specified for the option definition: "
- << ex.what());
- }
- }
-
- // Check the option definition parameters are valid.
- try {
- def->validate();
- } catch (const isc::Exception& ex) {
- isc_throw(DhcpConfigError, "invalid option definition"
- << " parameters: " << ex.what());
- }
- // Option definition has been created successfully.
- option_space_name_ = space;
- option_definition_ = def;
- }
-
- /// Instance of option definition being created by this parser.
- OptionDefinitionPtr option_definition_;
- /// Name of the space the option definition belongs to.
- std::string option_space_name_;
-
- /// Pointer to a storage where the option definition will be
- /// added when \ref commit is called.
- OptionDefStorage* storage_;
-
- /// Storage for boolean values.
- BooleanStorage boolean_values_;
- /// Storage for string values.
- StringStorage string_values_;
- /// Storage for uint32 values.
- Uint32Storage uint32_values_;
};
-/// @brief Parser for a list of option definitions.
+/// @brief This class parses a single IPv6 subnet.
///
-/// This parser iterates over all configuration entries that define
-/// option definitions and creates instances of these definitions.
-/// If the parsing is successful, the collection of created definitions
-/// is put into the provided storage.
-class OptionDefListParser : DhcpConfigParser {
+/// This is the IPv6 derivation of the SubnetConfigParser class and it parses
+/// the whole subnet definition. It creates parsersfor received configuration
+/// parameters as needed.
+class Subnet6ConfigParser : public SubnetConfigParser {
public:
- /// @brief Constructor.
- ///
- /// This constructor initializes the pointer to option definitions
- /// storage to NULL value. This pointer has to be set to point to
- /// the actual storage before the \ref build function is called.
- OptionDefListParser(const std::string&) {
- }
-
- /// @brief Parse configuration entries.
- ///
- /// This function parses configuration entries and creates instances
- /// of option definitions.
+ /// @brief Constructor
///
- /// @param option_def_list pointer to an element that holds entries
- /// that define option definitions.
- /// @throw DhcpConfigError if configuration parsing fails.
- void build(ConstElementPtr option_def_list) {
- // Clear existing items in the global storage.
- // We are going to replace all of them.
- option_def_intermediate.clearItems();
-
- if (!option_def_list) {
- isc_throw(DhcpConfigError, "parser error: a pointer to a list of"
- << " option definitions is NULL");
- }
-
- BOOST_FOREACH(ConstElementPtr option_def, option_def_list->listValue()) {
- boost::shared_ptr<OptionDefParser>
- parser(new OptionDefParser("single-option-def"));
- parser->setStorage(&option_def_intermediate);
- parser->build(option_def);
- parser->commit();
- }
+ /// @param ignored first parameter
+ /// stores global scope parameters, options, option defintions.
+ Subnet6ConfigParser(const std::string&)
+ :SubnetConfigParser("", globalContext()) {
}
- /// @brief Stores option definitions in the CfgMgr.
+ /// @brief Adds the created subnet to a server's configuration.
+ /// @throw throws Unexpected if dynamic cast fails.
void commit() {
-
- CfgMgr& cfg_mgr = CfgMgr::instance();
-
- cfg_mgr.deleteOptionDefs();
-
- // We need to move option definitions from the temporary
- // storage to the global storage.
- std::list<std::string> space_names =
- option_def_intermediate.getOptionSpaceNames();
- BOOST_FOREACH(std::string space_name, space_names) {
-
- BOOST_FOREACH(OptionDefinitionPtr def,
- *option_def_intermediate.getItems(space_name)) {
- // All option definitions should be initialized to non-NULL
- // values. The validation is expected to be made by the
- // OptionDefParser when creating an option definition.
- assert(def);
- cfg_mgr.addOptionDef(def, space_name);
+ if (subnet_) {
+ Subnet6Ptr sub6ptr = boost::dynamic_pointer_cast<Subnet6>(subnet_);
+ if (!sub6ptr) {
+ // If we hit this, it is a programming error.
+ isc_throw(Unexpected,
+ "Invalid cast in Subnet4ConfigParser::commit");
}
+ isc::dhcp::CfgMgr::instance().addSubnet6(sub6ptr);
}
}
- /// @brief Create an OptionDefListParser object.
- ///
- /// @param param_name configuration entry holding option definitions.
- ///
- /// @return OptionDefListParser object.
- static DhcpConfigParser* factory(const std::string& param_name) {
- return (new OptionDefListParser(param_name));
- }
-
-};
-
-/// @brief this class parses a single subnet
-///
-/// This class parses the whole subnet definition. It creates parsers
-/// for received configuration parameters as needed.
-class Subnet6ConfigParser : public DhcpConfigParser {
-public:
-
- /// @brief constructor
- Subnet6ConfigParser(const std::string& ) {
- // The parameter should always be "subnet", but we don't check
- // against that here in case some wants to reuse this parser somewhere.
- }
+protected:
- /// @brief parses parameter value
+ /// @brief creates parsers for entries in subnet definition
///
- /// @param subnet pointer to the content of subnet definition
+ /// @param config_id name of the entry
///
- /// @throw isc::DhcpConfigError if subnet configuration parsing failed.
- void build(ConstElementPtr subnet) {
-
- BOOST_FOREACH(ConfigPair param, subnet->mapValue()) {
- ParserPtr parser(createSubnet6ConfigParser(param.first));
- // The actual type of the parser is unknown here. We have to discover
- // the parser type here to invoke the corresponding setStorage function
- // on it. We discover parser type by trying to cast the parser to various
- // parser types and checking which one was successful. For this one
- // a setStorage and build methods are invoked.
-
- // Try uint32 type parser.
- if (!buildParser<Uint32Parser, Uint32Storage >(parser, uint32_values_,
- param.second) &&
- // Try string type parser.
- !buildParser<StringParser, StringStorage >(parser, string_values_,
- param.second) &&
- // Try pool parser.
- !buildParser<PoolParser, PoolStorage >(parser, pools_,
- param.second) &&
- // Try option data parser.
- !buildParser<OptionDataListParser, OptionStorage >(parser, options_,
- param.second)) {
- // Appropriate parsers are created in the createSubnet6ConfigParser
- // and they should be limited to those that we check here for. Thus,
- // if we fail to find a matching parser here it is a programming error.
- isc_throw(DhcpConfigError, "failed to find suitable parser");
- }
- }
-
- // In order to create new subnet we need to get the data out
- // of the child parsers first. The only way to do it is to
- // invoke commit on them because it will make them write
- // parsed data into storages we have supplied.
- // Note that triggering commits on child parsers does not
- // affect global data because we supplied pointers to storages
- // local to this object. Thus, even if this method fails
- // later on, the configuration remains consistent.
- BOOST_FOREACH(ParserPtr parser, parsers_) {
- parser->commit();
+ /// @return parser object for specified entry name. Note the caller is
+ /// responsible for deleting the parser created.
+ /// @throw isc::dhcp::DhcpConfigError if trying to create a parser
+ /// for unknown config element
+ DhcpConfigParser* createSubnetConfigParser(const std::string& config_id) {
+ DhcpConfigParser* parser = NULL;
+ if ((config_id.compare("preferred-lifetime") == 0) ||
+ (config_id.compare("valid-lifetime") == 0) ||
+ (config_id.compare("renew-timer") == 0) ||
+ (config_id.compare("rebind-timer") == 0)) {
+ parser = new Uint32Parser(config_id, uint32_values_);
+ } else if ((config_id.compare("subnet") == 0) ||
+ (config_id.compare("interface") == 0) ||
+ (config_id.compare("interface-id") == 0)) {
+ parser = new StringParser(config_id, string_values_);
+ } else if (config_id.compare("pool") == 0) {
+ parser = new Pool6Parser(config_id, pools_);
+ } else if (config_id.compare("option-data") == 0) {
+ parser = new OptionDataListParser(config_id, options_,
+ global_context_,
+ Dhcp6OptionDataParser::factory);
+ } else {
+ isc_throw(NotImplemented,
+ "parser error: Subnet6 parameter not supported: " << config_id);
}
- // Create a subnet.
- createSubnet();
+ return (parser);
}
- /// @brief Adds the created subnet to a server's configuration.
- void commit() {
- if (subnet_) {
- isc::dhcp::CfgMgr::instance().addSubnet6(subnet_);
- }
- }
-private:
-
- /// @brief Set storage for a parser and invoke build.
- ///
- /// This helper method casts the provided parser pointer to the specified
- /// type. If the cast is successful it sets the corresponding storage for
- /// this parser, invokes build on it and saves the parser.
+ /// @brief Determines if the given option space name and code describe
+ /// a standard option for the DHCP6 server.
///
- /// @tparam T parser type to which parser argument should be cast.
- /// @tparam Y storage type for the specified parser type.
- /// @param parser parser on which build must be invoked.
- /// @param storage reference to a storage that will be set for a parser.
- /// @param subnet subnet element read from the configuration and being parsed.
- /// @return true if parser pointer was successfully cast to specialized
- /// parser type provided as Y.
- template<typename T, typename Y>
- bool buildParser(const ParserPtr& parser, Y& storage, const ConstElementPtr& subnet) {
- // We need to cast to T in order to set storage for the parser.
- boost::shared_ptr<T> cast_parser = boost::dynamic_pointer_cast<T>(parser);
- // It is common that this cast is not successful because we try to cast to all
- // supported parser types as we don't know the type of a parser in advance.
- if (cast_parser) {
- // Cast, successful so we go ahead with setting storage and actual parse.
- cast_parser->setStorage(&storage);
- parser->build(subnet);
- parsers_.push_back(parser);
- // We indicate that cast was successful so as the calling function
- // may skip attempts to cast to other parser types and proceed to
- // next element.
- return (true);
- }
- // It was not successful. Indicate that another parser type
- // should be tried.
- return (false);
+ /// @param option_space is the name of the option space to consider
+ /// @param code is the numeric option code to consider
+ /// @return returns true if the space and code are part of the server's
+ /// standard options.
+ bool isServerStdOption(std::string option_space, uint32_t code) {
+ return ((option_space.compare("dhcp6") == 0)
+ && LibDHCP::isStandardOption(Option::V6, code));
}
- /// @brief Append sub-options to an option.
- ///
- /// @param option_space a name of the encapsulated option space.
- /// @param option option instance to append sub-options to.
- void appendSubOptions(const std::string& option_space, OptionPtr& option) {
- // Only non-NULL options are stored in option container.
- // If this option pointer is NULL this is a serious error.
- assert(option);
-
- OptionDefinitionPtr def;
- if (option_space == "dhcp6" &&
- LibDHCP::isStandardOption(Option::V6, option->getType())) {
- def = LibDHCP::getOptionDef(Option::V6, option->getType());
- // Definitions for some of the standard options hasn't been
- // implemented so it is ok to leave here.
- if (!def) {
- return;
- }
- } else {
- const OptionDefContainerPtr defs =
- option_def_intermediate.getItems(option_space);
- const OptionDefContainerTypeIndex& idx = defs->get<1>();
- const OptionDefContainerTypeRange& range =
- idx.equal_range(option->getType());
- // There is no definition so we have to leave.
- if (std::distance(range.first, range.second) == 0) {
- return;
- }
-
- def = *range.first;
-
- // If the definition exists, it must be non-NULL.
- // Otherwise it is a programming error.
- assert(def);
- }
-
- // We need to get option definition for the particular option space
- // and code. This definition holds the information whether our
- // option encapsulates any option space.
- // Get the encapsulated option space name.
- std::string encapsulated_space = def->getEncapsulatedSpace();
- // If option space name is empty it means that our option does not
- // encapsulate any option space (does not include sub-options).
- if (!encapsulated_space.empty()) {
- // Get the sub-options that belong to the encapsulated
- // option space.
- const Subnet::OptionContainerPtr sub_opts =
- option_defaults.getItems(encapsulated_space);
- // Append sub-options to the option.
- BOOST_FOREACH(Subnet::OptionDescriptor desc, *sub_opts) {
- if (desc.option) {
- option->addOption(desc.option);
- }
- }
- }
+ /// @brief Returns the option definition for a given option code from
+ /// the DHCP6 server's standard set of options.
+ /// @param code is the numeric option code of the desired option definition.
+ /// @return returns a pointer the option definition
+ OptionDefinitionPtr getServerStdOptionDefinition (uint32_t code) {
+ return (LibDHCP::getOptionDef(Option::V6, code));
}
- /// @brief Create a new subnet using a data from child parsers.
- ///
- /// @throw isc::dhcp::DhcpConfigError if subnet configuration parsing failed.
- void createSubnet() {
- std::string subnet_txt;
- try {
- subnet_txt = string_values_.getParam("subnet");
- } catch (DhcpConfigError) {
- // rethrow with precise error
- isc_throw(DhcpConfigError,
- "Mandatory subnet definition in subnet missing");
- }
-
- // Remove any spaces or tabs.
- boost::erase_all(subnet_txt, " ");
- boost::erase_all(subnet_txt, "\t");
-
- // The subnet format is prefix/len. We are going to extract
- // the prefix portion of a subnet string to create IOAddress
- // object from it. IOAddress will be passed to the Subnet's
- // constructor later on. In order to extract the prefix we
- // need to get all characters preceding "/".
- size_t pos = subnet_txt.find("/");
- if (pos == string::npos) {
- isc_throw(DhcpConfigError,
- "Invalid subnet syntax (prefix/len expected):" << subnet_txt);
- }
-
- // Try to create the address object. It also validates that
- // the address syntax is ok.
- IOAddress addr(subnet_txt.substr(0, pos));
- uint8_t len = boost::lexical_cast<unsigned int>(subnet_txt.substr(pos + 1));
+ /// @brief Issues a DHCP6 server specific warning regarding duplicate subnet
+ /// options.
+ ///
+ /// @param code is the numeric option code of the duplicate option
+ /// @param addr is the subnet address
+ /// @todo A means to know the correct logger and perhaps a common
+ /// message would allow this message to be emitted by the base class.
+ virtual void duplicate_option_warning(uint32_t code,
+ isc::asiolink::IOAddress& addr) {
+ LOG_WARN(dhcp6_logger, DHCP6_CONFIG_OPTION_DUPLICATE)
+ .arg(code).arg(addr.toText());
+ }
+ /// @brief Instantiates the IPv6 Subnet based on a given IPv6 address
+ /// and prefix length.
+ ///
+ /// @param addr is IPv6 prefix of the subnet.
+ /// @param len is the prefix length
+ void initSubnet(isc::asiolink::IOAddress addr, uint8_t len) {
// Get all 'time' parameters using inheritance.
// If the subnet-specific value is defined then use it, else
// use the global value. The global value must always be
@@ -1476,30 +281,33 @@ private:
Triplet<uint32_t> pref = getParam("preferred-lifetime");
Triplet<uint32_t> valid = getParam("valid-lifetime");
- // Get interface name. If it is defined, then the subnet is available
- // directly over specified network interface.
- std::string iface;
- try {
- iface = string_values_.getParam("interface");
- } catch (const DhcpConfigError&) {
- // iface not mandatory so swallow the exception
- }
-
// Get interface-id option content. For now we support string
// represenation only
std::string ifaceid;
try {
- ifaceid = string_values_.getParam("interface-id");
- } catch (const DhcpConfigError&) {
+ ifaceid = string_values_->getParam("interface-id");
+ } catch (const DhcpConfigError &) {
// interface-id is not mandatory
}
- if (!iface.empty() && !ifaceid.empty()) {
- isc_throw(isc::dhcp::DhcpConfigError,
+ // Specifying both interface for locally reachable subnets and
+ // interface id for relays is mutually exclusive. Need to test for
+ // this condition.
+ if (!ifaceid.empty()) {
+ std::string iface;
+ try {
+ iface = string_values_->getParam("interface");
+ } catch (const DhcpConfigError &) {
+ // iface not mandatory
+ }
+
+ if (!iface.empty()) {
+ isc_throw(isc::dhcp::DhcpConfigError,
"parser error: interface (defined for locally reachable "
"subnets) and interface-id (defined for subnets reachable"
" via relays) cannot be defined at the same time for "
"subnet " << addr.toText() << "/" << (int)len);
+ }
}
stringstream tmp;
@@ -1510,172 +318,22 @@ private:
LOG_INFO(dhcp6_logger, DHCP6_CONFIG_NEW_SUBNET).arg(tmp.str());
// Create a new subnet.
- subnet_.reset(new Subnet6(addr, len, t1, t2, pref, valid));
-
- // Add pools to it.
- for (PoolStorage::iterator it = pools_.begin(); it != pools_.end(); ++it) {
- subnet_->addPool(*it);
- }
-
- // Configure interface, if defined
- if (!iface.empty()) {
- if (!IfaceMgr::instance().getIface(iface)) {
- isc_throw(DhcpConfigError, "Specified interface name " << iface
- << " for subnet " << subnet_->toText() << " is not present"
- << " in the system.");
- }
-
- subnet_->setIface(iface);
- }
+ Subnet6* subnet6 = new Subnet6(addr, len, t1, t2, pref, valid);
// Configure interface-id for remote interfaces, if defined
if (!ifaceid.empty()) {
OptionBuffer tmp(ifaceid.begin(), ifaceid.end());
OptionPtr opt(new Option(Option::V6, D6O_INTERFACE_ID, tmp));
- subnet_->setInterfaceId(opt);
+ subnet6->setInterfaceId(opt);
}
- // We are going to move configured options to the Subnet object.
- // Configured options reside in the container where options
- // are grouped by space names. Thus we need to get all space names
- // and iterate over all options that belong to them.
- std::list<std::string> space_names = options_.getOptionSpaceNames();
- BOOST_FOREACH(std::string option_space, space_names) {
- // Get all options within a particular option space.
- BOOST_FOREACH(Subnet::OptionDescriptor desc,
- *options_.getItems(option_space)) {
- // The pointer should be non-NULL. The validation is expected
- // to be performed by the OptionDataParser before adding an
- // option descriptor to the container.
- assert(desc.option);
- // We want to check whether an option with the particular
- // option code has been already added. If so, we want
- // to issue a warning.
- Subnet::OptionDescriptor existing_desc =
- subnet_->getOptionDescriptor("option_space",
- desc.option->getType());
- if (existing_desc.option) {
- LOG_WARN(dhcp6_logger, DHCP6_CONFIG_OPTION_DUPLICATE)
- .arg(desc.option->getType()).arg(addr.toText());
- }
- // Add sub-options (if any).
- appendSubOptions(option_space, desc.option);
- // In any case, we add the option to the subnet.
- subnet_->addOption(desc.option, false, option_space);
- }
- }
-
- // Check all global options and add them to the subnet object if
- // they have been configured in the global scope. If they have been
- // configured in the subnet scope we don't add global option because
- // the one configured in the subnet scope always takes precedence.
- space_names = option_defaults.getOptionSpaceNames();
- BOOST_FOREACH(std::string option_space, space_names) {
- // Get all global options for the particular option space.
- BOOST_FOREACH(Subnet::OptionDescriptor desc,
- *option_defaults.getItems(option_space)) {
- // The pointer should be non-NULL. The validation is expected
- // to be performed by the OptionDataParser before adding an
- // option descriptor to the container.
- assert(desc.option);
- // Check if the particular option has been already added.
- // This would mean that it has been configured in the
- // subnet scope. Since option values configured in the
- // subnet scope take precedence over globally configured
- // values we don't add option from the global storage
- // if there is one already.
- Subnet::OptionDescriptor existing_desc =
- subnet_->getOptionDescriptor(option_space, desc.option->getType());
- if (!existing_desc.option) {
- // Add sub-options (if any).
- appendSubOptions(option_space, desc.option);
-
- subnet_->addOption(desc.option, false, option_space);
- }
- }
- }
+ subnet_.reset(subnet6);
}
- /// @brief creates parsers for entries in subnet definition
- ///
- /// @param config_id name od the entry
- ///
- /// @return parser object for specified entry name
- /// @throw isc::dhcp::DhcpConfigError if trying to create a parser
- /// for unknown config element
- DhcpConfigParser* createSubnet6ConfigParser(const std::string& config_id) {
- FactoryMap factories;
-
- factories["preferred-lifetime"] = Uint32Parser::factory;
- factories["valid-lifetime"] = Uint32Parser::factory;
- factories["renew-timer"] = Uint32Parser::factory;
- factories["rebind-timer"] = Uint32Parser::factory;
- factories["subnet"] = StringParser::factory;
- factories["pool"] = PoolParser::factory;
- factories["option-data"] = OptionDataListParser::factory;
- factories["interface"] = StringParser::factory;
- factories["interface-id"] = StringParser::factory;
-
- FactoryMap::iterator f = factories.find(config_id);
- if (f == factories.end()) {
- // Used for debugging only.
- // return new DebugParser(config_id);
-
- isc_throw(isc::dhcp::DhcpConfigError,
- "parser error: subnet6 parameter not supported: "
- << config_id);
- }
- return (f->second(config_id));
- }
-
- /// @brief Returns value for a given parameter (after using inheritance)
- ///
- /// This method implements inheritance. For a given parameter name, it first
- /// checks if there is a global value for it and overwrites it with specific
- /// value if such value was defined in subnet.
- ///
- /// @param name name of the parameter
- /// @return triplet with the parameter name
- /// @throw DhcpConfigError when requested parameter is not present
- isc::dhcp::Triplet<uint32_t> getParam(const std::string& name) {
- uint32_t value = 0;
- try {
- // look for local value
- value = uint32_values_.getParam(name);
- } catch (DhcpConfigError) {
- try {
- // no local, use global value
- value = uint32_defaults.getParam(name);
- } catch (DhcpConfigError) {
- isc_throw(DhcpConfigError, "Mandatory parameter " << name
- << " missing (no global default and no subnet-"
- << "specific value)");
- }
- }
-
- return (Triplet<uint32_t>(value));
- }
-
- /// storage for subnet-specific uint32 values
- Uint32Storage uint32_values_;
-
- /// storage for subnet-specific integer values
- StringStorage string_values_;
-
- /// storage for pools belonging to this subnet
- PoolStorage pools_;
-
- /// storage for options belonging to this subnet
- OptionStorage options_;
-
- /// parsers are stored here
- ParserCollection parsers_;
-
- /// Pointer to the created subnet object.
- isc::dhcp::Subnet6Ptr subnet_;
};
-/// @brief this class parses a list of subnets
+
+/// @brief this class parses a list of DHCP6 subnets
///
/// This is a wrapper parser that handles the whole list of Subnet6
/// definitions. It iterates over all entries and creates Subnet6ConfigParser
@@ -1685,8 +343,9 @@ public:
/// @brief constructor
///
+ /// @param dummy first argument, always ignored. All parsers accept a
+ /// string parameter "name" as their first argument.
Subnets6ListConfigParser(const std::string&) {
- /// parameter name is ignored
}
/// @brief parses contents of the list
@@ -1696,12 +355,7 @@ public:
///
/// @param subnets_list pointer to a list of IPv6 subnets
void build(ConstElementPtr subnets_list) {
-
- // No need to define FactoryMap here. There's only one type
- // used: Subnet6ConfigParser
-
BOOST_FOREACH(ConstElementPtr subnet, subnets_list->listValue()) {
-
ParserPtr parser(new Subnet6ConfigParser("subnet"));
parser->build(subnet);
subnets_.push_back(parser);
@@ -1711,8 +365,8 @@ public:
/// @brief commits subnets definitions.
///
- /// Iterates over all Subnet6 parsers. Each parser contains definitions
- /// of a single subnet and its parameters and commits each subnet separately.
+ /// Iterates over all Subnet6 parsers. Each parser contains definitions of
+ /// a single subnet and its parameters and commits each subnet separately.
void commit() {
// @todo: Implement more subtle reconfiguration than toss
// the old one and replace with the new one.
@@ -1749,53 +403,63 @@ namespace dhcp {
///
/// @param config_id pointer to received global configuration entry
/// @return parser for specified global DHCPv6 parameter
-/// @throw NotImplemented if trying to create a parser for unknown config element
-DhcpConfigParser* createGlobalDhcpConfigParser(const std::string& config_id) {
- FactoryMap factories;
-
- factories["preferred-lifetime"] = Uint32Parser::factory;
- factories["valid-lifetime"] = Uint32Parser::factory;
- factories["renew-timer"] = Uint32Parser::factory;
- factories["rebind-timer"] = Uint32Parser::factory;
- factories["interface"] = InterfaceListConfigParser::factory;
- factories["subnet6"] = Subnets6ListConfigParser::factory;
- factories["option-data"] = OptionDataListParser::factory;
- factories["option-def"] = OptionDefListParser::factory;
- factories["version"] = StringParser::factory;
- factories["lease-database"] = DbAccessParser::factory;
-
- FactoryMap::iterator f = factories.find(config_id);
- if (f == factories.end()) {
- // Used for debugging only.
- // return new DebugParser(config_id);
-
+/// @throw NotImplemented if trying to create a parser for unknown config
+/// element
+DhcpConfigParser* createGlobal6DhcpConfigParser(const std::string& config_id) {
+ DhcpConfigParser* parser = NULL;
+ if ((config_id.compare("preferred-lifetime") == 0) ||
+ (config_id.compare("valid-lifetime") == 0) ||
+ (config_id.compare("renew-timer") == 0) ||
+ (config_id.compare("rebind-timer") == 0)) {
+ parser = new Uint32Parser(config_id,
+ globalContext()->uint32_values_);
+ } else if (config_id.compare("interface") == 0) {
+ parser = new InterfaceListConfigParser(config_id);
+ } else if (config_id.compare("subnet6") == 0) {
+ parser = new Subnets6ListConfigParser(config_id);
+ } else if (config_id.compare("option-data") == 0) {
+ parser = new OptionDataListParser(config_id,
+ globalContext()->options_,
+ globalContext(),
+ Dhcp6OptionDataParser::factory);
+ } else if (config_id.compare("option-def") == 0) {
+ parser = new OptionDefListParser(config_id,
+ globalContext()->option_defs_);
+ } else if (config_id.compare("version") == 0) {
+ parser = new StringParser(config_id,
+ globalContext()->string_values_);
+ } else if (config_id.compare("lease-database") == 0) {
+ parser = new DbAccessParser(config_id);
+ } else {
isc_throw(NotImplemented,
- "Parser error: Global configuration parameter not supported: "
- << config_id);
+ "Parser error: Global configuration parameter not supported: "
+ << config_id);
}
- return (f->second(config_id));
+
+ return (parser);
}
-ConstElementPtr
-configureDhcp6Server(Dhcpv6Srv&, ConstElementPtr config_set) {
+isc::data::ConstElementPtr
+configureDhcp6Server(Dhcpv6Srv&, isc::data::ConstElementPtr config_set) {
if (!config_set) {
ConstElementPtr answer = isc::config::createAnswer(1,
string("Can't parse NULL config"));
return (answer);
}
- /// @todo: append most essential info here (like "2 new subnets configured")
+ /// @todo: Append most essential info here (like "2 new subnets configured")
string config_details;
- LOG_DEBUG(dhcp6_logger, DBG_DHCP6_COMMAND, DHCP6_CONFIG_START).arg(config_set->str());
+ LOG_DEBUG(dhcp6_logger, DBG_DHCP6_COMMAND,
+ DHCP6_CONFIG_START).arg(config_set->str());
// Some of the values specified in the configuration depend on
- // other values. Typically, the values in the subnet4 structure
+ // other values. Typically, the values in the subnet6 structure
// depend on the global values. Also, option values configuration
// must be performed after the option definitions configurations.
// Thus we group parsers and will fire them in the right order:
- // all parsers other than subnet4 and option-data parser,
- // option-data parser, subnet4 parser.
+ // all parsers other than subnet6 and option-data parser,
+ // option-data parser, subnet6 parser.
ParserCollection independent_parsers;
ParserPtr subnet_parser;
ParserPtr option_parser;
@@ -1807,10 +471,7 @@ configureDhcp6Server(Dhcpv6Srv&, ConstElementPtr config_set) {
// parsing operation fails after the global storage has been
// modified. We need to preserve the original global data here
// so as we can rollback changes when an error occurs.
- Uint32Storage uint32_local(uint32_defaults);
- StringStorage string_local(string_defaults);
- OptionStorage option_local(option_defaults);
- OptionDefStorage option_def_local(option_def_intermediate);
+ ParserContext original_context(*globalContext());
// answer will hold the result.
ConstElementPtr answer;
@@ -1827,15 +488,13 @@ configureDhcp6Server(Dhcpv6Srv&, ConstElementPtr config_set) {
const std::map<std::string, ConstElementPtr>& values_map =
config_set->mapValue();
BOOST_FOREACH(config_pair, values_map) {
- ParserPtr parser(createGlobalDhcpConfigParser(config_pair.first));
+ ParserPtr parser(createGlobal6DhcpConfigParser(config_pair.first));
LOG_DEBUG(dhcp6_logger, DBG_DHCP6_DETAIL, DHCP6_PARSER_CREATED)
.arg(config_pair.first);
if (config_pair.first == "subnet6") {
subnet_parser = parser;
-
} else if (config_pair.first == "option-data") {
option_parser = parser;
-
} else {
// Those parsers should be started before other
// parsers so we can call build straight away.
@@ -1908,10 +567,7 @@ configureDhcp6Server(Dhcpv6Srv&, ConstElementPtr config_set) {
// Rollback changes as the configuration parsing failed.
if (rollback) {
- std::swap(uint32_defaults, uint32_local);
- std::swap(string_defaults, string_local);
- std::swap(option_defaults, option_local);
- std::swap(option_def_intermediate, option_def_local);
+ globalContext().reset(new ParserContext(original_context));
return (answer);
}
@@ -1922,5 +578,10 @@ configureDhcp6Server(Dhcpv6Srv&, ConstElementPtr config_set) {
return (answer);
}
+ParserContextPtr& globalContext() {
+ static ParserContextPtr global_context_ptr(new ParserContext(Option::V6));
+ return (global_context_ptr);
+}
+
}; // end of isc::dhcp namespace
}; // end of isc namespace
diff --git a/src/bin/dhcp6/config_parser.h b/src/bin/dhcp6/config_parser.h
index 6d7a807..2dce79e 100644
--- a/src/bin/dhcp6/config_parser.h
+++ b/src/bin/dhcp6/config_parser.h
@@ -20,6 +20,8 @@
#include <cc/data.h>
#include <exceptions/exceptions.h>
+#include <dhcpsrv/dhcp_parsers.h>
+
#include <string>
namespace isc {
@@ -29,9 +31,9 @@ class Dhcpv6Srv;
/// @brief Configures DHCPv6 server
///
-/// This function is called every time a new configuration is received. The extra
-/// parameter is a reference to DHCPv6 server component. It is currently not used
-/// and CfgMgr::instance() is accessed instead.
+/// This function is called every time a new configuration is received. The
+/// extra parameter is a reference to DHCPv6 server component. It is currently
+/// not used and CfgMgr::instance() is accessed instead.
///
/// This method does not throw. It catches all exceptions and returns them as
/// reconfiguration statuses. It may return the following response codes:
@@ -47,6 +49,11 @@ class Dhcpv6Srv;
isc::data::ConstElementPtr
configureDhcp6Server(Dhcpv6Srv& server, isc::data::ConstElementPtr config_set);
+/// @brief Returns the global context
+///
+/// @returns a reference to the global context
+ParserContextPtr& globalContext();
+
}; // end of isc::dhcp namespace
}; // end of isc namespace
diff --git a/src/lib/dhcpsrv/Makefile.am b/src/lib/dhcpsrv/Makefile.am
index f646dd6..29e8c2f 100644
--- a/src/lib/dhcpsrv/Makefile.am
+++ b/src/lib/dhcpsrv/Makefile.am
@@ -39,6 +39,7 @@ libb10_dhcpsrv_la_SOURCES += dbaccess_parser.cc dbaccess_parser.h
libb10_dhcpsrv_la_SOURCES += dhcpsrv_log.cc dhcpsrv_log.h
libb10_dhcpsrv_la_SOURCES += cfgmgr.cc cfgmgr.h
libb10_dhcpsrv_la_SOURCES += dhcp_config_parser.h
+libb10_dhcpsrv_la_SOURCES += dhcp_parsers.cc dhcp_parsers.h
libb10_dhcpsrv_la_SOURCES += key_from_key.h
libb10_dhcpsrv_la_SOURCES += lease_mgr.cc lease_mgr.h
libb10_dhcpsrv_la_SOURCES += lease_mgr_factory.cc lease_mgr_factory.h
@@ -59,6 +60,7 @@ libb10_dhcpsrv_la_CPPFLAGS = $(AM_CPPFLAGS) $(LOG4CPLUS_INCLUDES)
libb10_dhcpsrv_la_LIBADD = $(top_builddir)/src/lib/asiolink/libb10-asiolink.la
libb10_dhcpsrv_la_LIBADD += $(top_builddir)/src/lib/dhcp/libb10-dhcp++.la
libb10_dhcpsrv_la_LIBADD += $(top_builddir)/src/lib/util/libb10-util.la
+libb10_dhcpsrv_la_LIBADD += $(top_builddir)/src/lib/cc/libb10-cc.la
libb10_dhcpsrv_la_LDFLAGS = -no-undefined -version-info 3:0:0
if HAVE_MYSQL
libb10_dhcpsrv_la_LDFLAGS += $(MYSQL_LIBS)
diff --git a/src/lib/dhcpsrv/dhcp_config_parser.h b/src/lib/dhcpsrv/dhcp_config_parser.h
index 8abdfc8..77e46c8 100644
--- a/src/lib/dhcpsrv/dhcp_config_parser.h
+++ b/src/lib/dhcpsrv/dhcp_config_parser.h
@@ -130,79 +130,6 @@ public:
virtual void commit() = 0;
};
-/// @brief A template class that stores named elements of a given data type.
-///
-/// This template class is provides data value storage for configuration parameters
-/// of a given data type. The values are stored by parameter name and as instances
-/// of type "ValueType".
-///
-/// @param ValueType is the data type of the elements to store.
-template<typename ValueType>
-class ValueStorage {
- public:
- /// @brief Stores the the parameter and its value in the store.
- ///
- /// If the parameter does not exist in the store, then it will be added,
- /// otherwise its data value will be updated with the given value.
- ///
- /// @param name is the name of the paramater to store.
- /// @param value is the data value to store.
- void setParam(const std::string name, const ValueType& value) {
- values_[name] = value;
- }
-
- /// @brief Returns the data value for the given parameter.
- ///
- /// Finds and returns the data value for the given parameter.
- /// @param name is the name of the parameter for which the data
- /// value is desired.
- ///
- /// @return The paramater's data value of type <ValueType>.
- /// @throw DhcpConfigError if the parameter is not found.
- ValueType getParam(const std::string& name) const {
- typename std::map<std::string, ValueType>::const_iterator param
- = values_.find(name);
-
- if (param == values_.end()) {
- isc_throw(DhcpConfigError, "Missing parameter '"
- << name << "'");
- }
-
- return (param->second);
- }
-
- /// @brief Remove the parameter from the store.
- ///
- /// Deletes the entry for the given parameter from the store if it
- /// exists.
- ///
- /// @param name is the name of the paramater to delete.
- void delParam(const std::string& name) {
- values_.erase(name);
- }
-
- /// @brief Deletes all of the entries from the store.
- ///
- void clear() {
- values_.clear();
- }
-
-
- private:
- /// @brief An std::map of the data values, keyed by parameter names.
- std::map<std::string, ValueType> values_;
-};
-
-
-/// @brief a collection of elements that store uint32 values (e.g. renew-timer = 900)
-typedef ValueStorage<uint32_t> Uint32Storage;
-
-/// @brief a collection of elements that store string values
-typedef ValueStorage<std::string> StringStorage;
-
-/// @brief Storage for parsed boolean values.
-typedef ValueStorage<bool> BooleanStorage;
-
}; // end of isc::dhcp namespace
}; // end of isc namespace
diff --git a/src/lib/dhcpsrv/dhcp_parsers.cc b/src/lib/dhcpsrv/dhcp_parsers.cc
new file mode 100644
index 0000000..d21538b
--- /dev/null
+++ b/src/lib/dhcpsrv/dhcp_parsers.cc
@@ -0,0 +1,957 @@
+// Copyright (C) 2013 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 <dhcp/iface_mgr.h>
+#include <dhcp/libdhcp++.h>
+#include <dhcpsrv/cfgmgr.h>
+#include <dhcpsrv/dhcp_parsers.h>
+#include <util/encode/hex.h>
+#include <util/strutil.h>
+
+#include <boost/foreach.hpp>
+#include <boost/lexical_cast.hpp>
+#include <boost/algorithm/string.hpp>
+#include <boost/lexical_cast.hpp>
+
+#include <string>
+#include <map>
+
+using namespace std;
+using namespace isc::data;
+
+namespace isc {
+namespace dhcp {
+
+// *********************** ParserContext *************************
+
+ParserContext::ParserContext(Option::Universe universe):
+ boolean_values_(new BooleanStorage()),
+ uint32_values_(new Uint32Storage()),
+ string_values_(new StringStorage()),
+ options_(new OptionStorage()),
+ option_defs_(new OptionDefStorage()),
+ universe_(universe) {
+ }
+
+ParserContext::ParserContext(const ParserContext& rhs):
+ boolean_values_(new BooleanStorage(*(rhs.boolean_values_))),
+ uint32_values_(new Uint32Storage(*(rhs.uint32_values_))),
+ string_values_(new StringStorage(*(rhs.string_values_))),
+ options_(new OptionStorage(*(rhs.options_))),
+ option_defs_(new OptionDefStorage(*(rhs.option_defs_))),
+ universe_(rhs.universe_) {
+ }
+
+ParserContext&
+ParserContext::operator=(const ParserContext& rhs) {
+ if (this != &rhs) {
+ boolean_values_ =
+ BooleanStoragePtr(new BooleanStorage(*(rhs.boolean_values_)));
+ uint32_values_ =
+ Uint32StoragePtr(new Uint32Storage(*(rhs.uint32_values_)));
+ string_values_ =
+ StringStoragePtr(new StringStorage(*(rhs.string_values_)));
+ options_ = OptionStoragePtr(new OptionStorage(*(rhs.options_)));
+ option_defs_ =
+ OptionDefStoragePtr(new OptionDefStorage(*(rhs.option_defs_)));
+ universe_ = rhs.universe_;
+ }
+ return (*this);
+ }
+
+
+// **************************** DebugParser *************************
+
+DebugParser::DebugParser(const std::string& param_name)
+ :param_name_(param_name) {
+}
+
+void
+DebugParser::build(ConstElementPtr new_config) {
+ std::cout << "Build for token: [" << param_name_ << "] = ["
+ << value_->str() << "]" << std::endl;
+ value_ = new_config;
+}
+
+void
+DebugParser::commit() {
+ // Debug message. The whole DebugParser class is used only for parser
+ // debugging, and is not used in production code. It is very convenient
+ // to keep it around. Please do not turn this cout into logger calls.
+ std::cout << "Commit for token: [" << param_name_ << "] = ["
+ << value_->str() << "]" << std::endl;
+}
+
+// **************************** BooleanParser *************************
+
+template<> void ValueParser<bool>::build(isc::data::ConstElementPtr value) {
+ // The Config Manager checks if user specified a
+ // valid value for a boolean parameter: True or False.
+ // We should have a boolean Element, use value directly
+ try {
+ value_ = value->boolValue();
+ } catch (const isc::data::TypeError &) {
+ isc_throw(BadValue, " Wrong value type for " << param_name_
+ << " : build called with a non-boolean element.");
+ }
+}
+
+// **************************** Uin32Parser *************************
+
+template<> void ValueParser<uint32_t>::build(ConstElementPtr value) {
+ int64_t check;
+ string x = value->str();
+ try {
+ check = boost::lexical_cast<int64_t>(x);
+ } catch (const boost::bad_lexical_cast &) {
+ isc_throw(BadValue, "Failed to parse value " << value->str()
+ << " as unsigned 32-bit integer.");
+ }
+ if (check > std::numeric_limits<uint32_t>::max()) {
+ isc_throw(BadValue, "Value " << value->str() << "is too large"
+ << " for unsigned 32-bit integer.");
+ }
+ if (check < 0) {
+ isc_throw(BadValue, "Value " << value->str() << "is negative."
+ << " Only 0 or larger are allowed for unsigned 32-bit integer.");
+ }
+
+ // value is small enough to fit
+ value_ = static_cast<uint32_t>(check);
+}
+
+// **************************** StringParser *************************
+
+template <> void ValueParser<std::string>::build(ConstElementPtr value) {
+ value_ = value->str();
+ boost::erase_all(value_, "\"");
+}
+
+// ******************** InterfaceListConfigParser *************************
+
+InterfaceListConfigParser::InterfaceListConfigParser(const std::string&
+ param_name) {
+ if (param_name != "interface") {
+ isc_throw(BadValue, "Internal error. Interface configuration "
+ "parser called for the wrong parameter: " << param_name);
+ }
+}
+
+void
+InterfaceListConfigParser::build(ConstElementPtr value) {
+ BOOST_FOREACH(ConstElementPtr iface, value->listValue()) {
+ interfaces_.push_back(iface->str());
+ }
+}
+
+void
+InterfaceListConfigParser::commit() {
+ /// @todo: Implement per interface listening. Currently always listening
+ /// on all interfaces.
+}
+
+// **************************** OptionDataParser *************************
+OptionDataParser::OptionDataParser(const std::string&, OptionStoragePtr options,
+ ParserContextPtr global_context)
+ : boolean_values_(new BooleanStorage()),
+ string_values_(new StringStorage()), uint32_values_(new Uint32Storage()),
+ options_(options), option_descriptor_(false),
+ global_context_(global_context) {
+ if (!options_) {
+ isc_throw(isc::dhcp::DhcpConfigError, "parser logic error:"
+ << "options storage may not be NULL");
+ }
+
+ if (!global_context_) {
+ isc_throw(isc::dhcp::DhcpConfigError, "parser logic error:"
+ << "context may may not be NULL");
+ }
+}
+
+void
+OptionDataParser::build(ConstElementPtr option_data_entries) {
+ BOOST_FOREACH(ConfigPair param, option_data_entries->mapValue()) {
+ ParserPtr parser;
+ if (param.first == "name" || param.first == "data" ||
+ param.first == "space") {
+ StringParserPtr name_parser(new StringParser(param.first,
+ string_values_));
+ parser = name_parser;
+ } else if (param.first == "code") {
+ Uint32ParserPtr code_parser(new Uint32Parser(param.first,
+ uint32_values_));
+ parser = code_parser;
+ } else if (param.first == "csv-format") {
+ BooleanParserPtr value_parser(new BooleanParser(param.first,
+ boolean_values_));
+ parser = value_parser;
+ } else {
+ isc_throw(DhcpConfigError,
+ "Parser error: option-data parameter not supported: "
+ << param.first);
+ }
+
+ parser->build(param.second);
+ // Before we can create an option we need to get the data from
+ // the child parsers. The only way to do it is to invoke commit
+ // on them so as they store the values in appropriate storages
+ // that this class provided to them. Note that this will not
+ // modify values stored in the global storages so the configuration
+ // will remain consistent even parsing fails somewhere further on.
+ parser->commit();
+ }
+
+ // Try to create the option instance.
+ createOption();
+}
+
+void
+OptionDataParser::commit() {
+ if (!option_descriptor_.option) {
+ // Before we can commit the new option should be configured. If it is
+ // not than somebody must have called commit() before build().
+ isc_throw(isc::InvalidOperation,
+ "parser logic error: no option has been configured and"
+ " thus there is nothing to commit. Has build() been called?");
+ }
+
+ uint16_t opt_type = option_descriptor_.option->getType();
+ Subnet::OptionContainerPtr options = options_->getItems(option_space_);
+ // The getItems() should never return NULL pointer. If there are no
+ // options configured for the particular option space a pointer
+ // to an empty container should be returned.
+ assert(options);
+ Subnet::OptionContainerTypeIndex& idx = options->get<1>();
+ // Try to find options with the particular option code in the main
+ // storage. If found, remove these options because they will be
+ // replaced with new one.
+ Subnet::OptionContainerTypeRange range = idx.equal_range(opt_type);
+ if (std::distance(range.first, range.second) > 0) {
+ idx.erase(range.first, range.second);
+ }
+
+ // Append new option to the main storage.
+ options_->addItem(option_descriptor_, option_space_);
+}
+
+void
+OptionDataParser::createOption() {
+ // Option code is held in the uint32_t storage but is supposed to
+ // be uint16_t value. We need to check that value in the configuration
+ // does not exceed range of uint8_t and is not zero.
+ uint32_t option_code = uint32_values_->getParam("code");
+ if (option_code == 0) {
+ isc_throw(DhcpConfigError, "option code must not be zero."
+ << " Option code '0' is reserved in DHCPv4.");
+ } else if (option_code > std::numeric_limits<uint8_t>::max()) {
+ isc_throw(DhcpConfigError, "invalid option code '" << option_code
+ << "', it must not exceed '"
+ << std::numeric_limits<uint8_t>::max() << "'");
+ }
+
+ // Check that the option name has been specified, is non-empty and does not
+ // contain spaces
+ std::string option_name = string_values_->getParam("name");
+ if (option_name.empty()) {
+ isc_throw(DhcpConfigError, "name of the option with code '"
+ << option_code << "' is empty");
+ } else if (option_name.find(" ") != std::string::npos) {
+ isc_throw(DhcpConfigError, "invalid option name '" << option_name
+ << "', space character is not allowed");
+ }
+
+ std::string option_space = string_values_->getParam("space");
+ if (!OptionSpace::validateName(option_space)) {
+ isc_throw(DhcpConfigError, "invalid option space name '"
+ << option_space << "' specified for option '"
+ << option_name << "' (code '" << option_code
+ << "')");
+ }
+
+ // Find the Option Definition for the option by its option code.
+ // findOptionDefinition will throw if not found, no need to test.
+ OptionDefinitionPtr def;
+ if (!(def = findServerSpaceOptionDefinition(option_space, option_code))) {
+ // If we are not dealing with a standard option then we
+ // need to search for its definition among user-configured
+ // options. They are expected to be in the global storage
+ // already.
+ OptionDefContainerPtr defs =
+ global_context_->option_defs_->getItems(option_space);
+
+ // The getItems() should never return the NULL pointer. If there are
+ // no option definitions for the particular option space a pointer
+ // to an empty container should be returned.
+ assert(defs);
+ const OptionDefContainerTypeIndex& idx = defs->get<1>();
+ OptionDefContainerTypeRange range = idx.equal_range(option_code);
+ if (std::distance(range.first, range.second) > 0) {
+ def = *range.first;
+ }
+ if (!def) {
+ isc_throw(DhcpConfigError, "definition for the option '"
+ << option_space << "." << option_name
+ << "' having code '" << option_code
+ << "' does not exist");
+ }
+ }
+
+ // Get option data from the configuration database ('data' field).
+ const std::string option_data = string_values_->getParam("data");
+ const bool csv_format = boolean_values_->getParam("csv-format");
+
+ // Transform string of hexadecimal digits into binary format.
+ std::vector<uint8_t> binary;
+ std::vector<std::string> data_tokens;
+
+ if (csv_format) {
+ // If the option data is specified as a string of comma
+ // separated values then we need to split this string into
+ // individual values - each value will be used to initialize
+ // one data field of an option.
+ data_tokens = isc::util::str::tokens(option_data, ",");
+ } else {
+ // Otherwise, the option data is specified as a string of
+ // hexadecimal digits that we have to turn into binary format.
+ try {
+ util::encode::decodeHex(option_data, binary);
+ } catch (...) {
+ isc_throw(DhcpConfigError, "option data is not a valid"
+ << " string of hexadecimal digits: " << option_data);
+ }
+ }
+
+ OptionPtr option;
+ if (!def) {
+ if (csv_format) {
+ isc_throw(DhcpConfigError, "the CSV option data format can be"
+ " used to specify values for an option that has a"
+ " definition. The option with code " << option_code
+ << " does not have a definition.");
+ }
+
+ // @todo We have a limited set of option definitions intiialized at
+ // the moment. In the future we want to initialize option definitions
+ // for all options. Consequently an error will be issued if an option
+ // definition does not exist for a particular option code. For now it is
+ // ok to create generic option if definition does not exist.
+ OptionPtr option(new Option(global_context_->universe_,
+ static_cast<uint16_t>(option_code), binary));
+ // The created option is stored in option_descriptor_ class member
+ // until the commit stage when it is inserted into the main storage.
+ // If an option with the same code exists in main storage already the
+ // old option is replaced.
+ option_descriptor_.option = option;
+ option_descriptor_.persistent = false;
+ } else {
+
+ // Option name should match the definition. The option name
+ // may seem to be redundant but in the future we may want
+ // to reference options and definitions using their names
+ // and/or option codes so keeping the option name in the
+ // definition of option value makes sense.
+ if (def->getName() != option_name) {
+ isc_throw(DhcpConfigError, "specified option name '"
+ << option_name << "' does not match the "
+ << "option definition: '" << option_space
+ << "." << def->getName() << "'");
+ }
+
+ // Option definition has been found so let's use it to create
+ // an instance of our option.
+ try {
+ OptionPtr option = csv_format ?
+ def->optionFactory(global_context_->universe_,
+ option_code, data_tokens) :
+ def->optionFactory(global_context_->universe_,
+ option_code, binary);
+ Subnet::OptionDescriptor desc(option, false);
+ option_descriptor_.option = option;
+ option_descriptor_.persistent = false;
+ } catch (const isc::Exception& ex) {
+ isc_throw(DhcpConfigError, "option data does not match"
+ << " option definition (space: " << option_space
+ << ", code: " << option_code << "): "
+ << ex.what());
+ }
+ }
+
+ // All went good, so we can set the option space name.
+ option_space_ = option_space;
+}
+
+// **************************** OptionDataListParser *************************
+OptionDataListParser::OptionDataListParser(const std::string&,
+ OptionStoragePtr options, ParserContextPtr global_context,
+ OptionDataParserFactory* optionDataParserFactory)
+ : options_(options), local_options_(new OptionStorage()),
+ global_context_(global_context),
+ optionDataParserFactory_(optionDataParserFactory) {
+ if (!options_) {
+ isc_throw(isc::dhcp::DhcpConfigError, "parser logic error:"
+ << "options storage may not be NULL");
+ }
+
+ if (!options_) {
+ isc_throw(isc::dhcp::DhcpConfigError, "parser logic error:"
+ << "context may not be NULL");
+ }
+
+ if (!optionDataParserFactory_) {
+ isc_throw(isc::dhcp::DhcpConfigError, "parser logic error:"
+ << "option data parser factory may not be NULL");
+ }
+}
+
+void
+OptionDataListParser::build(ConstElementPtr option_data_list) {
+ BOOST_FOREACH(ConstElementPtr option_value, option_data_list->listValue()) {
+ boost::shared_ptr<OptionDataParser>
+ parser((*optionDataParserFactory_)("option-data",
+ local_options_, global_context_));
+
+ // options_ member will hold instances of all options thus
+ // each OptionDataParser takes it as a storage.
+ // Build the instance of a single option.
+ parser->build(option_value);
+ // Store a parser as it will be used to commit.
+ parsers_.push_back(parser);
+ }
+}
+
+void
+OptionDataListParser::commit() {
+ BOOST_FOREACH(ParserPtr parser, parsers_) {
+ parser->commit();
+ }
+
+ // Parsing was successful and we have all configured
+ // options in local storage. We can now replace old values
+ // with new values.
+ std::swap(*local_options_, *options_);
+}
+
+// ******************************** OptionDefParser ****************************
+OptionDefParser::OptionDefParser(const std::string&,
+ OptionDefStoragePtr storage)
+ : storage_(storage), boolean_values_(new BooleanStorage()),
+ string_values_(new StringStorage()), uint32_values_(new Uint32Storage()) {
+ if (!storage_) {
+ isc_throw(isc::dhcp::DhcpConfigError, "parser logic error:"
+ << "options storage may not be NULL");
+ }
+}
+
+void
+OptionDefParser::build(ConstElementPtr option_def) {
+ // Parse the elements that make up the option definition.
+ BOOST_FOREACH(ConfigPair param, option_def->mapValue()) {
+ std::string entry(param.first);
+ ParserPtr parser;
+ if (entry == "name" || entry == "type" || entry == "record-types"
+ || entry == "space" || entry == "encapsulate") {
+ StringParserPtr str_parser(new StringParser(entry,
+ string_values_));
+ parser = str_parser;
+ } else if (entry == "code") {
+ Uint32ParserPtr code_parser(new Uint32Parser(entry,
+ uint32_values_));
+ parser = code_parser;
+ } else if (entry == "array") {
+ BooleanParserPtr array_parser(new BooleanParser(entry,
+ boolean_values_));
+ parser = array_parser;
+ } else {
+ isc_throw(DhcpConfigError, "invalid parameter: " << entry);
+ }
+
+ parser->build(param.second);
+ parser->commit();
+ }
+
+ // Create an instance of option definition.
+ createOptionDef();
+
+ // Get all items we collected so far for the particular option space.
+ OptionDefContainerPtr defs = storage_->getItems(option_space_name_);
+
+ // Check if there are any items with option code the same as the
+ // one specified for the definition we are now creating.
+ const OptionDefContainerTypeIndex& idx = defs->get<1>();
+ const OptionDefContainerTypeRange& range =
+ idx.equal_range(option_definition_->getCode());
+
+ // If there are any items with this option code already we need
+ // to issue an error because we don't allow duplicates for
+ // option definitions within an option space.
+ if (std::distance(range.first, range.second) > 0) {
+ isc_throw(DhcpConfigError, "duplicated option definition for"
+ << " code '" << option_definition_->getCode() << "'");
+ }
+}
+
+void
+OptionDefParser::commit() {
+ if (storage_ && option_definition_ &&
+ OptionSpace::validateName(option_space_name_)) {
+ storage_->addItem(option_definition_, option_space_name_);
+ }
+}
+
+void
+OptionDefParser::createOptionDef() {
+ // Get the option space name and validate it.
+ std::string space = string_values_->getParam("space");
+ if (!OptionSpace::validateName(space)) {
+ isc_throw(DhcpConfigError, "invalid option space name '"
+ << space << "'");
+ }
+
+ // Get other parameters that are needed to create the
+ // option definition.
+ std::string name = string_values_->getParam("name");
+ uint32_t code = uint32_values_->getParam("code");
+ std::string type = string_values_->getParam("type");
+ bool array_type = boolean_values_->getParam("array");
+ std::string encapsulates = string_values_->getParam("encapsulate");
+
+ // Create option definition.
+ OptionDefinitionPtr def;
+ // We need to check if user has set encapsulated option space
+ // name. If so, different constructor will be used.
+ if (!encapsulates.empty()) {
+ // Arrays can't be used together with sub-options.
+ if (array_type) {
+ isc_throw(DhcpConfigError, "option '" << space << "."
+ << "name" << "', comprising an array of data"
+ << " fields may not encapsulate any option space");
+
+ } else if (encapsulates == space) {
+ isc_throw(DhcpConfigError, "option must not encapsulate"
+ << " an option space it belongs to: '"
+ << space << "." << name << "' is set to"
+ << " encapsulate '" << space << "'");
+
+ } else {
+ def.reset(new OptionDefinition(name, code, type,
+ encapsulates.c_str()));
+ }
+
+ } else {
+ def.reset(new OptionDefinition(name, code, type, array_type));
+
+ }
+
+ // The record-types field may carry a list of comma separated names
+ // of data types that form a record.
+ std::string record_types = string_values_->getParam("record-types");
+
+ // Split the list of record types into tokens.
+ std::vector<std::string> record_tokens =
+ isc::util::str::tokens(record_types, ",");
+ // Iterate over each token and add a record type into
+ // option definition.
+ BOOST_FOREACH(std::string record_type, record_tokens) {
+ try {
+ boost::trim(record_type);
+ if (!record_type.empty()) {
+ def->addRecordField(record_type);
+ }
+ } catch (const Exception& ex) {
+ isc_throw(DhcpConfigError, "invalid record type values"
+ << " specified for the option definition: "
+ << ex.what());
+ }
+ }
+
+ // Check the option definition parameters are valid.
+ try {
+ def->validate();
+ } catch (const isc::Exception& ex) {
+ isc_throw(DhcpConfigError, "invalid option definition"
+ << " parameters: " << ex.what());
+ }
+
+ // Option definition has been created successfully.
+ option_space_name_ = space;
+ option_definition_ = def;
+}
+
+// ******************************** OptionDefListParser ************************
+OptionDefListParser::OptionDefListParser(const std::string&,
+ OptionDefStoragePtr storage) :storage_(storage) {
+ if (!storage_) {
+ isc_throw(isc::dhcp::DhcpConfigError, "parser logic error:"
+ << "storage may not be NULL");
+ }
+}
+
+void
+OptionDefListParser::build(ConstElementPtr option_def_list) {
+ // Clear existing items in the storage.
+ // We are going to replace all of them.
+ storage_->clearItems();
+
+ if (!option_def_list) {
+ isc_throw(DhcpConfigError, "parser error: a pointer to a list of"
+ << " option definitions is NULL");
+ }
+
+ BOOST_FOREACH(ConstElementPtr option_def, option_def_list->listValue()) {
+ boost::shared_ptr<OptionDefParser>
+ parser(new OptionDefParser("single-option-def", storage_));
+ parser->build(option_def);
+ parser->commit();
+ }
+}
+
+void
+OptionDefListParser::commit() {
+ CfgMgr& cfg_mgr = CfgMgr::instance();
+ cfg_mgr.deleteOptionDefs();
+
+ // We need to move option definitions from the temporary
+ // storage to the storage.
+ std::list<std::string> space_names =
+ storage_->getOptionSpaceNames();
+
+ BOOST_FOREACH(std::string space_name, space_names) {
+ BOOST_FOREACH(OptionDefinitionPtr def,
+ *(storage_->getItems(space_name))) {
+ // All option definitions should be initialized to non-NULL
+ // values. The validation is expected to be made by the
+ // OptionDefParser when creating an option definition.
+ assert(def);
+ cfg_mgr.addOptionDef(def, space_name);
+ }
+ }
+}
+
+//****************************** PoolParser ********************************
+PoolParser::PoolParser(const std::string&, PoolStoragePtr pools)
+ :pools_(pools) {
+
+ if (!pools_) {
+ isc_throw(isc::dhcp::DhcpConfigError, "parser logic error:"
+ << "storage may not be NULL");
+ }
+}
+
+void
+PoolParser::build(ConstElementPtr pools_list) {
+ BOOST_FOREACH(ConstElementPtr text_pool, pools_list->listValue()) {
+ // That should be a single pool representation. It should contain
+ // text is form prefix/len or first - last. Note that spaces
+ // are allowed
+ string txt = text_pool->stringValue();
+
+ // first let's remove any whitespaces
+ boost::erase_all(txt, " "); // space
+ boost::erase_all(txt, "\t"); // tabulation
+
+ // Is this prefix/len notation?
+ size_t pos = txt.find("/");
+ if (pos != string::npos) {
+ isc::asiolink::IOAddress addr("::");
+ uint8_t len = 0;
+ try {
+ addr = isc::asiolink::IOAddress(txt.substr(0, pos));
+
+ // start with the first character after /
+ string prefix_len = txt.substr(pos + 1);
+
+ // It is lexical cast to int and then downcast to uint8_t.
+ // Direct cast to uint8_t (which is really an unsigned char)
+ // will result in interpreting the first digit as output
+ // value and throwing exception if length is written on two
+ // digits (because there are extra characters left over).
+
+ // No checks for values over 128. Range correctness will
+ // be checked in Pool4 constructor.
+ len = boost::lexical_cast<int>(prefix_len);
+ } catch (...) {
+ isc_throw(DhcpConfigError, "Failed to parse pool "
+ "definition: " << text_pool->stringValue());
+ }
+
+ PoolPtr pool(poolMaker(addr, len));
+ local_pools_.push_back(pool);
+ continue;
+ }
+
+ // Is this min-max notation?
+ pos = txt.find("-");
+ if (pos != string::npos) {
+ // using min-max notation
+ isc::asiolink::IOAddress min(txt.substr(0,pos));
+ isc::asiolink::IOAddress max(txt.substr(pos + 1));
+
+ PoolPtr pool(poolMaker(min, max));
+ local_pools_.push_back(pool);
+ continue;
+ }
+
+ isc_throw(DhcpConfigError, "Failed to parse pool definition:"
+ << text_pool->stringValue() <<
+ ". Does not contain - (for min-max) nor / (prefix/len)");
+ }
+}
+
+void
+PoolParser::commit() {
+ if (pools_) {
+ // local_pools_ holds the values produced by the build function.
+ // At this point parsing should have completed successfuly so
+ // we can append new data to the supplied storage.
+ pools_->insert(pools_->end(), local_pools_.begin(), local_pools_.end());
+ }
+}
+
+//****************************** SubnetConfigParser *************************
+
+SubnetConfigParser::SubnetConfigParser(const std::string&,
+ ParserContextPtr global_context)
+ : uint32_values_(new Uint32Storage()), string_values_(new StringStorage()),
+ pools_(new PoolStorage()), options_(new OptionStorage()),
+ global_context_(global_context) {
+ // The first parameter should always be "subnet", but we don't check
+ // against that here in case some wants to reuse this parser somewhere.
+ if (!global_context_) {
+ isc_throw(isc::dhcp::DhcpConfigError, "parser logic error:"
+ << "context storage may not be NULL");
+ }
+}
+
+void
+SubnetConfigParser::build(ConstElementPtr subnet) {
+ BOOST_FOREACH(ConfigPair param, subnet->mapValue()) {
+ ParserPtr parser(createSubnetConfigParser(param.first));
+ parser->build(param.second);
+ parsers_.push_back(parser);
+ }
+
+ // In order to create new subnet we need to get the data out
+ // of the child parsers first. The only way to do it is to
+ // invoke commit on them because it will make them write
+ // parsed data into storages we have supplied.
+ // Note that triggering commits on child parsers does not
+ // affect global data because we supplied pointers to storages
+ // local to this object. Thus, even if this method fails
+ // later on, the configuration remains consistent.
+ BOOST_FOREACH(ParserPtr parser, parsers_) {
+ parser->commit();
+ }
+
+ // Create a subnet.
+ createSubnet();
+}
+
+void
+SubnetConfigParser::appendSubOptions(const std::string& option_space,
+ OptionPtr& option) {
+ // Only non-NULL options are stored in option container.
+ // If this option pointer is NULL this is a serious error.
+ assert(option);
+
+ OptionDefinitionPtr def;
+ if (isServerStdOption(option_space, option->getType())) {
+ def = getServerStdOptionDefinition(option->getType());
+ // Definitions for some of the standard options hasn't been
+ // implemented so it is ok to leave here.
+ if (!def) {
+ return;
+ }
+ } else {
+ const OptionDefContainerPtr defs =
+ global_context_->option_defs_->getItems(option_space);
+
+ const OptionDefContainerTypeIndex& idx = defs->get<1>();
+ const OptionDefContainerTypeRange& range =
+ idx.equal_range(option->getType());
+ // There is no definition so we have to leave.
+ if (std::distance(range.first, range.second) == 0) {
+ return;
+ }
+
+ def = *range.first;
+
+ // If the definition exists, it must be non-NULL.
+ // Otherwise it is a programming error.
+ assert(def);
+ }
+
+ // We need to get option definition for the particular option space
+ // and code. This definition holds the information whether our
+ // option encapsulates any option space.
+ // Get the encapsulated option space name.
+ std::string encapsulated_space = def->getEncapsulatedSpace();
+ // If option space name is empty it means that our option does not
+ // encapsulate any option space (does not include sub-options).
+ if (!encapsulated_space.empty()) {
+ // Get the sub-options that belong to the encapsulated
+ // option space.
+ const Subnet::OptionContainerPtr sub_opts =
+ global_context_->options_->getItems(encapsulated_space);
+ // Append sub-options to the option.
+ BOOST_FOREACH(Subnet::OptionDescriptor desc, *sub_opts) {
+ if (desc.option) {
+ option->addOption(desc.option);
+ }
+ }
+ }
+}
+
+void
+SubnetConfigParser::createSubnet() {
+ std::string subnet_txt;
+ try {
+ subnet_txt = string_values_->getParam("subnet");
+ } catch (const DhcpConfigError &) {
+ // rethrow with precise error
+ isc_throw(DhcpConfigError,
+ "Mandatory subnet definition in subnet missing");
+ }
+
+ // Remove any spaces or tabs.
+ boost::erase_all(subnet_txt, " ");
+ boost::erase_all(subnet_txt, "\t");
+
+ // The subnet format is prefix/len. We are going to extract
+ // the prefix portion of a subnet string to create IOAddress
+ // object from it. IOAddress will be passed to the Subnet's
+ // constructor later on. In order to extract the prefix we
+ // need to get all characters preceding "/".
+ size_t pos = subnet_txt.find("/");
+ if (pos == string::npos) {
+ isc_throw(DhcpConfigError,
+ "Invalid subnet syntax (prefix/len expected):" << subnet_txt);
+ }
+
+ // Try to create the address object. It also validates that
+ // the address syntax is ok.
+ isc::asiolink::IOAddress addr(subnet_txt.substr(0, pos));
+ uint8_t len = boost::lexical_cast<unsigned int>(subnet_txt.substr(pos + 1));
+
+ // Call the subclass's method to instantiate the subnet
+ initSubnet(addr, len);
+
+ // Add pools to it.
+ for (PoolStorage::iterator it = pools_->begin(); it != pools_->end();
+ ++it) {
+ subnet_->addPool(*it);
+ }
+
+ // Configure interface, if defined
+
+ // Get interface name. If it is defined, then the subnet is available
+ // directly over specified network interface.
+ std::string iface;
+ try {
+ iface = string_values_->getParam("interface");
+ } catch (const DhcpConfigError &) {
+ // iface not mandatory so swallow the exception
+ }
+
+ if (!iface.empty()) {
+ if (!IfaceMgr::instance().getIface(iface)) {
+ isc_throw(DhcpConfigError, "Specified interface name " << iface
+ << " for subnet " << subnet_->toText()
+ << " is not present" << " in the system.");
+ }
+
+ subnet_->setIface(iface);
+ }
+
+ // We are going to move configured options to the Subnet object.
+ // Configured options reside in the container where options
+ // are grouped by space names. Thus we need to get all space names
+ // and iterate over all options that belong to them.
+ std::list<std::string> space_names = options_->getOptionSpaceNames();
+ BOOST_FOREACH(std::string option_space, space_names) {
+ // Get all options within a particular option space.
+ BOOST_FOREACH(Subnet::OptionDescriptor desc,
+ *options_->getItems(option_space)) {
+ // The pointer should be non-NULL. The validation is expected
+ // to be performed by the OptionDataParser before adding an
+ // option descriptor to the container.
+ assert(desc.option);
+ // We want to check whether an option with the particular
+ // option code has been already added. If so, we want
+ // to issue a warning.
+ Subnet::OptionDescriptor existing_desc =
+ subnet_->getOptionDescriptor("option_space",
+ desc.option->getType());
+ if (existing_desc.option) {
+ duplicate_option_warning(desc.option->getType(), addr);
+ }
+ // Add sub-options (if any).
+ appendSubOptions(option_space, desc.option);
+ // In any case, we add the option to the subnet.
+ subnet_->addOption(desc.option, false, option_space);
+ }
+ }
+
+ // Check all global options and add them to the subnet object if
+ // they have been configured in the global scope. If they have been
+ // configured in the subnet scope we don't add global option because
+ // the one configured in the subnet scope always takes precedence.
+ space_names = global_context_->options_->getOptionSpaceNames();
+ BOOST_FOREACH(std::string option_space, space_names) {
+ // Get all global options for the particular option space.
+ BOOST_FOREACH(Subnet::OptionDescriptor desc,
+ *(global_context_->options_->getItems(option_space))) {
+ // The pointer should be non-NULL. The validation is expected
+ // to be performed by the OptionDataParser before adding an
+ // option descriptor to the container.
+ assert(desc.option);
+ // Check if the particular option has been already added.
+ // This would mean that it has been configured in the
+ // subnet scope. Since option values configured in the
+ // subnet scope take precedence over globally configured
+ // values we don't add option from the global storage
+ // if there is one already.
+ Subnet::OptionDescriptor existing_desc =
+ subnet_->getOptionDescriptor(option_space,
+ desc.option->getType());
+ if (!existing_desc.option) {
+ // Add sub-options (if any).
+ appendSubOptions(option_space, desc.option);
+ subnet_->addOption(desc.option, false, option_space);
+ }
+ }
+ }
+}
+
+isc::dhcp::Triplet<uint32_t>
+SubnetConfigParser::getParam(const std::string& name) {
+ uint32_t value = 0;
+ try {
+ // look for local value
+ value = uint32_values_->getParam(name);
+ } catch (const DhcpConfigError &) {
+ try {
+ // no local, use global value
+ value = global_context_->uint32_values_->getParam(name);
+ } catch (const DhcpConfigError &) {
+ isc_throw(DhcpConfigError, "Mandatory parameter " << name
+ << " missing (no global default and no subnet-"
+ << "specific value)");
+ }
+ }
+
+ return (Triplet<uint32_t>(value));
+}
+
+}; // namespace dhcp
+}; // namespace isc
diff --git a/src/lib/dhcpsrv/dhcp_parsers.h b/src/lib/dhcpsrv/dhcp_parsers.h
new file mode 100644
index 0000000..e453204
--- /dev/null
+++ b/src/lib/dhcpsrv/dhcp_parsers.h
@@ -0,0 +1,765 @@
+// Copyright (C) 2013 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 DHCP_PARSERS_H
+#define DHCP_PARSERS_H
+
+#include <asiolink/io_address.h>
+#include <cc/data.h>
+#include <dhcp/option_definition.h>
+#include <dhcpsrv/dhcp_config_parser.h>
+#include <dhcpsrv/option_space_container.h>
+#include <dhcpsrv/subnet.h>
+#include <exceptions/exceptions.h>
+
+#include <stdint.h>
+#include <string>
+#include <vector>
+
+namespace isc {
+namespace dhcp {
+
+/// @brief Storage for option definitions.
+typedef OptionSpaceContainer<OptionDefContainer,
+ OptionDefinitionPtr> OptionDefStorage;
+
+/// @brief Shared pointer to option definitions storage.
+typedef boost::shared_ptr<OptionDefStorage> OptionDefStoragePtr;
+
+/// Collection of containers holding option spaces. Each container within
+/// a particular option space holds so-called option descriptors.
+typedef OptionSpaceContainer<Subnet::OptionContainer,
+ Subnet::OptionDescriptor> OptionStorage;
+/// @brief Shared pointer to option storage.
+typedef boost::shared_ptr<OptionStorage> OptionStoragePtr;
+
+/// @brief A template class that stores named elements of a given data type.
+///
+/// This template class is provides data value storage for configuration parameters
+/// of a given data type. The values are stored by parameter name and as instances
+/// of type "ValueType".
+///
+/// @param ValueType is the data type of the elements to store.
+template<typename ValueType>
+class ValueStorage {
+ public:
+ /// @brief Stores the the parameter and its value in the store.
+ ///
+ /// If the parameter does not exist in the store, then it will be added,
+ /// otherwise its data value will be updated with the given value.
+ ///
+ /// @param name is the name of the paramater to store.
+ /// @param value is the data value to store.
+ void setParam(const std::string& name, const ValueType& value) {
+ values_[name] = value;
+ }
+
+ /// @brief Returns the data value for the given parameter.
+ ///
+ /// Finds and returns the data value for the given parameter.
+ /// @param name is the name of the parameter for which the data
+ /// value is desired.
+ ///
+ /// @return The paramater's data value of type <ValueType>.
+ /// @throw DhcpConfigError if the parameter is not found.
+ ValueType getParam(const std::string& name) const {
+ typename std::map<std::string, ValueType>::const_iterator param
+ = values_.find(name);
+
+ if (param == values_.end()) {
+ isc_throw(DhcpConfigError, "Missing parameter '"
+ << name << "'");
+ }
+
+ return (param->second);
+ }
+
+ /// @brief Remove the parameter from the store.
+ ///
+ /// Deletes the entry for the given parameter from the store if it
+ /// exists.
+ ///
+ /// @param name is the name of the paramater to delete.
+ void delParam(const std::string& name) {
+ values_.erase(name);
+ }
+
+ /// @brief Deletes all of the entries from the store.
+ ///
+ void clear() {
+ values_.clear();
+ }
+
+
+ private:
+ /// @brief An std::map of the data values, keyed by parameter names.
+ std::map<std::string, ValueType> values_;
+};
+
+
+/// @brief a collection of elements that store uint32 values
+typedef ValueStorage<uint32_t> Uint32Storage;
+typedef boost::shared_ptr<Uint32Storage> Uint32StoragePtr;
+
+/// @brief a collection of elements that store string values
+typedef ValueStorage<std::string> StringStorage;
+typedef boost::shared_ptr<StringStorage> StringStoragePtr;
+
+/// @brief Storage for parsed boolean values.
+typedef ValueStorage<bool> BooleanStorage;
+typedef boost::shared_ptr<BooleanStorage> BooleanStoragePtr;
+
+/// @brief Container for the current parsing context. It provides a
+/// single enclosure for the storage of configuration parameters,
+/// options, option definitions, and other context specific information
+/// that needs to be accessible throughout the parsing and parsing
+/// constructs.
+class ParserContext {
+public:
+ /// @brief Constructor
+ ///
+ /// @param universe is the Option::Universe value of this
+ /// context.
+ ParserContext(Option::Universe universe);
+
+ /// @brief Copy constructor
+ ParserContext(const ParserContext& rhs);
+
+ /// @brief Storage for boolean parameters.
+ BooleanStoragePtr boolean_values_;
+
+ /// @brief Storage for uint32 parameters.
+ Uint32StoragePtr uint32_values_;
+
+ /// @brief Storage for string parameters.
+ StringStoragePtr string_values_;
+
+ /// @brief Storage for options.
+ OptionStoragePtr options_;
+
+ /// @brief Storage for option definitions.
+ OptionDefStoragePtr option_defs_;
+
+ /// @brief The parsing universe of this context.
+ Option::Universe universe_;
+
+ /// @brief Assignment operator
+ ParserContext& operator=(const ParserContext& rhs);
+};
+
+/// @brief Pointer to various parser context.
+typedef boost::shared_ptr<ParserContext> ParserContextPtr;
+
+/// @brief Simple data-type parser template class
+///
+/// This is the template class for simple data-type parsers. It supports
+/// parsing a configuration parameter with specific data-type for its
+/// possible values. It provides a common constructor, commit, and templated
+/// data storage. The "build" method implementation must be provided by a
+/// declaring type.
+/// @param ValueType is the data type of the configuration paramater value
+/// the parser should handle.
+template<typename ValueType>
+class ValueParser : public DhcpConfigParser {
+public:
+
+ /// @brief Constructor.
+ ///
+ /// @param param_name name of the parameter.
+ /// @param storage is a pointer to the storage container where the parsed
+ /// value be stored upon commit.
+ /// @throw isc::dhcp::DhcpConfigError if a provided parameter's
+ /// name is empty.
+ /// @throw isc::dhcp::DhcpConfigError if storage is null.
+ ValueParser(const std::string& param_name,
+ boost::shared_ptr<ValueStorage<ValueType> > storage)
+ : storage_(storage), param_name_(param_name), value_() {
+ // Empty parameter name is invalid.
+ if (param_name_.empty()) {
+ isc_throw(isc::dhcp::DhcpConfigError, "parser logic error:"
+ << "empty parameter name provided");
+ }
+
+ // NUll storage is invalid.
+ if (!storage_) {
+ isc_throw(isc::dhcp::DhcpConfigError, "parser logic error:"
+ << "storage may not be NULL");
+ }
+ }
+
+
+ /// @brief Parse a given element into a value of type <ValueType>
+ ///
+ /// @param value a value to be parsed.
+ ///
+ /// @throw isc::BadValue Typically the implementing type will throw
+ /// a BadValue exception when given an invalid Element to parse.
+ void build(isc::data::ConstElementPtr value);
+
+ /// @brief Put a parsed value to the storage.
+ void commit() {
+ // If a given parameter already exists in the storage we override
+ // its value. If it doesn't we insert a new element.
+ storage_->setParam(param_name_, value_);
+ }
+
+private:
+ /// Pointer to the storage where committed value is stored.
+ boost::shared_ptr<ValueStorage<ValueType> > storage_;
+
+ /// Name of the parameter which value is parsed with this parser.
+ std::string param_name_;
+
+ /// Parsed value.
+ ValueType value_;
+};
+
+/// @brief typedefs for simple data type parsers
+typedef ValueParser<bool> BooleanParser;
+typedef ValueParser<uint32_t> Uint32Parser;
+typedef ValueParser<std::string> StringParser;
+
+/// @brief a dummy configuration parser
+///
+/// It is a debugging parser. It does not configure anything,
+/// will accept any configuration and will just print it out
+/// on commit. Useful for debugging existing configurations and
+/// adding new ones.
+class DebugParser : public DhcpConfigParser {
+public:
+
+ /// @brief Constructor
+ ///
+ /// See @ref DhcpConfigParser class for details.
+ ///
+ /// @param param_name name of the parsed parameter
+ DebugParser(const std::string& param_name);
+
+ /// @brief builds parameter value
+ ///
+ /// See @ref DhcpConfigParser class for details.
+ ///
+ /// @param new_config pointer to the new configuration
+ virtual void build(isc::data::ConstElementPtr new_config);
+
+ /// @brief pretends to apply the configuration
+ ///
+ /// This is a method required by base class. It pretends to apply the
+ /// configuration, but in fact it only prints the parameter out.
+ ///
+ /// See @ref DhcpConfigParser class for details.
+ virtual void commit();
+
+private:
+ /// name of the parsed parameter
+ std::string param_name_;
+
+ /// pointer to the actual value of the parameter
+ isc::data::ConstElementPtr value_;
+
+};
+
+/// @brief parser for interface list definition
+///
+/// This parser handles Dhcp4/interface entry.
+/// 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.
+///
+/// It is useful for parsing Dhcp4/interface parameter.
+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
+ /// @throw BadValue if supplied parameter name is not "interface"
+ InterfaceListConfigParser(const std::string& param_name);
+
+ /// @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 commits interfaces list configuration
+ virtual void commit();
+
+private:
+ /// contains list of network interfaces
+ std::vector<std::string> interfaces_;
+};
+
+
+/// @brief Parser for option data value.
+///
+/// This parser parses configuration entries that specify value of
+/// a single option. These entries include option name, option code
+/// and data carried by the option. The option data can be specified
+/// in one of the two available formats: binary value represented as
+/// a string of hexadecimal digits or a list of comma separated values.
+/// The format being used is controlled by csv-format configuration
+/// parameter. When setting this value to True, the latter format is
+/// used. The subsequent values in the CSV format apply to relevant
+/// option data fields in the configured option. For example the
+/// configuration: "data" : "192.168.2.0, 56, hello world" can be
+/// used to set values for the option comprising IPv4 address,
+/// integer and string data field. Note that order matters. If the
+/// order of values does not match the order of data fields within
+/// an option the configuration will not be accepted. If parsing
+/// is successful then an instance of an option is created and
+/// added to the storage provided by the calling class.
+class OptionDataParser : public DhcpConfigParser {
+public:
+ /// @brief Constructor.
+ ///
+ /// @param dummy first argument is ignored, all Parser constructors
+ /// accept string as first argument.
+ /// @param options is the option storage in which to store the parsed option
+ /// upon "commit".
+ /// @param global_context is a pointer to the global context which
+ /// stores global scope parameters, options, option defintions.
+ /// @throw isc::dhcp::DhcpConfigError if options or global_context are null.
+ OptionDataParser(const std::string&, OptionStoragePtr options,
+ ParserContextPtr global_context);
+
+ /// @brief Parses the single option data.
+ ///
+ /// This method parses the data of a single option from the configuration.
+ /// The option data includes option name, option code and data being
+ /// carried by this option. Eventually it creates the instance of the
+ /// option.
+ ///
+ /// @param option_data_entries collection of entries that define value
+ /// for a particular option.
+ /// @throw DhcpConfigError if invalid parameter specified in
+ /// the configuration.
+ /// @throw isc::InvalidOperation if failed to set storage prior to
+ /// calling build.
+ virtual void build(isc::data::ConstElementPtr option_data_entries);
+
+ /// @brief Commits option value.
+ ///
+ /// This function adds a new option to the storage or replaces an existing
+ /// option with the same code.
+ ///
+ /// @throw isc::InvalidOperation if failed to set pointer to storage or
+ /// failed
+ /// to call build() prior to commit. If that happens data in the storage
+ /// remain un-modified.
+ virtual void commit();
+
+ /// @brief virtual destructor to ensure orderly destruction of derivations.
+ virtual ~OptionDataParser(){};
+
+protected:
+ /// @brief Finds an option definition within the server's option space
+ ///
+ /// Given an option space and an option code, find the correpsonding
+ /// option defintion within the server's option defintion storage. This
+ /// method is pure virtual requiring derivations to manage which option
+ /// space(s) is valid for search.
+ ///
+ /// @param option_space name of the parameter option space
+ /// @param option_code numeric value of the parameter to find
+ /// @return OptionDefintionPtr of the option defintion or an
+ /// empty OptionDefinitionPtr if not found.
+ /// @throw DhcpConfigError if the option space requested is not valid
+ /// for this server.
+ virtual OptionDefinitionPtr findServerSpaceOptionDefinition (
+ std::string& option_space, uint32_t option_code) = 0;
+
+private:
+
+ /// @brief Create option instance.
+ ///
+ /// Creates an instance of an option and adds it to the provided
+ /// options storage. If the option data parsed by \ref build function
+ /// are invalid or insufficient this function emits an exception.
+ ///
+ /// @warning this function does not check if options_ storage pointer
+ /// is intitialized but this check is not needed here because it is done
+ /// in the \ref build function.
+ ///
+ /// @throw DhcpConfigError if parameters provided in the configuration
+ /// are invalid.
+ void createOption();
+
+ /// Storage for boolean values.
+ BooleanStoragePtr boolean_values_;
+
+ /// Storage for string values (e.g. option name or data).
+ StringStoragePtr string_values_;
+
+ /// Storage for uint32 values (e.g. option code).
+ Uint32StoragePtr uint32_values_;
+
+ /// Pointer to options storage. This storage is provided by
+ /// the calling class and is shared by all OptionDataParser objects.
+ OptionStoragePtr options_;
+
+ /// Option descriptor holds newly configured option.
+ Subnet::OptionDescriptor option_descriptor_;
+
+ /// Option space name where the option belongs to.
+ std::string option_space_;
+
+ /// Parsing context which contains global values, options and option
+ /// definitions.
+ ParserContextPtr global_context_;
+};
+
+///@brief Function pointer for OptionDataParser factory methods
+typedef OptionDataParser *OptionDataParserFactory(const std::string&,
+ OptionStoragePtr options, ParserContextPtr global_context);
+
+/// @brief Parser for option data values within a subnet.
+///
+/// This parser iterates over all entries that define options
+/// data for a particular subnet and creates a collection of options.
+/// If parsing is successful, all these options are added to the Subnet
+/// object.
+class OptionDataListParser : public DhcpConfigParser {
+public:
+ /// @brief Constructor.
+ ///
+ /// @param string& nominally would be param name, this is always ignored.
+ /// @param options parsed option storage for options in this list
+ /// @param global_context is a pointer to the global context which
+ /// stores global scope parameters, options, option defintions.
+ /// @param optionDataParserFactory factory method for creating individual
+ /// option parsers
+ /// @throw isc::dhcp::DhcpConfigError if options or global_context are null.
+ OptionDataListParser(const std::string&, OptionStoragePtr options,
+ ParserContextPtr global_context,
+ OptionDataParserFactory *optionDataParserFactory);
+
+ /// @brief Parses entries that define options' data for a subnet.
+ ///
+ /// This method iterates over all entries that define option data
+ /// for options within a single subnet and creates options' instances.
+ ///
+ /// @param option_data_list pointer to a list of options' data sets.
+ /// @throw DhcpConfigError if option parsing failed.
+ void build(isc::data::ConstElementPtr option_data_list);
+
+ /// @brief Commit all option values.
+ ///
+ /// This function invokes commit for all option values.
+ void commit();
+
+private:
+ /// Pointer to options instances storage.
+ OptionStoragePtr options_;
+
+ /// Intermediate option storage. This storage is used by
+ /// lower level parsers to add new options. Values held
+ /// in this storage are assigned to main storage (options_)
+ /// if overall parsing was successful.
+ OptionStoragePtr local_options_;
+
+ /// Collection of parsers;
+ ParserCollection parsers_;
+
+ /// Parsing context which contains global values, options and option
+ /// definitions.
+ ParserContextPtr global_context_;
+
+ /// Factory to create server-specific option data parsers
+ OptionDataParserFactory *optionDataParserFactory_;
+};
+
+
+/// @brief Parser for a single option definition.
+///
+/// This parser creates an instance of a single option definition.
+class OptionDefParser : public DhcpConfigParser {
+public:
+ /// @brief Constructor.
+ ///
+ /// @param dummy first argument is ignored, all Parser constructors
+ /// accept string as first argument.
+ /// @param storage is the definition storage in which to store the parsed
+ /// definition upon "commit".
+ /// @throw isc::dhcp::DhcpConfigError if storage is null.
+ OptionDefParser(const std::string&, OptionDefStoragePtr storage);
+
+ /// @brief Parses an entry that describes single option definition.
+ ///
+ /// @param option_def a configuration entry to be parsed.
+ ///
+ /// @throw DhcpConfigError if parsing was unsuccessful.
+ void build(isc::data::ConstElementPtr option_def);
+
+ /// @brief Stores the parsed option definition in a storage.
+ void commit();
+
+private:
+
+ /// @brief Create option definition from the parsed parameters.
+ void createOptionDef();
+
+ /// Instance of option definition being created by this parser.
+ OptionDefinitionPtr option_definition_;
+ /// Name of the space the option definition belongs to.
+ std::string option_space_name_;
+
+ /// Pointer to a storage where the option definition will be
+ /// added when \ref commit is called.
+ OptionDefStoragePtr storage_;
+
+ /// Storage for boolean values.
+ BooleanStoragePtr boolean_values_;
+
+ /// Storage for string values.
+ StringStoragePtr string_values_;
+
+ /// Storage for uint32 values.
+ Uint32StoragePtr uint32_values_;
+};
+
+/// @brief Parser for a list of option definitions.
+///
+/// This parser iterates over all configuration entries that define
+/// option definitions and creates instances of these definitions.
+/// If the parsing is successful, the collection of created definitions
+/// is put into the provided storage.
+class OptionDefListParser : public DhcpConfigParser {
+public:
+ /// @brief Constructor.
+ ///
+ /// @param dummy first argument is ignored, all Parser constructors
+ /// accept string as first argument.
+ /// @param storage is the definition storage in which to store the parsed
+ /// definitions in this list
+ /// @throw isc::dhcp::DhcpConfigError if storage is null.
+ OptionDefListParser(const std::string&, OptionDefStoragePtr storage);
+
+ /// @brief Parse configuration entries.
+ ///
+ /// This function parses configuration entries and creates instances
+ /// of option definitions.
+ ///
+ /// @param option_def_list pointer to an element that holds entries
+ /// that define option definitions.
+ /// @throw DhcpConfigError if configuration parsing fails.
+ void build(isc::data::ConstElementPtr option_def_list);
+
+ /// @brief Stores option definitions in the CfgMgr.
+ void commit();
+
+private:
+ /// @brief storage for option definitions.
+ OptionDefStoragePtr storage_;
+};
+
+/// @brief a collection of pools
+///
+/// That type is used as intermediate storage, when pools are parsed, but there is
+/// no subnet object created yet to store them.
+typedef std::vector<PoolPtr> PoolStorage;
+typedef boost::shared_ptr<PoolStorage> PoolStoragePtr;
+
+/// @brief parser for pool definition
+///
+/// This abstract parser handles pool definitions, i.e. a list of entries of one
+/// of two syntaxes: min-max and prefix/len. Pool objects are created
+/// and stored in chosen PoolStorage container.
+///
+/// It is useful for parsing Dhcp<4/6>/subnet<4/6>[X]/pool parameters.
+class PoolParser : public DhcpConfigParser {
+public:
+
+ /// @brief constructor.
+
+
+ /// @param dummy first argument is ignored, all Parser constructors
+ /// accept string as first argument.
+ /// @param pools is the storage in which to store the parsed pool
+ /// upon "commit".
+ /// @throw isc::dhcp::DhcpConfigError if storage is null.
+ PoolParser(const std::string&, PoolStoragePtr pools);
+
+ /// @brief parses the actual list
+ ///
+ /// This method parses the actual list of interfaces.
+ /// No validation is done at this stage, everything is interpreted as
+ /// interface name.
+ /// @param pools_list list of pools defined for a subnet
+ /// @throw isc::dhcp::DhcpConfigError when pool parsing fails
+ virtual void build(isc::data::ConstElementPtr pools_list);
+
+ /// @brief Stores the parsed values in a storage provided
+ /// by an upper level parser.
+ virtual void commit();
+
+protected:
+ /// @brief Creates a Pool object given a IPv4 prefix and the prefix length.
+ ///
+ /// @param addr is the IP prefix of the pool.
+ /// @param len is the prefix length.
+ /// @param ignored dummy parameter to provide symmetry between
+ /// @return returns a PoolPtr to the new Pool object.
+ virtual PoolPtr poolMaker(isc::asiolink::IOAddress &addr, uint32_t len,
+ int32_t ptype=0) = 0;
+
+ /// @brief Creates a Pool object given starting and ending IP addresses.
+ ///
+ /// @param min is the first IP address in the pool.
+ /// @param max is the last IP address in the pool.
+ /// @param ptype is the type of pool to create (not used by all derivations)
+ /// @return returns a PoolPtr to the new Pool object.
+ virtual PoolPtr poolMaker(isc::asiolink::IOAddress &min,
+ isc::asiolink::IOAddress &max, int32_t ptype=0) = 0;
+
+ /// @brief pointer to the actual Pools storage
+ ///
+ /// That is typically a storage somewhere in Subnet parser
+ /// (an upper level parser).
+ PoolStoragePtr pools_;
+
+ /// A temporary storage for pools configuration. It is a
+ /// storage where pools are stored by build function.
+ PoolStorage local_pools_;
+};
+
+/// @brief this class parses a single subnet
+///
+/// This class parses the whole subnet definition. It creates parsers
+/// for received configuration parameters as needed.
+class SubnetConfigParser : public DhcpConfigParser {
+public:
+
+ /// @brief constructor
+ SubnetConfigParser(const std::string&, ParserContextPtr global_context);
+
+ /// @brief parses parameter value
+ ///
+ /// @param subnet pointer to the content of subnet definition
+ ///
+ /// @throw isc::DhcpConfigError if subnet configuration parsing failed.
+ virtual void build(isc::data::ConstElementPtr subnet);
+
+ /// @brief Adds the created subnet to a server's configuration.
+ virtual void commit() = 0;
+
+protected:
+ /// @brief creates parsers for entries in subnet definition
+ ///
+ /// @param config_id name od the entry
+ ///
+ /// @return parser object for specified entry name
+ /// @throw isc::dhcp::DhcpConfigError if trying to create a parser
+ /// for unknown config element
+ virtual DhcpConfigParser* createSubnetConfigParser(
+ const std::string& config_id) = 0;
+
+ /// @brief Determines if the given option space name and code describe
+ /// a standard option for the server.
+ ///
+ /// @param option_space is the name of the option space to consider
+ /// @param code is the numeric option code to consider
+ /// @return returns true if the space and code are part of the server's
+ /// standard options.
+ virtual bool isServerStdOption(std::string option_space, uint32_t code) = 0;
+
+ /// @brief Returns the option definition for a given option code from
+ /// the server's standard set of options.
+ /// @param code is the numeric option code of the desired option definition.
+ /// @return returns a pointer the option definition
+ virtual OptionDefinitionPtr getServerStdOptionDefinition (
+ uint32_t code) = 0;
+
+ /// @brief Issues a server specific warning regarding duplicate subnet
+ /// options.
+ ///
+ /// @param code is the numeric option code of the duplicate option
+ /// @param addr is the subnet address
+ /// @todo a means to know the correct logger and perhaps a common
+ /// message would allow this method to be emitted by the base class.
+ virtual void duplicate_option_warning(uint32_t code,
+ isc::asiolink::IOAddress& addr) = 0;
+
+ /// @brief Instantiates the subnet based on a given IP prefix and prefix
+ /// length.
+ ///
+ /// @param addr is the IP prefix of the subnet.
+ /// @param len is the prefix length
+ virtual void initSubnet(isc::asiolink::IOAddress addr, uint8_t len) = 0;
+
+ /// @brief Returns value for a given parameter (after using inheritance)
+ ///
+ /// This method implements inheritance. For a given parameter name, it first
+ /// checks if there is a global value for it and overwrites it with specific
+ /// value if such value was defined in subnet.
+ ///
+ /// @param name name of the parameter
+ /// @return triplet with the parameter name
+ /// @throw DhcpConfigError when requested parameter is not present
+ isc::dhcp::Triplet<uint32_t> getParam(const std::string& name);
+
+private:
+
+ /// @brief Append sub-options to an option.
+ ///
+ /// @param option_space a name of the encapsulated option space.
+ /// @param option option instance to append sub-options to.
+ void appendSubOptions(const std::string& option_space, OptionPtr& option);
+
+ /// @brief Create a new subnet using a data from child parsers.
+ ///
+ /// @throw isc::dhcp::DhcpConfigError if subnet configuration parsing
+ /// failed.
+ void createSubnet();
+
+protected:
+
+ /// Storage for subnet-specific integer values.
+ Uint32StoragePtr uint32_values_;
+
+ /// Storage for subnet-specific string values.
+ StringStoragePtr string_values_;
+
+ /// Storage for pools belonging to this subnet.
+ PoolStoragePtr pools_;
+
+ /// Storage for options belonging to this subnet.
+ OptionStoragePtr options_;
+
+ /// Parsers are stored here.
+ ParserCollection parsers_;
+
+ /// Pointer to the created subnet object.
+ isc::dhcp::SubnetPtr subnet_;
+
+ /// Parsing context which contains global values, options and option
+ /// definitions.
+ ParserContextPtr global_context_;
+};
+
+// Pointers to various parser objects.
+typedef boost::shared_ptr<BooleanParser> BooleanParserPtr;
+typedef boost::shared_ptr<StringParser> StringParserPtr;
+typedef boost::shared_ptr<Uint32Parser> Uint32ParserPtr;
+
+}; // end of isc::dhcp namespace
+}; // end of isc namespace
+
+#endif // DHCP_PARSERS_H
+
diff --git a/src/lib/dhcpsrv/subnet.cc b/src/lib/dhcpsrv/subnet.cc
index daf3f9e..50a0fee 100644
--- a/src/lib/dhcpsrv/subnet.cc
+++ b/src/lib/dhcpsrv/subnet.cc
@@ -33,11 +33,13 @@ Subnet::Subnet(const isc::asiolink::IOAddress& prefix, uint8_t len,
last_allocated_(lastAddrInPrefix(prefix, len)) {
if ((prefix.isV6() && len > 128) ||
(prefix.isV4() && len > 32)) {
- isc_throw(BadValue, "Invalid prefix length specified for subnet: " << len);
+ isc_throw(BadValue,
+ "Invalid prefix length specified for subnet: " << len);
}
}
-bool Subnet::inRange(const isc::asiolink::IOAddress& addr) const {
+bool
+Subnet::inRange(const isc::asiolink::IOAddress& addr) const {
IOAddress first = firstAddrInPrefix(prefix_, prefix_len_);
IOAddress last = lastAddrInPrefix(prefix_, prefix_len_);
@@ -84,7 +86,8 @@ Subnet::getOptionDescriptor(const std::string& option_space,
return (*range.first);
}
-std::string Subnet::toText() const {
+std::string
+Subnet::toText() const {
std::stringstream tmp;
tmp << prefix_.toText() << "/" << static_cast<unsigned int>(prefix_len_);
return (tmp.str());
@@ -101,12 +104,14 @@ Subnet4::Subnet4(const isc::asiolink::IOAddress& prefix, uint8_t length,
}
}
-void Subnet::addPool(const PoolPtr& pool) {
+void
+Subnet::addPool(const PoolPtr& pool) {
IOAddress first_addr = pool->getFirstAddress();
IOAddress last_addr = pool->getLastAddress();
if (!inRange(first_addr) || !inRange(last_addr)) {
- isc_throw(BadValue, "Pool (" << first_addr.toText() << "-" << last_addr.toText()
+ isc_throw(BadValue, "Pool (" << first_addr.toText() << "-"
+ << last_addr.toText()
<< " does not belong in this (" << prefix_.toText() << "/"
<< static_cast<int>(prefix_len_) << ") subnet4");
}
@@ -119,15 +124,16 @@ void Subnet::addPool(const PoolPtr& pool) {
PoolPtr Subnet::getPool(isc::asiolink::IOAddress hint) {
PoolPtr candidate;
- for (PoolCollection::iterator pool = pools_.begin(); pool != pools_.end(); ++pool) {
+ for (PoolCollection::iterator pool = pools_.begin();
+ pool != pools_.end(); ++pool) {
- // if we won't find anything better, then let's just use the first pool
+ // If we won't find anything better, then let's just use the first pool
if (!candidate) {
candidate = *pool;
}
- // if the client provided a pool and there's a pool that hint is valid in,
- // then let's use that pool
+ // If the client provided a pool and there's a pool that hint is valid
+ // in, then let's use that pool
if ((*pool)->inRange(hint)) {
return (*pool);
}
@@ -135,29 +141,44 @@ PoolPtr Subnet::getPool(isc::asiolink::IOAddress hint) {
return (candidate);
}
+void
+Subnet::setIface(const std::string& iface_name) {
+ iface_ = iface_name;
+}
+
+std::string
+Subnet::getIface() const {
+ return (iface_);
+}
+
+
void
Subnet4::validateOption(const OptionPtr& option) const {
if (!option) {
- isc_throw(isc::BadValue, "option configured for subnet must not be NULL");
+ isc_throw(isc::BadValue,
+ "option configured for subnet must not be NULL");
} else if (option->getUniverse() != Option::V4) {
- isc_throw(isc::BadValue, "expected V4 option to be added to the subnet");
+ isc_throw(isc::BadValue,
+ "expected V4 option to be added to the subnet");
}
}
-bool Subnet::inPool(const isc::asiolink::IOAddress& addr) const {
+bool
+Subnet::inPool(const isc::asiolink::IOAddress& addr) const {
// Let's start with checking if it even belongs to that subnet.
if (!inRange(addr)) {
return (false);
}
- for (PoolCollection::const_iterator pool = pools_.begin(); pool != pools_.end(); ++pool) {
+ for (PoolCollection::const_iterator pool = pools_.begin();
+ pool != pools_.end(); ++pool) {
if ((*pool)->inRange(addr)) {
return (true);
}
}
- // there's no pool that address belongs to
+ // There's no pool that address belongs to
return (false);
}
@@ -177,21 +198,13 @@ Subnet6::Subnet6(const isc::asiolink::IOAddress& prefix, uint8_t length,
void
Subnet6::validateOption(const OptionPtr& option) const {
if (!option) {
- isc_throw(isc::BadValue, "option configured for subnet must not be NULL");
+ isc_throw(isc::BadValue,
+ "option configured for subnet must not be NULL");
} else if (option->getUniverse() != Option::V6) {
- isc_throw(isc::BadValue, "expected V6 option to be added to the subnet");
+ isc_throw(isc::BadValue,
+ "expected V6 option to be added to the subnet");
}
}
-
-void Subnet6::setIface(const std::string& iface_name) {
- iface_ = iface_name;
-}
-
-std::string Subnet6::getIface() const {
- return (iface_);
-}
-
-
} // end of isc::dhcp namespace
} // end of isc namespace
diff --git a/src/lib/dhcpsrv/subnet.h b/src/lib/dhcpsrv/subnet.h
index 107ff1d..0ac5109 100644
--- a/src/lib/dhcpsrv/subnet.h
+++ b/src/lib/dhcpsrv/subnet.h
@@ -299,7 +299,18 @@ public:
return pools_;
}
- /// @brief returns textual representation of the subnet (e.g. "2001:db8::/64")
+ /// @brief sets name of the network interface for directly attached networks
+ ///
+ /// @param iface_name name of the interface
+ void setIface(const std::string& iface_name);
+
+ /// @brief network interface name used to reach subnet (or "" for remote
+ /// subnets)
+ /// @return network interface name for directly attached subnets or ""
+ std::string getIface() const;
+
+ /// @brief returns textual representation of the subnet (e.g.
+ /// "2001:db8::/64")
///
/// @return textual representation
virtual std::string toText() const;
@@ -451,18 +462,6 @@ public:
return (preferred_);
}
- /// @brief sets name of the network interface for directly attached networks
- ///
- /// A subnet may be reachable directly (not via relays). In DHCPv6 it is not
- /// possible to decide that based on addresses assigned to network interfaces,
- /// as DHCPv6 operates on link-local (and site local) addresses.
- /// @param iface_name name of the interface
- void setIface(const std::string& iface_name);
-
- /// @brief network interface name used to reach subnet (or "" for remote subnets)
- /// @return network interface name for directly attached subnets or ""
- std::string getIface() const;
-
/// @brief sets interface-id option (if defined)
///
/// @param ifaceid pointer to interface-id option
diff --git a/src/lib/dhcpsrv/tests/Makefile.am b/src/lib/dhcpsrv/tests/Makefile.am
index e19fd87..9a81ef6 100644
--- a/src/lib/dhcpsrv/tests/Makefile.am
+++ b/src/lib/dhcpsrv/tests/Makefile.am
@@ -34,6 +34,7 @@ libdhcpsrv_unittests_SOURCES += dbaccess_parser_unittest.cc
libdhcpsrv_unittests_SOURCES += lease_mgr_factory_unittest.cc
libdhcpsrv_unittests_SOURCES += lease_mgr_unittest.cc
libdhcpsrv_unittests_SOURCES += memfile_lease_mgr_unittest.cc
+libdhcpsrv_unittests_SOURCES += dhcp_parsers_unittest.cc
if HAVE_MYSQL
libdhcpsrv_unittests_SOURCES += mysql_lease_mgr_unittest.cc
endif
diff --git a/src/lib/dhcpsrv/tests/cfgmgr_unittest.cc b/src/lib/dhcpsrv/tests/cfgmgr_unittest.cc
index ba2f290..77c3e36 100644
--- a/src/lib/dhcpsrv/tests/cfgmgr_unittest.cc
+++ b/src/lib/dhcpsrv/tests/cfgmgr_unittest.cc
@@ -15,7 +15,7 @@
#include <config.h>
#include <dhcpsrv/cfgmgr.h>
-#include <dhcpsrv/dhcp_config_parser.h>
+#include <dhcpsrv/dhcp_parsers.h>
#include <exceptions/exceptions.h>
#include <dhcp/dhcp6.h>
@@ -164,6 +164,7 @@ public:
// make sure we start with a clean configuration
CfgMgr::instance().deleteSubnets4();
CfgMgr::instance().deleteSubnets6();
+ CfgMgr::instance().deleteOptionDefs();
}
/// @brief generates interface-id option based on provided text
diff --git a/src/lib/dhcpsrv/tests/dhcp_parsers_unittest.cc b/src/lib/dhcpsrv/tests/dhcp_parsers_unittest.cc
new file mode 100644
index 0000000..687ef92
--- /dev/null
+++ b/src/lib/dhcpsrv/tests/dhcp_parsers_unittest.cc
@@ -0,0 +1,519 @@
+// Copyright (C) 2012-2013 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 <config/ccsession.h>
+#include <dhcp/option.h>
+#include <dhcp/option_custom.h>
+#include <dhcp/option_int.h>
+#include <dhcpsrv/cfgmgr.h>
+#include <dhcpsrv/subnet.h>
+#include <dhcpsrv/dhcp_parsers.h>
+#include <exceptions/exceptions.h>
+
+#include <gtest/gtest.h>
+#include <boost/foreach.hpp>
+
+#include <map>
+#include <string>
+
+using namespace std;
+using namespace isc;
+using namespace isc::dhcp;
+using namespace isc::data;
+using namespace isc::config;
+
+namespace {
+
+/// @brief DHCP Parser test fixture class
+class DhcpParserTest : public ::testing::Test {
+public:
+ /// @brief Constructor
+ ///
+ DhcpParserTest() {
+ }
+};
+
+
+/// @brief Check BooleanParser basic functionality.
+///
+/// Verifies that the parser:
+/// 1. Does not allow empty for storage.
+/// 2. Rejects a non-boolean element.
+/// 3. Builds with a valid true value.
+/// 4. Bbuils with a valid false value.
+/// 5. Updates storage upon commit.
+TEST_F(DhcpParserTest, booleanParserTest) {
+
+ const std::string name = "boolParm";
+
+ // Verify that parser does not allow empty for storage.
+ BooleanStoragePtr bs;
+ EXPECT_THROW(BooleanParser(name, bs), isc::dhcp::DhcpConfigError);
+
+ // Construct parser for testing.
+ BooleanStoragePtr storage(new BooleanStorage());
+ BooleanParser parser(name, storage);
+
+ // Verify that parser with rejects a non-boolean element.
+ ElementPtr wrong_element = Element::create("I am a string");
+ EXPECT_THROW(parser.build(wrong_element), isc::BadValue);
+
+ // Verify that parser will build with a valid true value.
+ bool test_value = true;
+ ElementPtr element = Element::create(test_value);
+ ASSERT_NO_THROW(parser.build(element));
+
+ // Verify that commit updates storage.
+ bool actual_value = !test_value;
+ parser.commit();
+ EXPECT_NO_THROW((actual_value = storage->getParam(name)));
+ EXPECT_EQ(test_value, actual_value);
+
+ // Verify that parser will build with a valid false value.
+ test_value = false;
+ element->setValue(test_value);
+ EXPECT_NO_THROW(parser.build(element));
+
+ // Verify that commit updates storage.
+ actual_value = ~test_value;
+ parser.commit();
+ EXPECT_NO_THROW((actual_value = storage->getParam(name)));
+ EXPECT_EQ(test_value, actual_value);
+}
+
+/// @brief Check StringParser basic functionality
+///
+/// Verifies that the parser:
+/// 1. Does not allow empty for storage.
+/// 2. Builds with a nont string value.
+/// 3. Builds with a string value.
+/// 4. Updates storage upon commit.
+TEST_F(DhcpParserTest, stringParserTest) {
+
+ const std::string name = "strParm";
+
+ // Verify that parser does not allow empty for storage.
+ StringStoragePtr bs;
+ EXPECT_THROW(StringParser(name, bs), isc::dhcp::DhcpConfigError);
+
+ // Construct parser for testing.
+ StringStoragePtr storage(new StringStorage());
+ StringParser parser(name, storage);
+
+ // Verify that parser with accepts a non-string element.
+ ElementPtr element = Element::create(9999);
+ EXPECT_NO_THROW(parser.build(element));
+
+ // Verify that commit updates storage.
+ parser.commit();
+ std::string actual_value;
+ EXPECT_NO_THROW((actual_value = storage->getParam(name)));
+ EXPECT_EQ("9999", actual_value);
+
+ // Verify that parser will build with a string value.
+ const std::string test_value = "test value";
+ element = Element::create(test_value);
+ ASSERT_NO_THROW(parser.build(element));
+
+ // Verify that commit updates storage.
+ parser.commit();
+ EXPECT_NO_THROW((actual_value = storage->getParam(name)));
+ EXPECT_EQ(test_value, actual_value);
+}
+
+/// @brief Check Uint32Parser basic functionality
+///
+/// Verifies that the parser:
+/// 1. Does not allow empty for storage.
+/// 2. Rejects a non-integer element.
+/// 3. Rejects a negative value.
+/// 4. Rejects too large a value.
+/// 5. Builds with value of zero.
+/// 6. Builds with a value greater than zero.
+/// 7. Updates storage upon commit.
+TEST_F(DhcpParserTest, uint32ParserTest) {
+
+ const std::string name = "intParm";
+
+ // Verify that parser does not allow empty for storage.
+ Uint32StoragePtr bs;
+ EXPECT_THROW(Uint32Parser(name, bs), isc::dhcp::DhcpConfigError);
+
+ // Construct parser for testing.
+ Uint32StoragePtr storage(new Uint32Storage());
+ Uint32Parser parser(name, storage);
+
+ // Verify that parser with rejects a non-interger element.
+ ElementPtr wrong_element = Element::create("I am a string");
+ EXPECT_THROW(parser.build(wrong_element), isc::BadValue);
+
+ // Verify that parser with rejects a negative value.
+ ElementPtr int_element = Element::create(-1);
+ EXPECT_THROW(parser.build(int_element), isc::BadValue);
+
+ // Verify that parser with rejects too large a value provided we are on
+ // 64-bit platform.
+ if (sizeof(long) > sizeof(uint32_t)) {
+ long max = (long)(std::numeric_limits<uint32_t>::max()) + 1;
+ int_element->setValue(max);
+ EXPECT_THROW(parser.build(int_element), isc::BadValue);
+ }
+
+ // Verify that parser will build with value of zero.
+ int test_value = 0;
+ int_element->setValue((long)test_value);
+ ASSERT_NO_THROW(parser.build(int_element));
+
+ // Verify that commit updates storage.
+ parser.commit();
+ uint32_t actual_value = 0;
+ EXPECT_NO_THROW((actual_value = storage->getParam(name)));
+ EXPECT_EQ(test_value, actual_value);
+
+ // Verify that parser will build with a valid positive value.
+ test_value = 77;
+ int_element->setValue((long)test_value);
+ ASSERT_NO_THROW(parser.build(int_element));
+
+ // Verify that commit updates storage.
+ parser.commit();
+ EXPECT_NO_THROW((actual_value = storage->getParam(name)));
+ EXPECT_EQ(test_value, actual_value);
+}
+
+/// @brief Check InterfaceListParser basic functionality
+///
+/// Verifies that the parser:
+/// 1. Does not allow empty for storage.
+/// 2. Does not allow name other than "interface"
+///
+/// InterfaceListParser doesn't do very much, this test will need to
+/// expand once it does.
+TEST_F(DhcpParserTest, interfaceListParserTest) {
+
+ const std::string name = "interface";
+
+ // Verify that parser constructor fails if parameter name isn't "interface"
+ EXPECT_THROW(InterfaceListConfigParser("bogus_name"), isc::BadValue);
+
+ InterfaceListConfigParser parser(name);
+ ElementPtr list_element = Element::createList();
+ list_element->add(Element::create("eth0"));
+ list_element->add(Element::create("eth1"));
+}
+
+/// @brief Test Implementation of abstract OptionDataParser class. Allows
+/// testing basic option parsing.
+class UtestOptionDataParser : public OptionDataParser {
+public:
+
+ UtestOptionDataParser(const std::string&,
+ OptionStoragePtr options, ParserContextPtr global_context)
+ :OptionDataParser("", options, global_context) {
+ }
+
+ static OptionDataParser* factory(const std::string& param_name,
+ OptionStoragePtr options, ParserContextPtr global_context) {
+ return (new UtestOptionDataParser(param_name, options, global_context));
+ }
+
+protected:
+ // Dummy out last two params since test derivation doesn't use them.
+ virtual OptionDefinitionPtr findServerSpaceOptionDefinition (
+ std::string&, uint32_t) {
+ OptionDefinitionPtr def;
+ // always return empty
+ return (def);
+ }
+};
+
+/// @brief Test Fixture class which provides basic structure for testing
+/// configuration parsing. This is essentially the same structure provided
+/// by dhcp servers.
+class ParseConfigTest : public ::testing::Test {
+public:
+ /// @brief Constructor
+ ParseConfigTest() {
+ reset_context();
+ }
+
+ ~ParseConfigTest() {
+ reset_context();
+ }
+
+ /// @brief Parses a configuration.
+ ///
+ /// Parse the given configuration, populating the context storage with
+ /// the parsed elements.
+ ///
+ /// @param config_set is the set of elements to parse.
+ /// @return returns an ConstElementPtr containing the numeric result
+ /// code and outcome comment.
+ isc::data::ConstElementPtr parseElementSet(isc::data::ConstElementPtr
+ config_set) {
+ // Answer will hold the result.
+ ConstElementPtr answer;
+ if (!config_set) {
+ answer = isc::config::createAnswer(1,
+ string("Can't parse NULL config"));
+ return (answer);
+ }
+
+ // option parsing must be done last, so save it if we hit if first
+ ParserPtr option_parser;
+
+ ConfigPair config_pair;
+ try {
+ // Iteraate over the config elements.
+ const std::map<std::string, ConstElementPtr>& values_map =
+ config_set->mapValue();
+ BOOST_FOREACH(config_pair, values_map) {
+ // Create the parser based on element name.
+ ParserPtr parser(createConfigParser(config_pair.first));
+ // Options must be parsed last
+ if (config_pair.first == "option-data") {
+ option_parser = parser;
+ } else {
+ // Anything else we can call build straight away.
+ parser->build(config_pair.second);
+ parser->commit();
+ }
+ }
+
+ // The option values parser is the next one to be run.
+ std::map<std::string, ConstElementPtr>::const_iterator
+ option_config = values_map.find("option-data");
+ if (option_config != values_map.end()) {
+ option_parser->build(option_config->second);
+ option_parser->commit();
+ }
+
+ // Everything was fine. Configuration is successful.
+ answer = isc::config::createAnswer(0, "Configuration committed.");
+ } catch (const isc::Exception& ex) {
+ answer = isc::config::createAnswer(1,
+ string("Configuration parsing failed: ") + ex.what());
+
+ } catch (...) {
+ answer = isc::config::createAnswer(1,
+ string("Configuration parsing failed"));
+ }
+
+ return (answer);
+ }
+
+ /// @brief Create an element parser based on the element name.
+ ///
+ /// Note that currently it only supports option-defs and option-data,
+ ///
+ /// @param config_id is the name of the configuration element.
+ /// @return returns a raw pointer to DhcpConfigParser. Note caller is
+ /// responsible for deleting it once no longer needed.
+ /// @throw throws NotImplemented if element name isn't supported.
+ DhcpConfigParser* createConfigParser(const std::string& config_id) {
+ DhcpConfigParser* parser = NULL;
+ if (config_id.compare("option-data") == 0) {
+ parser = new OptionDataListParser(config_id,
+ parser_context_->options_,
+ parser_context_,
+ UtestOptionDataParser::factory);
+ } else if (config_id.compare("option-def") == 0) {
+ parser = new OptionDefListParser(config_id,
+ parser_context_->option_defs_);
+ } else {
+ isc_throw(NotImplemented,
+ "Parser error: configuration parameter not supported: "
+ << config_id);
+ }
+
+ return (parser);
+ }
+
+ /// @brief Convenicee method for parsing a configuration
+ ///
+ /// Given a configuration string, convert it into Elements
+ /// and parse them.
+ /// @param config is the configuration string to parse
+ ///
+ /// @return retuns 0 if the configuration parsed successfully,
+ /// non-zero otherwise failure.
+ int parseConfiguration (std::string &config) {
+ int rcode_ = 1;
+ // Turn config into elements.
+ // Test json just to make sure its valid.
+ ElementPtr json = Element::fromJSON(config);
+ EXPECT_TRUE(json);
+ if (json) {
+ ConstElementPtr status = parseElementSet(json);
+ ConstElementPtr comment_ = parseAnswer(rcode_, status);
+ }
+
+ return (rcode_);
+ }
+
+ /// @brief Find an option definition for a given space and code within
+ /// the parser context.
+ /// @param space is the space name of the desired option.
+ /// @param code is the numeric "type" of the desired option.
+ /// @return returns an OptionDefinitionPtr which points to the found
+ /// definition or is empty.
+ /// ASSERT_ tests don't work inside functions that return values
+ OptionDefinitionPtr getOptionDef(std::string space, uint32_t code)
+ {
+ OptionDefinitionPtr def;
+ OptionDefContainerPtr defs =
+ parser_context_->option_defs_->getItems(space);
+ // Should always be able to get definitions list even if it is empty.
+ EXPECT_TRUE(defs);
+ if (defs) {
+ // Attempt to find desired definiton.
+ const OptionDefContainerTypeIndex& idx = defs->get<1>();
+ const OptionDefContainerTypeRange& range = idx.equal_range(code);
+ int cnt = std::distance(range.first, range.second);
+ EXPECT_EQ(1, cnt);
+ if (cnt == 1) {
+ def = *(idx.begin());
+ }
+ }
+ return (def);
+ }
+
+ /// @brief Find an option for a given space and code within the parser
+ /// context.
+ /// @param space is the space name of the desired option.
+ /// @param code is the numeric "type" of the desired option.
+ /// @return returns an OptionPtr which points to the found
+ /// option or is empty.
+ /// ASSERT_ tests don't work inside functions that return values
+ OptionPtr getOptionPtr(std::string space, uint32_t code)
+ {
+ OptionPtr option_ptr;
+ Subnet::OptionContainerPtr options =
+ parser_context_->options_->getItems(space);
+ // Should always be able to get options list even if it is empty.
+ EXPECT_TRUE(options);
+ if (options) {
+ // Attempt to find desired option.
+ const Subnet::OptionContainerTypeIndex& idx = options->get<1>();
+ const Subnet::OptionContainerTypeRange& range =
+ idx.equal_range(code);
+ int cnt = std::distance(range.first, range.second);
+ EXPECT_EQ(1, cnt);
+ if (cnt == 1) {
+ Subnet::OptionDescriptor desc = *(idx.begin());
+ option_ptr = desc.option;
+ EXPECT_TRUE(option_ptr);
+ }
+ }
+
+ return (option_ptr);
+ }
+
+ /// @brief Wipes the contents of the context to allowing another parsing
+ /// during a given test if needed.
+ void reset_context(){
+ // Note set context universe to V6 as it has to be something.
+ CfgMgr::instance().deleteSubnets4();
+ CfgMgr::instance().deleteSubnets6();
+ CfgMgr::instance().deleteOptionDefs();
+ parser_context_.reset(new ParserContext(Option::V6));
+ }
+
+ /// @brief Parser context - provides storage for options and definitions
+ ParserContextPtr parser_context_;
+};
+
+/// @brief Check Basic parsing of option definitions.
+///
+/// Note that this tests basic operation of the OptionDefinitionListParser and
+/// OptionDefinitionParser. It uses a simple configuration consisting of one
+/// one definition and verifies that it is parsed and committed to storage
+/// correctly.
+TEST_F(ParseConfigTest, basicOptionDefTest) {
+
+ // Configuration string.
+ std::string config =
+ "{ \"option-def\": [ {"
+ " \"name\": \"foo\","
+ " \"code\": 100,"
+ " \"type\": \"ipv4-address\","
+ " \"array\": False,"
+ " \"record-types\": \"\","
+ " \"space\": \"isc\","
+ " \"encapsulate\": \"\""
+ " } ]"
+ "}";
+
+ // Verify that the configuration string parses.
+ int rcode = parseConfiguration(config);
+ ASSERT_TRUE(rcode == 0);
+
+ // Verify that the option definition can be retrieved.
+ OptionDefinitionPtr def = getOptionDef("isc", 100);
+ ASSERT_TRUE(def);
+
+ // Verify that the option definition is correct.
+ EXPECT_EQ("foo", def->getName());
+ EXPECT_EQ(100, def->getCode());
+ EXPECT_FALSE(def->getArrayType());
+ EXPECT_EQ(OPT_IPV4_ADDRESS_TYPE, def->getType());
+ EXPECT_TRUE(def->getEncapsulatedSpace().empty());
+}
+
+/// @brief Check Basic parsing of options.
+///
+/// Note that this tests basic operation of the OptionDataListParser and
+/// OptionDataParser. It uses a simple configuration consisting of one
+/// one definition and matching option data. It verifies that the option
+/// is parsed and committed to storage correctly.
+TEST_F(ParseConfigTest, basicOptionDataTest) {
+
+ // Configuration string.
+ std::string config =
+ "{ \"option-def\": [ {"
+ " \"name\": \"foo\","
+ " \"code\": 100,"
+ " \"type\": \"ipv4-address\","
+ " \"array\": False,"
+ " \"record-types\": \"\","
+ " \"space\": \"isc\","
+ " \"encapsulate\": \"\""
+ " } ], "
+ " \"option-data\": [ {"
+ " \"name\": \"foo\","
+ " \"space\": \"isc\","
+ " \"code\": 100,"
+ " \"data\": \"192.168.2.1\","
+ " \"csv-format\": True"
+ " } ]"
+ "}";
+
+ // Verify that the configuration string parses.
+ int rcode = parseConfiguration(config);
+ ASSERT_TRUE(rcode == 0);
+
+ // Verify that the option can be retrieved.
+ OptionPtr opt_ptr = getOptionPtr("isc", 100);
+ ASSERT_TRUE(opt_ptr);
+
+ // Verify that the option definition is correct.
+ std::string val = "type=100, len=4, data fields:\n "
+ " #0 192.168.2.1 ( ipv4-address ) \n";
+
+ EXPECT_EQ(val, opt_ptr->toText());
+}
+
+}; // Anonymous namespace
+
More information about the bind10-changes
mailing list