Browse Source

[2269] Configuration parser for DHCPv6 implemented.

Tomek Mrugalski 12 years ago
parent
commit
e2a552fcaa

+ 3 - 3
doc/guide/bind10-guide.xml

@@ -2911,7 +2911,7 @@ Dhcp6/subnet6	         []     list    (default)</screen>
         <screen>
 &gt; <userinput>config add Dhcp6/subnet6</userinput>
 &gt; <userinput>config set Dhcp6/subnet6[0]/subnet "2001:db8:1::/64"</userinput>
-&gt; <userinput>config set Dhcp6/subnet6[0]/pool6 [ "2001:db8:1::1 - 2001:db8:1::ffff" ]</userinput>
+&gt; <userinput>config set Dhcp6/subnet6[0]/pool [ "2001:db8:1::1 - 2001:db8:1::ffff" ]</userinput>
 &gt; <userinput>config commit</userinput></screen>
         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)</screen>
         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:
         <screen>
-&gt; <userinput>config set Dhcp6/subnet6[0]/pool6 [ "2001:db8:1::1 - 2001:db8:1::ffff", "2001:db8:1:0:5::/80" ]</userinput>
+&gt; <userinput>config set Dhcp6/subnet6[0]/pool [ "2001:db8:1::1 - 2001:db8:1::ffff", "2001:db8:1:0:5::/80" ]</userinput>
 &gt; <userinput>config commit</userinput></screen>
         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)</screen>
         <screen>
 &gt; <userinput>config add Dhcp6/subnet6</userinput>
 &gt; <userinput>config set Dhcp6/subnet6[1]/subnet "2001:db8:beef::/48"</userinput>
-&gt; <userinput>config set Dhcp6/subnet6[1]/pool6 [ "2001:db8:beef::/48" ]</userinput>
+&gt; <userinput>config set Dhcp6/subnet6[1]/pool [ "2001:db8:beef::/48" ]</userinput>
 &gt; <userinput>config commit</userinput></screen>
         Arrays are counted from 0. subnet[0] refers to the subnet defined in the
         previous example.  The <command>config add Dhcp6/subnet6</command> adds

+ 2 - 0
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
 

+ 507 - 0
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 <stdint.h>
+#include <iostream>
+#include <vector>
+#include <map>
+#include <boost/foreach.hpp>
+#include <boost/shared_ptr.hpp>
+#include <boost/scoped_ptr.hpp>
+#include <boost/lexical_cast.hpp>
+#include <boost/algorithm/string.hpp>
+#include <cc/data.h>
+#include <asiolink/io_address.h>
+#include <dhcp6/config_parser.h>
+#include <dhcp/triplet.h>
+#include <dhcp/pool.h>
+#include <dhcp/subnet.h>
+#include <dhcp/cfgmgr.h>
+
+using namespace std;
+using namespace isc::data;
+using namespace isc::asiolink;
+
+namespace isc {
+namespace dhcp {
+
+typedef boost::shared_ptr<Dhcp6ConfigParser> ParserPtr;
+typedef pair<string, ConstElementPtr> ConfigPair;
+typedef std::vector<ParserPtr> ParserCollection;
+typedef Dhcp6ConfigParser* ParserFactory(const std::string& config_id);
+
+typedef std::map<std::string, ParserFactory*> FactoryMap;
+
+typedef std::map<string, uint32_t> Uint32Storage;
+/// @brief That is a map with global parameters that will be used as defaults
+Uint32Storage uint32_defaults;
+
+typedef std::map<string, string> StringStorage;
+StringStorage string_defaults;
+
+typedef std::vector<Pool6Ptr> 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<uint32_t>(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<string, uint32_t>(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<string, string>(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<string> 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<int>(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<Uint32Parser> uintParser =
+                boost::dynamic_pointer_cast<Uint32Parser>(parser);
+            if (uintParser) {
+                uintParser->setStorage(&uint32_values_);
+            }
+
+            boost::shared_ptr<StringParser> stringParser =
+                boost::dynamic_pointer_cast<StringParser>(parser);
+            if (stringParser) {
+                stringParser->setStorage(&string_values_);
+            }
+
+            boost::shared_ptr<PoolParser> poolParser =
+                boost::dynamic_pointer_cast<PoolParser>(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<unsigned int>(subnet_txt.substr(pos + 1));
+
+        Triplet<uint32_t> t1 = getParam("renew-timer");
+        Triplet<uint32_t> t2 = getParam("rebind-timer");
+        Triplet<uint32_t> pref = getParam("preferred-lifetime");
+        Triplet<uint32_t> 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<string, ParserFactory*>(
+                             "preferred-lifetime", Uint32Parser::Factory));
+        factories.insert(pair<string, ParserFactory*>(
+                             "valid-lifetime", Uint32Parser::Factory));
+        factories.insert(pair<string, ParserFactory*>(
+                             "renew-timer", Uint32Parser::Factory));
+        factories.insert(pair<string, ParserFactory*>(
+                             "rebind-timer", Uint32Parser::Factory));
+
+        factories.insert(pair<string, ParserFactory*>(
+                             "subnet", StringParser::Factory));
+
+        factories.insert(pair<string, ParserFactory*>(
+                             "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<uint32_t> 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<uint32_t>(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<string, ParserFactory*>(
+                         "preferred-lifetime", Uint32Parser::Factory));
+    factories.insert(pair<string, ParserFactory*>(
+                         "valid-lifetime", Uint32Parser::Factory));
+    factories.insert(pair<string, ParserFactory*>(
+                         "renew-timer", Uint32Parser::Factory));
+    factories.insert(pair<string, ParserFactory*>(
+                         "rebind-timer", Uint32Parser::Factory));
+
+    factories.insert(pair<string, ParserFactory*>(
+                         "interface", InterfaceListConfigParser::Factory));
+    factories.insert(pair<string, ParserFactory*>(
+                         "subnet6", Subnets6ListConfigParser::Factory));
+
+    factories.insert(pair<string, ParserFactory*>(
+                         "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

+ 169 - 0
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 <string>
+#include <exceptions/exceptions.h>
+#include <cc/data.h>
+
+#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

+ 14 - 0
src/bin/dhcp6/ctrl_dhcp6_srv.cc

@@ -25,6 +25,7 @@
 #include <dhcp6/ctrl_dhcp6_srv.h>
 #include <dhcp6/dhcp6_log.h>
 #include <dhcp6/spec_config.h>
+#include <dhcp6/config_parser.h>
 #include <dhcp/iface_mgr.h>
 #include <exceptions/exceptions.h>
 #include <util/buffer.h>
@@ -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.

+ 9 - 2
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",

+ 6 - 0
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.

+ 2 - 0
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

+ 5 - 0
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