BIND 10 master, updated. de5fd0b12800235b15ee02cbed8e862ea6c974ad [master] Merge branch 'trac2269' (configuration for b10-dhcp6)
BIND 10 source code commits
bind10-changes at lists.isc.org
Thu Oct 11 12:50:27 UTC 2012
The branch, master has been updated
via de5fd0b12800235b15ee02cbed8e862ea6c974ad (commit)
via 028bed9014b15facf1a29d3d4a822c9d14fc6411 (commit)
via 1517f32c9e68baacfc68c2aab412b29045d846b9 (commit)
via d5a5e815fc96116ee205256b9cba7c11752f0692 (commit)
via 5b9ab4bc5c038d3fe6e12a0cf90c33e5f54d0f45 (commit)
via 43636d3f31d1684984eb5337b91a418dd1725fbe (commit)
via 938d8b3145666d6f7d7084668b0ee665b3458e4a (commit)
via 680279e6e9ac7a15d23b8a4d6d03a391166a7c8b (commit)
via ff837bb1fc512920556a796c40cacc20407b165c (commit)
via 7e7171deec8d2a31147a9d5700f4315e2ea3e87b (commit)
via 4a6553ba36f68f141e7420b070ad6d621f9a8465 (commit)
via e2a552fcaa6fcc1f0d39defd990bd5a35a4c73a3 (commit)
via e3cc1803c6fb2aa3373528b6095eb9cf6e8b24d1 (commit)
via fb86b2f215ca1a08440c5c200f22af7747c9de2a (commit)
via 5ee024c32a3ce4afd33d0d5d4df59fcde90d739d (commit)
from 4784ca566b009035404a5abf1759c2eedf5a7515 (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 de5fd0b12800235b15ee02cbed8e862ea6c974ad
Merge: 4784ca5 028bed9
Author: Tomek Mrugalski <tomasz at isc.org>
Date: Thu Oct 11 14:40:04 2012 +0200
[master] Merge branch 'trac2269' (configuration for b10-dhcp6)
-----------------------------------------------------------------------
Summary of changes:
ChangeLog | 8 +
doc/devel/02-dhcp.dox | 23 -
doc/devel/mainpage.dox | 5 +
doc/guide/bind10-guide.xml | 99 ++-
src/bin/auth/auth_config.h | 2 +-
src/bin/dhcp6/Makefile.am | 2 +
src/bin/dhcp6/config_parser.cc | 797 +++++++++++++++++++++++++
src/bin/dhcp6/config_parser.h | 147 +++++
src/bin/dhcp6/ctrl_dhcp6_srv.cc | 30 +-
src/bin/dhcp6/dhcp6.dox | 79 +++
src/bin/dhcp6/dhcp6.spec | 92 ++-
src/bin/dhcp6/dhcp6_messages.mes | 20 +
src/bin/dhcp6/dhcp6_srv.cc | 7 +
src/bin/dhcp6/tests/Makefile.am | 3 +
src/bin/dhcp6/tests/config_parser_unittest.cc | 243 ++++++++
src/lib/dhcp/cfgmgr.h | 15 +
src/lib/dhcp/tests/cfgmgr_unittest.cc | 4 +
src/lib/dhcp/tests/subnet_unittest.cc | 4 +-
src/lib/dhcp/triplet.h | 5 +
19 files changed, 1543 insertions(+), 42 deletions(-)
create mode 100644 src/bin/dhcp6/config_parser.cc
create mode 100644 src/bin/dhcp6/config_parser.h
create mode 100644 src/bin/dhcp6/dhcp6.dox
create mode 100644 src/bin/dhcp6/tests/config_parser_unittest.cc
-----------------------------------------------------------------------
diff --git a/ChangeLog b/ChangeLog
index 8a81718..16fb41e 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,11 @@
+491. [func] tomek
+ b10-dhcp6: Configuration for DHCPv6 has been implemented.
+ Currently it is possible to configure IPv6 subnets and pools
+ within those subnets, global and per subnet values of renew,
+ rebind, preferred and valid lifetimes. Configured parameters
+ are accepted, but are not used yet by the allocation engine yet.
+ (Trac #2269, git 028bed9014b15facf1a29d3d4a822c9d14fc6411)
+
490. [func] tomek
libdhcp++: An abstract API for lease database has been
implemented. It offers a common interface to all concrete
diff --git a/doc/devel/02-dhcp.dox b/doc/devel/02-dhcp.dox
index 5217f73..a4bbff9 100644
--- a/doc/devel/02-dhcp.dox
+++ b/doc/devel/02-dhcp.dox
@@ -57,29 +57,6 @@
* that does not support msgq. That is useful for embedded environments.
* It may also be useful in validation.
*
- * @page dhcpv6 DHCPv6 Server Component
- *
- * BIND10 offers DHCPv6 server implementation. It is implemented as
- * b10-dhcp6 component. Its primary code is located in
- * isc::dhcp::Dhcpv6Srv class. It uses \ref libdhcp extensively,
- * especially lib::dhcp::Pkt6, 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.
- *
- * DHCPv6 server component does not support relayed traffic yet, as
- * support for relay decapsulation is not implemented yet.
- *
- * DHCPv6 server component does not use BIND10 logging yet.
- *
- * @section dhcpv6Session 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.
- *
* @page libdhcp libdhcp++
*
* @section libdhcpIntro Libdhcp++ Library Introduction
diff --git a/doc/devel/mainpage.dox b/doc/devel/mainpage.dox
index ca9d881..0da5287 100644
--- a/doc/devel/mainpage.dox
+++ b/doc/devel/mainpage.dox
@@ -15,12 +15,17 @@
* <a href="http://bind10.isc.org/">BIND10 webpage (http://bind10.isc.org)</a>
*
* @section DNS
+ * - Authoritative DNS (todo)
+ * - Recursive resolver (todo)
* - @subpage DataScrubbing
*
* @section DHCP
* - @subpage dhcpv4
* - @subpage dhcpv4Session
* - @subpage dhcpv6
+ * - @subpage dhcpv6-session
+ * - @subpage dhcpv6-config-parser
+ * - @subpage dhcpv6-config-inherit
* - @subpage libdhcp
* - @subpage libdhcpIntro
* - @subpage libdhcpIfaceMgr
diff --git a/doc/guide/bind10-guide.xml b/doc/guide/bind10-guide.xml
index 2e66ad5..6065616 100644
--- a/doc/guide/bind10-guide.xml
+++ b/doc/guide/bind10-guide.xml
@@ -2751,13 +2751,13 @@ then change those defaults with config set Resolver/forward_addresses[0]/address
<title>DHCPv4 Server Configuration</title>
<para>
The DHCPv4 server does not have a lease database implemented yet
- nor any support for configuration, so every time the same set
+ nor any support for configuration, so the same set
of configuration options (including the same fixed address)
will be assigned every time.
</para>
<para>
At this stage of development, the only way to alter the server
- configuration is to tweak its source code. To do so, please
+ 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:
<screen>
@@ -2944,16 +2944,95 @@ const std::string HARDCODED_SERVER_ID = "192.0.2.1";</screen>
<section id="dhcp6-config">
<title>DHCPv6 Server Configuration</title>
<para>
- The DHCPv6 server does not have lease database implemented yet
- or any support for configuration, so every time 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 Dhcp6</userinput></screen>
+ When starting Dhcp6 daemon for the first time, the default configuration
+ will be available. It will look similar to this:
+ <screen>
+> <userinput>config show Dhcp6</userinput>
+Dhcp6/interface "eth0" string (default)
+Dhcp6/renew-timer 1000 integer (default)
+Dhcp6/rebind-timer 2000 integer (default)
+Dhcp6/preferred-lifetime 3000 integer (default)
+Dhcp6/valid-lifetime 4000 integer (default)
+Dhcp6/subnet6 [] list (default)</screen>
</para>
+
<para>
- At this stage of development, the only way to alter server
- configuration is to tweak its source code. To do so, please
- edit src/bin/dhcp6/dhcp6_srv.cc file, modify the following
- parameters and recompile:
+ 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 Dhcp6/valid-lifetime 7200</userinput>
+> <userinput>config commit</userinput></screen>
+ Please note that most Dhcp6 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 DHCPv6 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 2001:db8:1::/64
+ prefix. The Administrator of that network has decided that addresses from range
+ 2001:db8:1::1 to 2001:db8:1::ffff are going to be managed by the Dhcp6
+ server. Such a configuration can be achieved in the following way:
+ <screen>
+> <userinput>config add Dhcp6/subnet6</userinput>
+> <userinput>config set Dhcp6/subnet6[0]/subnet "2001:db8:1::/64"</userinput>
+> <userinput>config set Dhcp6/subnet6[0]/pool [ "2001:db8:1::0 - 2001:db8:1::ffff" ]</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
+ 2001:db8:1:0:5::/80 should be also be managed by the server. It could be written as
+ 2001:db8:1:0:5:: to 2001:db8:1::5:ffff:ffff:ffff, but typing so many 'f's
+ is cumbersome. It can be expressed more simply as 2001:db8:1:0:5::/80. Both
+ formats are supported by Dhcp6 and can be mixed in the pool list.
+ For example, one could define the following pools:
+ <screen>
+> <userinput>config set Dhcp6/subnet6[0]/pool [ "2001:db8:1::1 - 2001:db8:1::ffff", "2001:db8:1:0:5::/80" ]</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.
+ </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 Dhcp6/subnet6</userinput>
+> <userinput>config set Dhcp6/subnet6[1]/subnet "2001:db8:beef::/48"</userinput>
+> <userinput>config set Dhcp6/subnet6[1]/pool [ "2001:db8:beef::/48" ]</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 Dhcp6/subnet6</command> adds
+ another (second) subnet. It can be referred to as
+ <command>Dhcp6/subnet6[1]</command>. In this example, we allow server to
+ dynamically assign all addresses available in the whole subnet. Although
+ very wasteful, it is certainly a valid configuration to dedicate the
+ whole /48 subnet for that purpose.
+ </para>
+ <para>
+ When configuring a DHCPv6 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) address from that pool. For example for pool 2001:db8::/64 the
+ 2001:db8:: address may be assigned as well. If you want to avoid this,
+ please use min-max notation.
+ </para>
+
+ <para>
+ 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/dhcp6_srv.cc file, modify the following parameters and
+ recompile:
<screen>
const std::string HARDCODED_LEASE = "2001:db8:1::1234:abcd";
const uint32_t HARDCODED_T1 = 1500; // in seconds
diff --git a/src/bin/auth/auth_config.h b/src/bin/auth/auth_config.h
index 6f18810..8e816a3 100644
--- a/src/bin/auth/auth_config.h
+++ b/src/bin/auth/auth_config.h
@@ -93,7 +93,7 @@ public:
/// 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
+ /// would be passed a 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
diff --git a/src/bin/dhcp6/Makefile.am b/src/bin/dhcp6/Makefile.am
index 4dec4e7..68aadea 100644
--- a/src/bin/dhcp6/Makefile.am
+++ b/src/bin/dhcp6/Makefile.am
@@ -46,6 +46,7 @@ pkglibexec_PROGRAMS = b10-dhcp6
b10_dhcp6_SOURCES = main.cc
b10_dhcp6_SOURCES += ctrl_dhcp6_srv.cc ctrl_dhcp6_srv.h
+b10_dhcp6_SOURCES += config_parser.cc config_parser.h
b10_dhcp6_SOURCES += dhcp6_log.cc dhcp6_log.h
b10_dhcp6_SOURCES += dhcp6_srv.cc dhcp6_srv.h
@@ -62,6 +63,7 @@ b10_dhcp6_LDADD = $(top_builddir)/src/lib/exceptions/libb10-exceptions.la
b10_dhcp6_LDADD += $(top_builddir)/src/lib/asiolink/libb10-asiolink.la
b10_dhcp6_LDADD += $(top_builddir)/src/lib/log/libb10-log.la
b10_dhcp6_LDADD += $(top_builddir)/src/lib/dhcp/libb10-dhcp++.la
+b10_dhcp6_LDADD += $(top_builddir)/src/lib/dhcp/libb10-dhcpsrv.la
b10_dhcp6_LDADD += $(top_builddir)/src/lib/config/libb10-cfgclient.la
b10_dhcp6_LDADD += $(top_builddir)/src/lib/cc/libb10-cc.la
diff --git a/src/bin/dhcp6/config_parser.cc b/src/bin/dhcp6/config_parser.cc
new file mode 100644
index 0000000..dbffc40
--- /dev/null
+++ b/src/bin/dhcp6/config_parser.cc
@@ -0,0 +1,797 @@
+// 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 <asiolink/io_address.h>
+#include <cc/data.h>
+#include <config/ccsession.h>
+#include <log/logger_support.h>
+#include <dhcp/triplet.h>
+#include <dhcp/pool.h>
+#include <dhcp/subnet.h>
+#include <dhcp/cfgmgr.h>
+#include <dhcp6/config_parser.h>
+#include <dhcp6/dhcp6_log.h>
+
+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 DhcpConfigParser* ParserFactory(const std::string& config_id);
+
+/// @brief a collection of factories that creates parsers for specified element names
+typedef std::map<std::string, ParserFactory*> FactoryMap;
+
+/// @brief a collection of elements that store uint32 values (e.g. renew-timer = 900)
+typedef std::map<string, uint32_t> Uint32Storage;
+
+/// @brief a collection of elements that store string values
+typedef std::map<string, string> StringStorage;
+
+/// @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<Pool6Ptr> 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 DhcpConfigParser {
+public:
+
+ /// @brief Constructor
+ ///
+ /// See \ref DhcpConfigParser class for details.
+ ///
+ /// @param param_name name of the parsed parameter
+ DebugParser(const std::string& param_name)
+ :param_name_(param_name) {
+ }
+
+ /// @brief builds parameter value
+ ///
+ /// See \ref DhcpConfigParser class for details.
+ ///
+ /// @param new_config pointer to the new configuration
+ virtual void build(ConstElementPtr new_config) {
+ std::cout << "Build for token: [" << param_name_ << "] = ["
+ << value_->str() << "]" << std::endl;
+ value_ = new_config;
+ }
+
+ /// @brief pretends to apply the configuration
+ ///
+ /// This is a method required by base class. It pretends to apply the
+ /// configuration, but in fact it only prints the parameter out.
+ ///
+ /// See \ref DhcpConfigParser class for details.
+ virtual void commit() {
+ // Debug message. The whole DebugParser class is used only for parser
+ // debugging, and is not used in production code. It is very convenient
+ // to keep it around. Please do not turn this cout into logger calls.
+ std::cout << "Commit for token: [" << param_name_ << "] = ["
+ << value_->str() << "]" << std::endl;
+ }
+
+ /// @brief factory that constructs DebugParser objects
+ ///
+ /// @param param_name name of the parameter to be parsed
+ static DhcpConfigParser* Factory(const std::string& param_name) {
+ return (new DebugParser(param_name));
+ }
+
+protected:
+ /// 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 DhcpConfigParser.
+///
+/// For overview of usability of this generic purpose parser, see
+/// \ref dhcpv6-config-inherit page.
+class Uint32Parser : public DhcpConfigParser {
+public:
+
+ /// @brief constructor for Uint32Parser
+ /// @param param_name name of the configuration parameter being parsed
+ Uint32Parser(const std::string& param_name)
+ :storage_(&uint32_defaults), param_name_(param_name) {
+ }
+
+ /// @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
+ 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.");
+ }
+ storage_->insert(pair<string, uint32_t>(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 \ref Subnet6Parser when constructing
+ /// the subnet.
+ virtual void commit() {
+ }
+
+ /// @brief factory that constructs Uint32Parser objects
+ ///
+ /// @param param_name name of the parameter to be parsed
+ static DhcpConfigParser* Factory(const std::string& param_name) {
+ return (new Uint32Parser(param_name));
+ }
+
+ /// @brief sets storage for value of this parameter
+ ///
+ /// See \ref dhcpv6-config-inherit for details.
+ ///
+ /// @param storage pointer to the storage container
+ void setStorage(Uint32Storage* storage) {
+ storage_ = storage;
+ }
+
+protected:
+ /// pointer to the storage, where parsed value will be stored
+ Uint32Storage* storage_;
+
+ /// name of the parameter to be parsed
+ std::string param_name_;
+
+ /// the actual parsed value
+ uint32_t value_;
+};
+
+/// @brief Configuration parser for string parameters
+///
+/// This class is a generic parser that is able to handle any string
+/// parameter. By default it stores the value in external global container
+/// (string_defaults). If used in smaller scopes (e.g. to parse parameters
+/// in subnet config), it can be pointed to a different storage, using
+/// setStorage() method. This class follows the parser interface, laid out
+/// in its base class, \ref DhcpConfigParser.
+///
+/// For overview of usability of this generic purpose parser, see
+/// \ref dhcpv6-config-inherit page.
+class StringParser : public DhcpConfigParser {
+public:
+
+ /// @brief constructor for StringParser
+ /// @param param_name name of the configuration parameter being parsed
+ StringParser(const std::string& param_name)
+ :storage_(&string_defaults), param_name_(param_name) {
+ }
+
+ /// @brief parses parameter value
+ ///
+ /// Parses configuration entry and stored 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_->insert(pair<string, string>(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 DhcpConfigParser* Factory(const std::string& param_name) {
+ return (new StringParser(param_name));
+ }
+
+ /// @brief sets storage for value of this parameter
+ ///
+ /// See \ref dhcpv6-config-inherit for details.
+ ///
+ /// @param storage pointer to the storage container
+ void setStorage(StringStorage* storage) {
+ storage_ = storage;
+ }
+
+protected:
+ /// pointer to the storage, where parsed value will be stored
+ StringStorage* storage_;
+
+ /// name of the parameter to be parsed
+ std::string param_name_;
+
+ /// the actual parsed value
+ std::string value_;
+};
+
+
+/// @brief parser for interface list definition
+///
+/// This parser handles Dhcp6/interface entry.
+/// It contains a list of network interfaces that the server listens on.
+/// In particular, it can contain an entry called "all" or "any" that
+/// designates all interfaces.
+///
+/// It is useful for parsing Dhcp6/interface parameter.
+class InterfaceListConfigParser : public DhcpConfigParser {
+public:
+
+ /// @brief constructor
+ ///
+ /// As this is a dedicated parser, it must be used to parse
+ /// "interface" parameter only. All other types will throw exception.
+ ///
+ /// @param param_name name of the configuration parameter being parsed
+ 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);
+ }
+ }
+
+ /// @brief parses parameters value
+ ///
+ /// Parses configuration entry (list of parameters) and stores it in
+ /// storage. See \ref setStorage() for details.
+ ///
+ /// @param value pointer to the content of parsed values
+ virtual void build(ConstElementPtr value) {
+ BOOST_FOREACH(ConstElementPtr iface, value->listValue()) {
+ interfaces_.push_back(iface->str());
+ }
+ }
+
+ /// @brief commits interfaces list configuration
+ virtual void commit() {
+ /// @todo: Implement per interface listening. Currently always listening
+ /// on all interfaces.
+ }
+
+ /// @brief factory that constructs InterfaceListConfigParser objects
+ ///
+ /// @param param_name name of the parameter to be parsed
+ static DhcpConfigParser* Factory(const std::string& param_name) {
+ return (new InterfaceListConfigParser(param_name));
+ }
+
+protected:
+ /// contains list of network interfaces
+ vector<string> interfaces_;
+};
+
+/// @brief parser for pool definition
+///
+/// This parser handles pool definitions, i.e. a list of entries of one
+/// of two syntaxes: min-max and prefix/len. Pool6 objects are created
+/// and stored in chosen PoolStorage container.
+///
+/// As there are no default values for pool, setStorage() must be called
+/// before build(). Otherwise exception will be thrown.
+///
+/// It is useful for parsing Dhcp6/subnet6[X]/pool parameters.
+class PoolParser : public DhcpConfigParser {
+public:
+
+ /// @brief constructor.
+ PoolParser(const std::string& /*param_name*/)
+ :pools_(NULL) {
+ // ignore parameter name, it is always Dhcp6/subnet6[X]/pool
+ }
+
+ /// @brief parses the actual list
+ ///
+ /// This method parses the actual list of interfaces.
+ /// No validation is done at this stage, everything is interpreted as
+ /// interface name.
+ 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 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 Pool6 constructor.
+ len = boost::lexical_cast<int>(prefix_len);
+ } catch (...) {
+ isc_throw(Dhcp6ConfigError, "Failed to parse pool "
+ "definition: " << text_pool->stringValue());
+ }
+
+ Pool6Ptr pool(new Pool6(Pool6::TYPE_IA, 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 - 1));
+ IOAddress max(txt.substr(pos + 1));
+
+ 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)");
+ }
+ }
+
+ /// @brief sets storage for value of this parameter
+ ///
+ /// See \ref dhcpv6-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 parser. 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 DhcpConfigParser* Factory(const std::string& param_name) {
+ return (new PoolParser(param_name));
+ }
+
+protected:
+ /// @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 Subnet6ConfigParser : public DhcpConfigParser {
+public:
+
+ /// @brief constructor
+ Subnet6ConfigParser(const std::string& ) {
+ // The parameter should always be "subnet", but we don't check here
+ // against it in case some 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(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_);
+ } else {
+
+ boost::shared_ptr<StringParser> stringParser =
+ boost::dynamic_pointer_cast<StringParser>(parser);
+ if (stringParser) {
+ stringParser->setStorage(&string_values_);
+ } else {
+
+ 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
+ }
+
+ /// @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 Subnet6
+ /// objects. Subnet6 are then added to DHCP CfgMgr.
+ void commit() {
+
+ StringStorage::const_iterator it = string_values_.find("subnet");
+ 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");
+
+ /// @todo: Convert this to logger once the parser is working reliably
+ stringstream tmp;
+ tmp << addr.toText() << "/" << (int)len
+ << " with params t1=" << t1 << ", t2=" << t2 << ", pref="
+ << pref << ", valid=" << valid;
+
+ LOG_INFO(dhcp6_logger, DHCP6_CONFIG_NEW_SUBNET).arg(tmp.str());
+
+ Subnet6Ptr subnet(new Subnet6(addr, len, t1, t2, pref, valid));
+
+ for (PoolStorage::iterator it = pools_.begin(); it != pools_.end(); ++it) {
+ subnet->addPool6(*it);
+ }
+
+ CfgMgr::instance().addSubnet6(subnet);
+ }
+
+protected:
+
+ /// @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
+ DhcpConfigParser* 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 DebugParser(config_id);
+
+ isc_throw(NotImplemented,
+ "Parser error: Subnet6 parameter not supported: "
+ << config_id);
+ }
+ return (f->second(config_id));
+ }
+
+ /// @brief returns value for a given parameter (after using inheritance)
+ ///
+ /// This method implements inheritance. For a given parameter name, it first
+ /// checks if there is a global value for it and overwrites it with specific
+ /// value if such value was defined in subnet.
+ ///
+ /// @param name name of the parameter
+ /// @return triplet with the parameter name
+ 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)");
+ }
+ }
+
+ /// 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 Subnet6
+/// definitions. It iterates over all entries and creates Subnet6ConfigParser
+/// for each entry.
+class Subnets6ListConfigParser : public DhcpConfigParser {
+public:
+
+ /// @brief constructor
+ ///
+ Subnets6ListConfigParser(const std::string&) {
+ /// parameter name is ignored
+ }
+
+ /// @brief parses contents of the list
+ ///
+ /// Iterates over all entries on the list and creates Subnet6ConfigParser
+ /// for each entry.
+ ///
+ /// @param subnets_list pointer to a list of IPv6 subnets
+ void build(ConstElementPtr subnets_list) {
+
+ // No need to define FactoryMap here. There's only one type
+ // used: Subnet6ConfigParser
+
+ BOOST_FOREACH(ConstElementPtr subnet, subnets_list->listValue()) {
+
+ ParserPtr parser(new Subnet6ConfigParser("subnet"));
+ parser->build(subnet);
+ subnets_.push_back(parser);
+ }
+
+ }
+
+ /// @brief commits subnets definitions.
+ ///
+ /// Iterates over all Subnet6 parsers. Each parser contains definitions
+ /// of a single subnet and its parameters and commits each subnet separately.
+ void commit() {
+ // @todo: Implement more subtle reconfiguration than toss
+ // the old one and replace with the new one.
+
+ // remove old subnets
+ CfgMgr::instance().deleteSubnets6();
+
+ BOOST_FOREACH(ParserPtr subnet, subnets_) {
+ subnet->commit();
+ }
+
+ }
+
+ /// @brief Returns Subnet6ListConfigParser object
+ /// @param param_name name of the parameter
+ /// @return Subnets6ListConfigParser object
+ static DhcpConfigParser* Factory(const std::string& param_name) {
+ return (new Subnets6ListConfigParser(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 Dhcp6/param1, Dhcp6/param2 and so forth.
+///
+/// @param config_id pointer to received global configuration entry
+/// @return parser for specified global DHCPv6 parameter
+DhcpConfigParser* createGlobalDhcpConfigParser(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 DebugParser(config_id);
+
+ isc_throw(NotImplemented,
+ "Parser error: Global configuration parameter not supported: "
+ << config_id);
+ }
+ return (f->second(config_id));
+}
+
+/// @brief configures DHCPv6 server
+///
+/// This function is called every time a new configuration is received. The extra
+/// parameter is a reference to DHCPv6 server component. It is currently not used
+/// and CfgMgr::instance() is accessed instead.
+///
+/// This 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 for DHCPv6 server
+/// @return answer that contains result of reconfiguration
+ConstElementPtr
+configureDhcp6Server(Dhcpv6Srv& , ConstElementPtr config_set) {
+ if (!config_set) {
+ isc_throw(Dhcp6ConfigError,
+ "Null pointer is passed to configuration parser");
+ }
+
+ /// @todo: append most essential info here (like "2 new subnets configured")
+ string config_details;
+
+ LOG_DEBUG(dhcp6_logger, DBG_DHCP6_COMMAND, DHCP6_CONFIG_START).arg(config_set->str());
+
+ ParserCollection parsers;
+ try {
+ BOOST_FOREACH(ConfigPair config_pair, config_set->mapValue()) {
+
+ ParserPtr parser(createGlobalDhcpConfigParser(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(dhcp6_logger, DHCP6_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/dhcp6/config_parser.h b/src/bin/dhcp6/config_parser.h
new file mode 100644
index 0000000..6758c99
--- /dev/null
+++ b/src/bin/dhcp6/config_parser.h
@@ -0,0 +1,147 @@
+// 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:
+
+/// @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) {}
+};
+
+class DhcpConfigParser {
+ ///
+ /// \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:
+ DhcpConfigParser(const DhcpConfigParser& source);
+ DhcpConfigParser& operator=(const DhcpConfigParser& 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).
+ DhcpConfigParser() {}
+public:
+ /// The destructor.
+ virtual ~DhcpConfigParser() {}
+ //@}
+
+ /// \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 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;
+};
+
+/// @brief a pointer to configuration parser
+typedef boost::shared_ptr<DhcpConfigParser> 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 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 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 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.
+isc::data::ConstElementPtr
+configureDhcp6Server(Dhcpv6Srv& server,
+ isc::data::ConstElementPtr config_set);
+
+}; // end of isc::dhcp namespace
+}; // end of isc namespace
+
+#endif // DHCP6_CONFIG_PARSER_H
diff --git a/src/bin/dhcp6/ctrl_dhcp6_srv.cc b/src/bin/dhcp6/ctrl_dhcp6_srv.cc
index 4afb203..7370583 100644
--- a/src/bin/dhcp6/ctrl_dhcp6_srv.cc
+++ b/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,8 +48,15 @@ ConstElementPtr
ControlledDhcpv6Srv::dhcp6ConfigHandler(ConstElementPtr new_config) {
LOG_DEBUG(dhcp6_logger, DBG_DHCP6_COMMAND, DHCP6_CONFIG_UPDATE)
.arg(new_config->str());
- ConstElementPtr answer = isc::config::createAnswer(0,
- "Thank you for sending config.");
+
+ if (server_) {
+ return (configureDhcp6Server(*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);
}
@@ -86,7 +94,7 @@ void ControlledDhcpv6Srv::sessionReader(void) {
}
void ControlledDhcpv6Srv::establishSession() {
-
+
string specfile;
if (getenv("B10_FROM_BUILD")) {
specfile = string(getenv("B10_FROM_BUILD")) +
@@ -96,15 +104,27 @@ void ControlledDhcpv6Srv::establishSession() {
}
/// @todo: Check if session is not established already. Throw, if it is.
-
+
LOG_DEBUG(dhcp6_logger, DBG_DHCP6_START, DHCP6_CCSESSION_STARTING)
.arg(specfile);
cc_session_ = new Session(io_service_.get_io_service());
config_session_ = new ModuleCCSession(specfile, *cc_session_,
- dhcp6ConfigHandler,
+ NULL,
dhcp6CommandHandler, 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(dhcp6ConfigHandler);
+
+ try {
+ configureDhcp6Server(*this, config_session_->getFullConfig());
+ } catch (const Dhcp6ConfigError& ex) {
+ LOG_ERROR(dhcp6_logger, DHCP6_CONFIG_LOAD_FAIL).arg(ex.what());
+ }
+
/// Integrate the asynchronous I/O model of BIND 10 configuration
/// control with the "select" model of the DHCP server. This is
/// fully explained in \ref dhcpv6Session.
diff --git a/src/bin/dhcp6/dhcp6.dox b/src/bin/dhcp6/dhcp6.dox
new file mode 100644
index 0000000..fe842de
--- /dev/null
+++ b/src/bin/dhcp6/dhcp6.dox
@@ -0,0 +1,79 @@
+/**
+ @page dhcpv6 DHCPv6 Server Component
+
+ BIND10 offers DHCPv6 server implementation. It is implemented as
+ b10-dhcp6 component. Its primary code is located in
+ isc::dhcp::Dhcpv6Srv class. It uses \ref libdhcp extensively,
+ especially lib::dhcp::Pkt6, 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.
+
+ DHCPv6 server component does not support relayed traffic yet, as
+ support for relay decapsulation is not implemented yet.
+
+ DHCPv6 server component does not use BIND10 logging yet.
+
+ @section dhcpv6-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
+
+ b10-dhcp6 component uses BIND10 cfgmgr for commands and configuration. During
+ initial configuration (See \ref
+ isc::dhcp::ControlledDhcpv6Srv::establishSession()), the configuration handler
+ callback is installed (see isc::dhcp::ControlledDhcpv6Srv::dhcp6ConfigHandler().
+ It is called every time there is a new configuration. In particular, it is
+ called every time during daemon start process. It contains a
+ isc::data::ConstElementPtr to a new configuration. This simple handler calls
+ \ref isc::dhcp::configureDhcp6Server() method that processes received configuration.
+
+ This method iterates over list of received configuration elements and creates a
+ list of parsers for each received entry. Parser is an object that is derived
+ from a \ref isc::dhcp::Dhcp6ConfigParser class. Once a parser is created
+ (constructor), its value is set (using build() method). Once all parsers are
+ build, the configuration is then applied ("commited") and commit() method is
+ called.
+
+ All parsers are defined in src/bin/dhcp6/config_parser.cc file. Some of them
+ are generic (e.g. \ref isc::dhcp::Uint32Parser that is able to handle any
+ unsigned 32 bit integer), but some are very specialized (e.g. \ref
+ isc::dhcp::Subnets6ListConfigParser parses definitions of Subnet6 lists). In
+ some cases, e.g. subnet6 definitions, the configuration entry is not a simple
+ value, but a map or a list itself. In such case, the parser iterates over all
+ elements and creates parsers for a given scope. This process may be repeated
+ (sort of) recursively.
+
+ @section dhcpv6-config-inherit DHCPv6 Configuration Inheritance
+
+ One notable useful features of DHCP configuration is its parameter inheritance.
+ For example, renew-timer value may be specified at a global scope and it then
+ applies to all subnets. However, some subnets may have it overwritten with more
+ specific values that takes precedence over global values that are considered
+ defaults. Some parsers (e.g. \ref isc::dhcp::Uint32Parser and \ref
+ isc::dhcp::StringParser) implement that inheritance. By default, they store
+ values in global uint32_defaults and string_defaults storages. However, it is
+ possible to instruct them to store parsed values in more specific
+ storages. That capability is used, e.g. in \ref isc::dhcp::Subnet6ConfigParser
+ that has its own storage that is unique for each subnet. Finally, during commit
+ phase (commit() method), appropriate parsers can use apply parameter inheritance.
+
+ Debugging configuration parser may be confusing. Therefore there is a special
+ class called \ref isc::dhcp::DummyParser. It does not configure anything, but just
+ accepts any parameter of any type. If requested to commit configuration, it will
+ print out received parameter name and its value. This class is not currently used,
+ but it is convenient to have it every time a new parameter is added to DHCP
+ configuration. For that purpose it should be left in the code.
+
+ Parameter inheritance is done during reconfiguration phase, as reconfigurations
+ are rare, so extra logic here is not a problem. On the other hand, values of
+ those parameters may be used thousands times per second, so its use must be as
+ simple as possible. In fact, currently the code has to call Subnet6->getT1() and
+ do not implement any fancy inheritance logic.
+
+ */
diff --git a/src/bin/dhcp6/dhcp6.spec b/src/bin/dhcp6/dhcp6.spec
index 2a82a2d..f35f606 100644
--- a/src/bin/dhcp6/dhcp6.spec
+++ b/src/bin/dhcp6/dhcp6.spec
@@ -4,9 +4,97 @@
"module_description": "DHCPv6 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": "preferred-lifetime",
+ "item_type": "integer",
+ "item_optional": false,
+ "item_default": 3000
+ },
+
+ { "item_name": "valid-lifetime",
+ "item_type": "integer",
+ "item_optional": false,
+ "item_default": 4000
+ },
+
+ { "item_name": "subnet6",
+ "item_type": "list",
+ "item_optional": false,
+ "item_default": [],
+ "list_item_spec":
+ {
+ "item_name": "single-subnet6",
+ "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": "preferred-lifetime",
+ "item_type": "integer",
+ "item_optional": false,
+ "item_default": 3000
+ },
+
+ { "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/dhcp6/dhcp6_messages.mes b/src/bin/dhcp6/dhcp6_messages.mes
index 1564940..2399c19 100644
--- a/src/bin/dhcp6/dhcp6_messages.mes
+++ b/src/bin/dhcp6/dhcp6_messages.mes
@@ -109,3 +109,23 @@ 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 start, but nothing will be
+served until the configuration has been corrected.
+
+% DHCP6_CONFIG_START DHCPv6 server is processing the following configuration: %1
+This is a debug message that is issued every time the server receives a
+configuration. That happens start up and also when a server configuration
+change is committed by the administrator.
+
+% DHCP6_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 subnet.
+
+% DHCP6_CONFIG_COMPLETE DHCPv6 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.
diff --git a/src/bin/dhcp6/dhcp6_srv.cc b/src/bin/dhcp6/dhcp6_srv.cc
index 54fa2b5..7c57a61 100644
--- a/src/bin/dhcp6/dhcp6_srv.cc
+++ b/src/bin/dhcp6/dhcp6_srv.cc
@@ -42,6 +42,13 @@ const uint32_t HARDCODED_VALID_LIFETIME = 7200; // in seconds
const std::string HARDCODED_DNS_SERVER = "2001:db8:1::1";
Dhcpv6Srv::Dhcpv6Srv(uint16_t port) {
+ if (port == 0) {
+ // used for testing purposes. Some tests, e.g. configuration parser,
+ // require Dhcpv6Srv object, but they don't really need it to do
+ // anything. This speed up and simplifies the tests.
+ return;
+ }
+
LOG_DEBUG(dhcp6_logger, DBG_DHCP6_START, DHCP6_OPEN_SOCKET).arg(port);
// First call to instance() will create IfaceMgr (it's a singleton)
diff --git a/src/bin/dhcp6/tests/Makefile.am b/src/bin/dhcp6/tests/Makefile.am
index de87582..1629ae6 100644
--- a/src/bin/dhcp6/tests/Makefile.am
+++ b/src/bin/dhcp6/tests/Makefile.am
@@ -46,9 +46,11 @@ TESTS += dhcp6_unittests
dhcp6_unittests_SOURCES = dhcp6_unittests.cc
dhcp6_unittests_SOURCES += dhcp6_srv_unittest.cc
dhcp6_unittests_SOURCES += ctrl_dhcp6_srv_unittest.cc
+dhcp6_unittests_SOURCES += config_parser_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
@@ -62,6 +64,7 @@ dhcp6_unittests_LDFLAGS = $(AM_LDFLAGS) $(GTEST_LDFLAGS)
dhcp6_unittests_LDADD = $(GTEST_LDADD)
dhcp6_unittests_LDADD += $(top_builddir)/src/lib/asiolink/libb10-asiolink.la
dhcp6_unittests_LDADD += $(top_builddir)/src/lib/dhcp/libb10-dhcp++.la
+dhcp6_unittests_LDADD += $(top_builddir)/src/lib/dhcp/libb10-dhcpsrv.la
dhcp6_unittests_LDADD += $(top_builddir)/src/lib/log/libb10-log.la
dhcp6_unittests_LDADD += $(top_builddir)/src/lib/exceptions/libb10-exceptions.la
dhcp6_unittests_LDADD += $(top_builddir)/src/lib/config/libb10-cfgclient.la
diff --git a/src/bin/dhcp6/tests/config_parser_unittest.cc b/src/bin/dhcp6/tests/config_parser_unittest.cc
new file mode 100644
index 0000000..22592e8
--- /dev/null
+++ b/src/bin/dhcp6/tests/config_parser_unittest.cc
@@ -0,0 +1,243 @@
+// 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 <iostream>
+#include <fstream>
+#include <sstream>
+
+#include <arpa/inet.h>
+#include <gtest/gtest.h>
+
+#include <dhcp6/dhcp6_srv.h>
+#include <dhcp6/config_parser.h>
+#include <config/ccsession.h>
+#include <dhcp/subnet.h>
+#include <dhcp/cfgmgr.h>
+
+using namespace std;
+using namespace isc;
+using namespace isc::dhcp;
+using namespace isc::asiolink;
+using namespace isc::data;
+using namespace isc::config;
+
+namespace {
+
+class Dhcp6ParserTest : public ::testing::Test {
+public:
+ Dhcp6ParserTest()
+ :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 Dhcpv6Srv(0);
+ }
+
+ ~Dhcp6ParserTest() {
+ delete srv_;
+ };
+
+ Dhcpv6Srv* 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(Dhcp6ParserTest, version) {
+
+ ConstElementPtr x;
+
+ EXPECT_NO_THROW(x = configureDhcp6Server(*srv_,
+ Element::fromJSON("{\"version\": 0}")));
+
+ // returned value must be 0 (configuration accepted)
+ ASSERT_TRUE(x);
+ comment_ = parseAnswer(rcode_, x);
+ EXPECT_EQ(0, rcode_);
+}
+
+/// The goal of this test is to verify that the code accepts only
+/// valid commands and malformed or unsupported parameters are rejected.
+TEST_F(Dhcp6ParserTest, bogus_command) {
+
+ ConstElementPtr x;
+
+ EXPECT_NO_THROW(x = configureDhcp6Server(*srv_,
+ Element::fromJSON("{\"bogus\": 5}")));
+
+ // returned value must be 1 (configuration parse error)
+ ASSERT_TRUE(x);
+ comment_ = parseAnswer(rcode_, x);
+ EXPECT_EQ(1, rcode_);
+}
+
+/// 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(Dhcp6ParserTest, empty_subnet) {
+
+ ConstElementPtr status;
+
+ EXPECT_NO_THROW(status = configureDhcp6Server(*srv_,
+ Element::fromJSON("{ \"interface\": [ \"all\" ],"
+ "\"preferred-lifetime\": 3000,"
+ "\"rebind-timer\": 2000, "
+ "\"renew-timer\": 1000, "
+ "\"subnet6\": [ ], "
+ "\"valid-lifetime\": 4000 }")));
+
+ // returned value should be 0 (success)
+ ASSERT_TRUE(status);
+ comment_ = parseAnswer(rcode_, status);
+ EXPECT_EQ(0, rcode_);
+}
+
+/// The goal of this test is to verify if defined subnet uses global
+/// parameter timer definitions.
+TEST_F(Dhcp6ParserTest, subnet_global_defaults) {
+
+ ConstElementPtr status;
+
+ string config = "{ \"interface\": [ \"all\" ],"
+ "\"preferred-lifetime\": 3000,"
+ "\"rebind-timer\": 2000, "
+ "\"renew-timer\": 1000, "
+ "\"subnet6\": [ { "
+ " \"pool\": [ \"2001:db8:1::1 - 2001:db8:1::ffff\" ],"
+ " \"subnet\": \"2001:db8:1::/64\" } ],"
+ "\"valid-lifetime\": 4000 }";
+ cout << config << endl;
+
+ ElementPtr json = Element::fromJSON(config);
+
+ EXPECT_NO_THROW(status = configureDhcp6Server(*srv_, json));
+
+ // check if returned status is OK
+ ASSERT_TRUE(status);
+ comment_ = parseAnswer(rcode_, status);
+ EXPECT_EQ(0, rcode_);
+
+ // Now check if the configuration was indeed handled and we have
+ // expected pool configured.
+ Subnet6Ptr subnet = CfgMgr::instance().getSubnet6(IOAddress("2001:db8:1::5"));
+ ASSERT_TRUE(subnet);
+ EXPECT_EQ(1000, subnet->getT1());
+ EXPECT_EQ(2000, subnet->getT2());
+ EXPECT_EQ(3000, subnet->getPreferred());
+ EXPECT_EQ(4000, subnet->getValid());
+}
+
+// This test checks if it is possible to override global values
+// on a per subnet basis.
+TEST_F(Dhcp6ParserTest, subnet_local) {
+
+ ConstElementPtr status;
+
+ string config = "{ \"interface\": [ \"all\" ],"
+ "\"preferred-lifetime\": 3000,"
+ "\"rebind-timer\": 2000, "
+ "\"renew-timer\": 1000, "
+ "\"subnet6\": [ { "
+ " \"pool\": [ \"2001:db8:1::1 - 2001:db8:1::ffff\" ],"
+ " \"renew-timer\": 1, "
+ " \"rebind-timer\": 2, "
+ " \"preferred-lifetime\": 3,"
+ " \"valid-lifetime\": 4,"
+ " \"subnet\": \"2001:db8:1::/64\" } ],"
+ "\"valid-lifetime\": 4000 }";
+ cout << config << endl;
+
+ ElementPtr json = Element::fromJSON(config);
+
+ EXPECT_NO_THROW(status = configureDhcp6Server(*srv_, json));
+
+ // returned value should be 0 (configuration success)
+ ASSERT_TRUE(status);
+ comment_ = parseAnswer(rcode_, status);
+ EXPECT_EQ(0, rcode_);
+
+ Subnet6Ptr subnet = CfgMgr::instance().getSubnet6(IOAddress("2001:db8:1::5"));
+ ASSERT_TRUE(subnet);
+ EXPECT_EQ(1, subnet->getT1());
+ EXPECT_EQ(2, subnet->getT2());
+ EXPECT_EQ(3, subnet->getPreferred());
+ EXPECT_EQ(4, subnet->getValid());
+}
+
+// Test verifies that a subnet with pool values that do not belong to that
+// pool are rejected.
+TEST_F(Dhcp6ParserTest, pool_out_of_subnet) {
+
+ ConstElementPtr status;
+
+ string config = "{ \"interface\": [ \"all\" ],"
+ "\"preferred-lifetime\": 3000,"
+ "\"rebind-timer\": 2000, "
+ "\"renew-timer\": 1000, "
+ "\"subnet6\": [ { "
+ " \"pool\": [ \"4001:db8:1::/80\" ],"
+ " \"subnet\": \"2001:db8:1::/64\" } ],"
+ "\"valid-lifetime\": 4000 }";
+ cout << config << endl;
+
+ ElementPtr json = Element::fromJSON(config);
+
+ EXPECT_NO_THROW(status = configureDhcp6Server(*srv_, json));
+
+ // returned value must be 2 (values error)
+ // as the pool does not belong to that subnet
+ ASSERT_TRUE(status);
+ comment_ = parseAnswer(rcode_, status);
+ EXPECT_EQ(2, rcode_);
+}
+
+// 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(Dhcp6ParserTest, pool_prefix_len) {
+
+ ConstElementPtr x;
+
+ string config = "{ \"interface\": [ \"all\" ],"
+ "\"preferred-lifetime\": 3000,"
+ "\"rebind-timer\": 2000, "
+ "\"renew-timer\": 1000, "
+ "\"subnet6\": [ { "
+ " \"pool\": [ \"2001:db8:1::/80\" ],"
+ " \"subnet\": \"2001:db8:1::/64\" } ],"
+ "\"valid-lifetime\": 4000 }";
+ cout << config << endl;
+
+ ElementPtr json = Element::fromJSON(config);
+
+ EXPECT_NO_THROW(x = configureDhcp6Server(*srv_, json));
+
+ // returned value must be 1 (configuration parse error)
+ ASSERT_TRUE(x);
+ comment_ = parseAnswer(rcode_, x);
+ EXPECT_EQ(0, rcode_);
+
+ Subnet6Ptr subnet = CfgMgr::instance().getSubnet6(IOAddress("2001:db8:1::5"));
+ ASSERT_TRUE(subnet);
+ EXPECT_EQ(1000, subnet->getT1());
+ EXPECT_EQ(2000, subnet->getT2());
+ EXPECT_EQ(3000, subnet->getPreferred());
+ EXPECT_EQ(4000, subnet->getValid());
+}
+
+};
diff --git a/src/lib/dhcp/cfgmgr.h b/src/lib/dhcp/cfgmgr.h
index 5b73f2b..f57ef99 100644
--- a/src/lib/dhcp/cfgmgr.h
+++ b/src/lib/dhcp/cfgmgr.h
@@ -98,6 +98,21 @@ public:
/// to remove subnets. The only case where subnet6 removal would be
/// needed is a dynamic server reconfiguration - a use case that is not
/// planned to be supported any time soon.
+
+ /// @brief removes all subnets
+ ///
+ /// This method removes all existing 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
+ /// 2000::/64 and 2000::/48 the same subnet or is it something
+ /// completely new?
+ void deleteSubnets6() {
+ subnets6_.clear();
+ }
+
protected:
/// @brief Protected constructor.
diff --git a/src/lib/dhcp/tests/cfgmgr_unittest.cc b/src/lib/dhcp/tests/cfgmgr_unittest.cc
index a11acbf..7bd4823 100644
--- a/src/lib/dhcp/tests/cfgmgr_unittest.cc
+++ b/src/lib/dhcp/tests/cfgmgr_unittest.cc
@@ -58,6 +58,10 @@ TEST(CfgMgrTest, subnet6) {
EXPECT_EQ(subnet2, cfg_mgr.getSubnet6(IOAddress("3000::dead:beef")));
EXPECT_EQ(Subnet6Ptr(), cfg_mgr.getSubnet6(IOAddress("5000::1")));
+ cfg_mgr.deleteSubnets6();
+ EXPECT_EQ(Subnet6Ptr(), cfg_mgr.getSubnet6(IOAddress("200::123")));
+ EXPECT_EQ(Subnet6Ptr(), cfg_mgr.getSubnet6(IOAddress("3000::123")));
+ EXPECT_EQ(Subnet6Ptr(), cfg_mgr.getSubnet6(IOAddress("4000::123")));
}
} // end of anonymous namespace
diff --git a/src/lib/dhcp/tests/subnet_unittest.cc b/src/lib/dhcp/tests/subnet_unittest.cc
index 6afebb8..4ea2c70 100644
--- a/src/lib/dhcp/tests/subnet_unittest.cc
+++ b/src/lib/dhcp/tests/subnet_unittest.cc
@@ -106,7 +106,9 @@ TEST(Subnet6Test, Subnet6_Pool6_checks) {
Pool6Ptr pool3(new Pool6(Pool6::TYPE_IA, IOAddress("3000::"), 16));
EXPECT_THROW(subnet->addPool6(pool3), BadValue);
-}
+ Pool6Ptr pool4(new Pool6(Pool6::TYPE_IA, IOAddress("4001:db8:1::"), 80));
+ EXPECT_THROW(subnet->addPool6(pool4), BadValue);
+}
};
diff --git a/src/lib/dhcp/triplet.h b/src/lib/dhcp/triplet.h
index d45f003..d9388fe 100644
--- a/src/lib/dhcp/triplet.h
+++ b/src/lib/dhcp/triplet.h
@@ -12,6 +12,9 @@
// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
// PERFORMANCE OF THIS SOFTWARE.
+#ifndef TRIPLET_H
+#define TRIPLET_H
+
#include <exceptions/exceptions.h>
namespace isc {
@@ -108,3 +111,5 @@ protected:
} // namespace isc::dhcp
} // namespace isc
+
+#endif // ifdef TRIPLET_H
More information about the bind10-changes
mailing list