BIND 10 master, updated. c328aed83b7d9506291283b6afbeabb4a5455b07 [2270] Merge branch 'master' into trac2270

BIND 10 source code commits bind10-changes at lists.isc.org
Wed Dec 12 13:58:38 UTC 2012


The branch, master has been updated
       via  c328aed83b7d9506291283b6afbeabb4a5455b07 (commit)
       via  de29c07129d41c96ee0d5eebdd30a1ea7fb9ac8a (commit)
       via  8792cbaca81edb2106863f8043fe490d148db54a (commit)
       via  9c2193e32b9c07dfe1db532cb26de1c1e16d1b48 (commit)
       via  7e8e8bcaaba5f836b3868f6601e8b4e8314a431e (commit)
       via  0589469f4a57ee95f16ea7e316365ae0641c4c3c (commit)
       via  c84efb2586d129061f0fc0e1bcf291e0582b0f9d (commit)
       via  bdeb3c67d12483d7ed6945e7d190195764e24fcc (commit)
       via  65a83c79b14885d4a4106e1a707ef431b72053e4 (commit)
       via  d0af0ecaadd8950008ef65ee2b40b56fa21a1e82 (commit)
       via  142825f0caeba214a4d1884be4067c62d1cc3551 (commit)
       via  4b59798e65bfc49e153a4fd96fcc684c1fbe91b6 (commit)
       via  d2c2edbfa7d78ebeae4c79eef532814ac7072556 (commit)
       via  552657fff4e4538628e7d2b98c4fbe93a07d307c (commit)
       via  650a6c1b4f315e6e89de673502d5a82359f424d8 (commit)
      from  92758ecffc245b5f9a86dbc8a2201a9c1397f7d8 (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 c328aed83b7d9506291283b6afbeabb4a5455b07
Merge: de29c07 92758ec
Author: Tomek Mrugalski <tomasz at isc.org>
Date:   Wed Dec 12 14:50:38 2012 +0100

    [2270] Merge branch 'master' into trac2270
    
    Conflicts:
    	ChangeLog
    	doc/devel/02-dhcp.dox
    	src/bin/dhcp4/ctrl_dhcp4_srv.cc
    	src/bin/dhcp4/dhcp4_srv.cc
    	src/bin/dhcp4/dhcp4_srv.h
    	src/bin/dhcp6/config_parser.cc
    	src/bin/dhcp6/config_parser.h

commit de29c07129d41c96ee0d5eebdd30a1ea7fb9ac8a
Author: Tomek Mrugalski <tomasz at isc.org>
Date:   Wed Dec 12 13:02:40 2012 +0100

    [2270] Headers reordered.

commit 8792cbaca81edb2106863f8043fe490d148db54a
Author: Tomek Mrugalski <tomasz at isc.org>
Date:   Tue Dec 11 18:07:37 2012 +0100

    [2270] Changes after review.

commit 9c2193e32b9c07dfe1db532cb26de1c1e16d1b48
Author: Tomek Mrugalski <tomasz at isc.org>
Date:   Tue Dec 11 18:06:19 2012 +0100

    [2270] dhcp6 now handles reconfigures better (reverted clear() on defaults)

commit 7e8e8bcaaba5f836b3868f6601e8b4e8314a431e
Author: Tomek Mrugalski <tomasz at isc.org>
Date:   Thu Dec 6 10:34:47 2012 +0100

    [2270] ChangeLog updated.

commit 0589469f4a57ee95f16ea7e316365ae0641c4c3c
Author: Tomek Mrugalski <tomasz at isc.org>
Date:   Wed Dec 5 15:38:14 2012 +0100

    [2270] Dhcp4ConfigParser members are now private.

commit c84efb2586d129061f0fc0e1bcf291e0582b0f9d
Author: Tomek Mrugalski <tomasz at isc.org>
Date:   Wed Dec 5 15:27:30 2012 +0100

    [2270] Changes after review
    
    - added boundary checks for Uint32Parser
    - fixed #include order
    - Class renamed to Dhcp4ConfigParser
    - added extra test for Uint32Parser
    - Many Doxygen fixes and clean-ups

commit bdeb3c67d12483d7ed6945e7d190195764e24fcc
Author: Tomek Mrugalski <tomasz at isc.org>
Date:   Wed Dec 5 15:23:34 2012 +0100

    [2270] Fix for repeated reconf in dhcp6 (old defaults are now removed)

commit 65a83c79b14885d4a4106e1a707ef431b72053e4
Author: Tomek Mrugalski <tomasz at isc.org>
Date:   Wed Dec 5 15:22:05 2012 +0100

    [2270] Fixes in Doxygen comments in src/bin/dhcp6

commit d0af0ecaadd8950008ef65ee2b40b56fa21a1e82
Author: Marcin Siodelski <marcin at isc.org>
Date:   Wed Oct 17 13:18:25 2012 +0200

    [2270] Corrected some minor typos.

commit 142825f0caeba214a4d1884be4067c62d1cc3551
Author: Tomek Mrugalski <tomasz at isc.org>
Date:   Fri Oct 12 15:06:23 2012 +0200

    [2270] Comment about code overlap added.

commit 4b59798e65bfc49e153a4fd96fcc684c1fbe91b6
Author: Tomek Mrugalski <tomasz at isc.org>
Date:   Fri Oct 12 14:55:59 2012 +0200

    [2270] Documentation for DHCPv4 config. parser written.

commit d2c2edbfa7d78ebeae4c79eef532814ac7072556
Author: Tomek Mrugalski <tomasz at isc.org>
Date:   Fri Oct 12 13:53:10 2012 +0200

    [2270] DHCPv6 pool parser fix.

commit 552657fff4e4538628e7d2b98c4fbe93a07d307c
Author: Tomek Mrugalski <tomasz at isc.org>
Date:   Fri Oct 12 13:52:36 2012 +0200

    [2270] DHCPv4 parser fixes. Tests now pass.

commit 650a6c1b4f315e6e89de673502d5a82359f424d8
Author: Tomek Mrugalski <tomasz at isc.org>
Date:   Thu Oct 11 19:08:23 2012 +0200

    [2270] Configuration parser for DHCPv4

-----------------------------------------------------------------------

Summary of changes:
 ChangeLog                                     |    7 +
 doc/devel/mainpage.dox                        |   14 +-
 doc/guide/bind10-guide.xml                    |   96 ++-
 src/bin/dhcp4/Makefile.am                     |    2 +
 src/bin/dhcp4/config_parser.cc                |  772 +++++++++++++++++++++++++
 src/bin/{dhcp6 => dhcp4}/config_parser.h      |   95 +--
 src/bin/dhcp4/ctrl_dhcp4_srv.cc               |   29 +-
 src/bin/dhcp4/ctrl_dhcp4_srv.h                |    4 +-
 src/bin/dhcp4/dhcp4.dox                       |   68 +++
 src/bin/dhcp4/dhcp4.spec                      |   80 ++-
 src/bin/dhcp4/dhcp4_messages.mes              |   20 +
 src/bin/dhcp4/dhcp4_srv.h                     |    4 +-
 src/bin/dhcp4/tests/Makefile.am               |    3 +
 src/bin/dhcp4/tests/config_parser_unittest.cc |  294 ++++++++++
 src/bin/dhcp6/config_parser.cc                |   39 +-
 src/bin/dhcp6/config_parser.h                 |   25 +-
 src/bin/dhcp6/dhcp6.dox                       |    8 +-
 src/lib/dhcpsrv/cfgmgr.cc                     |    8 +
 src/lib/dhcpsrv/cfgmgr.h                      |   19 +-
 19 files changed, 1491 insertions(+), 96 deletions(-)
 create mode 100644 src/bin/dhcp4/config_parser.cc
 copy src/bin/{dhcp6 => dhcp4}/config_parser.h (62%)
 create mode 100644 src/bin/dhcp4/dhcp4.dox
 create mode 100644 src/bin/dhcp4/tests/config_parser_unittest.cc

-----------------------------------------------------------------------
diff --git a/ChangeLog b/ChangeLog
index 06207ec..9164587 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,10 @@
+525.	[func]		tomek
+	b10-dhcp4: DHCPv4 server is now able to parse configuration. It
+	is possible to specify IPv4 subnets with dynamic pools within
+	them. Although configuration is accepted, it is not used yet. This
+	will be implemented shortly.
+	(Trac #2270, git de29c07129d41c96ee0d5eebdd30a1ea7fb9ac8a)
+
 524.	[func]		tomek
 	b10-dhcp6 is now able to handle RENEW messages. Leases are
 	renewed and REPLY responses are sent back to clients.
diff --git a/doc/devel/mainpage.dox b/doc/devel/mainpage.dox
index db42c14..407723e 100644
--- a/doc/devel/mainpage.dox
+++ b/doc/devel/mainpage.dox
@@ -20,12 +20,14 @@
  * - @subpage DataScrubbing
  *
  * @section DHCP
- * - @subpage dhcpv4
- *   - @subpage dhcpv4Session
- * - @subpage dhcpv6
- *   - @subpage dhcpv6-session
- *   - @subpage dhcpv6-config-parser
- *   - @subpage dhcpv6-config-inherit
+ * - @subpage dhcp4
+ *   - @subpage dhcp4-session
+ *   - @subpage dhcp4-config-parser
+ *   - @subpage dhcp4-config-inherit
+ * - @subpage dhcp6
+ *   - @subpage dhcp6-session
+ *   - @subpage dhcp6-config-parser
+ *   - @subpage dhcp6-config-inherit
  * - @subpage libdhcp
  *   - @subpage libdhcpIntro
  *   - @subpage libdhcpIfaceMgr
diff --git a/doc/guide/bind10-guide.xml b/doc/guide/bind10-guide.xml
index ebc0ea9..61a9ee4 100644
--- a/doc/guide/bind10-guide.xml
+++ b/doc/guide/bind10-guide.xml
@@ -3404,16 +3404,94 @@ then change those defaults with config set Resolver/forward_addresses[0]/address
     <section id="dhcp4-config">
       <title>DHCPv4 Server Configuration</title>
       <para>
-        The DHCPv4 server does not have a lease database implemented yet
-        nor any support for configuration, so the same set
-        of configuration options (including the same fixed address)
-        will be assigned every time.
+        Once the server is started, it can be configured. To view the
+        current configuration, use the following command in <command>bindctl</command>:
+        <screen>
+> <userinput>config show Dhcp4</userinput></screen>
+        When starting Dhcp4 daemon for the first time, the default configuration
+        will be available. It will look similar to this:
+        <screen>
+> <userinput>config show Dhcp4</userinput>
+Dhcp4/interface/                list    (default)
+Dhcp4/renew-timer        1000   integer	(default)
+Dhcp4/rebind-timer       2000   integer	(default)
+Dhcp4/preferred-lifetime 3000   integer	(default)
+Dhcp4/valid-lifetime	 4000   integer	(default)
+Dhcp4/subnet4	         []     list    (default)</screen>
       </para>
+
+      <para>
+        To change one of the parameters, simply follow
+        the usual <command>bindctl</command> procedure. For example, to make the
+        leases longer, change their valid-lifetime parameter:
+        <screen>
+> <userinput>config set Dhcp4/valid-lifetime 7200</userinput>
+> <userinput>config commit</userinput></screen>
+        Please note that most Dhcp4 parameters are of global scope
+        and apply to all defined subnets, unless they are overridden on a
+        per-subnet basis.
+      </para>
+
+      <para>
+        The essential role of DHCPv4 server is address assignment. The server
+        has to be configured with at least one subnet and one pool of dynamic
+        addresses to be managed. For example, assume that the server
+        is connected to a network segment that uses the 192.0.2.0/24
+        prefix. The Administrator of that network has decided that addresses from range
+        192.0.2.10 to 192.0.2.20 are going to be managed by the Dhcp4
+        server. Such a configuration can be achieved in the following way:
+        <screen>
+> <userinput>config add Dhcp4/subnet4</userinput>
+> <userinput>config set Dhcp4/subnet4[0]/subnet "192.0.2.0/24"</userinput>
+> <userinput>config set Dhcp4/subnet4[0]/pool [ "192.0.2.10 - 192.0.2.20" ]</userinput>
+> <userinput>config commit</userinput></screen>
+        Note that subnet is defined as a simple string, but the pool parameter
+        is actually a list of pools: for this reason, the pool definition is
+        enclosed in square brackets, even though only one range of addresses
+        is specified.</para>
+        <para>It is possible to define more than one pool in a
+        subnet: continuing the previous example, further assume that
+        192.0.2.64/26 should be also be managed by the server. It could be written as
+        192.0.2.64 to 192.0.2.127. Alternatively, it can be expressed more simply as
+        192.0.2.64/26. Both formats are supported by Dhcp4 and can be mixed in the pool list.
+        For example, one could define the following pools:
+        <screen>
+> <userinput>config set Dhcp4/subnet4[0]/pool [ "192.0.2.10-192.0.2.20", "192.0.2.64/26" ]</userinput>
+> <userinput>config commit</userinput></screen>
+        The number of pools is not limited, but for performance reasons it is recommended to
+        use as few as possible. Space and tabulations in pool definitions are ignored, so
+        spaces before and after hyphen are optional. They can be used to improve readability.
+      </para>
+      <para>
+         The server may be configured to serve more than one subnet. To add a second subnet,
+         use a command similar to the following:
+        <screen>
+> <userinput>config add Dhcp4/subnet4</userinput>
+> <userinput>config set Dhcp4/subnet4[1]/subnet "192.0.3.0/24"</userinput>
+> <userinput>config set Dhcp4/subnet4[1]/pool [ "192.0.3.0/24" ]</userinput>
+> <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 Dhcp4/subnet4</command> adds
+        another (second) subnet. It can be referred to as
+        <command>Dhcp4/subnet4[1]</command>. In this example, we allow server to
+        dynamically assign all addresses available in the whole subnet.
+      </para>
+      <para>
+        When configuring a DHCPv4 server using prefix/length notation, please pay
+        attention to the boundary values. When specifying that the server should use
+        a given pool, it will be able to allocate also first (typically network
+        address) and the last (typically broadcast address) address from that pool.
+        In the aforementioned example of pool 192.0.3.0/24, both 192.0.3.0 and
+        192.0.3.255 addresses may be assigned as well. This may be invalid in some
+        network configurations. If you want to avoid this, please use min-max notation.
+      </para>
+
       <para>
-        At this stage of development, the only way to alter the server
-        configuration is to modify its source code. To do so, please
-        edit src/bin/dhcp4/dhcp4_srv.cc file and modify following
-        parameters and recompile:
+        Note: Although configuration is now accepted, it is not internally used
+        by they server yet.  At this stage of development, the only way to alter
+        server configuration is to modify its source code. To do so, please edit
+        src/bin/dhcp6/dhcp4_srv.cc file, modify the following parameters and
+        recompile:
         <screen>
 const std::string HARDCODED_LEASE = "192.0.2.222"; // assigned lease
 const std::string HARDCODED_NETMASK = "255.255.255.0";
@@ -3423,7 +3501,7 @@ const std::string HARDCODED_DNS_SERVER = "192.0.2.2";
 const std::string HARDCODED_DOMAIN_NAME = "isc.example.com";
 const std::string HARDCODED_SERVER_ID = "192.0.2.1";</screen>
 
-        Lease database and configuration support is planned for 2012.
+        Lease database and configuration support is planned for end of 2012.
       </para>
     </section>
 
diff --git a/src/bin/dhcp4/Makefile.am b/src/bin/dhcp4/Makefile.am
index e0c97d1..c896591 100644
--- a/src/bin/dhcp4/Makefile.am
+++ b/src/bin/dhcp4/Makefile.am
@@ -44,6 +44,7 @@ pkglibexec_PROGRAMS = b10-dhcp4
 
 b10_dhcp4_SOURCES  = main.cc
 b10_dhcp4_SOURCES += ctrl_dhcp4_srv.cc ctrl_dhcp4_srv.h
+b10_dhcp4_SOURCES += config_parser.cc config_parser.h
 b10_dhcp4_SOURCES += dhcp4_log.cc dhcp4_log.h
 b10_dhcp4_SOURCES += dhcp4_srv.cc dhcp4_srv.h
 
@@ -57,6 +58,7 @@ b10_dhcp4_CXXFLAGS = -Wno-unused-parameter
 endif
 
 b10_dhcp4_LDADD  = $(top_builddir)/src/lib/dhcp/libb10-dhcp++.la
+b10_dhcp4_LDADD += $(top_builddir)/src/lib/dhcpsrv/libb10-dhcpsrv.la
 b10_dhcp4_LDADD += $(top_builddir)/src/lib/exceptions/libb10-exceptions.la
 b10_dhcp4_LDADD += $(top_builddir)/src/lib/asiolink/libb10-asiolink.la
 b10_dhcp4_LDADD += $(top_builddir)/src/lib/log/libb10-log.la
diff --git a/src/bin/dhcp4/config_parser.cc b/src/bin/dhcp4/config_parser.cc
new file mode 100644
index 0000000..aa2fa4f
--- /dev/null
+++ b/src/bin/dhcp4/config_parser.cc
@@ -0,0 +1,772 @@
+// 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 <config/ccsession.h>
+#include <dhcpsrv/cfgmgr.h>
+#include <dhcp4/config_parser.h>
+#include <dhcp4/dhcp4_log.h>
+#include <boost/foreach.hpp>
+#include <boost/lexical_cast.hpp>
+#include <boost/algorithm/string.hpp>
+#include <limits>
+#include <iostream>
+#include <vector>
+#include <map>
+
+using namespace std;
+using namespace isc::data;
+using namespace isc::asiolink;
+
+namespace isc {
+namespace dhcp {
+
+/// @brief auxiliary type used for storing element name and its parser
+typedef pair<string, ConstElementPtr> ConfigPair;
+
+/// @brief a factory method that will create a parser for a given element name
+typedef Dhcp4ConfigParser* 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 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;
+
+/// @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 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 Dhcp4ConfigParser {
+public:
+
+    /// @brief Constructor
+    ///
+    /// See \ref Dhcp4ConfigParser 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 Dhcp4ConfigParser 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 Dhcp4ConfigParser 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 Dhcp4ConfigParser* 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 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 Dhcp4ConfigParser.
+///
+/// For overview of usability of this generic purpose parser, see
+/// \ref dhcp4-config-inherit page.
+class Uint32Parser : public Dhcp4ConfigParser {
+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) {
+    }
+
+    /// @brief builds parameter value
+    ///
+    /// Parses configuration entry and stores it in a storage. See
+    /// \ref setStorage() for details.
+    ///
+    /// @param value pointer to the content of parsed values
+    /// @throw BadValue if supplied value could not be base to uint32_t
+    virtual void 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);
+
+        (*storage_)[param_name_] = value_;
+    }
+
+    /// @brief does nothing
+    ///
+    /// This method is required for all parsers. The value itself
+    /// is not commited anywhere. Higher level parsers are expected to
+    /// use values stored in the storage, e.g. renew-timer for a given
+    /// subnet is stored in subnet-specific storage. It is not commited
+    /// here, but is rather used by \ref Subnet4ConfigParser when constructing
+    /// the subnet.
+    virtual void commit() {
+    }
+
+    /// @brief factory that constructs Uint32Parser objects
+    ///
+    /// @param param_name name of the parameter to be parsed
+    static Dhcp4ConfigParser* Factory(const std::string& param_name) {
+        return (new Uint32Parser(param_name));
+    }
+
+    /// @brief sets storage for value of this parameter
+    ///
+    /// See \ref dhcp4-config-inherit 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 Dhcp4ConfigParser.
+///
+/// For overview of usability of this generic purpose parser, see
+/// \ref dhcp4-config-inherit page.
+class StringParser : public Dhcp4ConfigParser {
+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) {
+    }
+
+    /// @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_, "\"");
+
+        (*storage_)[param_name_] = value_;
+    }
+
+    /// @brief does nothing
+    ///
+    /// This method is required for all parser. The value itself
+    /// is not commited anywhere. Higher level parsers are expected to
+    /// use values stored in the storage, e.g. renew-timer for a given
+    /// subnet is stored in subnet-specific storage. It is not commited
+    /// here, but is rather used by its parent parser when constructing
+    /// an object, e.g. the subnet.
+    virtual void commit() {
+    }
+
+    /// @brief factory that constructs StringParser objects
+    ///
+    /// @param param_name name of the parameter to be parsed
+    static Dhcp4ConfigParser* Factory(const std::string& param_name) {
+        return (new StringParser(param_name));
+    }
+
+    /// @brief sets storage for value of this parameter
+    ///
+    /// See \ref dhcp4-config-inherit 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 Dhcp4ConfigParser {
+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 Dhcp4ConfigParser* 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 Dhcp4ConfigParser {
+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 Dhcp4ConfigError 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(Dhcp4ConfigError, "Failed to parse pool "
+                              "definition: " << text_pool->stringValue());
+                }
+
+                Pool4Ptr pool(new Pool4(addr, len));
+                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));
+
+                pools_->push_back(pool);
+                continue;
+            }
+
+            isc_throw(Dhcp4ConfigError, "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 dhcp4-config-inherit for details.
+    ///
+    /// @param storage pointer to the storage container
+    void setStorage(PoolStorage* storage) {
+        pools_ = storage;
+    }
+
+    /// @brief does nothing.
+    ///
+    /// This method is required for all parsers. The value itself
+    /// is not commited anywhere. Higher level parsers (for subnet) are expected
+    /// to use values stored in the storage.
+    virtual void commit() {}
+
+    /// @brief factory that constructs PoolParser objects
+    ///
+    /// @param param_name name of the parameter to be parsed
+    static Dhcp4ConfigParser* 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_;
+};
+
+/// @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 Dhcp4ConfigParser {
+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.
+    }
+
+    /// @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));
+
+            // if this is an Uint32 parser, tell it to store the values
+            // in values_, rather than in global storage
+            boost::shared_ptr<Uint32Parser> uint_parser =
+                boost::dynamic_pointer_cast<Uint32Parser>(parser);
+            if (uint_parser) {
+                uint_parser->setStorage(&uint32_values_);
+            } else {
+
+                boost::shared_ptr<StringParser> string_parser =
+                    boost::dynamic_pointer_cast<StringParser>(parser);
+                if (string_parser) {
+                    string_parser->setStorage(&string_values_);
+                } else {
+
+                    boost::shared_ptr<PoolParser> pool_parser =
+                        boost::dynamic_pointer_cast<PoolParser>(parser);
+                    if (pool_parser) {
+                        pool_parser->setStorage(&pools_);
+                    }
+                }
+            }
+
+            parser->build(param.second);
+            parsers_.push_back(parser);
+        }
+
+        // Ok, we now have subnet parsed
+    }
+
+    /// @brief commits received configuration.
+    ///
+    /// 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 Dhcp4ConfigError if there are any issues encountered during commit
+    void commit() {
+
+        StringStorage::const_iterator it = string_values_.find("subnet");
+        if (it == string_values_.end()) {
+            isc_throw(Dhcp4ConfigError,
+                      "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(Dhcp4ConfigError,
+                      "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> valid = getParam("valid-lifetime");
+
+        /// @todo: Convert this to logger once the parser is working reliably
+        stringstream tmp;
+        tmp << addr.toText() << "/" << (int)len
+            << " with params t1=" << t1 << ", t2=" << t2 << ", valid=" << valid;
+
+        LOG_INFO(dhcp4_logger, DHCP4_CONFIG_NEW_SUBNET).arg(tmp.str());
+
+        Subnet4Ptr subnet(new Subnet4(addr, len, t1, t2, valid));
+
+        for (PoolStorage::iterator it = pools_.begin(); it != pools_.end(); ++it) {
+            subnet->addPool4(*it);
+        }
+
+        CfgMgr::instance().addSubnet4(subnet);
+    }
+
+private:
+
+    /// @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
+    Dhcp4ConfigParser* 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;
+
+        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 Dhcp4ConfigError when requested parameter is not present
+    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(Dhcp4ConfigError, "Mandatory parameter " << name
+                      << " missing (no global default and no subnet-"
+                      << "specific 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_;
+
+    /// parsers are stored here
+    ParserCollection parsers_;
+};
+
+/// @brief this class parses list of subnets
+///
+/// This is a wrapper parser that handles the whole list of Subnet4
+/// definitions. It iterates over all entries and creates Subnet4ConfigParser
+/// for each entry.
+class Subnets4ListConfigParser : public Dhcp4ConfigParser {
+public:
+
+    /// @brief constructor
+    ///
+    Subnets4ListConfigParser(const std::string&) {
+        /// parameter name is ignored
+    }
+
+    /// @brief parses contents of the list
+    ///
+    /// Iterates over all entries on the list and creates Subnet4ConfigParser
+    /// for each entry.
+    ///
+    /// @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.
+    void commit() {
+        // @todo: Implement more subtle reconfiguration than toss
+        // the old one and replace with the new one.
+
+        // remove old subnets
+        CfgMgr::instance().deleteSubnets4();
+
+        BOOST_FOREACH(ParserPtr subnet, subnets_) {
+            subnet->commit();
+        }
+
+    }
+
+    /// @brief Returns Subnet4ListConfigParser object
+    /// @param param_name name of the parameter
+    /// @return Subnets4ListConfigParser object
+    static Dhcp4ConfigParser* Factory(const std::string& param_name) {
+        return (new Subnets4ListConfigParser(param_name));
+    }
+
+    /// @brief collection of subnet parsers.
+    ParserCollection subnets_;
+};
+
+/// @brief creates global parsers
+///
+/// This method creates global parsers that parse global parameters, i.e.
+/// those that take format of Dhcp4/param1, Dhcp4/param2 and so forth.
+///
+/// @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
+Dhcp4ConfigParser* 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["version"] = StringParser::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: Global configuration parameter not supported: "
+                  << config_id);
+    }
+    return (f->second(config_id));
+}
+
+isc::data::ConstElementPtr
+configureDhcp4Server(Dhcpv4Srv& , 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")
+    string config_details;
+
+    LOG_DEBUG(dhcp4_logger, DBG_DHCP4_COMMAND, DHCP4_CONFIG_START).arg(config_set->str());
+
+    ParserCollection parsers;
+    try {
+        BOOST_FOREACH(ConfigPair config_pair, config_set->mapValue()) {
+
+            ParserPtr parser(createGlobalDhcp4ConfigParser(config_pair.first));
+            parser->build(config_pair.second);
+            parsers.push_back(parser);
+        }
+    } catch (const isc::Exception& ex) {
+        ConstElementPtr answer = isc::config::createAnswer(1,
+                                 string("Configuration parsing failed:") + ex.what());
+        return (answer);
+    } catch (...) {
+        // for things like bad_cast in boost::lexical_cast
+        ConstElementPtr answer = isc::config::createAnswer(1,
+                                 string("Configuration parsing failed"));
+    }
+
+    try {
+        BOOST_FOREACH(ParserPtr parser, parsers) {
+            parser->commit();
+        }
+    }
+    catch (const isc::Exception& ex) {
+        ConstElementPtr answer = isc::config::createAnswer(2,
+                                 string("Configuration commit failed:") + ex.what());
+        return (answer);
+    } catch (...) {
+        // for things like bad_cast in boost::lexical_cast
+        ConstElementPtr answer = isc::config::createAnswer(2,
+                                 string("Configuration commit failed"));
+    }
+
+    LOG_INFO(dhcp4_logger, DHCP4_CONFIG_COMPLETE).arg(config_details);
+
+    ConstElementPtr answer = isc::config::createAnswer(0, "Configuration commited.");
+    return (answer);
+}
+
+}; // 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
new file mode 100644
index 0000000..2ee9cf6
--- /dev/null
+++ b/src/bin/dhcp4/config_parser.h
@@ -0,0 +1,167 @@
+// 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 <exceptions/exceptions.h>
+#include <cc/data.h>
+#include <string>
+
+#ifndef DHCP4_CONFIG_PARSER_H
+#define DHCP4_CONFIG_PARSER_H
+
+/// @todo: This header file and its .cc counterpart are very similar between
+/// DHCPv4 and DHCPv6. They should be merged. A ticket #2355.
+
+namespace isc {
+namespace dhcp {
+
+class Dhcpv4Srv;
+
+/// @brief a collection of elements that store uint32 values (e.g. renew-timer = 900)
+typedef std::map<std::string, uint32_t> Uint32Storage;
+
+/// @brief a collection of elements that store string values
+typedef std::map<std::string, std::string> StringStorage;
+
+/// An exception that is thrown if an error occurs while configuring an
+/// \c Dhcpv4Srv object.
+class Dhcp4ConfigError : public isc::Exception {
+public:
+
+    /// @brief constructor
+    ///
+    /// @param file name of the file, where exception occurred
+    /// @param line line of the file, where exception occurred
+    /// @param what text description of the issue that caused exception
+    Dhcp4ConfigError(const char* file, size_t line, const char* what)
+        : isc::Exception(file, line, what) {}
+};
+
+/// @brief Base abstract class for all DHCPv4 parsers
+///
+/// Each instance of a class derived from this class parses one specific config
+/// element. Sometimes elements are simple (e.g. a string) and sometimes quite
+/// complex (e.g. a subnet). In such case, it is likely that a parser will
+/// spawn child parsers to parse child elements in the configuration.
+/// @todo: Merge this class with DhcpConfigParser in src/bin/dhcp6
+class Dhcp4ConfigParser {
+    ///
+    /// \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:
+
+    // Private construtor and assignment operator assures that nobody
+    // will be able to copy or assign a parser. There are no defined
+    // bodies for them.
+    Dhcp4ConfigParser(const Dhcp4ConfigParser& source);
+    Dhcp4ConfigParser& operator=(const Dhcp4ConfigParser& 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).
+    Dhcp4ConfigParser() {}
+public:
+    /// The destructor.
+    virtual ~Dhcp4ConfigParser() {}
+    //@}
+
+    /// \brief 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.
+    ///
+    /// 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 in the
+    /// life of the object. 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;
+
+    /// \brief 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 configureDhcp4Server(), 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;
+};
+
+/// @brief a pointer to configuration parser
+typedef boost::shared_ptr<Dhcp4ConfigParser> ParserPtr;
+
+/// @brief a collection of parsers
+///
+/// This container is used to store pointer to parsers for a given scope.
+typedef std::vector<ParserPtr> ParserCollection;
+
+
+/// \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.
+/// It provides the strong exception guarantee as long as the underlying
+/// derived class implementations of \c DhcpConfigParser 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 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 method does not throw. It catches all exceptions and returns them as
+/// reconfiguration statuses. It may return the following response codes:
+/// 0 - configuration successful
+/// 1 - malformed configuration (parsing failed)
+/// 2 - logical error (parsing was successful, but the values are invalid)
+///
+/// @param config_set a new configuration (JSON) for DHCPv4 server
+/// @return answer that contains result of reconfiguration
+isc::data::ConstElementPtr
+configureDhcp4Server(Dhcpv4Srv&,
+                     isc::data::ConstElementPtr config_set);
+
+}; // end of isc::dhcp namespace
+}; // end of isc namespace
+
+#endif // DHCP4_CONFIG_PARSER_H
diff --git a/src/bin/dhcp4/ctrl_dhcp4_srv.cc b/src/bin/dhcp4/ctrl_dhcp4_srv.cc
index b02bf72..20eedeb 100644
--- a/src/bin/dhcp4/ctrl_dhcp4_srv.cc
+++ b/src/bin/dhcp4/ctrl_dhcp4_srv.cc
@@ -22,8 +22,11 @@
 #include <dhcp4/ctrl_dhcp4_srv.h>
 #include <dhcp4/dhcp4_log.h>
 #include <dhcp4/spec_config.h>
