From: Tomek Mrugalski Date: Tue, 25 Sep 2012 16:26:37 +0000 (+0200) Subject: [2269] Configuration parser for DHCPv6 implemented. X-Git-Tag: trac2402_base~17^2~10^2~10 X-Git-Url: http://git.ipfire.org/gitweb.cgi?a=commitdiff_plain;h=e2a552fcaa6fcc1f0d39defd990bd5a35a4c73a3;p=thirdparty%2Fkea.git [2269] Configuration parser for DHCPv6 implemented. --- diff --git a/doc/guide/bind10-guide.xml b/doc/guide/bind10-guide.xml index 7b6c5fa01d..061f7f3bb9 100644 --- a/doc/guide/bind10-guide.xml +++ b/doc/guide/bind10-guide.xml @@ -2911,7 +2911,7 @@ Dhcp6/subnet6 [] list (default) > config add Dhcp6/subnet6 > config set Dhcp6/subnet6[0]/subnet "2001:db8:1::/64" -> config set Dhcp6/subnet6[0]/pool6 [ "2001:db8:1::1 - 2001:db8:1::ffff" ] +> config set Dhcp6/subnet6[0]/pool [ "2001:db8:1::1 - 2001:db8:1::ffff" ] > config commit Please note that subnet is defined as a simple string, but the pool is an actual list of pools, therefore must be defined with square @@ -2922,7 +2922,7 @@ Dhcp6/subnet6 [] list (default) is cumbersome. It can be expressed simply as 2001:db8:1:0:5::/80. Both formats are supported by Dhcp6. For example, one could define the following pools: -> config set Dhcp6/subnet6[0]/pool6 [ "2001:db8:1::1 - 2001:db8:1::ffff", "2001:db8:1:0:5::/80" ] +> config set Dhcp6/subnet6[0]/pool [ "2001:db8:1::1 - 2001:db8:1::ffff", "2001:db8:1:0:5::/80" ] > config commit Number of subnets is not limited, but for performance reasons it is recommended to keep them as low as possible. @@ -2933,7 +2933,7 @@ Dhcp6/subnet6 [] list (default) > config add Dhcp6/subnet6 > config set Dhcp6/subnet6[1]/subnet "2001:db8:beef::/48" -> config set Dhcp6/subnet6[1]/pool6 [ "2001:db8:beef::/48" ] +> config set Dhcp6/subnet6[1]/pool [ "2001:db8:beef::/48" ] > config commit Arrays are counted from 0. subnet[0] refers to the subnet defined in the previous example. The config add Dhcp6/subnet6 adds diff --git a/src/bin/dhcp6/Makefile.am b/src/bin/dhcp6/Makefile.am index 4dec4e7da6..68aadea5ad 100644 --- a/src/bin/dhcp6/Makefile.am +++ b/src/bin/dhcp6/Makefile.am @@ -46,6 +46,7 @@ pkglibexec_PROGRAMS = b10-dhcp6 b10_dhcp6_SOURCES = main.cc b10_dhcp6_SOURCES += ctrl_dhcp6_srv.cc ctrl_dhcp6_srv.h +b10_dhcp6_SOURCES += config_parser.cc config_parser.h b10_dhcp6_SOURCES += dhcp6_log.cc dhcp6_log.h b10_dhcp6_SOURCES += dhcp6_srv.cc dhcp6_srv.h @@ -62,6 +63,7 @@ b10_dhcp6_LDADD = $(top_builddir)/src/lib/exceptions/libb10-exceptions.la b10_dhcp6_LDADD += $(top_builddir)/src/lib/asiolink/libb10-asiolink.la b10_dhcp6_LDADD += $(top_builddir)/src/lib/log/libb10-log.la b10_dhcp6_LDADD += $(top_builddir)/src/lib/dhcp/libb10-dhcp++.la +b10_dhcp6_LDADD += $(top_builddir)/src/lib/dhcp/libb10-dhcpsrv.la b10_dhcp6_LDADD += $(top_builddir)/src/lib/config/libb10-cfgclient.la b10_dhcp6_LDADD += $(top_builddir)/src/lib/cc/libb10-cc.la diff --git a/src/bin/dhcp6/config_parser.cc b/src/bin/dhcp6/config_parser.cc new file mode 100644 index 0000000000..377c3d48be --- /dev/null +++ b/src/bin/dhcp6/config_parser.cc @@ -0,0 +1,507 @@ +// Copyright (C) 2010 Internet Systems Consortium, Inc. ("ISC") +// +// Permission to use, copy, modify, and/or distribute this software for any +// purpose with or without fee is hereby granted, provided that the above +// copyright notice and this permission notice appear in all copies. +// +// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH +// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY +// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT, +// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM +// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE +// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR +// PERFORMANCE OF THIS SOFTWARE. + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace std; +using namespace isc::data; +using namespace isc::asiolink; + +namespace isc { +namespace dhcp { + +typedef boost::shared_ptr ParserPtr; +typedef pair ConfigPair; +typedef std::vector ParserCollection; +typedef Dhcp6ConfigParser* ParserFactory(const std::string& config_id); + +typedef std::map FactoryMap; + +typedef std::map Uint32Storage; +/// @brief That is a map with global parameters that will be used as defaults +Uint32Storage uint32_defaults; + +typedef std::map StringStorage; +StringStorage string_defaults; + +typedef std::vector PoolStorage; +PoolStorage pool_defaults; + +/// @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. +class DummyParser : public Dhcp6ConfigParser { +public: + DummyParser(const std::string& param_name) + :param_name_(param_name) { + } + virtual void build(ConstElementPtr new_config) { + value_ = new_config; + } + virtual void commit() { + // debug message. The whole DummyParser 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; + } + + static Dhcp6ConfigParser* Factory(const std::string& param_name) { + return (new DummyParser(param_name)); + } + +protected: + std::string param_name_; + ConstElementPtr value_; +}; + +class Uint32Parser : public Dhcp6ConfigParser { +public: + Uint32Parser(const std::string& param_name) + :storage_(&uint32_defaults), param_name_(param_name) { + } + + virtual void build(ConstElementPtr value) { + try { + value_ = boost::lexical_cast(value->str()); + } catch (const boost::bad_lexical_cast &) { + isc_throw(BadValue, "Failed to parse value " << value->str() + << " as unsigned 32-bit integer."); + } + cout << "### storing " << param_name_ << "=" << value_ << + " in " << storage_ << endl; + storage_->insert(pair(param_name_, value_)); + } + + virtual void commit() { + } + + static Dhcp6ConfigParser* Factory(const std::string& param_name) { + return (new Uint32Parser(param_name)); + } + + void setStorage(Uint32Storage* storage) { + storage_ = storage; + } + +protected: + Uint32Storage * storage_; + std::string param_name_; + uint32_t value_; +}; + +class StringParser : public Dhcp6ConfigParser { +public: + StringParser(const std::string& param_name) + :storage_(&string_defaults), param_name_(param_name) { + } + + virtual void build(ConstElementPtr value) { + value_ = value->str(); + boost::erase_all(value_, "\""); + storage_->insert(pair(param_name_, value_)); + } + + virtual void commit() { + } + + static Dhcp6ConfigParser* Factory(const std::string& param_name) { + return (new StringParser(param_name)); + } + + void setStorage(StringStorage * storage) { + storage_ = storage; + } + +protected: + StringStorage * storage_; + std::string param_name_; + std::string value_; +}; + +class InterfaceListConfigParser : public Dhcp6ConfigParser { +public: + InterfaceListConfigParser(const std::string& param_name) { + if (param_name != "interface") { + isc_throw(NotImplemented, "Internal error. Interface configuration " + "parser called for the wrong parameter: " << param_name); + } + } + virtual void build(ConstElementPtr value) { + BOOST_FOREACH(ConstElementPtr iface, value->listValue()) { + interfaces_.push_back(iface->str()); + cout << "#### Configured to listen on interface " << iface->str() << endl; + } + } + + virtual void commit() { + /// @todo: Implement per interface listening. Currently always listening on all + /// interfaces. + } + + static Dhcp6ConfigParser* Factory(const std::string& param_name) { + return (new InterfaceListConfigParser(param_name)); + } + +protected: + vector interfaces_; +}; + +class PoolParser : public Dhcp6ConfigParser { +public: + PoolParser(const std::string& /*param_name*/) + :pools_(NULL) { + // ignore parameter name, it is always Dhcp6/subnet6[X]/pool + } + void build(ConstElementPtr pools_list) { + // setStorage() should have been called before build + if (!pools_) { + isc_throw(NotImplemented, "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 spaces or tabs + boost::erase_all(txt, " "); + boost::erase_all(txt, "\t"); + + size_t pos = txt.find("/"); + if (pos != string::npos) { + IOAddress addr("::"); + uint8_t len = 0; + try { + addr = IOAddress(txt.substr(0, pos)); + string num = 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 written on two + // digits (because there are extra characters left over) + len = boost::lexical_cast(num); + } catch (...) { + isc_throw(Dhcp6ConfigError, "Failed to parse pool " + "definition: " << text_pool->stringValue()); + } + + cout << "#### Creating Pool6(TYPE_IA, " << addr.toText() << "/" + << (int)len << ")" << endl; + // using prefix/len notation + Pool6Ptr pool(new Pool6(Pool6::TYPE_IA, addr, len)); + pools_->push_back(pool); + continue; + } + + pos = txt.find("-"); + if (pos != string::npos) { + IOAddress min(txt.substr(0,pos-1)); + IOAddress max(txt.substr(pos+1)); + + cout << "#### Creating Pool6(TYPE_IA, " << min.toText() << "," + << max.toText() << ")" << endl; + + Pool6Ptr pool(new Pool6(Pool6::TYPE_IA, min, max)); + + pools_->push_back(pool); + continue; + } + + isc_throw(Dhcp6ConfigError, "Failed to parse pool definition:" + << text_pool->stringValue() << + ". Does not contain - (for min-max) nor / (prefix/len)"); + } + } + void setStorage(PoolStorage* storage) { + pools_ = storage; + } + + void commit() {} + + static Dhcp6ConfigParser* Factory(const std::string& param_name) { + return (new PoolParser(param_name)); + } + +protected: + PoolStorage * pools_; + +}; + +/// @brief this class parses a single subnet +class Subnet6ConfigParser : public Dhcp6ConfigParser { +public: + Subnet6ConfigParser(const std::string& param_name) { + } + + void build(ConstElementPtr subnet) { + + cout << "#### Subnet6ConfigParser::build(): parsing: [" << subnet->str() << "]" << endl; + BOOST_FOREACH(ConfigPair param, subnet->mapValue()) { + + ParserPtr parser(createSubnet6ConfigParser(param.first)); + + // if this is an Uint32 parser, tell it to store the values + // in values_, rather than in global storage + boost::shared_ptr uintParser = + boost::dynamic_pointer_cast(parser); + if (uintParser) { + uintParser->setStorage(&uint32_values_); + } + + boost::shared_ptr stringParser = + boost::dynamic_pointer_cast(parser); + if (stringParser) { + stringParser->setStorage(&string_values_); + } + + boost::shared_ptr poolParser = + boost::dynamic_pointer_cast(parser); + if (poolParser) { + poolParser->setStorage(&pools_); + } + + parser->build(param.second); + parsers_.push_back(parser); + } + + // Ok, we now have subnet parsed + } + + void commit() { + + StringStorage::const_iterator it = string_values_.find("subnet"); + cout << "#### Subnet6ConfigParser::commit() string_values_.size()=" + << string_values_.size() << endl; + if (it == string_values_.end()) { + isc_throw(Dhcp6ConfigError, + "Mandatory subnet definition in subnet missing"); + } + string subnet_txt = it->second; + boost::erase_all(subnet_txt, " "); + boost::erase_all(subnet_txt, "\t"); + + size_t pos = subnet_txt.find("/"); + if (pos == string::npos) { + isc_throw(Dhcp6ConfigError, + "Invalid subnet syntax (prefix/len expected):" << it->second); + } + IOAddress addr(subnet_txt.substr(0, pos)); + uint8_t len = boost::lexical_cast(subnet_txt.substr(pos + 1)); + + Triplet t1 = getParam("renew-timer"); + Triplet t2 = getParam("rebind-timer"); + Triplet pref = getParam("preferred-lifetime"); + Triplet valid = getParam("valid-lifetime"); + + cout << "#### Adding subnet " << addr.toText() << "/" << (int)len + << " with params t1=" << t1 << ", t2=" << t2 << ", pref=" + << pref << ", valid=" << valid << endl; + + Subnet6Ptr subnet(new Subnet6(addr, len, t1, t2, pref, valid)); + + + + CfgMgr::instance().addSubnet6(subnet); + } + +protected: + Dhcp6ConfigParser* createSubnet6ConfigParser(const std::string& config_id) { + FactoryMap factories; + + factories.insert(pair( + "preferred-lifetime", Uint32Parser::Factory)); + factories.insert(pair( + "valid-lifetime", Uint32Parser::Factory)); + factories.insert(pair( + "renew-timer", Uint32Parser::Factory)); + factories.insert(pair( + "rebind-timer", Uint32Parser::Factory)); + + factories.insert(pair( + "subnet", StringParser::Factory)); + + factories.insert(pair( + "pool", PoolParser::Factory)); + + FactoryMap::iterator f = factories.find(config_id); + if (f == factories.end()) { + // Used for debugging only. + // return new DummyParser(config_id); + + isc_throw(NotImplemented, + "Parser error: Subnet6 parameter not supported: " + << config_id); + } + return (f->second(config_id)); + } + + Triplet getParam(const std::string& name) { + uint32_t value = 0; + bool found = false; + Uint32Storage::iterator global = uint32_defaults.find(name); + if (global != uint32_defaults.end()) { + value = global->second; + found = true; + } + + Uint32Storage::iterator local = uint32_values_.find(name); + if (local != uint32_values_.end()) { + value = local->second; + found = true; + } + + if (found) { + return (Triplet(value)); + } else { + isc_throw(Dhcp6ConfigError, "Mandatory parameter " << name + << " missing (no global default and no subnet-" + << "specific value)"); + } + } + + Uint32Storage uint32_values_; + StringStorage string_values_; + PoolStorage pools_; + ParserCollection parsers_; +}; + +/// @brief this class parses list of subnets +class Subnets6ListConfigParser : public Dhcp6ConfigParser { +public: + Subnets6ListConfigParser(const std::string& param_name) { + } + + 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); + } + + } + + void commit() { + BOOST_FOREACH(ParserPtr subnet, subnets_) { + subnet->commit(); + } + + } + + static Dhcp6ConfigParser* Factory(const std::string& param_name) { + return (new Subnets6ListConfigParser(param_name)); + } + + ParserCollection subnets_; +}; + +/// @brief creates global parsers +/// +/// This method creates global parsers that parse global parameters, i.e. +/// those that take format of Dhcp6/param1, Dhcp6/param2 and so forth. +Dhcp6ConfigParser* createGlobalDhcp6ConfigParser(const std::string& config_id) { + FactoryMap factories; + + // + factories.insert(pair( + "preferred-lifetime", Uint32Parser::Factory)); + factories.insert(pair( + "valid-lifetime", Uint32Parser::Factory)); + factories.insert(pair( + "renew-timer", Uint32Parser::Factory)); + factories.insert(pair( + "rebind-timer", Uint32Parser::Factory)); + + factories.insert(pair( + "interface", InterfaceListConfigParser::Factory)); + factories.insert(pair( + "subnet6", Subnets6ListConfigParser::Factory)); + + factories.insert(pair( + "version", StringParser::Factory)); + + FactoryMap::iterator f = factories.find(config_id); + if (f == factories.end()) { + // Used for debugging only. + // return new DummyParser(config_id); + + isc_throw(NotImplemented, + "Parser error: Global configuration parameter not supported: " + << config_id); + } + return (f->second(config_id)); +} + +void +configureDhcp6Server(Dhcpv6Srv& server, ConstElementPtr config_set) { + if (!config_set) { + isc_throw(Dhcp6ConfigError, + "Null pointer is passed to configuration parser"); + } + + ParserCollection parsers; + try { + BOOST_FOREACH(ConfigPair config_pair, config_set->mapValue()) { + + ParserPtr parser(createGlobalDhcp6ConfigParser(config_pair.first)); + parser->build(config_pair.second); + parsers.push_back(parser); + } + } catch (const Dhcp6ConfigError& ex) { + throw; // simply rethrowing it + } catch (const isc::Exception& ex) { + isc_throw(Dhcp6ConfigError, "Server configuration failed: " << + ex.what()); + } + + try { + BOOST_FOREACH(ParserPtr parser, parsers) { + parser->commit(); + } + } catch (...) { + isc_throw(Dhcp6ConfigError, "Unrecoverable error: " + "a configuration parser threw in commit"); + } +} + +}; // 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 new file mode 100644 index 0000000000..497a89c15c --- /dev/null +++ b/src/bin/dhcp6/config_parser.h @@ -0,0 +1,169 @@ +// Copyright (C) 2012 Internet Systems Consortium, Inc. ("ISC") +// +// Permission to use, copy, modify, and/or distribute this software for any +// purpose with or without fee is hereby granted, provided that the above +// copyright notice and this permission notice appear in all copies. +// +// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH +// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY +// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT, +// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM +// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE +// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR +// PERFORMANCE OF THIS SOFTWARE. + +#include +#include +#include + +#ifndef DHCP6_CONFIG_PARSER_H +#define DHCP6_CONFIG_PARSER_H + +namespace isc { +namespace dhcp { + +class Dhcpv6Srv; + +/// An exception that is thrown if an error occurs while configuring an +/// \c Dhcpv6Srv object. +class Dhcp6ConfigError : public isc::Exception { +public: +Dhcp6ConfigError(const char* file, size_t line, const char* what) : + isc::Exception(file, line, what) {} +}; + +class Dhcp6ConfigParser { + /// + /// \name Constructors and Destructor + /// + /// Note: The copy constructor and the assignment operator are + /// intentionally defined as private to make it explicit that this is a + /// pure base class. + //@{ +private: + Dhcp6ConfigParser(const Dhcp6ConfigParser& source); + Dhcp6ConfigParser& operator=(const Dhcp6ConfigParser& source); +protected: + /// \brief The default constructor. + /// + /// This is intentionally defined as \c protected as this base class should + /// never be instantiated (except as part of a derived class). + Dhcp6ConfigParser() {} +public: + /// The destructor. + virtual ~Dhcp6ConfigParser() {} + //@} + + /// Prepare configuration value. + /// + /// This method parses the "value part" of the configuration identifier + /// that corresponds to this derived class and prepares a new value to + /// apply to the server. + /// In the above example, the derived class for the identifier "param1" + /// would be passed an data \c Element storing an integer whose value + /// is 10, and would record that value internally; + /// the derived class for the identifier "param2" would be passed a + /// map element and (after parsing) convert it into some internal + /// data structure. + /// + /// This method must validate the given value both in terms of syntax + /// and semantics of the configuration, so that the server will be + /// validly configured at the time of \c commit(). Note: the given + /// configuration value is normally syntactically validated, but the + /// \c build() implementation must also expect invalid input. If it + /// detects an error it may throw an exception of a derived class + /// of \c isc::Exception. + /// + /// Preparing a configuration value will often require resource + /// allocation. If it fails, it may throw a corresponding standard + /// exception. + /// + /// This method is not expected to be called more than once. Although + /// multiple calls are not prohibited by the interface, the behavior + /// is undefined. + /// + /// \param config_value The configuration value for the identifier + /// corresponding to the derived class. + virtual void build(isc::data::ConstElementPtr config_value) = 0; + + /// Apply the prepared configuration value to the server. + /// + /// This method is expected to be exception free, and, as a consequence, + /// it should normally not involve resource allocation. + /// Typically it would simply perform exception free assignment or swap + /// operation on the value prepared in \c build(). + /// In some cases, however, it may be very difficult to meet this + /// condition in a realistic way, while the failure case should really + /// be very rare. In such a case it may throw, and, if the parser is + /// called via \c configureDhcp6Server(), the caller will convert the + /// exception as a fatal error. + /// + /// This method is expected to be called after \c build(), and only once. + /// The result is undefined otherwise. + virtual void commit() = 0; +}; + +/// Configure an \c Dhcpv6Srv object 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. +/// It provides the strong exception guarantee as long as the underlying +/// derived class implementations of \c Dhcp6ConfigParser meet the assumption, +/// that is, it ensures that either configuration is fully applied or the +/// state of the server is intact. +/// +/// If a syntax or semantics level error happens during the configuration +/// (such as malformed configuration or invalid configuration parameter), +/// this function throws an exception of class \c Dhcp6ConfigError. +/// If the given configuration requires resource allocation and it fails, +/// a corresponding standard exception will be thrown. +/// Other exceptions may also be thrown, depending on the implementation of +/// the underlying derived class of \c Dhcp6ConfigError. +/// In any case the strong guarantee is provided as described above except +/// in the very rare cases where the \c commit() method of a parser throws +/// an exception. If that happens this function converts the exception +/// into a \c FatalError exception and rethrows it. This exception is +/// expected to be caught at the highest level of the application to terminate +/// the program gracefully. +/// +/// \param server The \c Dhcpv6Srv object to be configured. +/// \param config_set A JSON style configuration to apply to \c server. +void configureDhcp6Server(Dhcpv6Srv& server, + isc::data::ConstElementPtr config_set); + + +/// Create a new \c Dhcp6ConfigParser object for a given configuration +/// identifier. +/// +/// It internally identifies an appropriate derived class for the given +/// identifier and creates a new instance of that class. The caller can +/// then configure the \c server regarding the identifier by calling +/// the \c build() and \c commit() methods of the returned object. +/// +/// In practice, this function is only expected to be used as a backend of +/// \c configureDhcp6Server() and is not supposed to be called directly +/// by applications. It is publicly available mainly for testing purposes. +/// When called directly, the created object must be deleted by the caller. +/// Note: this means if this module and the caller use incompatible sets of +/// new/delete, it may cause unexpected strange failure. We could avoid that +/// by providing a separate deallocation function or by using a smart pointer, +/// but since the expected usage of this function is very limited (i.e. for +/// our own testing purposes) it would be an overkilling. We therefore prefer +/// simplicity and keeping the interface intuitive. +/// +/// If the resource allocation for the new object fails, a corresponding +/// standard exception will be thrown. Otherwise this function is not +/// expected to throw an exception, unless the constructor of the underlying +/// derived class implementation (unexpectedly) throws. +/// +/// \param server The \c Dhcpv6Srv object to be configured. +/// \param config_id The configuration identifier for which a parser object +/// is to be created. +/// \return A pointer to an \c Dhcp6ConfigParser object. +Dhcp6ConfigParser* createDhcp6ConfigParser(Dhcpv6Srv& server, + const std::string& config_id); + +}; // end of isc::dhcp namespace +}; // end of isc namespace + +#endif // DHCP6_CONFIG_PARSER_H diff --git a/src/bin/dhcp6/ctrl_dhcp6_srv.cc b/src/bin/dhcp6/ctrl_dhcp6_srv.cc index 4afb2039fd..6e2f0d8087 100644 --- a/src/bin/dhcp6/ctrl_dhcp6_srv.cc +++ b/src/bin/dhcp6/ctrl_dhcp6_srv.cc @@ -25,6 +25,7 @@ #include #include #include +#include #include #include #include @@ -47,6 +48,13 @@ ConstElementPtr ControlledDhcpv6Srv::dhcp6ConfigHandler(ConstElementPtr new_config) { LOG_DEBUG(dhcp6_logger, DBG_DHCP6_COMMAND, DHCP6_CONFIG_UPDATE) .arg(new_config->str()); + + /// @todo: commented out as server is not able to understand partial + /// configuration (only the diff and not the whole configuration) + /* if (server_) { + configureDhcp6Server(*server_, new_config); + } */ + ConstElementPtr answer = isc::config::createAnswer(0, "Thank you for sending config."); return (answer); @@ -105,6 +113,12 @@ void ControlledDhcpv6Srv::establishSession() { dhcp6CommandHandler, false); config_session_->start(); + try { + configureDhcp6Server(*this, config_session_->getFullConfig()); + } catch (const Dhcp6ConfigError& ex) { + LOG_ERROR(dhcp6_logger, DHCP6_CONFIG_LOAD_FAIL).arg(ex.what()); + } + /// Integrate the asynchronous I/O model of BIND 10 configuration /// control with the "select" model of the DHCP server. This is /// fully explained in \ref dhcpv6Session. diff --git a/src/bin/dhcp6/dhcp6.spec b/src/bin/dhcp6/dhcp6.spec index 279049a754..b61ef2a8b2 100644 --- a/src/bin/dhcp6/dhcp6.spec +++ b/src/bin/dhcp6/dhcp6.spec @@ -4,9 +4,16 @@ "module_description": "DHCPv6 server daemon", "config_data": [ { "item_name": "interface", - "item_type": "string", + "item_type": "list", "item_optional": false, - "item_default": "all" + "item_default": [ "all" ], + "list_item_spec": + { + "item_name": "interface_name", + "item_type": "string", + "item_optional": false, + "item_default": "all" + } } , { "item_name": "renew-timer", diff --git a/src/bin/dhcp6/dhcp6_messages.mes b/src/bin/dhcp6/dhcp6_messages.mes index a3615319a7..dcfb2caad6 100644 --- a/src/bin/dhcp6/dhcp6_messages.mes +++ b/src/bin/dhcp6/dhcp6_messages.mes @@ -99,3 +99,9 @@ processed any command-line switches and is starting. This is a debug message issued during the IPv6 DHCP server startup. It lists some information about the parameters with which the server is running. + +% DHCP6_CONFIG_LOAD_FAIL failed to load configuration: %1 +This critical error message indicates that the initial DHCPv6 +configuration has failed. The server will continue to run, but +administrator's intervention is required. The server's configuration +must be fixed before it can become usable. diff --git a/src/bin/dhcp6/tests/Makefile.am b/src/bin/dhcp6/tests/Makefile.am index 1d9308fcaa..30d2c384cd 100644 --- a/src/bin/dhcp6/tests/Makefile.am +++ b/src/bin/dhcp6/tests/Makefile.am @@ -48,6 +48,7 @@ dhcp6_unittests_SOURCES += ctrl_dhcp6_srv_unittest.cc dhcp6_unittests_SOURCES += ../dhcp6_srv.h ../dhcp6_srv.cc dhcp6_unittests_SOURCES += ../dhcp6_log.h ../dhcp6_log.cc dhcp6_unittests_SOURCES += ../ctrl_dhcp6_srv.cc +dhcp6_unittests_SOURCES += ../config_parser.cc ../config_parser.h nodist_dhcp6_unittests_SOURCES = ../dhcp6_messages.h ../dhcp6_messages.cc if USE_CLANGPP @@ -61,6 +62,7 @@ dhcp6_unittests_LDFLAGS = $(AM_LDFLAGS) $(GTEST_LDFLAGS) dhcp6_unittests_LDADD = $(GTEST_LDADD) dhcp6_unittests_LDADD += $(top_builddir)/src/lib/asiolink/libb10-asiolink.la dhcp6_unittests_LDADD += $(top_builddir)/src/lib/dhcp/libb10-dhcp++.la +dhcp6_unittests_LDADD += $(top_builddir)/src/lib/dhcp/libb10-dhcpsrv.la dhcp6_unittests_LDADD += $(top_builddir)/src/lib/log/libb10-log.la dhcp6_unittests_LDADD += $(top_builddir)/src/lib/exceptions/libb10-exceptions.la dhcp6_unittests_LDADD += $(top_builddir)/src/lib/config/libb10-cfgclient.la diff --git a/src/lib/dhcp/triplet.h b/src/lib/dhcp/triplet.h index 549698babd..f5fa20b238 100644 --- a/src/lib/dhcp/triplet.h +++ b/src/lib/dhcp/triplet.h @@ -12,6 +12,9 @@ // OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR // PERFORMANCE OF THIS SOFTWARE. +#ifndef TRIPLET_H +#define TRIPLET_H + namespace isc { namespace dhcp { @@ -106,3 +109,5 @@ protected: } // namespace isc::dhcp } // namespace isc + +#endif // ifdef TRIPLET_H