+#include <dhcp4/config_parser.h>
 #include <exceptions/exceptions.h>
 #include <util/buffer.h>
+#include <cassert>
+#include <iostream>
 
 #include <cassert>
 #include <iostream>
@@ -46,8 +49,14 @@ ConstElementPtr
 ControlledDhcpv4Srv::dhcp4ConfigHandler(ConstElementPtr new_config) {
     LOG_DEBUG(dhcp4_logger, DBG_DHCP4_COMMAND, DHCP4_CONFIG_UPDATE)
               .arg(new_config->str());
-    ConstElementPtr answer = isc::config::createAnswer(0,
-                             "Thank you for sending config.");
+    if (server_) {
+        return (configureDhcp4Server(*server_, new_config));
+    }
+
+    // That should never happen as we install config_handler after we instantiate
+    // the server.
+    ConstElementPtr answer = isc::config::createAnswer(1,
+           "Configuration rejected, server is during startup/shutdown phase.");
     return (answer);
 }
 
@@ -100,13 +109,25 @@ void ControlledDhcpv4Srv::establishSession() {
               .arg(specfile);
     cc_session_ = new Session(io_service_.get_io_service());
     config_session_ = new ModuleCCSession(specfile, *cc_session_,
-                                          dhcp4ConfigHandler,
+                                          NULL,
                                           dhcp4CommandHandler, false);
     config_session_->start();
 
+    // We initially create ModuleCCSession() without configHandler, as
+    // the session module is too eager to send partial configuration.
+    // We want to get the full configuration, so we explicitly call
+    // getFullConfig() and then pass it to our configHandler.
+    config_session_->setConfigHandler(dhcp4ConfigHandler);
+
+    try {
+        configureDhcp4Server(*this, config_session_->getFullConfig());
+    } catch (const Dhcp4ConfigError& ex) {
+        LOG_ERROR(dhcp4_logger, DHCP4_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 dhcpv4Session.
+    /// fully explained in \ref dhcp4-session.
     int ctrl_socket = cc_session_->getSocketDesc();
     LOG_DEBUG(dhcp4_logger, DBG_DHCP4_START, DHCP4_CCSESSION_STARTED)
               .arg(ctrl_socket);
diff --git a/src/bin/dhcp4/ctrl_dhcp4_srv.h b/src/bin/dhcp4/ctrl_dhcp4_srv.h
index 9fd7668..c1a26cc 100644
--- a/src/bin/dhcp4/ctrl_dhcp4_srv.h
+++ b/src/bin/dhcp4/ctrl_dhcp4_srv.h
@@ -34,7 +34,7 @@ namespace dhcp {
 /// embedded environments.
 ///
 /// For detailed explanation or relations between main(), ControlledDhcpv4Srv,
-/// Dhcpv4Srv and other classes, see \ref dhcpv4Session.
+/// Dhcpv4Srv and other classes, see \ref dhcp4-session.
 class ControlledDhcpv4Srv : public isc::dhcp::Dhcpv4Srv {
 public:
 
@@ -66,7 +66,7 @@ public:
 
     /// @brief Session callback, processes received commands.
     ///
-    /// @param command_id text represenation of the command (e.g. "shutdown")
+    /// @param command text represenation of the command (e.g. "shutdown")
     /// @param args optional parameters
     ///
     /// @return status of the command
diff --git a/src/bin/dhcp4/dhcp4.dox b/src/bin/dhcp4/dhcp4.dox
new file mode 100644
index 0000000..54f2bd3
--- /dev/null
+++ b/src/bin/dhcp4/dhcp4.dox
@@ -0,0 +1,68 @@
+/**
+ @page dhcp4 DHCPv4 Server Component
+
+BIND10 offers DHCPv4 server implementation. It is implemented as
+b10-dhcp4 component.  Its primary code is located in
+isc::dhcp::Dhcpv4Srv class. It uses \ref libdhcp extensively,
+especially isc::dhcp::Pkt4, isc::dhcp::Option and
+isc::dhcp::IfaceMgr classes. Currently this code offers skeleton
+functionality, i.e. it is able to receive and process incoming
+requests and trasmit responses. However, it does not have database
+management, so it returns only one, hardcoded lease to whoever asks
+for it.
+
+DHCPv4 server component does not support direct traffic (relayed
+only), as support for transmission to hosts without IPv4 address
+assigned is not implemented in IfaceMgr yet.
+
+ at section dhcp4-session BIND10 message queue integration
+
+DHCPv4 server component is now integrated with BIND10 message queue.
+The integration is performed by establishSession() and disconnectSession()
+functions in isc::dhcp::ControlledDhcpv4Srv class. main() method deifined
+in the src/bin/dhcp4/main.cc file instantiates isc::dhcp::ControlledDhcpv4Srv
+class that establishes connection with msgq and install necessary handlers
+for receiving commands and configuration updates. It is derived from
+a base isc::dhcp::Dhcpv4Srv class that implements DHCPv4 server functionality,
+without any controlling mechanisms.
+
+ControlledDhcpv4Srv instantiates several components to make management
+session possible. In particular, isc::cc::Session cc_session
+object uses ASIO for establishing connection. It registers its socket
+in isc::asiolink::IOService io_service object. Typically, other components
+(e.g. auth or resolver) that use ASIO for their communication, register their
+other sockets in the
+same io_service and then just call io_service.run() method that does
+not return, until one of the callback decides that it is time to shut down
+the whole component cal calls io_service.stop(). DHCPv4 works in a
+different way. It does receive messages using select()
+(see isc::dhcp::IfaceMgr::receive4()), which is incompatible with ASIO.
+To solve this problem, socket descriptor is extracted from cc_session
+object and is passed to IfaceMgr by using isc::dhcp::IfaceMgr::set_session_socket().
+IfaceMgr then uses this socket in its select() call. If there is some
+data to be read, it calls registered callback that is supposed to
+read and process incoming data.
+
+This somewhat complicated approach is needed for a simple reason. In
+embedded deployments there will be no message queue. Not referring directly
+to anything related to message queue in isc::dhcp::Dhcpv4Srv and
+isc::dhcp::IfaceMgr classes brings in two benefits. First, the can
+be used with and without message queue. Second benefit is related to the
+first one: \ref libdhcp is supposed to be simple and robust and not require
+many dependencies. One notable example of a use case that benefits from
+this approach is a perfdhcp tool. Finally, the idea is that it should be
+possible to instantiate Dhcpv4Srv object directly, thus getting a server
+that does not support msgq. That is useful for embedded environments.
+It may also be useful in validation.
+
+ at section dhcp4-config-parser Configuration Parser in DHCPv4
+
+This parser follows exactly the same logic as its DHCPv6 counterpart.
+See \ref dhcp6-config-parser.
+
+ at section dhcp4-config-inherit DHCPv4 configuration inheritance
+
+Configuration inheritance in DHCPv4 follows exactly the same logic as its DHCPv6
+counterpart. See \ref dhcp6-config-inherit.
+
+*/
diff --git a/src/bin/dhcp4/dhcp4.spec b/src/bin/dhcp4/dhcp4.spec
index 7584b48..d5066e8 100644
--- a/src/bin/dhcp4/dhcp4.spec
+++ b/src/bin/dhcp4/dhcp4.spec
@@ -4,9 +4,85 @@
     "module_description": "DHCPv4 server daemon",
     "config_data": [
       { "item_name": "interface",
-        "item_type": "string",
+        "item_type": "list",
         "item_optional": false,
-        "item_default": "eth0"
+        "item_default": [ "all" ],
+        "list_item_spec":
+        {
+          "item_name": "interface_name",
+          "item_type": "string",
+          "item_optional": false,
+          "item_default": "all"
+        }
+      } ,
+
+      { "item_name": "renew-timer",
+        "item_type": "integer",
+        "item_optional": false,
+        "item_default": 1000
+      },
+
+      { "item_name": "rebind-timer",
+        "item_type": "integer",
+        "item_optional": false,
+        "item_default": 2000
+      },
+
+      { "item_name": "valid-lifetime",
+        "item_type": "integer",
+        "item_optional": false,
+        "item_default": 4000
+      },
+
+      { "item_name": "subnet4",
+        "item_type": "list",
+        "item_optional": false,
+        "item_default": [],
+        "list_item_spec":
+        {
+            "item_name": "single-subnet4",
+            "item_type": "map",
+            "item_optional": false,
+            "item_default": {},
+            "map_item_spec": [
+
+                { "item_name": "subnet",
+                  "item_type": "string",
+                  "item_optional": false,
+                  "item_default": ""
+                },
+
+                { "item_name": "renew-timer",
+                  "item_type": "integer",
+                  "item_optional": false,
+                  "item_default": 1000
+                },
+
+                { "item_name": "rebind-timer",
+                  "item_type": "integer",
+                  "item_optional": false,
+                  "item_default": 2000
+                },
+
+                { "item_name": "valid-lifetime",
+                  "item_type": "integer",
+                  "item_optional": false,
+                  "item_default": 7200
+                },
+                { "item_name": "pool",
+                  "item_type": "list",
+                  "item_optional": false,
+                  "item_default": [],
+                    "list_item_spec":
+                    {
+                        "item_name": "type",
+                        "item_type": "string",
+                        "item_optional": false,
+                        "item_default": ""
+                    }
+                }
+            ]
+        }
       }
     ],
     "commands": [
diff --git a/src/bin/dhcp4/dhcp4_messages.mes b/src/bin/dhcp4/dhcp4_messages.mes
index 392b332..994f004 100644
--- a/src/bin/dhcp4/dhcp4_messages.mes
+++ b/src/bin/dhcp4/dhcp4_messages.mes
@@ -26,10 +26,30 @@ to establish a session with the BIND 10 control channel.
 A debug message listing the command (and possible arguments) received
 from the BIND 10 control system by the IPv4 DHCP server.
 
+% DHCP4_CONFIG_LOAD_FAIL failed to load configuration: %1
+This critical error message indicates that the initial DHCPv4
+configuration has failed. The server will start, but nothing will be
+served until the configuration has been corrected.
+
 % DHCP4_CONFIG_UPDATE updated configuration received: %1
 A debug message indicating that the IPv4 DHCP server has received an
 updated configuration from the BIND 10 configuration system.
 
+% DHCP4_CONFIG_START DHCPv4 server is processing the following configuration: %1
+This is a debug message that is issued every time the server receives a
+configuration. That happens at start up and also when a server configuration
+change is committed by the administrator.
+
+% DHCP4_CONFIG_NEW_SUBNET A new subnet has been added to configuration: %1
+This is an informational message reporting that the configuration has
+been extended to include the specified IPv4 subnet.
+
+% DHCP4_CONFIG_COMPLETE DHCPv4 server has completed configuration: %1
+This is an informational message announcing the successful processing of a
+new configuration. it is output during server startup, and when an updated
+configuration is committed by the administrator.  Additional information
+may be provided.
+
 % DHCP4_NOT_RUNNING IPv4 DHCP server is not running
 A warning message is issued when an attempt is made to shut down the
 IPv4 DHCP server but it is not running.
diff --git a/src/bin/dhcp4/dhcp4_srv.h b/src/bin/dhcp4/dhcp4_srv.h
index 48ccde6..724b351 100644
--- a/src/bin/dhcp4/dhcp4_srv.h
+++ b/src/bin/dhcp4/dhcp4_srv.h
@@ -36,11 +36,11 @@ namespace dhcp {
 /// appropriate responses.
 ///
 /// This class does not support any controlling mechanisms directly.
-/// See derived \ref ControlledDhcv4Srv class for support for
+/// See derived \ref ControlledDhcpv4Srv class for support for
 /// command and configuration updates over msgq.
 ///
 /// For detailed explanation or relations between main(), ControlledDhcpv4Srv,
-/// Dhcpv4Srv and other classes, see \ref dhcpv4Session.
+/// Dhcpv4Srv and other classes, see \ref dhcp4-session.
 class Dhcpv4Srv : public boost::noncopyable {
 
     public:
diff --git a/src/bin/dhcp4/tests/Makefile.am b/src/bin/dhcp4/tests/Makefile.am
index ddc3000..e601919 100644
--- a/src/bin/dhcp4/tests/Makefile.am
+++ b/src/bin/dhcp4/tests/Makefile.am
@@ -49,9 +49,11 @@ TESTS += dhcp4_unittests
 
 dhcp4_unittests_SOURCES = ../dhcp4_srv.h ../dhcp4_srv.cc ../ctrl_dhcp4_srv.cc
 dhcp4_unittests_SOURCES += ../dhcp4_log.h ../dhcp4_log.cc
+dhcp4_unittests_SOURCES += ../config_parser.cc ../config_parser.h
 dhcp4_unittests_SOURCES += dhcp4_unittests.cc
 dhcp4_unittests_SOURCES += dhcp4_srv_unittest.cc
 dhcp4_unittests_SOURCES += ctrl_dhcp4_srv_unittest.cc
+dhcp4_unittests_SOURCES += config_parser_unittest.cc
 nodist_dhcp4_unittests_SOURCES = ../dhcp4_messages.h ../dhcp4_messages.cc
 
 if USE_CLANGPP
@@ -65,6 +67,7 @@ dhcp4_unittests_LDFLAGS = $(AM_LDFLAGS) $(GTEST_LDFLAGS)
 dhcp4_unittests_LDADD = $(GTEST_LDADD)
 dhcp4_unittests_LDADD += $(top_builddir)/src/lib/asiolink/libb10-asiolink.la
 dhcp4_unittests_LDADD += $(top_builddir)/src/lib/dhcp/libb10-dhcp++.la
+dhcp4_unittests_LDADD += $(top_builddir)/src/lib/dhcpsrv/libb10-dhcpsrv.la
 dhcp4_unittests_LDADD += $(top_builddir)/src/lib/exceptions/libb10-exceptions.la
 dhcp4_unittests_LDADD += $(top_builddir)/src/lib/log/libb10-log.la
 dhcp4_unittests_LDADD += $(top_builddir)/src/lib/asiolink/libb10-asiolink.la
diff --git a/src/bin/dhcp4/tests/config_parser_unittest.cc b/src/bin/dhcp4/tests/config_parser_unittest.cc
new file mode 100644
index 0000000..22307df
--- /dev/null
+++ b/src/bin/dhcp4/tests/config_parser_unittest.cc
@@ -0,0 +1,294 @@
+// 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 <config.h>
+
+#include <arpa/inet.h>
+#include <gtest/gtest.h>
+
+#include <dhcp4/dhcp4_srv.h>
+#include <dhcp4/config_parser.h>
+#include <config/ccsession.h>
+#include <dhcpsrv/subnet.h>
+#include <dhcpsrv/cfgmgr.h>
+#include <iostream>
+#include <fstream>
+#include <sstream>
+#include <limits.h>
+
+using namespace std;
+using namespace isc;
+using namespace isc::dhcp;
+using namespace isc::asiolink;
+using namespace isc::data;
+using namespace isc::config;
+
+namespace isc {
+namespace dhcp {
+extern Uint32Storage uint32_defaults;
+}
+}
+
+namespace {
+
+class Dhcp4ParserTest : public ::testing::Test {
+public:
+    Dhcp4ParserTest()
+    :rcode_(-1) {
+        // Open port 0 means to not do anything at all. We don't want to
+        // deal with sockets here, just check if configuration handling
+        // is sane.
+        srv_ = new Dhcpv4Srv(0);
+    }
+
+    // Checks if global parameter of name have expected_value
+    void checkGlobalUint32(string name, uint32_t expected_value) {
+        Uint32Storage::const_iterator it = uint32_defaults.find(name);
+        if (it == uint32_defaults.end()) {
+            ADD_FAILURE() << "Expected uint32 with name " << name
+                          << " not found";
+            return;
+        }
+        EXPECT_EQ(expected_value, it->second);
+    }
+
+    // Checks if config_result (result of DHCP server configuration) has
+    // expected code (0 for success, other for failures).
+    // Also stores result in rcode_ and comment_.
+    void checkResult(ConstElementPtr status, int expected_code) {
+        ASSERT_TRUE(status);
+        comment_ = parseAnswer(rcode_, status);
+        EXPECT_EQ(expected_code, rcode_);
+    }
+
+    ~Dhcp4ParserTest() {
+        delete srv_;
+    };
+
+    Dhcpv4Srv* srv_;
+
+    int rcode_;
+    ConstElementPtr comment_;
+};
+
+// Goal of this test is a verification if a very simple config update
+// with just a bumped version number. That's the simplest possible
+// config update.
+TEST_F(Dhcp4ParserTest, version) {
+
+    ConstElementPtr x;
+
+    EXPECT_NO_THROW(x = configureDhcp4Server(*srv_,
+                    Element::fromJSON("{\"version\": 0}")));
+
+    // returned value must be 0 (configuration accepted)
+    checkResult(x, 0);
+}
+
+/// The goal of this test is to verify that the code accepts only
+/// valid commands and malformed or unsupported parameters are rejected.
+TEST_F(Dhcp4ParserTest, bogusCommand) {
+
+    ConstElementPtr x;
+
+    EXPECT_NO_THROW(x = configureDhcp4Server(*srv_,
+                    Element::fromJSON("{\"bogus\": 5}")));
+
+    // returned value must be 1 (configuration parse error)
+    checkResult(x, 1);
+}
+
+/// The goal of this test is to verify if wrongly defined subnet will
+/// be rejected. Properly defined subnet must include at least one
+/// pool definition.
+TEST_F(Dhcp4ParserTest, emptySubnet) {
+
+    ConstElementPtr status;
+
+    EXPECT_NO_THROW(status = configureDhcp4Server(*srv_,
+                    Element::fromJSON("{ \"interface\": [ \"all\" ],"
+                                      "\"rebind-timer\": 2000, "
+                                      "\"renew-timer\": 1000, "
+                                      "\"subnet4\": [  ], "
+                                      "\"valid-lifetime\": 4000 }")));
+
+    // returned value should be 0 (success)
+    checkResult(status, 0);
+
+    checkGlobalUint32("rebind-timer", 2000);
+    checkGlobalUint32("renew-timer", 1000);
+    checkGlobalUint32("valid-lifetime", 4000);
+}
+
+/// The goal of this test is to verify if defined subnet uses global
+/// parameter timer definitions.
+TEST_F(Dhcp4ParserTest, subnetGlobalDefaults) {
+
+    ConstElementPtr status;
+
+    string config = "{ \"interface\": [ \"all\" ],"
+        "\"rebind-timer\": 2000, "
+        "\"renew-timer\": 1000, "
+        "\"subnet4\": [ { "
+        "    \"pool\": [ \"192.0.2.1 - 192.0.2.100\" ],"
+        "    \"subnet\": \"192.0.2.0/24\" } ],"
+        "\"valid-lifetime\": 4000 }";
+    cout << config << endl;
+
+    ElementPtr json = Element::fromJSON(config);
+
+    EXPECT_NO_THROW(status = configureDhcp4Server(*srv_, json));
+
+    // check if returned status is OK
+    checkResult(status, 0);
+
+    // Now check if the configuration was indeed handled and we have
+    // expected pool configured.
+    Subnet4Ptr subnet = CfgMgr::instance().getSubnet4(IOAddress("192.0.2.200"));
+    ASSERT_TRUE(subnet);
+    EXPECT_EQ(1000, subnet->getT1());
+    EXPECT_EQ(2000, subnet->getT2());
+    EXPECT_EQ(4000, subnet->getValid());
+}
+
+// This test checks if it is possible to override global values
+// on a per subnet basis.
+TEST_F(Dhcp4ParserTest, subnetLocal) {
+
+    ConstElementPtr status;
+
+    string config = "{ \"interface\": [ \"all\" ],"
+        "\"rebind-timer\": 2000, "
+        "\"renew-timer\": 1000, "
+        "\"subnet4\": [ { "
+        "    \"pool\": [ \"192.0.2.1 - 192.0.2.100\" ],"
+        "    \"renew-timer\": 1, "
+        "    \"rebind-timer\": 2, "
+        "    \"valid-lifetime\": 4,"
+        "    \"subnet\": \"192.0.2.0/24\" } ],"
+        "\"valid-lifetime\": 4000 }";
+    cout << config << endl;
+
+    ElementPtr json = Element::fromJSON(config);
+
+    EXPECT_NO_THROW(status = configureDhcp4Server(*srv_, json));
+
+    // returned value should be 0 (configuration success)
+    checkResult(status, 0);
+
+    Subnet4Ptr subnet = CfgMgr::instance().getSubnet4(IOAddress("192.0.2.200"));
+    ASSERT_TRUE(subnet);
+    EXPECT_EQ(1, subnet->getT1());
+    EXPECT_EQ(2, subnet->getT2());
+    EXPECT_EQ(4, subnet->getValid());
+}
+
+// Test verifies that a subnet with pool values that do not belong to that
+// pool are rejected.
+TEST_F(Dhcp4ParserTest, poolOutOfSubnet) {
+
+    ConstElementPtr status;
+
+    string config = "{ \"interface\": [ \"all\" ],"
+        "\"rebind-timer\": 2000, "
+        "\"renew-timer\": 1000, "
+        "\"subnet4\": [ { "
+        "    \"pool\": [ \"192.0.4.0/28\" ],"
+        "    \"subnet\": \"192.0.2.0/24\" } ],"
+        "\"valid-lifetime\": 4000 }";
+    cout << config << endl;
+
+    ElementPtr json = Element::fromJSON(config);
+
+    EXPECT_NO_THROW(status = configureDhcp4Server(*srv_, json));
+
+    // returned value must be 2 (values error)
+    // as the pool does not belong to that subnet
+    checkResult(status, 2);
+}
+
+// Goal of this test is to verify if pools can be defined
+// using prefix/length notation. There is no separate test for min-max
+// notation as it was tested in several previous tests.
+TEST_F(Dhcp4ParserTest, poolPrefixLen) {
+
+    ConstElementPtr status;
+
+    string config = "{ \"interface\": [ \"all\" ],"
+        "\"rebind-timer\": 2000, "
+        "\"renew-timer\": 1000, "
+        "\"subnet4\": [ { "
+        "    \"pool\": [ \"192.0.2.128/28\" ],"
+        "    \"subnet\": \"192.0.2.0/24\" } ],"
+        "\"valid-lifetime\": 4000 }";
+    cout << config << endl;
+
+    ElementPtr json = Element::fromJSON(config);
+
+    EXPECT_NO_THROW(status = configureDhcp4Server(*srv_, json));
+
+    // returned value must be 0 (configuration accepted)
+    checkResult(status, 0);
+
+    Subnet4Ptr subnet = CfgMgr::instance().getSubnet4(IOAddress("192.0.2.200"));
+    ASSERT_TRUE(subnet);
+    EXPECT_EQ(1000, subnet->getT1());
+    EXPECT_EQ(2000, subnet->getT2());
+    EXPECT_EQ(4000, subnet->getValid());
+}
+
+/// This test checks if Uint32Parser can really parse the whole range
+/// and properly err of out of range values. As we can't call Uint32Parser
+/// directly, we are exploiting the fact that it is used to parse global
+/// parameter renew-timer and the results are stored in uint32_defaults.
+TEST_F(Dhcp4ParserTest, Uint32Parser) {
+
+    ConstElementPtr status;
+
+    // CASE 1: 0 - minimum value, should work
+    EXPECT_NO_THROW(status = configureDhcp4Server(*srv_,
+                    Element::fromJSON("{\"version\": 0,"
+                                      "\"renew-timer\": 0}")));
+
+    // returned value must be ok (0 is a proper value)
+    checkResult(status, 0);
+    checkGlobalUint32("renew-timer", 0);
+
+    // CASE 2: 4294967295U (UINT_MAX) should work as well
+    EXPECT_NO_THROW(status = configureDhcp4Server(*srv_,
+                    Element::fromJSON("{\"version\": 0,"
+                                      "\"renew-timer\": 4294967295}")));
+
+    // returned value must be ok (0 is a proper value)
+    checkResult(status, 0);
+    checkGlobalUint32("renew-timer", 4294967295U);
+
+    // CASE 3: 4294967296U (UINT_MAX + 1) should not work
+    EXPECT_NO_THROW(status = configureDhcp4Server(*srv_,
+                    Element::fromJSON("{\"version\": 0,"
+                                      "\"renew-timer\": 4294967296}")));
+
+    // returned value must be rejected (1 configuration error)
+    checkResult(status, 1);
+
+    // CASE 4: -1 (UINT_MIN -1 ) should not work
+    EXPECT_NO_THROW(status = configureDhcp4Server(*srv_,
+                    Element::fromJSON("{\"version\": 0,"
+                                      "\"renew-timer\": -1}")));
+
+    // returned value must be rejected (1 configuration error)
+    checkResult(status, 1);
+}
+
+};
diff --git a/src/bin/dhcp6/config_parser.cc b/src/bin/dhcp6/config_parser.cc
index 1c55649..3db6aec 100644
--- a/src/bin/dhcp6/config_parser.cc
+++ b/src/bin/dhcp6/config_parser.cc
@@ -129,7 +129,7 @@ public:
         return (new DebugParser(param_name));
     }
 
-protected:
+private:
     /// name of the parsed parameter
     std::string param_name_;
 
@@ -147,7 +147,7 @@ protected:
 /// in its base class, \ref DhcpConfigParser.
 ///
 /// For overview of usability of this generic purpose parser, see
-/// \ref dhcpv6-config-inherit page.
+/// \ref dhcp6-config-inherit page.
 ///
 /// @todo this class should be turned into the template class which
 /// will handle all uintX_types of data (see ticket #2415).
@@ -163,7 +163,7 @@ public:
     /// @brief builds parameter value
     ///
     /// Parses configuration entry and stores it in a storage. See
-    /// \ref setStorage() for details.
+    /// \ref Uint32Parser::setStorage() for details.
     ///
     /// @param value pointer to the content of parsed values
     virtual void build(ConstElementPtr value) {
@@ -222,14 +222,14 @@ public:
 
     /// @brief sets storage for value of this parameter
     ///
-    /// See \ref dhcpv6-config-inherit for details.
+    /// See \ref dhcp6-config-inherit for details.
     ///
     /// @param storage pointer to the storage container
     void setStorage(Uint32Storage* storage) {
         storage_ = storage;
     }
 
-protected:
+private:
     /// pointer to the storage, where parsed value will be stored
     Uint32Storage* storage_;
 
@@ -250,7 +250,7 @@ protected:
 /// in its base class, \ref DhcpConfigParser.
 ///
 /// For overview of usability of this generic purpose parser, see
-/// \ref dhcpv6-config-inherit page.
+/// \ref dhcp6-config-inherit page.
 class StringParser : public DhcpConfigParser {
 public:
 
@@ -269,6 +269,7 @@ public:
     virtual void build(ConstElementPtr value) {
         value_ = value->str();
         boost::erase_all(value_, "\"");
+
         // If a given parameter already exists in the storage we override
         // its value. If it doesn't we insert a new element.
         (*storage_)[param_name_] = value_;
@@ -293,14 +294,14 @@ public:
 
     /// @brief sets storage for value of this parameter
     ///
-    /// See \ref dhcpv6-config-inherit for details.
+    /// See \ref dhcp6-config-inherit for details.
     ///
     /// @param storage pointer to the storage container
     void setStorage(StringStorage* storage) {
         storage_ = storage;
     }
 
-protected:
+private:
     /// pointer to the storage, where parsed value will be stored
     StringStorage* storage_;
 
@@ -329,9 +330,10 @@ public:
     /// "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(NotImplemented, "Internal error. Interface configuration "
+            isc_throw(BadValue, "Internal error. Interface configuration "
                       "parser called for the wrong parameter: " << param_name);
         }
     }
@@ -361,7 +363,7 @@ public:
         return (new InterfaceListConfigParser(param_name));
     }
 
-protected:
+private:
     /// contains list of network interfaces
     vector<string> interfaces_;
 };
@@ -390,6 +392,8 @@ public:
     /// 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 BadValue if storage was not specified (setStorage() not called)
     void build(ConstElementPtr pools_list) {
         // setStorage() should have been called before build
         if (!pools_) {
@@ -442,7 +446,7 @@ public:
             pos = txt.find("-");
             if (pos != string::npos) {
                 // using min-max notation
-                IOAddress min(txt.substr(0,pos - 1));
+                IOAddress min(txt.substr(0,pos));
                 IOAddress max(txt.substr(pos + 1));
 
                 Pool6Ptr pool(new Pool6(Pool6::TYPE_IA, min, max));
@@ -459,7 +463,7 @@ public:
 
     /// @brief sets storage for value of this parameter
     ///
-    /// See \ref dhcpv6-config-inherit for details.
+    /// See \ref dhcp6-config-inherit for details.
     ///
     /// @param storage pointer to the storage container
     void setStorage(PoolStorage* storage) {
@@ -480,7 +484,7 @@ public:
         return (new PoolParser(param_name));
     }
 
-protected:
+private:
     /// @brief pointer to the actual Pools storage
     ///
     /// This is typically a storage somewhere in Subnet parser
@@ -1005,6 +1009,7 @@ private:
     ///
     /// @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* createSubnet6ConfigParser(const std::string& config_id) {
         FactoryMap factories;
 
@@ -1047,6 +1052,7 @@ private:
     ///
     /// @param name name of the parameter
     /// @return triplet with the parameter name
+    /// @throw Dhcp6ConfigError when requested parameter is not present
     Triplet<uint32_t> getParam(const std::string& name) {
         uint32_t value = 0;
         bool found = false;
@@ -1156,6 +1162,7 @@ public:
 ///
 /// @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;
 
@@ -1205,11 +1212,13 @@ DhcpConfigParser* createGlobalDhcpConfigParser(const std::string& config_id) {
 ///
 /// @param config_set a new configuration for DHCPv6 server
 /// @return answer that contains result of reconfiguration
+/// @throw Dhcp6ConfigError if trying to create a parser for NULL config
 ConstElementPtr
 configureDhcp6Server(Dhcpv6Srv& , ConstElementPtr config_set) {
     if (!config_set) {
-        isc_throw(Dhcp6ConfigError,
-                  "Null pointer is passed to configuration parser");
+        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")
diff --git a/src/bin/dhcp6/config_parser.h b/src/bin/dhcp6/config_parser.h
index 9f7c3ae..ed44bb9 100644
--- a/src/bin/dhcp6/config_parser.h
+++ b/src/bin/dhcp6/config_parser.h
@@ -15,9 +15,11 @@
 #ifndef DHCP6_CONFIG_PARSER_H
 #define DHCP6_CONFIG_PARSER_H
 
+/// @todo: This header file and its .cc counterpart are very similar between
+/// DHCPv4 and DHCPv6. They should be merged. See ticket #2355.
+
 #include <cc/data.h>
 #include <exceptions/exceptions.h>
-
 #include <string>
 
 namespace isc {
@@ -30,15 +32,22 @@ class Dhcpv6Srv;
 class Dhcp6ConfigError : public isc::Exception {
 public:
 
-/// @brief constructor
-///
-/// @param file name of the file, where exception occurred
-/// @param line line of the file, where exception occurred
-/// @param what text description of the issue that caused exception
-Dhcp6ConfigError(const char* file, size_t line, const char* what) :
-    isc::Exception(file, line, what) {}
+    /// @brief constructor
+    ///
+    /// @param file name of the file, where exception occurred
+    /// @param line line of the file, where exception occurred
+    /// @param what text description of the issue that caused exception
+    Dhcp6ConfigError(const char* file, size_t line, const char* what)
+        : isc::Exception(file, line, what) {}
 };
 
+/// @brief Base abstract class for all DHCPv6 parsers
+///
+/// Each instance of a class derived from this class parses one specific config
+/// element. Sometimes elements are simple (e.g. a string) and sometimes quite
+/// complex (e.g. a subnet). In such case, it is likely that a parser will
+/// spawn child parsers to parse child elements in the configuration.
+/// @todo: Merge this class with Dhcp4ConfigParser in src/bin/dhcp4
 class DhcpConfigParser {
     ///
     /// \name Constructors and Destructor
diff --git a/src/bin/dhcp6/dhcp6.dox b/src/bin/dhcp6/dhcp6.dox
index c234f40..7e9204a 100644
--- a/src/bin/dhcp6/dhcp6.dox
+++ b/src/bin/dhcp6/dhcp6.dox
@@ -1,5 +1,5 @@
 /**
- @page dhcpv6 DHCPv6 Server Component
+ @page dhcp6 DHCPv6 Server Component
 
  BIND10 offers DHCPv6 server implementation. It is implemented as
  b10-dhcp6 component. Its primary code is located in
@@ -16,13 +16,13 @@
 
  DHCPv6 server component does not use BIND10 logging yet.
 
- @section dhcpv6-session BIND10 message queue integration
+ @section dhcp6-session BIND10 message queue integration
 
  DHCPv4 server component is now integrated with BIND10 message queue.
  It follows the same principle as DHCPv4. See \ref dhcpv4Session for
  details.
 
- @section dhcpv6-config-parser Configuration Parser in DHCPv6
+ @section dhcp6-config-parser Configuration Parser in DHCPv6
 
  b10-dhcp6 component uses BIND10 cfgmgr for commands and configuration. During
  initial configuration (See \ref
@@ -49,7 +49,7 @@
  elements and creates parsers for a given scope. This process may be repeated
  (sort of) recursively.
 
- @section dhcpv6-config-inherit DHCPv6 Configuration Inheritance
+ @section dhcp6-config-inherit DHCPv6 Configuration Inheritance
 
  One notable useful feature of DHCP configuration is its parameter inheritance.
  For example, renew-timer value may be specified at a global scope and it then
diff --git a/src/lib/dhcpsrv/cfgmgr.cc b/src/lib/dhcpsrv/cfgmgr.cc
index 3c46b13..7dc5f55 100644
--- a/src/lib/dhcpsrv/cfgmgr.cc
+++ b/src/lib/dhcpsrv/cfgmgr.cc
@@ -101,6 +101,14 @@ void CfgMgr::addSubnet4(const Subnet4Ptr& subnet) {
     subnets4_.push_back(subnet);
 }
 
+void CfgMgr::deleteSubnets4() {
+    subnets4_.clear();
+}
+
+void CfgMgr::deleteSubnets6() {
+    subnets6_.clear();
+}
+
 CfgMgr::CfgMgr() {
 }
 
diff --git a/src/lib/dhcpsrv/cfgmgr.h b/src/lib/dhcpsrv/cfgmgr.h
index 5bff64a..db2bfd5 100644
--- a/src/lib/dhcpsrv/cfgmgr.h
+++ b/src/lib/dhcpsrv/cfgmgr.h
@@ -101,9 +101,9 @@ public:
     /// needed is a dynamic server reconfiguration - a use case that is not
     /// planned to be supported any time soon.
 
-    /// @brief removes all subnets
+    /// @brief removes all IPv6 subnets
     ///
-    /// This method removes all existing subnets. It is used during
+    /// This method removes all existing IPv6 subnets. It is used during
     /// reconfiguration - old configuration is wiped and new definitions
     /// are used to recreate subnets.
     ///
@@ -111,9 +111,7 @@ public:
     /// between old and new configuration is tricky. For example: is
     /// 2000::/64 and 2000::/48 the same subnet or is it something
     /// completely new?
-    void deleteSubnets6() {
-        subnets6_.clear();
-    }
+    void deleteSubnets6();
 
     /// @brief get IPv4 subnet by address
     ///
@@ -130,7 +128,16 @@ public:
     void addSubnet4(const Subnet4Ptr& subnet);
 
     /// @brief removes all IPv4 subnets
-    void removeSubnets4();
+    ///
+    /// This method removes all existing IPv4 subnets. It is used during
+    /// reconfiguration - old configuration is wiped and new definitions
+    /// are used to recreate subnets.
+    ///
+    /// @todo Implement more intelligent approach. Note that comparison
+    /// between old and new configuration is tricky. For example: is
+    /// 192.0.2.0/23 and 192.0.2.0/24 the same subnet or is it something
+    /// completely new?
+    void deleteSubnets4();
 protected:
 
     /// @brief Protected constructor.



More information about the bind10-changes mailing list