Browse Source

Merge remote-tracking branch 'origin/master' into radius

Baptiste Jonglez 7 years ago
parent
commit
6c63cdd451
32 changed files with 1972 additions and 1204 deletions
  1. 7 1
      ChangeLog
  2. 500 16
      doc/guide/hooks.xml
  3. 0 1
      src/bin/dhcp4/Makefile.am
  4. 2 268
      src/bin/dhcp4/json_config_parser.cc
  5. 1 1
      src/bin/dhcp4/tests/get_config_unittest.cc
  6. 1 1
      src/bin/dhcp4/tests/simple_parser4_unittest.cc
  7. 0 2
      src/bin/dhcp6/Makefile.am
  8. 0 9
      src/bin/dhcp6/dhcp6_messages.mes
  9. 2 386
      src/bin/dhcp6/json_config_parser.cc
  10. 1 1
      src/bin/dhcp6/tests/get_config_unittest.cc
  11. 1 1
      src/bin/dhcp6/tests/simple_parser6_unittest.cc
  12. 11 0
      src/lib/dhcpsrv/Makefile.am
  13. 22 14
      src/lib/dhcpsrv/cfg_subnets4.cc
  14. 7 8
      src/lib/dhcpsrv/cfg_subnets4.h
  15. 22 15
      src/lib/dhcpsrv/cfg_subnets6.cc
  16. 7 8
      src/lib/dhcpsrv/cfg_subnets6.h
  17. 1 1
      src/lib/dhcpsrv/cfgmgr.cc
  18. 20 4
      src/lib/dhcpsrv/cfgmgr.h
  19. 20 0
      src/lib/dhcpsrv/dhcpsrv_messages.mes
  20. 1 0
      src/lib/dhcpsrv/parsers/client_class_def_parser.cc
  21. 461 303
      src/lib/dhcpsrv/parsers/dhcp_parsers.cc
  22. 274 158
      src/lib/dhcpsrv/parsers/dhcp_parsers.h
  23. 4 4
      src/lib/dhcpsrv/parsers/host_reservation_parser.cc
  24. 369 0
      src/lib/dhcpsrv/parsers/option_data_parser.cc
  25. 182 0
      src/lib/dhcpsrv/parsers/option_data_parser.h
  26. 1 1
      src/bin/dhcp4/simple_parser4.cc
  27. 0 0
      src/lib/dhcpsrv/parsers/simple_parser4.h
  28. 1 1
      src/bin/dhcp6/simple_parser6.cc
  29. 0 0
      src/lib/dhcpsrv/parsers/simple_parser6.h
  30. 28 0
      src/lib/dhcpsrv/tests/cfg_subnets4_unittest.cc
  31. 25 0
      src/lib/dhcpsrv/tests/cfg_subnets6_unittest.cc
  32. 1 0
      src/lib/dhcpsrv/tests/dhcp_parsers_unittest.cc

+ 7 - 1
ChangeLog

@@ -1,3 +1,9 @@
+1288.	[func]		marcin
+	Documented subnet_cmds library in the Kea User's Guide.  Also moved
+	several classes from dhcp<4/6> into lib/dhcpsrv to facilitate subnet
+	command parsing.
+	(Trac #5315, git d259f330a1690b20eb368f6252f5da2cdb6187de)
+
 1287.	[bug]		tmark
 1287.	[bug]		tmark
 	Logger configuration now correctly uses maxsize and maxver parameters,
 	Logger configuration now correctly uses maxsize and maxver parameters,
 	allowing log rotation to function properly.  Prior to this these values
 	allowing log rotation to function properly.  Prior to this these values
@@ -110,7 +116,7 @@
 1268.	[func]		fdupont
 1268.	[func]		fdupont
 	Kea now re-detects network interfaces every time configuration is
 	Kea now re-detects network interfaces every time configuration is
 	changed. 're-detect' parameter added to restore old behavior, if
 	changed. 're-detect' parameter added to restore old behavior, if
-        needed.
+	needed.
 	(Trac #3389, git b2ce84790c33d1580dcfa773d7fcfb65c27addc7)
 	(Trac #3389, git b2ce84790c33d1580dcfa773d7fcfb65c27addc7)
 
 
 1267.	[bug]		fdupont
 1267.	[bug]		fdupont

+ 500 - 16
doc/guide/hooks.xml

@@ -241,6 +241,21 @@
               capabilities related to host reservations will be added in the
               capabilities related to host reservations will be added in the
               future.</entry>
               future.</entry>
             </row>
             </row>
+            <row>
+              <entry>Subnet Commands</entry>
+              <entry>Support customers</entry>
+              <entry>Kea 1.3.0</entry>
+              <entry>In deployments in which subnet configuration needs to
+              be frequently updated, it is a hard requirement that such updates be
+              performed without the need for a full DHCP server reconfiguration
+              or restart. This hooks library allows for incremental changes
+              to the subnet configuration such as: adding a subnet, removing
+              a subnet. It also allows for listing all available subnets and
+              fetching detailed information about a selected subnet. The
+              commands exposed by this library do not affect other subnets
+              or configuration parameters currently used by the server.
+              </entry>
+            </row>
           </tbody>
           </tbody>
           </tgroup>
           </tgroup>
           </table>
           </table>
@@ -1112,7 +1127,6 @@ An example deletion by (subnet-id, identifier-type, identifier) looks as follows
     }
     }
 }
 }
 </screen>
 </screen>
-
         </para>
         </para>
 
 
         <para><command>lease6-add</command> command requires four
         <para><command>lease6-add</command> command requires four
@@ -1423,7 +1437,7 @@ as follows:
   }
   }
 }</screen>
 }</screen>
           </para>
           </para>
-          
+
           <para>
           <para>
             An example command updating IPv6 lease looks as follows:
             An example command updating IPv6 lease looks as follows:
 <screen>{
 <screen>{
@@ -1476,21 +1490,491 @@ as follows:
 
 
           <para>Note: not all backends support this command.</para>
           <para>Note: not all backends support this command.</para>
         </section>
         </section>
+      </section>
+    </section>
+
+      <section id="subnet-cmds">
+        <title>subnet_cmds: Subnet Commands</title>
+        <para>
+          This section describes a hook application that offers a number of new
+          commands used to query and manipulate subnet configurations in Kea.
+          This application is very useful in deployments with a large number of
+          subnets being managed by the DHCP servers and when the subnets are
+          frequently updated. The commands offer lightweight approach for
+          manipulating subnets without a need to fully reconfigure the server
+          and without affecting existing servers' configurations.
+        </para>
+
+        <para>Currently this library is only available to ISC customers with a
+        support contract.</para>
+
+        <para>The following commands are currently supported:
+        <itemizedlist mark='bullet'>
+          <listitem>
+            <simpara><command>subnet4-list/subnet6-list</command>: lists all configured subnets
+            </simpara>
+          </listitem>
+          <listitem>
+          <simpara>
+            <command>subnet4-get/subnet6-get</command>: retrieves detailed information about a selected subnet
+          </simpara>
+          </listitem>
+          <listitem>
+          <simpara>
+            <command>subnet4-add/subnet6-add</command>: adds new subnet into server's configuration
+          </simpara>
+          </listitem>
+          <listitem>
+          <simpara>
+            <command>subnet4-del/subnet6-del</command>: removes a subnet from the server's configuration
+          </simpara>
+          </listitem>
+        </itemizedlist>
+        </para>
+
+        <section>
+          <title>subnet4-list command</title>
+          <para>
+            This command is used to list all currently configured subnets. The
+            subnets are returned in a brief form, i.e. a subnet identifier
+            and subnet prefix is included for each subnet. In order to retrieve
+            the detailed information about the subnet the
+            <command>subnet4-get</command> should be used.
+          </para>
+          <para>
+            This command has the simple structure:
+<screen>
+{
+    "command": "subnet4-list"
+}
+</screen>
+          </para>
+          <para>
+            The list of subnets returned as a result of this command is returned
+            in the following format:
+<screen>
+{
+    "result": 0,
+    "text": "2 IPv4 subnets found",
+    "arguments": {
+    "subnets": [
+        {
+            "id": 10,
+            "subnet": "10.0.0.0/8"
+        },
+        {
+            "id": 100,
+            "subnet": "192.0.2.0/24"
+        }
+    ]
+}
+</screen>
+          </para>
+          <para>
+            If no IPv4 subnets are found, an error code is returned along with
+            the error description.
+          </para>
+        </section>
+
+        <section>
+          <title>subnet6-list command</title>
+          <para>
+            This command is used to list all currently configured subnets. The
+            subnets are returned in a brief form, i.e. a subnet identifier
+            and subnet prefix is included for each subnet. In order to retrieve
+            the detailed information about the subnet the
+            <command>subnet6-get</command> should be used.
+          </para>
+          <para>
+            This command has the simple structure:
+<screen>
+{
+    "command": "subnet6-list"
+}
+</screen>
+          </para>
+          <para>
+            The list of subnets returned as a result of this command is returned
+            in the following format:
+<screen>
+{
+    "result": 0,
+    "text": "2 IPv6 subnets found",
+    "arguments": {
+    "subnets": [
+        {
+            "id": 11,
+            "subnet": "2001:db8:1::/64"
+        },
+        {
+            "id": 233,
+            "subnet": "3000::/16"
+        }
+    ]
+}
+</screen>
+          </para>
+          <para>
+            If no IPv6 subnets are found, an error code is returned along with
+            the error description.
+          </para>
+        </section>
+
+      <section>
+        <title>subnet4-get command</title>
+        <para>This command is used to retrieve detailed information about the
+        specified subnet. This command usually follows the
+        <command>subnet4-list</command>, which is used to discover available
+        subnets with their respective subnet identifiers and prefixes. Any of
+        those parameters can be then used in <command>subnet4-get</command>
+        to fetch subnet information:
+<screen>
+{
+    "command": "subnet4-get",
+    "arguments": {
+        "id": 10
+    }
+}</screen>
+
+or
+
+<screen>
+{
+    "command": "subnet4-get",
+    "arguments": {
+        "subnet": "10.0.0.0/8"
+    }
+}
+</screen>
+        </para>
+
+        <para>
+          If the subnet exists the response will be similar to this:
+<screen>
+{
+    "result": 0,
+    "text": "Info about IPv4 subnet 10.0.0.0/8 (id 10) returned",
+    "arguments": {
+        "subnets": [
+            {
+                "subnet": "10.0.0.0/8",
+                "id": 1,
+                "option-data": [
+                    ....
+                ]
+                ...
+            }
+        ]
+    }
+}
+
+</screen>
+        </para>
+      </section>
+
+      <section>
+        <title>subnet6-get command</title>
+        <para>This command is used to retrieve detailed information about the
+        specified subnet. This command usually follows the
+        <command>subnet6-list</command>, which is used to discover available
+        subnets with their respective subnet identifiers and prefixes. Any of
+        those parameters can be then used in <command>subnet6-get</command>
+        to fetch subnet information:
+<screen>
+{
+    "command": "subnet6-get",
+    "arguments": {
+        "id": 11
+    }
+}
+</screen>
+
+or
+
+<screen>
+{
+    "command": "subnet6-get",
+    "arguments": {
+        "subnet": "2001:db8:1::/64"
+    }
+}</screen>
+
+If the subnet exists the response will be similar to this:
+<screen>
+{
+    "result": 0,
+    "text": "Info about IPv6 subnet 2001:db8:1::/64 (id 11) returned",
+    "arguments": {
+        "subnets": [
+            {
+                "subnet": "2001:db8:1::/64",
+                "id": 1,
+                "option-data": [
+                    ...
+                ]
+                ....
+            }
+        ]
+    }
+}
+</screen>
+        </para>
+      </section>
+
+      <section>
+        <title>subnet4-add</title>
+        <para>
+          This command is used to create and add new subnet to the existing
+          server configuration. This operation has no impact on other
+          subnets. The subnet identifier must be specified and must be
+          unique among all subnets. If the identifier or a subnet prefix is
+          not unique an error is reported and the subnet is not added.
+        </para>
+        <para>
+          The subnet information within this command has the same structure
+          as the subnet information in the server configuration file with the
+          exception that static host reservations must not be specified
+          within <command>subnet4-add</command>. The commands described in
+          <xref linkend="host-cmds"/> should be used to add, remove and
+          modify static reservations.
+<screen>
+{
+    "command": "subnet4-add",
+    "arguments": {
+        "subnets": [ {
+            "id": 123,
+            "subnet": "10.20.30.0/24",
+            ...
+        } ]
+    }
+}
+</screen>
+        </para>
+
+        <para>
+          The response to this command has the following structure:
+<screen>
+{
+    "result": 0,
+    "text": "IPv4 subnet added",
+    "arguments": {
+        "subnets": [
+            {
+                "id": 123,
+                "subnet": "10.20.30.0/24"
+            }
+        ]
+    }
+}
+</screen>
+        </para>
+      </section>
+
+      <section>
+        <title>subnet6-add</title>
+        <para>
+          This command is used to create and add new subnet to the existing
+          server configuration. This operation has no impact on other
+          subnets. The subnet identifier must be specified and must be
+          unique among all subnets. If the identifier or a subnet prefix is
+          not unique an error is reported and the subnet is not added.
+        </para>
+        <para>
+          The subnet information within this command has the same structure
+          as the subnet information in the server configuration file with the
+          exception that static host reservations must not be specified
+          within <command>subnet6-add</command>. The commands described in
+          <xref linkend="host-cmds"/> should be used to add, remove and
+          modify static reservations.
+<screen>
+{
+    "command": "subnet6-add",
+    "arguments": {
+        "subnet6": [ {
+            "id": 234,
+            "subnet": "2001:db8:1::/64",
+            ...
+        } ]
+    }
+}
+</screen>
+        </para>
+
+        <para>
+          The response to this command has the following structure:
+<screen>
+{
+    "result": 0,
+    "text": "IPv6 subnet added",
+    "arguments": {
+        "subnet6": [
+            {
+                "id": 234,
+                "subnet": "2001:db8:1::/64"
+            }
+        ]
+    }
+}
+</screen>
+        </para>
+
+        <para>
+          It is recommended, but not mandatory to specify subnet
+          id. If not specified, Kea will try to assign the next
+          subnet-id value. This automatic ID value generator is
+          simple. It returns a previously automatically assigned value
+          increased by 1. This works well, unless you manually create
+          a subnet with a value bigger than previously used. For
+          example, if you call subnet4-add five times, each without
+          id, Kea will assign IDs: 1,2,3,4 and 5 and it will work just
+          fine. However, if you try to call subnet4-add five times,
+          with the first subnet having subnet-id of value 3 and
+          remaining ones having no subnet-id, it will fail. The first
+          command (with explicit value) will use subnet-id 3, the
+          second command will create a subnet with id of 1, the third
+          will use value of 2 and finally the fourth will have the
+          subnet-id value auto-generated as 3. However, since there is
+          already a subnet with that id, it will fail.
+        </para>
+        <para>
+          The general recommendation is to either: never use explicit
+          values (so the auto-generated values will always work) or
+          always use explicit values (so the auto-generation is never
+          used). You can mix those two approaches only if you
+          understand how the internal automatic subnet-id generation works.
+        </para>
+
+      </section>
+
+      <section>
+        <title>subnet4-del command</title>
+        <para>
+          This command is used to remove a subnet from the server's configuration.
+          This command has no effect on other configured subnets but removing
+          a subnet has certain implications which the server's administrator
+          should be aware of.
+        </para>
+        <para>
+          In most cases the server has assigned some leases to the clients
+          belonging to the subnet. The server may also be configured with
+          static host reservations which are associated with this subnet.
+          The current implementation of the <command>subnet4-del</command>
+          removes neither the leases nor host reservations associated with
+          a subnet. This is the safest approach because the server doesn't
+          loose track of leases assigned to the clients from this subnet.
+          However, removal of the subnet may still cause configuration
+          errors and conflicts. For example: after removal of the subnet,
+          the server administrator may add a new subnet with the ID used
+          previously for the removed subnet. This means that the existing
+          leases and static reservations will be in conflict with this
+          new subnet. Thus, we recommend that this command is used with extreme
+          caution.
+        </para>
+
+        <para>The command has the following structure:
+<screen>
+{
+    "command": "subnet4-del",
+    "arguments": {
+        "id": 123
+    }
+}
+</screen>
+        </para>
+        <para>
+          The example successful response may look like this:
+<screen>
+{
+    "result": 0,
+    "text": "IPv4 subnet 192.0.2.0/24 (id 123) deleted",
+    "arguments": {
+        "subnets": [
+            {
+                "id": 123,
+                "subnet": "192.0.2.0/24"
+            }
+        ]
+    }
+}
+</screen>
+        </para>
+      </section>
+
+      <section>
+        <title>subnet6-del command</title>
+        <para>
+          This command is used to remove a subnet from the server's configuration.
+          This command has no effect on other configured subnets but removing
+          a subnet has certain implications which the server's administrator
+          should be aware of.
+        </para>
+        <para>
+          In most cases the server has assigned some leases to the clients
+          belonging to the subnet. The server may also be configured with
+          static host reservations which are associated with this subnet.
+          The current implementation of the <command>subnet6-del</command>
+          removes neither the leases nor host reservations associated with
+          a subnet. This is the safest approach because the server doesn't
+          loose track of leases assigned to the clients from this subnet.
+          However, removal of the subnet may still cause configuration
+          errors and conflicts. For example: after removal of the subnet,
+          the server administrator may add a new subnet with the ID used
+          previously for the removed subnet. This means that the existing
+          leases and static reservations will be in conflict with this
+          new subnet. Thus, we recommend that this command is used with extreme
+          caution.
+        </para>
 
 
+        <para>The command has the following structure:
+<screen>
+{
+    "command": "subnet6-del",
+    "arguments": {
+        "id": 234
+    }
+}
+</screen>
+        </para>
+        <para>
+          The example successful response may look like this:
+<screen>
+{
+    "result": 0,
+    "text": "IPv6 subnet 2001:db8:1::/64 (id 234) deleted",
+    "subnets": [
+        {
+            "id": 234,
+            "subnet": "2001:db8:1::/64"
+        }
+    ]
+}
+</screen>
+        </para>
       </section>
       </section>
+
     </section>
     </section>
+</section> <!-- end of subnet commands -->
+
+    <section id="user-context">
+      <title>User contexts</title>
+      <para>Hook libraries can have their own configuration parameters. That is
+      convenient if the parameter applies to the whole library. However,
+      sometimes it is very useful if certain configuration entities are extended
+      with additional configuration data. This is where the concept of user
+      contexts comes in. A sysadmin can define an arbitrary set of data and
+      attach it to Kea structures, as long as the data is specified as JSON map.
+      In particular, it is possible to define fields that are integers, strings,
+      boolean, lists and maps. It is possible to define nested structures of
+      arbitrary complexity. Kea does not use that data on its own, simply stores
+      and makes it available for the hook libraries.
+      </para>
+      <para>
+        As of Kea 1.2, the only structures that allow user contexts are address
+        and prefix pools, but it is expected to extend other structures with the
+        user context capability.
+      </para>
+    </section>
+
+
 
 
-    </section> <section id="user-context"> <title>User contexts</title>
-    <para>Hook libraries can have their own configuration parameters. That is
-    convenient if the parameter applies to the whole library. However, sometimes
-    it is very useful if certain configuration entities are extended with
-    additional configuration data. This is where the concept of user contexts
-    comes in. A sysadmin can define an arbitrary set of data and attach it to
-    Kea structures, as long as the data is specified as JSON map.  In
-    particular, it is possible to define fields that are integers, strings,
-    boolean, lists and maps. It is possible to define nested structures of
-    arbitrary complexity. Kea does not use that data on its own, simply stores
-    and makes it available for the hook libraries.  </para> <para> As of Kea
-    1.2, the only structures that allow user contexts are address and prefix
-    pools, but it is expected to extend other structures with the user context
-    capability.  </para> </section> </chapter> <!-- hooks-libraries -->
+   </chapter> <!-- hooks-libraries -->

+ 0 - 1
src/bin/dhcp4/Makefile.am

@@ -66,7 +66,6 @@ libdhcp4_la_SOURCES += dhcp4to6_ipc.cc dhcp4to6_ipc.h
 libdhcp4_la_SOURCES += dhcp4_lexer.ll location.hh position.hh stack.hh
 libdhcp4_la_SOURCES += dhcp4_lexer.ll location.hh position.hh stack.hh
 libdhcp4_la_SOURCES += dhcp4_parser.cc dhcp4_parser.h
 libdhcp4_la_SOURCES += dhcp4_parser.cc dhcp4_parser.h
 libdhcp4_la_SOURCES += parser_context.cc parser_context.h parser_context_decl.h
 libdhcp4_la_SOURCES += parser_context.cc parser_context.h parser_context_decl.h
-libdhcp4_la_SOURCES += simple_parser4.cc simple_parser4.h
 
 
 nodist_libdhcp4_la_SOURCES = dhcp4_messages.h dhcp4_messages.cc
 nodist_libdhcp4_la_SOURCES = dhcp4_messages.h dhcp4_messages.cc
 EXTRA_DIST += dhcp4_messages.mes
 EXTRA_DIST += dhcp4_messages.mes

+ 2 - 268
src/bin/dhcp4/json_config_parser.cc

@@ -8,7 +8,6 @@
 
 
 #include <cc/command_interpreter.h>
 #include <cc/command_interpreter.h>
 #include <dhcp4/dhcp4_log.h>
 #include <dhcp4/dhcp4_log.h>
-#include <dhcp4/simple_parser4.h>
 #include <dhcp/libdhcp++.h>
 #include <dhcp/libdhcp++.h>
 #include <dhcp/option_definition.h>
 #include <dhcp/option_definition.h>
 #include <dhcpsrv/cfg_option.h>
 #include <dhcpsrv/cfg_option.h>
@@ -21,6 +20,8 @@
 #include <dhcpsrv/parsers/host_reservation_parser.h>
 #include <dhcpsrv/parsers/host_reservation_parser.h>
 #include <dhcpsrv/parsers/host_reservations_list_parser.h>
 #include <dhcpsrv/parsers/host_reservations_list_parser.h>
 #include <dhcpsrv/parsers/ifaces_config_parser.h>
 #include <dhcpsrv/parsers/ifaces_config_parser.h>
+#include <dhcpsrv/parsers/option_data_parser.h>
+#include <dhcpsrv/parsers/simple_parser4.h>
 #include <dhcpsrv/timer_mgr.h>
 #include <dhcpsrv/timer_mgr.h>
 #include <hooks/hooks_parser.h>
 #include <hooks/hooks_parser.h>
 #include <config/command_mgr.h>
 #include <config/command_mgr.h>
@@ -46,273 +47,6 @@ using namespace isc::hooks;
 
 
 namespace {
 namespace {
 
 
-/// @brief Parser for IPv4 pool definitions.
-///
-/// This is the IPv4 derivation of the PoolParser class and handles pool
-/// definitions, i.e. a list of entries of one of two syntaxes: min-max and
-/// prefix/len for IPv4 pools. Pool4 objects are created and stored in chosen
-/// PoolStorage container.
-///
-/// It is useful for parsing Dhcp4/subnet4[X]/pool parameters.
-class Pool4Parser : public PoolParser {
-protected:
-    /// @brief Creates a Pool4 object given a IPv4 prefix and the prefix length.
-    ///
-    /// @param addr is the IPv4 prefix of the pool.
-    /// @param len is the prefix length.
-    /// @param ignored dummy parameter to provide symmetry between the
-    /// PoolParser derivations. The V6 derivation requires a third value.
-    /// @return returns a PoolPtr to the new Pool4 object.
-    PoolPtr poolMaker (IOAddress &addr, uint32_t len, int32_t) {
-        return (PoolPtr(new Pool4(addr, len)));
-    }
-
-    /// @brief Creates a Pool4 object given starting and ending IPv4 addresses.
-    ///
-    /// @param min is the first IPv4 address in the pool.
-    /// @param max is the last IPv4 address in the pool.
-    /// @param ignored dummy parameter to provide symmetry between the
-    /// PoolParser derivations. The V6 derivation requires a third value.
-    /// @return returns a PoolPtr to the new Pool4 object.
-    PoolPtr poolMaker (IOAddress &min, IOAddress &max, int32_t) {
-        return (PoolPtr(new Pool4(min, max)));
-    }
-};
-
-/// @brief Specialization of the pool list parser for DHCPv4
-class Pools4ListParser : PoolsListParser {
-public:
-
-    /// @brief parses the actual structure
-    ///
-    /// This method parses the actual list of pools.
-    ///
-    /// @param pools storage container in which to store the parsed pool.
-    /// @param pools_list a list of pool structures
-    /// @throw isc::dhcp::DhcpConfigError when pool parsing fails
-    void parse(PoolStoragePtr pools,
-               isc::data::ConstElementPtr pools_list) {
-        BOOST_FOREACH(ConstElementPtr pool, pools_list->listValue()) {
-            Pool4Parser parser;
-            parser.parse(pools, pool, AF_INET);
-        }
-    }
-};
-
-/// @anchor Subnet4ConfigParser
-/// @brief This class parses a single IPv4 subnet.
-///
-/// This is the IPv4 derivation of the SubnetConfigParser class and it parses
-/// the whole subnet definition. It creates parsersfor received configuration
-/// parameters as needed.
-class Subnet4ConfigParser : public SubnetConfigParser {
-public:
-    /// @brief Constructor
-    ///
-    /// stores global scope parameters, options, option definitions.
-    Subnet4ConfigParser()
-        :SubnetConfigParser(AF_INET) {
-    }
-
-    /// @brief Parses a single IPv4 subnet configuration and adds to the
-    /// Configuration Manager.
-    ///
-    /// @param subnet A new subnet being configured.
-    /// @return a pointer to created Subnet4 object
-    Subnet4Ptr parse(ConstElementPtr subnet) {
-        /// Parse Pools first.
-        ConstElementPtr pools = subnet->get("pools");
-        if (pools) {
-            Pools4ListParser parser;
-            parser.parse(pools_, pools);
-        }
-
-        SubnetPtr generic = SubnetConfigParser::parse(subnet);
-
-        if (!generic) {
-            isc_throw(DhcpConfigError,
-                      "Failed to create an IPv4 subnet (" <<
-                      subnet->getPosition() << ")");
-        }
-
-        Subnet4Ptr sn4ptr = boost::dynamic_pointer_cast<Subnet4>(subnet_);
-        if (!sn4ptr) {
-            // If we hit this, it is a programming error.
-            isc_throw(Unexpected,
-                      "Invalid Subnet4 cast in Subnet4ConfigParser::parse");
-        }
-
-        // Set relay information if it was parsed
-        if (relay_info_) {
-            sn4ptr->setRelayInfo(*relay_info_);
-        }
-
-        // Parse Host Reservations for this subnet if any.
-        ConstElementPtr reservations = subnet->get("reservations");
-        if (reservations) {
-            HostCollection hosts;
-            HostReservationsListParser<HostReservationParser4> parser;
-            parser.parse(subnet_->getID(), reservations, hosts);
-            for (auto h = hosts.begin(); h != hosts.end(); ++h) {
-                CfgMgr::instance().getStagingCfg()->getCfgHosts()->add(*h);
-            }
-        }
-
-        return (sn4ptr);
-    }
-
-protected:
-
-    /// @brief Instantiates the IPv4 Subnet based on a given IPv4 address
-    /// and prefix length.
-    ///
-    /// @param addr is IPv4 address of the subnet.
-    /// @param len is the prefix length
-    void initSubnet(isc::data::ConstElementPtr params,
-                    isc::asiolink::IOAddress addr, uint8_t len) {
-        // The renew-timer and rebind-timer are optional. If not set, the
-        // option 58 and 59 will not be sent to a client. In this case the
-        // client will use default values based on the valid-lifetime.
-        Triplet<uint32_t> t1 = getInteger(params, "renew-timer");
-        Triplet<uint32_t> t2 = getInteger(params, "rebind-timer");
-
-        // The valid-lifetime is mandatory. It may be specified for a
-        // particular subnet. If not, the global value should be present.
-        // If there is no global value, exception is thrown.
-        Triplet<uint32_t> valid = getInteger(params, "valid-lifetime");
-
-        // Subnet ID is optional. If it is not supplied the value of 0 is used,
-        // which means autogenerate. The value was inserted earlier by calling
-        // SimpleParser4::setAllDefaults.
-        SubnetID subnet_id = static_cast<SubnetID>(getInteger(params, "id"));
-
-        stringstream s;
-        s << addr << "/" << static_cast<int>(len) << " with params: ";
-        // t1 and t2 are optional may be not specified.
-        if (!t1.unspecified()) {
-            s << "t1=" << t1 << ", ";
-        }
-        if (!t2.unspecified()) {
-            s << "t2=" << t2 << ", ";
-        }
-        s <<"valid-lifetime=" << valid;
-
-        LOG_INFO(dhcp4_logger, DHCP4_CONFIG_NEW_SUBNET).arg(s.str());
-
-        Subnet4Ptr subnet4(new Subnet4(addr, len, t1, t2, valid, subnet_id));
-        subnet_ = subnet4;
-
-        // Set the match-client-id value for the subnet. It is always present.
-        // If not explicitly specified, the default value was filled in when
-        // SimpleParser4::setAllDefaults was called.
-        bool match_client_id = getBoolean(params, "match-client-id");
-        subnet4->setMatchClientId(match_client_id);
-
-        // Set next-server. The default value is 0.0.0.0. Nevertheless, the
-        // user could have messed that up by specifying incorrect value.
-        // To avoid using 0.0.0.0, user can specify "".
-        string next_server;
-        try {
-            next_server = getString(params, "next-server");
-            if (!next_server.empty()) {
-                subnet4->setSiaddr(IOAddress(next_server));
-            }
-        } catch (...) {
-            ConstElementPtr next = params->get("next-server");
-            string pos;
-            if (next)
-                pos = next->getPosition().str();
-            else
-                pos = params->getPosition().str();
-            isc_throw(DhcpConfigError, "invalid parameter next-server : "
-                      << next_server << "(" << pos << ")");
-        }
-
-        // 4o6 specific parameter: 4o6-interface. If not explicitly specified,
-        // it will have the default value of "".
-        string iface4o6 = getString(params, "4o6-interface");
-        if (!iface4o6.empty()) {
-            subnet4->get4o6().setIface4o6(iface4o6);
-            subnet4->get4o6().enabled(true);
-        }
-
-        // 4o6 specific parameter: 4o6-subnet. If not explicitly specified, it
-        // will have the default value of "".
-        string subnet4o6 = getString(params, "4o6-subnet");
-        if (!subnet4o6.empty()) {
-            size_t slash = subnet4o6.find("/");
-            if (slash == std::string::npos) {
-                isc_throw(DhcpConfigError, "Missing / in the 4o6-subnet parameter:"
-                          << subnet4o6 << ", expected format: prefix6/length");
-            }
-            string prefix = subnet4o6.substr(0, slash);
-            string lenstr = subnet4o6.substr(slash + 1);
-
-            uint8_t len = 128;
-            try {
-                len = boost::lexical_cast<unsigned int>(lenstr.c_str());
-            } catch (const boost::bad_lexical_cast &) {
-                isc_throw(DhcpConfigError, "Invalid prefix length specified in "
-                          "4o6-subnet parameter: " << subnet4o6 << ", expected 0..128 value");
-            }
-            subnet4->get4o6().setSubnet4o6(IOAddress(prefix), len);
-            subnet4->get4o6().enabled(true);
-        }
-
-        // Try 4o6 specific parameter: 4o6-interface-id
-        std::string ifaceid = getString(params, "4o6-interface-id");
-        if (!ifaceid.empty()) {
-            OptionBuffer tmp(ifaceid.begin(), ifaceid.end());
-            OptionPtr opt(new Option(Option::V6, D6O_INTERFACE_ID, tmp));
-            subnet4->get4o6().setInterfaceId(opt);
-            subnet4->get4o6().enabled(true);
-        }
-
-        /// client-class processing is now generic and handled in the common
-        /// code (see @ref isc::data::SubnetConfigParser::createSubnet)
-    }
-};
-
-/// @brief this class parses list of DHCP4 subnets
-///
-/// This is a wrapper parser that handles the whole list of Subnet4
-/// definitions. It iterates over all entries and creates Subnet4ConfigParser
-/// for each entry.
-class Subnets4ListConfigParser : public isc::data::SimpleParser {
-public:
-
-    /// @brief parses contents of the list
-    ///
-    /// Iterates over all entries on the list, parses its content
-    /// (by instantiating Subnet6ConfigParser) and adds to specified
-    /// configuration.
-    ///
-    /// @param subnets_list pointer to a list of IPv4 subnets
-    /// @return number of subnets created
-    size_t parse(SrvConfigPtr cfg, ConstElementPtr subnets_list) {
-        size_t cnt = 0;
-        BOOST_FOREACH(ConstElementPtr subnet_json, subnets_list->listValue()) {
-
-            Subnet4ConfigParser parser;
-            Subnet4Ptr subnet = parser.parse(subnet_json);
-            if (subnet) {
-
-                // Adding a subnet to the Configuration Manager may fail if the
-                // subnet id is invalid (duplicate). Thus, we catch exceptions
-                // here to append a position in the configuration string.
-                try {
-                    cfg->getCfgSubnets4()->add(subnet);
-                    cnt++;
-                } catch (const std::exception& ex) {
-                    isc_throw(DhcpConfigError, ex.what() << " ("
-                              << subnet_json->getPosition() << ")");
-                }
-            }
-        }
-        return (cnt);
-    }
-};
-
 /// @brief Parser that takes care of global DHCPv4 parameters.
 /// @brief Parser that takes care of global DHCPv4 parameters.
 ///
 ///
 /// See @ref parse method for a list of supported parameters.
 /// See @ref parse method for a list of supported parameters.

+ 1 - 1
src/bin/dhcp4/tests/get_config_unittest.cc

@@ -12,11 +12,11 @@
 #include <cc/cfg_to_element.h>
 #include <cc/cfg_to_element.h>
 #include <dhcp/tests/iface_mgr_test_config.h>
 #include <dhcp/tests/iface_mgr_test_config.h>
 #include <dhcpsrv/cfgmgr.h>
 #include <dhcpsrv/cfgmgr.h>
+#include <dhcpsrv/parsers/simple_parser4.h>
 #include <dhcp4/tests/dhcp4_test_utils.h>
 #include <dhcp4/tests/dhcp4_test_utils.h>
 #include <dhcp4/tests/get_config_unittest.h>
 #include <dhcp4/tests/get_config_unittest.h>
 #include <dhcp4/dhcp4_srv.h>
 #include <dhcp4/dhcp4_srv.h>
 #include <dhcp4/json_config_parser.h>
 #include <dhcp4/json_config_parser.h>
-#include <dhcp4/simple_parser4.h>
 
 
 #include <boost/algorithm/string.hpp>
 #include <boost/algorithm/string.hpp>
 #include <gtest/gtest.h>
 #include <gtest/gtest.h>

+ 1 - 1
src/bin/dhcp4/tests/simple_parser4_unittest.cc

@@ -6,7 +6,7 @@
 
 
 #include <config.h>
 #include <config.h>
 #include <gtest/gtest.h>
 #include <gtest/gtest.h>
-#include <dhcp4/simple_parser4.h>
+#include <dhcpsrv/parsers/simple_parser4.h>
 #include <dhcp4/tests/dhcp4_test_utils.h>
 #include <dhcp4/tests/dhcp4_test_utils.h>
 #include <cc/data.h>
 #include <cc/data.h>
 
 

+ 0 - 2
src/bin/dhcp6/Makefile.am

@@ -67,8 +67,6 @@ libdhcp6_la_SOURCES += dhcp6to4_ipc.cc dhcp6to4_ipc.h
 libdhcp6_la_SOURCES += dhcp6_lexer.ll location.hh position.hh stack.hh
 libdhcp6_la_SOURCES += dhcp6_lexer.ll location.hh position.hh stack.hh
 libdhcp6_la_SOURCES += dhcp6_parser.cc dhcp6_parser.h
 libdhcp6_la_SOURCES += dhcp6_parser.cc dhcp6_parser.h
 libdhcp6_la_SOURCES += parser_context.cc parser_context.h parser_context_decl.h
 libdhcp6_la_SOURCES += parser_context.cc parser_context.h parser_context_decl.h
-libdhcp6_la_SOURCES += simple_parser6.cc simple_parser6.h
-
 nodist_libdhcp6_la_SOURCES = dhcp6_messages.h dhcp6_messages.cc
 nodist_libdhcp6_la_SOURCES = dhcp6_messages.h dhcp6_messages.cc
 EXTRA_DIST += dhcp6_messages.mes
 EXTRA_DIST += dhcp6_messages.mes
 
 

+ 0 - 9
src/bin/dhcp6/dhcp6_messages.mes

@@ -97,15 +97,6 @@ If this is an initial configuration (during server's startup) the server
 will fail to start. If this is a dynamic reconfiguration attempt the
 will fail to start. If this is a dynamic reconfiguration attempt the
 server will continue to use an old configuration.
 server will continue to use an old configuration.
 
 
-% 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_OPTION_DUPLICATE multiple options with the code: %1 added to the subnet: %2
-This warning message is issued on an attempt to configure multiple options with the
-same option code for the particular subnet. Adding multiple options is uncommon
-for DHCPv6, but it is not prohibited.
-
 % DHCP6_CONFIG_RECEIVED received configuration: %1
 % DHCP6_CONFIG_RECEIVED received configuration: %1
 A debug message listing the configuration received by the DHCPv6 server.
 A debug message listing the configuration received by the DHCPv6 server.
 The source of that configuration depends on used configuration backend.
 The source of that configuration depends on used configuration backend.

+ 2 - 386
src/bin/dhcp6/json_config_parser.cc

@@ -13,7 +13,6 @@
 #include <dhcp/libdhcp++.h>
 #include <dhcp/libdhcp++.h>
 #include <dhcp6/json_config_parser.h>
 #include <dhcp6/json_config_parser.h>
 #include <dhcp6/dhcp6_log.h>
 #include <dhcp6/dhcp6_log.h>
-#include <dhcp6/simple_parser6.h>
 #include <dhcp/iface_mgr.h>
 #include <dhcp/iface_mgr.h>
 #include <dhcpsrv/cfg_option.h>
 #include <dhcpsrv/cfg_option.h>
 #include <dhcpsrv/cfgmgr.h>
 #include <dhcpsrv/cfgmgr.h>
@@ -30,6 +29,8 @@
 #include <dhcpsrv/parsers/host_reservation_parser.h>
 #include <dhcpsrv/parsers/host_reservation_parser.h>
 #include <dhcpsrv/parsers/host_reservations_list_parser.h>
 #include <dhcpsrv/parsers/host_reservations_list_parser.h>
 #include <dhcpsrv/parsers/ifaces_config_parser.h>
 #include <dhcpsrv/parsers/ifaces_config_parser.h>
+#include <dhcpsrv/parsers/option_data_parser.h>
+#include <dhcpsrv/parsers/simple_parser6.h>
 #include <hooks/hooks_parser.h>
 #include <hooks/hooks_parser.h>
 #include <log/logger_support.h>
 #include <log/logger_support.h>
 #include <util/encode/hex.h>
 #include <util/encode/hex.h>
@@ -63,391 +64,6 @@ typedef boost::shared_ptr<BooleanParser> BooleanParserPtr;
 typedef boost::shared_ptr<StringParser> StringParserPtr;
 typedef boost::shared_ptr<StringParser> StringParserPtr;
 typedef boost::shared_ptr<Uint32Parser> Uint32ParserPtr;
 typedef boost::shared_ptr<Uint32Parser> Uint32ParserPtr;
 
 
-/// @brief Parser for IPv6 pool definitions.
-///
-/// This is the IPv6 derivation of the PoolParser class and handles pool
-/// definitions, i.e. a list of entries of one of two syntaxes: min-max and
-/// prefix/len for IPv6 pools. Pool6 objects are created and stored in chosen
-/// PoolStorage container.
-///
-/// It is useful for parsing Dhcp6/subnet6[X]/pool parameters.
-class Pool6Parser : public PoolParser {
-protected:
-    /// @brief Creates a Pool6 object given a IPv6 prefix and the prefix length.
-    ///
-    /// @param addr is the IPv6 prefix of the pool.
-    /// @param len is the prefix length.
-    /// @param ptype is the type of IPv6 pool (Pool::PoolType). Note this is
-    /// passed in as an int32_t and cast to PoolType to accommodate a
-    /// polymorphic interface.
-    /// @return returns a PoolPtr to the new Pool4 object.
-    PoolPtr poolMaker (IOAddress &addr, uint32_t len, int32_t ptype)
-    {
-        return (PoolPtr(new Pool6(static_cast<isc::dhcp::Lease::Type>
-                                  (ptype), addr, len)));
-    }
-
-    /// @brief Creates a Pool6 object given starting and ending IPv6 addresses.
-    ///
-    /// @param min is the first IPv6 address in the pool.
-    /// @param max is the last IPv6 address in the pool.
-    /// @param ptype is the type of IPv6 pool (Pool::PoolType). Note this is
-    /// passed in as an int32_t and cast to PoolType to accommodate a
-    /// polymorphic interface.
-    /// @return returns a PoolPtr to the new Pool4 object.
-    PoolPtr poolMaker (IOAddress &min, IOAddress &max, int32_t ptype)
-    {
-        return (PoolPtr(new Pool6(static_cast<isc::dhcp::Lease::Type>
-                                  (ptype), min, max)));
-    }
-};
-
-/// @brief Specialization of the pool list parser for DHCPv6
-class Pools6ListParser : PoolsListParser {
-public:
-
-    /// @brief parses the actual structure
-    ///
-    /// This method parses the actual list of pools.
-    ///
-    /// @param pools storage container in which to store the parsed pool.
-    /// @param pools_list a list of pool structures
-    /// @throw isc::dhcp::DhcpConfigError when pool parsing fails
-    void parse(PoolStoragePtr pools,
-               isc::data::ConstElementPtr pools_list) {
-        BOOST_FOREACH(ConstElementPtr pool, pools_list->listValue()) {
-            Pool6Parser parser;
-            parser.parse(pools, pool, AF_INET6);
-        }
-    }
-};
-
-/// @brief Parser for IPv6 prefix delegation definitions.
-///
-/// This class handles prefix delegation pool definitions for IPv6 subnets
-/// Pool6 objects are created and stored in the given PoolStorage container.
-///
-/// PdPool definitions currently support three elements: prefix, prefix-len,
-/// and delegated-len, as shown in the example JSON text below:
-///
-/// @code
-///
-/// {
-///     "prefix": "2001:db8:1::",
-///     "prefix-len": 64,
-///     "delegated-len": 128
-/// }
-/// @endcode
-///
-class PdPoolParser : public isc::data::SimpleParser {
-public:
-
-    /// @brief Constructor.
-    ///
-    PdPoolParser() : options_(new CfgOption()) {
-    }
-
-    /// @brief Builds a prefix delegation pool from the given configuration
-    ///
-    /// This function parses configuration entries and creates an instance
-    /// of a dhcp::Pool6 configured for prefix delegation.
-    ///
-    /// @param pools storage container in which to store the parsed pool.
-    /// @param pd_pool_ pointer to an element that holds configuration entries
-    /// that define a prefix delegation pool.
-    ///
-    /// @throw DhcpConfigError if configuration parsing fails.
-    void parse(PoolStoragePtr pools, ConstElementPtr pd_pool_) {
-        std::string addr_str = getString(pd_pool_, "prefix");
-
-        uint8_t prefix_len = getUint8(pd_pool_, "prefix-len");
-
-        uint8_t delegated_len = getUint8(pd_pool_, "delegated-len");
-
-        std::string excluded_prefix_str = "::";
-        if (pd_pool_->contains("excluded-prefix")) {
-            excluded_prefix_str = getString(pd_pool_, "excluded-prefix");
-        }
-
-        uint8_t excluded_prefix_len = 0;
-        if (pd_pool_->contains("excluded-prefix-len")) {
-            excluded_prefix_len = getUint8(pd_pool_, "excluded-prefix-len");
-        }
-
-        ConstElementPtr option_data = pd_pool_->get("option-data");
-        if (option_data) {
-            OptionDataListParser opts_parser(AF_INET6);
-            opts_parser.parse(options_, option_data);
-        }
-                    
-        ConstElementPtr user_context = pd_pool_->get("user-context");
-        if (user_context) {
-            user_context_ = user_context;
-        }
-
-        // Check the pool parameters. It will throw an exception if any
-        // of the required parameters are invalid.
-        try {
-            // Attempt to construct the local pool.
-            pool_.reset(new Pool6(IOAddress(addr_str),
-                                  prefix_len,
-                                  delegated_len,
-                                  IOAddress(excluded_prefix_str),
-                                  excluded_prefix_len));
-            // Merge options specified for a pool into pool configuration.
-            options_->copyTo(*pool_->getCfgOption());
-        } catch (const std::exception& ex) {
-            // Some parameters don't exist or are invalid. Since we are not
-            // aware whether they don't exist or are invalid, let's append
-            // the position of the pool map element.
-            isc_throw(isc::dhcp::DhcpConfigError, ex.what()
-                      << " (" << pd_pool_->getPosition() << ")");
-        }
-
-        if (user_context_) {
-            pool_->setUserContext(user_context_);
-        }
-
-        // Add the local pool to the external storage ptr.
-        pools->push_back(pool_);
-    }
-
-private:
-
-    /// Pointer to the created pool object.
-    isc::dhcp::Pool6Ptr pool_;
-
-    /// A storage for pool specific option values.
-    CfgOptionPtr options_;
-
-    isc::data::ConstElementPtr user_context_;
-};
-
-/// @brief Parser for a list of prefix delegation pools.
-///
-/// This parser iterates over a list of prefix delegation pool entries and
-/// creates pool instances for each one. If the parsing is successful, the
-/// collection of pools is committed to the provided storage.
-class PdPoolsListParser : public PoolsListParser {
-public:
-
-    /// @brief Parse configuration entries.
-    ///
-    /// This function parses configuration entries and creates instances
-    /// of prefix delegation pools .
-    ///
-    /// @param storage is the pool storage in which to store the parsed
-    /// @param pd_pool_list pointer to an element that holds entries
-    /// that define a prefix delegation pool.
-    ///
-    /// @throw DhcpConfigError if configuration parsing fails.
-    void parse(PoolStoragePtr pools,
-               isc::data::ConstElementPtr pd_pool_list) {
-        // Loop through the list of pd pools.
-        BOOST_FOREACH(ConstElementPtr pd_pool, pd_pool_list->listValue()) {
-            PdPoolParser parser;
-            parser.parse(pools, pd_pool);
-        }
-    }
-};
-
-/// @anchor Subnet6ConfigParser
-/// @brief This class parses a single IPv6 subnet.
-///
-/// This is the IPv6 derivation of the SubnetConfigParser class and it parses
-/// the whole subnet definition. It creates parsersfor received configuration
-/// parameters as needed.
-class Subnet6ConfigParser : public SubnetConfigParser {
-public:
-
-    /// @brief Constructor
-    ///
-    /// stores global scope parameters, options, option definitions.
-    Subnet6ConfigParser()
-        :SubnetConfigParser(AF_INET6) {
-    }
-
-    /// @brief Parses a single IPv6 subnet configuration and adds to the
-    /// Configuration Manager.
-    ///
-    /// @param subnet A new subnet being configured.
-    /// @return a pointer to created Subnet6 object
-    Subnet6Ptr parse(ConstElementPtr subnet) {
-        /// Parse all pools first.
-        ConstElementPtr pools = subnet->get("pools");
-        if (pools) {
-            Pools6ListParser parser;
-            parser.parse(pools_, pools);
-        }
-        ConstElementPtr pd_pools = subnet->get("pd-pools");
-        if (pd_pools) {
-            PdPoolsListParser parser;
-            parser.parse(pools_, pd_pools);
-        }
-
-        SubnetPtr generic = SubnetConfigParser::parse(subnet);
-
-        if (!generic) {
-            isc_throw(DhcpConfigError,
-                      "Failed to create an IPv6 subnet (" <<
-                      subnet->getPosition() << ")");
-        }
-
-        Subnet6Ptr sn6ptr = boost::dynamic_pointer_cast<Subnet6>(subnet_);
-        if (!sn6ptr) {
-            // If we hit this, it is a programming error.
-            isc_throw(Unexpected,
-                      "Invalid Subnet6 cast in Subnet6ConfigParser::parse");
-        }
-
-        // Set relay information if it was provided
-        if (relay_info_) {
-            sn6ptr->setRelayInfo(*relay_info_);
-        }
-
-
-        // Parse Host Reservations for this subnet if any.
-        ConstElementPtr reservations = subnet->get("reservations");
-        if (reservations) {
-            HostCollection hosts;
-            HostReservationsListParser<HostReservationParser6> parser;
-            parser.parse(subnet_->getID(), reservations, hosts);
-            for (auto h = hosts.begin(); h != hosts.end(); ++h) {
-                CfgMgr::instance().getStagingCfg()->getCfgHosts()->add(*h);
-            }
-        }
-
-        return (sn6ptr);
-    }
-
-protected:
-    /// @brief Issues a DHCP6 server specific warning regarding duplicate subnet
-    /// options.
-    ///
-    /// @param code is the numeric option code of the duplicate option
-    /// @param addr is the subnet address
-    /// @todo A means to know the correct logger and perhaps a common
-    /// message would allow this message to be emitted by the base class.
-    virtual void duplicate_option_warning(uint32_t code,
-                                         isc::asiolink::IOAddress& addr) {
-        LOG_WARN(dhcp6_logger, DHCP6_CONFIG_OPTION_DUPLICATE)
-            .arg(code).arg(addr.toText());
-    }
-
-    /// @brief Instantiates the IPv6 Subnet based on a given IPv6 address
-    /// and prefix length.
-    ///
-    /// @param addr is IPv6 prefix of the subnet.
-    /// @param len is the prefix length
-    void initSubnet(isc::data::ConstElementPtr params,
-                    isc::asiolink::IOAddress addr, uint8_t len) {
-        // Get all 'time' parameters using inheritance.
-        // If the subnet-specific value is defined then use it, else
-        // use the global value. The global value must always be
-        // present. If it is not, it is an internal error and exception
-        // is thrown.
-        Triplet<uint32_t> t1 = getInteger(params, "renew-timer");
-        Triplet<uint32_t> t2 = getInteger(params, "rebind-timer");
-        Triplet<uint32_t> pref = getInteger(params, "preferred-lifetime");
-        Triplet<uint32_t> valid = getInteger(params, "valid-lifetime");
-
-        // Subnet ID is optional. If it is not supplied the value of 0 is used,
-        // which means autogenerate. The value was inserted earlier by calling
-        // SimpleParser6::setAllDefaults.
-        SubnetID subnet_id = static_cast<SubnetID>(getInteger(params, "id"));
-
-        // We want to log whether rapid-commit is enabled, so we get this
-        // before the actual subnet creation.
-        bool rapid_commit = getBoolean(params, "rapid-commit");
-
-        std::ostringstream output;
-        output << addr << "/" << static_cast<int>(len)
-               << " with params t1=" << t1 << ", t2="
-               << t2 << ", preferred-lifetime=" << pref
-               << ", valid-lifetime=" << valid
-               << ", rapid-commit is " << (rapid_commit ? "enabled" : "disabled");
-
-
-        LOG_INFO(dhcp6_logger, DHCP6_CONFIG_NEW_SUBNET).arg(output.str());
-
-        // Create a new subnet.
-        Subnet6* subnet6 = new Subnet6(addr, len, t1, t2, pref, valid,
-                                       subnet_id);
-        subnet_.reset(subnet6);
-
-        // Enable or disable Rapid Commit option support for the subnet.
-        subnet6->setRapidCommit(rapid_commit);
-
-        // Get interface-id option content. For now we support string
-        // representation only
-        std::string ifaceid = getString(params, "interface-id");
-        std::string iface = getString(params, "interface");
-
-        // Specifying both interface for locally reachable subnets and
-        // interface id for relays is mutually exclusive. Need to test for
-        // this condition.
-        if (!ifaceid.empty() && !iface.empty()) {
-            isc_throw(isc::dhcp::DhcpConfigError,
-                      "parser error: interface (defined for locally reachable "
-                      "subnets) and interface-id (defined for subnets reachable"
-                      " via relays) cannot be defined at the same time for "
-                      "subnet " << addr << "/" << (int)len << "("
-                      << params->getPosition() << ")");
-        }
-
-        // Configure interface-id for remote interfaces, if defined
-        if (!ifaceid.empty()) {
-            OptionBuffer tmp(ifaceid.begin(), ifaceid.end());
-            OptionPtr opt(new Option(Option::V6, D6O_INTERFACE_ID, tmp));
-            subnet6->setInterfaceId(opt);
-        }
-
-        /// client-class processing is now generic and handled in the common
-        /// code (see @ref isc::data::SubnetConfigParser::createSubnet)
-    }
-
-};
-
-
-/// @brief this class parses a list of DHCP6 subnets
-///
-/// This is a wrapper parser that handles the whole list of Subnet6
-/// definitions. It iterates over all entries and creates Subnet6ConfigParser
-/// for each entry.
-class Subnets6ListConfigParser : public isc::data::SimpleParser {
-public:
-
-    /// @brief parses contents of the list
-    ///
-    /// Iterates over all entries on the list, parses its content
-    /// (by instantiating Subnet6ConfigParser) and adds to specified
-    /// configuration.
-    ///
-    /// @param cfg configuration (parsed subnets will be stored here)
-    /// @param subnets_list pointer to a list of IPv6 subnets
-    /// @throw DhcpConfigError if CfgMgr rejects the subnet (e.g. subnet-id is a duplicate)
-    size_t parse(SrvConfigPtr cfg, ConstElementPtr subnets_list) {
-        size_t cnt = 0;
-        BOOST_FOREACH(ConstElementPtr subnet_json, subnets_list->listValue()) {
-
-            Subnet6ConfigParser parser;
-            Subnet6Ptr subnet = parser.parse(subnet_json);
-
-            // Adding a subnet to the Configuration Manager may fail if the
-            // subnet id is invalid (duplicate). Thus, we catch exceptions
-            // here to append a position in the configuration string.
-            try {
-                cfg->getCfgSubnets6()->add(subnet);
-                cnt++;
-            } catch (const std::exception& ex) {
-                isc_throw(DhcpConfigError, ex.what() << " ("
-                          << subnet_json->getPosition() << ")");
-            }
-        }
-        return (cnt);
-    }
-};
-
 /// @brief Parser for list of RSOO options
 /// @brief Parser for list of RSOO options
 ///
 ///
 /// This parser handles a Dhcp6/relay-supplied-options entry. It contains a
 /// This parser handles a Dhcp6/relay-supplied-options entry. It contains a

+ 1 - 1
src/bin/dhcp6/tests/get_config_unittest.cc

@@ -11,12 +11,12 @@
 #include <cc/simple_parser.h>
 #include <cc/simple_parser.h>
 #include <cc/cfg_to_element.h>
 #include <cc/cfg_to_element.h>
 #include <dhcp/tests/iface_mgr_test_config.h>
 #include <dhcp/tests/iface_mgr_test_config.h>
+#include <dhcpsrv/parsers/simple_parser6.h>
 #include <dhcpsrv/cfgmgr.h>
 #include <dhcpsrv/cfgmgr.h>
 #include <dhcp6/tests/dhcp6_test_utils.h>
 #include <dhcp6/tests/dhcp6_test_utils.h>
 #include <dhcp6/tests/get_config_unittest.h>
 #include <dhcp6/tests/get_config_unittest.h>
 #include <dhcp6/dhcp6_srv.h>
 #include <dhcp6/dhcp6_srv.h>
 #include <dhcp6/json_config_parser.h>
 #include <dhcp6/json_config_parser.h>
-#include <dhcp6/simple_parser6.h>
 
 
 #include <boost/algorithm/string.hpp>
 #include <boost/algorithm/string.hpp>
 #include <gtest/gtest.h>
 #include <gtest/gtest.h>

+ 1 - 1
src/bin/dhcp6/tests/simple_parser6_unittest.cc

@@ -6,7 +6,7 @@
 
 
 #include <config.h>
 #include <config.h>
 #include <gtest/gtest.h>
 #include <gtest/gtest.h>
-#include <dhcp6/simple_parser6.h>
+#include <dhcpsrv/parsers/simple_parser6.h>
 #include <dhcp6/tests/dhcp6_test_utils.h>
 #include <dhcp6/tests/dhcp6_test_utils.h>
 #include <cc/data.h>
 #include <cc/data.h>
 
 

+ 11 - 0
src/lib/dhcpsrv/Makefile.am

@@ -46,6 +46,11 @@ EXTRA_DIST += parsers/host_reservation_parser.h
 EXTRA_DIST += parsers/host_reservations_list_parser.h
 EXTRA_DIST += parsers/host_reservations_list_parser.h
 EXTRA_DIST += parsers/ifaces_config_parser.cc
 EXTRA_DIST += parsers/ifaces_config_parser.cc
 EXTRA_DIST += parsers/ifaces_config_parser.h
 EXTRA_DIST += parsers/ifaces_config_parser.h
+EXTRA_DIST += parsers/option_data_parser.h
+EXTRA_DIST += parsers/simple_parser4.cc
+EXTRA_DIST += parsers/simple_parser4.h
+EXTRA_DIST += parsers/simple_parser6.cc
+EXTRA_DIST += parsers/simple_parser6.h
 
 
 # Devel guide diagrams
 # Devel guide diagrams
 EXTRA_DIST += images/pgsql_host_data_source.svg
 EXTRA_DIST += images/pgsql_host_data_source.svg
@@ -177,6 +182,12 @@ libkea_dhcpsrv_la_SOURCES += parsers/host_reservation_parser.h
 libkea_dhcpsrv_la_SOURCES += parsers/host_reservations_list_parser.h
 libkea_dhcpsrv_la_SOURCES += parsers/host_reservations_list_parser.h
 libkea_dhcpsrv_la_SOURCES += parsers/ifaces_config_parser.cc
 libkea_dhcpsrv_la_SOURCES += parsers/ifaces_config_parser.cc
 libkea_dhcpsrv_la_SOURCES += parsers/ifaces_config_parser.h
 libkea_dhcpsrv_la_SOURCES += parsers/ifaces_config_parser.h
+libkea_dhcpsrv_la_SOURCES += parsers/option_data_parser.cc
+libkea_dhcpsrv_la_SOURCES += parsers/option_data_parser.h
+libkea_dhcpsrv_la_SOURCES += parsers/simple_parser4.cc
+libkea_dhcpsrv_la_SOURCES += parsers/simple_parser4.h
+libkea_dhcpsrv_la_SOURCES += parsers/simple_parser6.cc
+libkea_dhcpsrv_la_SOURCES += parsers/simple_parser6.h
 
 
 nodist_libkea_dhcpsrv_la_SOURCES = alloc_engine__messages.h
 nodist_libkea_dhcpsrv_la_SOURCES = alloc_engine__messages.h
 nodist_libkea_dhcpsrv_la_SOURCES += alloc_engine_messages.cc
 nodist_libkea_dhcpsrv_la_SOURCES += alloc_engine_messages.cc

+ 22 - 14
src/lib/dhcpsrv/cfg_subnets4.cc

@@ -23,17 +23,36 @@ namespace dhcp {
 
 
 void
 void
 CfgSubnets4::add(const Subnet4Ptr& subnet) {
 CfgSubnets4::add(const Subnet4Ptr& subnet) {
-    /// @todo: Check that this new subnet does not cross boundaries of any
-    /// other already defined subnet.
-    if (isDuplicate(*subnet)) {
+    if (getBySubnetId(subnet->getID())) {
         isc_throw(isc::dhcp::DuplicateSubnetID, "ID of the new IPv4 subnet '"
         isc_throw(isc::dhcp::DuplicateSubnetID, "ID of the new IPv4 subnet '"
                   << subnet->getID() << "' is already in use");
                   << subnet->getID() << "' is already in use");
+
+    } else if (getByPrefix(subnet->toText())) {
+        /// @todo: Check that this new subnet does not cross boundaries of any
+        /// other already defined subnet.
+        isc_throw(isc::dhcp::DuplicateSubnetID, "subnet with the prefix of '"
+                  << subnet->toText() << "' already exists");
     }
     }
+
     LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE, DHCPSRV_CFGMGR_ADD_SUBNET4)
     LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE, DHCPSRV_CFGMGR_ADD_SUBNET4)
               .arg(subnet->toText());
               .arg(subnet->toText());
     subnets_.push_back(subnet);
     subnets_.push_back(subnet);
 }
 }
 
 
+void
+CfgSubnets4::del(const ConstSubnet4Ptr& subnet) {
+    auto& index = subnets_.get<SubnetSubnetIdIndexTag>();
+    auto subnet_it = index.find(subnet->getID());
+    if (subnet_it == index.end()) {
+        isc_throw(BadValue, "no subnet with ID of '" << subnet->getID()
+                  << "' found");
+    }
+    index.erase(subnet_it);
+
+    LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE, DHCPSRV_CFGMGR_DEL_SUBNET4)
+        .arg(subnet->toText());
+}
+
 ConstSubnet4Ptr
 ConstSubnet4Ptr
 CfgSubnets4::getBySubnetId(const SubnetID& subnet_id) const {
 CfgSubnets4::getBySubnetId(const SubnetID& subnet_id) const {
     const auto& index = subnets_.get<SubnetSubnetIdIndexTag>();
     const auto& index = subnets_.get<SubnetSubnetIdIndexTag>();
@@ -246,17 +265,6 @@ CfgSubnets4::selectSubnet(const IOAddress& address,
     return (Subnet4Ptr());
     return (Subnet4Ptr());
 }
 }
 
 
-bool
-CfgSubnets4::isDuplicate(const Subnet4& subnet) const {
-    for (Subnet4Collection::const_iterator subnet_it = subnets_.begin();
-         subnet_it != subnets_.end(); ++subnet_it) {
-        if ((*subnet_it)->getID() == subnet.getID()) {
-            return (true);
-        }
-    }
-    return (false);
-}
-
 void
 void
 CfgSubnets4::removeStatistics() {
 CfgSubnets4::removeStatistics() {
     using namespace isc::stats;
     using namespace isc::stats;

+ 7 - 8
src/lib/dhcpsrv/cfg_subnets4.h

@@ -39,6 +39,13 @@ public:
     /// duplicates id of an existing subnet.
     /// duplicates id of an existing subnet.
     void add(const Subnet4Ptr& subnet);
     void add(const Subnet4Ptr& subnet);
 
 
+    /// @brief Removes subnet from the configuration.
+    ///
+    /// @param subnet Pointer to the subnet to be removed.
+    ///
+    /// @throw isc::BadValue if such subnet doesn't exist.
+    void del(const ConstSubnet4Ptr& subnet);
+
     /// @brief Returns pointer to the collection of all IPv4 subnets.
     /// @brief Returns pointer to the collection of all IPv4 subnets.
     ///
     ///
     /// This is used in a hook (subnet4_select), where the hook is able
     /// This is used in a hook (subnet4_select), where the hook is able
@@ -235,14 +242,6 @@ public:
 
 
 private:
 private:
 
 
-    /// @brief Checks that the IPv4 subnet with the given id already exists.
-    ///
-    /// @param subnet Subnet for which this function will check if the other
-    /// subnet with equal id already exists.
-    ///
-    /// @return true if the duplicate subnet exists.
-    bool isDuplicate(const Subnet4& subnet) const;
-
     /// @brief A container for IPv4 subnets.
     /// @brief A container for IPv4 subnets.
     Subnet4Collection subnets_;
     Subnet4Collection subnets_;
 
 

+ 22 - 15
src/lib/dhcpsrv/cfg_subnets6.cc

@@ -22,17 +22,36 @@ namespace dhcp {
 
 
 void
 void
 CfgSubnets6::add(const Subnet6Ptr& subnet) {
 CfgSubnets6::add(const Subnet6Ptr& subnet) {
-    /// @todo: Check that this new subnet does not cross boundaries of any
-    /// other already defined subnet.
-    if (isDuplicate(*subnet)) {
+    if (getBySubnetId(subnet->getID())) {
         isc_throw(isc::dhcp::DuplicateSubnetID, "ID of the new IPv6 subnet '"
         isc_throw(isc::dhcp::DuplicateSubnetID, "ID of the new IPv6 subnet '"
                   << subnet->getID() << "' is already in use");
                   << subnet->getID() << "' is already in use");
+
+    } else if (getByPrefix(subnet->toText())) {
+        /// @todo: Check that this new subnet does not cross boundaries of any
+        /// other already defined subnet.
+        isc_throw(isc::dhcp::DuplicateSubnetID, "subnet with the prefix of '"
+                  << subnet->toText() << "' already exists");
     }
     }
+
     LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE, DHCPSRV_CFGMGR_ADD_SUBNET6)
     LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE, DHCPSRV_CFGMGR_ADD_SUBNET6)
               .arg(subnet->toText());
               .arg(subnet->toText());
     subnets_.push_back(subnet);
     subnets_.push_back(subnet);
 }
 }
 
 
+void
+CfgSubnets6::del(const ConstSubnet6Ptr& subnet) {
+    auto& index = subnets_.get<SubnetSubnetIdIndexTag>();
+    auto subnet_it = index.find(subnet->getID());
+    if (subnet_it == index.end()) {
+        isc_throw(BadValue, "no subnet with ID of '" << subnet->getID()
+                  << "' found");
+    }
+    index.erase(subnet_it);
+
+    LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE, DHCPSRV_CFGMGR_DEL_SUBNET6)
+        .arg(subnet->toText());
+}
+
 ConstSubnet6Ptr
 ConstSubnet6Ptr
 CfgSubnets6::getBySubnetId(const SubnetID& subnet_id) const {
 CfgSubnets6::getBySubnetId(const SubnetID& subnet_id) const {
     const auto& index = subnets_.get<SubnetSubnetIdIndexTag>();
     const auto& index = subnets_.get<SubnetSubnetIdIndexTag>();
@@ -193,18 +212,6 @@ CfgSubnets6::getSubnet(const SubnetID id) const {
     return (Subnet6Ptr());
     return (Subnet6Ptr());
 }
 }
 
 
-
-bool
-CfgSubnets6::isDuplicate(const Subnet6& subnet) const {
-    for (Subnet6Collection::const_iterator subnet_it = subnets_.begin();
-         subnet_it != subnets_.end(); ++subnet_it) {
-        if ((*subnet_it)->getID() == subnet.getID()) {
-            return (true);
-        }
-    }
-    return (false);
-}
-
 void
 void
 CfgSubnets6::removeStatistics() {
 CfgSubnets6::removeStatistics() {
     using namespace isc::stats;
     using namespace isc::stats;

+ 7 - 8
src/lib/dhcpsrv/cfg_subnets6.h

@@ -40,6 +40,13 @@ public:
     /// duplicates id of an existing subnet.
     /// duplicates id of an existing subnet.
     void add(const Subnet6Ptr& subnet);
     void add(const Subnet6Ptr& subnet);
 
 
+    /// @brief Removes subnet from the configuration.
+    ///
+    /// @param subnet Pointer to the subnet to be removed.
+    ///
+    /// @throw isc::BadValue if such subnet doesn't exist.
+    void del(const ConstSubnet6Ptr& subnet);
+
     /// @brief Returns pointer to the collection of all IPv6 subnets.
     /// @brief Returns pointer to the collection of all IPv6 subnets.
     ///
     ///
     /// This is used in a hook (subnet6_select), where the hook is able
     /// This is used in a hook (subnet6_select), where the hook is able
@@ -241,14 +248,6 @@ private:
     selectSubnet(const OptionPtr& interface_id,
     selectSubnet(const OptionPtr& interface_id,
                  const ClientClasses& client_classes) const;
                  const ClientClasses& client_classes) const;
 
 
-    /// @brief Checks that the IPv6 subnet with the given id already exists.
-    ///
-    /// @param subnet Subnet for which this function will check if the other
-    /// subnet with equal id already exists.
-    ///
-    /// @return true if the duplicate subnet exists.
-    bool isDuplicate(const Subnet6& subnet) const;
-
     /// @brief A container for IPv6 subnets.
     /// @brief A container for IPv6 subnets.
     Subnet6Collection subnets_;
     Subnet6Collection subnets_;
 
 

+ 1 - 1
src/lib/dhcpsrv/cfgmgr.cc

@@ -150,7 +150,7 @@ CfgMgr::revert(const size_t index) {
     commit();
     commit();
 }
 }
 
 
-ConstSrvConfigPtr
+SrvConfigPtr
 CfgMgr::getCurrentCfg() {
 CfgMgr::getCurrentCfg() {
     ensureCurrentAllocated();
     ensureCurrentAllocated();
     return (configuration_);
     return (configuration_);

+ 20 - 4
src/lib/dhcpsrv/cfgmgr.h

@@ -196,10 +196,26 @@ public:
     ///
     ///
     /// This function returns pointer to the current configuration. If the
     /// This function returns pointer to the current configuration. If the
     /// current configuration is not set it will create a default configuration
     /// current configuration is not set it will create a default configuration
-    /// and return it. Current configuration returned is read-only.
-    ///
-    /// @return Non-null const pointer to the current configuration.
-    ConstSrvConfigPtr getCurrentCfg();
+    /// and return it.
+    ///
+    /// In the previous Kea releases this method used to return a const pointer
+    /// to the current configuration to ensure that it is not accidentally
+    /// modified while the server is running. This has been changed in Kea 1.3
+    /// release and now this function returns a non-const pointer. The reason
+    /// is that there are certain use cases when current configuration must
+    /// be modified without going through a full cycle of server
+    /// reconfiguration, e.g. add a subnet to the current configuration as
+    /// a result of receiving a command over control API. In such case the
+    /// performance of processing such command is critical and rebuilding the
+    /// whole configuration just for this small configuration change is out
+    /// of question.
+    ///
+    /// Nevertheless, such configuration updates should always be made with
+    /// caution and one has to make sure that the configuration data integrity
+    /// is preserved.
+    ///
+    /// @return Non-null pointer to the current configuration.
+    SrvConfigPtr getCurrentCfg();
 
 
     /// @brief Returns a pointer to the staging configuration.
     /// @brief Returns a pointer to the staging configuration.
     ///
     ///

+ 20 - 0
src/lib/dhcpsrv/dhcpsrv_messages.mes

@@ -44,11 +44,26 @@ there is a good reason for it, to avoid increased number of renewals and
 a need for rebinding (increase of multicast traffic, which may be received
 a need for rebinding (increase of multicast traffic, which may be received
 by multiple servers).
 by multiple servers).
 
 
+% DHCPSRV_CFGMGR_DEL_SUBNET4 IPv4 subnet %1 removed
+This debug message is issued when a subnet is successfully removed from the
+server configuration. The argument identifies the subnet removed.
+
+% DHCPSRV_CFGMGR_DEL_SUBNET6 IPv6 subnet %1 removed
+This debug message is issued when a subnet is successfully removed from the
+
+% DHCPSRV_CFGMGR_NEW_SUBNET4 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.
+
 % DHCPSRV_CFGMGR_NO_SUBNET4 no suitable subnet is defined for address hint %1
 % DHCPSRV_CFGMGR_NO_SUBNET4 no suitable subnet is defined for address hint %1
 This debug message is output when the DHCP configuration manager has received
 This debug message is output when the DHCP configuration manager has received
 a request for an IPv4 subnet for the specified address, but no such
 a request for an IPv4 subnet for the specified address, but no such
 subnet exists.
 subnet exists.
 
 
+% DHCPSRV_CFGMGR_NEW_SUBNET6 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.
+
 % DHCPSRV_CFGMGR_NO_SUBNET6 no suitable subnet is defined for address hint %1
 % DHCPSRV_CFGMGR_NO_SUBNET6 no suitable subnet is defined for address hint %1
 This debug message is output when the DHCP configuration manager has received
 This debug message is output when the DHCP configuration manager has received
 a request for an IPv6 subnet for the specified address, but no such
 a request for an IPv6 subnet for the specified address, but no such
@@ -64,6 +79,11 @@ This is a debug message reporting that the DHCP configuration manager has
 returned the specified IPv6 subnet when given the address hint specified
 returned the specified IPv6 subnet when given the address hint specified
 because it is the only subnet defined.
 because it is the only subnet defined.
 
 
+% DHCPSRV_CFGMGR_OPTION_DUPLICATE multiple options with the code: %1 added to the subnet: %2
+This warning message is issued on an attempt to configure multiple options with the
+same option code for the particular subnet. Adding multiple options is uncommon
+for DHCPv6, but it is not prohibited.
+
 % DHCPSRV_CFGMGR_SOCKET_RAW_UNSUPPORTED use of raw sockets is unsupported on this OS, UDP sockets will be used
 % DHCPSRV_CFGMGR_SOCKET_RAW_UNSUPPORTED use of raw sockets is unsupported on this OS, UDP sockets will be used
 This warning message is logged when the user specified that the
 This warning message is logged when the user specified that the
 DHCPv4 server should use the raw sockets to receive the DHCP
 DHCPv4 server should use the raw sockets to receive the DHCP

+ 1 - 0
src/lib/dhcpsrv/parsers/client_class_def_parser.cc

@@ -9,6 +9,7 @@
 #include <dhcpsrv/client_class_def.h>
 #include <dhcpsrv/client_class_def.h>
 #include <dhcpsrv/parsers/dhcp_parsers.h>
 #include <dhcpsrv/parsers/dhcp_parsers.h>
 #include <dhcpsrv/parsers/client_class_def_parser.h>
 #include <dhcpsrv/parsers/client_class_def_parser.h>
+#include <dhcpsrv/parsers/option_data_parser.h>
 #include <eval/eval_context.h>
 #include <eval/eval_context.h>
 #include <asiolink/io_address.h>
 #include <asiolink/io_address.h>
 #include <asiolink/io_error.h>
 #include <asiolink/io_error.h>

+ 461 - 303
src/lib/dhcpsrv/parsers/dhcp_parsers.cc

@@ -10,7 +10,11 @@
 #include <dhcp/libdhcp++.h>
 #include <dhcp/libdhcp++.h>
 #include <dhcpsrv/cfgmgr.h>
 #include <dhcpsrv/cfgmgr.h>
 #include <dhcpsrv/cfg_option.h>
 #include <dhcpsrv/cfg_option.h>
+#include <dhcpsrv/dhcpsrv_log.h>
 #include <dhcpsrv/parsers/dhcp_parsers.h>
 #include <dhcpsrv/parsers/dhcp_parsers.h>
+#include <dhcpsrv/parsers/host_reservation_parser.h>
+#include <dhcpsrv/parsers/host_reservations_list_parser.h>
+#include <dhcpsrv/parsers/option_data_parser.h>
 #include <dhcpsrv/cfg_mac_source.h>
 #include <dhcpsrv/cfg_mac_source.h>
 #include <util/encode/hex.h>
 #include <util/encode/hex.h>
 #include <util/strutil.h>
 #include <util/strutil.h>
@@ -164,157 +168,11 @@ void ControlSocketParser::parse(SrvConfig& srv_cfg, isc::data::ConstElementPtr v
     srv_cfg.setControlSocketInfo(value);
     srv_cfg.setControlSocketInfo(value);
 }
 }
 
 
-// **************************** OptionDataParser *************************
-OptionDataParser::OptionDataParser(const uint16_t address_family)
-    : address_family_(address_family) {
-}
-
-std::pair<OptionDescriptor, std::string>
-OptionDataParser::parse(isc::data::ConstElementPtr single_option) {
-
-    // Try to create the option instance.
-    std::pair<OptionDescriptor, std::string> opt = createOption(single_option);
-
-    if (!opt.first.option_) {
-        isc_throw(isc::InvalidOperation,
-            "parser logic error: no option has been configured and"
-            " thus there is nothing to commit. Has build() been called?");
-    }
-
-    return (opt);
-}
-
-OptionalValue<uint32_t>
-OptionDataParser::extractCode(ConstElementPtr parent) const {
-    uint32_t code;
-    try {
-        code = getInteger(parent, "code");
-
-    } catch (const exception&) {
-        // The code parameter was not found. Return an unspecified
-        // value.
-        return (OptionalValue<uint32_t>());
-    }
-
-    if (code == 0) {
-        isc_throw(DhcpConfigError, "option code must not be zero "
-                  "(" << getPosition("code", parent) << ")");
 
 
-    } else if (address_family_ == AF_INET &&
-               code > std::numeric_limits<uint8_t>::max()) {
-        isc_throw(DhcpConfigError, "invalid option code '" << code
-                << "', it must not be greater than '"
-                  << static_cast<int>(std::numeric_limits<uint8_t>::max())
-                  << "' (" << getPosition("code", parent)
-                  << ")");
 
 
-    } else if (address_family_ == AF_INET6 &&
-               code > std::numeric_limits<uint16_t>::max()) {
-        isc_throw(DhcpConfigError, "invalid option code '" << code
-                << "', it must not exceed '"
-                  << std::numeric_limits<uint16_t>::max()
-                  << "' (" << getPosition("code", parent)
-                  << ")");
 
 
-    }
-
-    return (OptionalValue<uint32_t>(code, OptionalValueState(true)));
-}
 
 
-OptionalValue<std::string>
-OptionDataParser::extractName(ConstElementPtr parent) const {
-    std::string name;
-    try {
-        name = getString(parent, "name");
 
 
-    } catch (...) {
-        return (OptionalValue<std::string>());
-    }
-
-    if (name.find(" ") != std::string::npos) {
-        isc_throw(DhcpConfigError, "invalid option name '" << name
-                  << "', space character is not allowed ("
-                  << getPosition("name", parent) << ")");
-    }
-
-    return (OptionalValue<std::string>(name, OptionalValueState(true)));
-}
-
-std::string
-OptionDataParser::extractData(ConstElementPtr parent) const {
-    std::string data;
-    try {
-        data = getString(parent, "data");
-
-    } catch (...) {
-        // The "data" parameter was not found. Return an empty value.
-        return (data);
-    }
-
-    return (data);
-}
-
-OptionalValue<bool>
-OptionDataParser::extractCSVFormat(ConstElementPtr parent) const {
-    bool csv_format = true;
-    try {
-        csv_format = getBoolean(parent, "csv-format");
-
-    } catch (...) {
-        return (OptionalValue<bool>(csv_format));
-    }
-
-    return (OptionalValue<bool>(csv_format, OptionalValueState(true)));
-}
-
-std::string
-OptionDataParser::extractSpace(ConstElementPtr parent) const {
-    std::string space = address_family_ == AF_INET ?
-        DHCP4_OPTION_SPACE : DHCP6_OPTION_SPACE;
-    try {
-        space = getString(parent, "space");
-
-    } catch (...) {
-        return (space);
-    }
-
-    try {
-        if (!OptionSpace::validateName(space)) {
-            isc_throw(DhcpConfigError, "invalid option space name '"
-                      << space << "'");
-        }
-
-        if ((space == DHCP4_OPTION_SPACE) && (address_family_ == AF_INET6)) {
-            isc_throw(DhcpConfigError, "'" << DHCP4_OPTION_SPACE
-                      << "' option space name is reserved for DHCPv4 server");
-
-        } else if ((space == DHCP6_OPTION_SPACE) &&
-                   (address_family_ == AF_INET)) {
-            isc_throw(DhcpConfigError, "'" << DHCP6_OPTION_SPACE
-                      << "' option space name is reserved for DHCPv6 server");
-        }
-
-    } catch (std::exception& ex) {
-        // Append position of the option space parameter.
-        isc_throw(DhcpConfigError, ex.what() << " ("
-                  << getPosition("space", parent) << ")");
-    }
-
-    return (space);
-}
-
-OptionalValue<bool>
-OptionDataParser::extractPersistent(ConstElementPtr parent) const {
-    bool persist = false;
-    try {
-        persist = getBoolean(parent, "always-send");
-
-    } catch (...) {
-        return (OptionalValue<bool>(persist));
-    }
-
-    return (OptionalValue<bool>(persist, OptionalValueState(true)));
-}
 
 
 template<typename SearchKey>
 template<typename SearchKey>
 OptionDefinitionPtr
 OptionDefinitionPtr
@@ -342,163 +200,6 @@ OptionDataParser::findOptionDefinition(const std::string& option_space,
     return (def);
     return (def);
 }
 }
 
 
-std::pair<OptionDescriptor, std::string>
-OptionDataParser::createOption(ConstElementPtr option_data) {
-    const Option::Universe universe = address_family_ == AF_INET ?
-        Option::V4 : Option::V6;
-
-    OptionalValue<uint32_t> code_param =  extractCode(option_data);
-    OptionalValue<std::string> name_param = extractName(option_data);
-    OptionalValue<bool> csv_format_param = extractCSVFormat(option_data);
-    OptionalValue<bool> persist_param = extractPersistent(option_data);
-    std::string data_param = extractData(option_data);
-    std::string space_param = extractSpace(option_data);
-
-    // Require that option code or option name is specified.
-    if (!code_param.isSpecified() && !name_param.isSpecified()) {
-        isc_throw(DhcpConfigError, "option data configuration requires one of"
-                  " 'code' or 'name' parameters to be specified"
-                  << " (" << option_data->getPosition() << ")");
-    }
-
-    // Try to find a corresponding option definition using option code or
-    // option name.
-    OptionDefinitionPtr def = code_param.isSpecified() ?
-        findOptionDefinition(space_param, code_param) :
-        findOptionDefinition(space_param, name_param);
-
-    // If there is no definition, the user must not explicitly enable the
-    // use of csv-format.
-    if (!def) {
-        // If explicitly requested that the CSV format is to be used,
-        // the option definition is a must.
-        if (csv_format_param.isSpecified() && csv_format_param) {
-            isc_throw(DhcpConfigError, "definition for the option '"
-                      << space_param << "." << name_param
-                      << "' having code '" << code_param
-                      << "' does not exist ("
-                      << getPosition("name", option_data)
-                      << ")");
-
-        // If there is no option definition and the option code is not specified
-        // we have no means to find the option code.
-        } else if (name_param.isSpecified() && !code_param.isSpecified()) {
-            isc_throw(DhcpConfigError, "definition for the option '"
-                      << space_param << "." << name_param
-                      << "' does not exist ("
-                      << getPosition("name", option_data)
-                      << ")");
-        }
-    }
-
-    // Transform string of hexadecimal digits into binary format.
-    std::vector<uint8_t> binary;
-    std::vector<std::string> data_tokens;
-
-    // If the definition is available and csv-format hasn't been explicitly
-    // disabled, we will parse the data as comma separated values.
-    if (def && (!csv_format_param.isSpecified() || csv_format_param)) {
-        // If the option data is specified as a string of comma
-        // separated values then we need to split this string into
-        // individual values - each value will be used to initialize
-        // one data field of an option.
-        // It is the only usage of the escape option: this allows
-        // to embed commas in individual values and to return
-        // for instance a string value with embedded commas.
-        data_tokens = isc::util::str::tokens(data_param, ",", true);
-
-    } else {
-        // Otherwise, the option data is specified as a string of
-        // hexadecimal digits that we have to turn into binary format.
-        try {
-            // The decodeHex function expects that the string contains an
-            // even number of digits. If we don't meet this requirement,
-            // we have to insert a leading 0.
-            if (!data_param.empty() && ((data_param.length() % 2) != 0)) {
-                data_param = data_param.insert(0, "0");
-            }
-            util::encode::decodeHex(data_param, binary);
-        } catch (...) {
-            isc_throw(DhcpConfigError, "option data is not a valid"
-                      << " string of hexadecimal digits: " << data_param
-                      << " ("
-                      << getPosition("data", option_data)
-                      << ")");
-        }
-    }
-
-    OptionPtr option;
-    OptionDescriptor desc(false);
-
-    if (!def) {
-        // @todo We have a limited set of option definitions initialized at
-        // the moment.  In the future we want to initialize option definitions
-        // for all options.  Consequently an error will be issued if an option
-        // definition does not exist for a particular option code. For now it is
-        // ok to create generic option if definition does not exist.
-        OptionPtr option(new Option(universe, static_cast<uint16_t>(code_param),
-                                    binary));
-
-        desc.option_ = option;
-        desc.persistent_ = persist_param.isSpecified() && persist_param;
-    } else {
-
-        // Option name is specified it should match the name in the definition.
-        if (name_param.isSpecified() && (def->getName() != name_param.get())) {
-            isc_throw(DhcpConfigError, "specified option name '"
-                      << name_param << "' does not match the "
-                      << "option definition: '" << space_param
-                      << "." << def->getName() << "' ("
-                      << getPosition("name", option_data)
-                      << ")");
-        }
-
-        // Option definition has been found so let's use it to create
-        // an instance of our option.
-        try {
-            bool use_csv = !csv_format_param.isSpecified() || csv_format_param;
-            OptionPtr option = use_csv ?
-                def->optionFactory(universe, def->getCode(), data_tokens) :
-                def->optionFactory(universe, def->getCode(), binary);
-            desc.option_ = option;
-            desc.persistent_ = persist_param.isSpecified() && persist_param;
-            if (use_csv) {
-                desc.formatted_value_ = data_param;
-            }
-        } catch (const isc::Exception& ex) {
-            isc_throw(DhcpConfigError, "option data does not match"
-                      << " option definition (space: " << space_param
-                      << ", code: " << def->getCode() << "): "
-                      << ex.what() << " ("
-                      << getPosition("data", option_data)
-                      << ")");
-        }
-    }
-
-    // All went good, so we can set the option space name.
-    return make_pair(desc, space_param);
-}
-
-// **************************** OptionDataListParser *************************
-OptionDataListParser::OptionDataListParser(//const std::string&,
-                                           //const CfgOptionPtr& cfg,
-                                           const uint16_t address_family)
-    : address_family_(address_family) {
-}
-
-
-void OptionDataListParser::parse(const CfgOptionPtr& cfg,
-                                 isc::data::ConstElementPtr option_data_list) {
-    OptionDataParser option_parser(address_family_);
-    BOOST_FOREACH(ConstElementPtr data, option_data_list->listValue()) {
-        std::pair<OptionDescriptor, std::string> option =
-            option_parser.parse(data);
-        // Use the option description to keep the formatted value
-        cfg->add(option.first, option.second);
-        cfg->encapsulate();
-    }
-}
-
 // ******************************** OptionDefParser ****************************
 // ******************************** OptionDefParser ****************************
 
 
 std::pair<isc::dhcp::OptionDefinitionPtr, std::string>
 std::pair<isc::dhcp::OptionDefinitionPtr, std::string>
@@ -761,6 +462,28 @@ PoolParser::parse(PoolStoragePtr pools,
     }
     }
 }
 }
 
 
+//****************************** Pool4Parser *************************
+
+PoolPtr
+Pool4Parser::poolMaker (IOAddress &addr, uint32_t len, int32_t) {
+    return (PoolPtr(new Pool4(addr, len)));
+}
+
+PoolPtr
+Pool4Parser::poolMaker (IOAddress &min, IOAddress &max, int32_t) {
+    return (PoolPtr(new Pool4(min, max)));
+}
+
+//****************************** Pool4ListParser *************************
+
+void
+Pools4ListParser::parse(PoolStoragePtr pools, ConstElementPtr pools_list) {
+    BOOST_FOREACH(ConstElementPtr pool, pools_list->listValue()) {
+        Pool4Parser parser;
+        parser.parse(pools, pool, AF_INET);
+    }
+}
+
 //****************************** SubnetConfigParser *************************
 //****************************** SubnetConfigParser *************************
 
 
 SubnetConfigParser::SubnetConfigParser(uint16_t family)
 SubnetConfigParser::SubnetConfigParser(uint16_t family)
@@ -905,6 +628,441 @@ SubnetConfigParser::createSubnet(ConstElementPtr params) {
     options_->copyTo(*subnet_->getCfgOption());
     options_->copyTo(*subnet_->getCfgOption());
 }
 }
 
 
+//****************************** Subnet4ConfigParser *************************
+
+Subnet4ConfigParser::Subnet4ConfigParser()
+    :SubnetConfigParser(AF_INET) {
+}
+
+Subnet4Ptr
+Subnet4ConfigParser::parse(ConstElementPtr subnet) {
+    /// Parse Pools first.
+    ConstElementPtr pools = subnet->get("pools");
+    if (pools) {
+        Pools4ListParser parser;
+        parser.parse(pools_, pools);
+    }
+
+    SubnetPtr generic = SubnetConfigParser::parse(subnet);
+
+    if (!generic) {
+        isc_throw(DhcpConfigError,
+                  "Failed to create an IPv4 subnet (" <<
+                  subnet->getPosition() << ")");
+    }
+
+    Subnet4Ptr sn4ptr = boost::dynamic_pointer_cast<Subnet4>(subnet_);
+    if (!sn4ptr) {
+        // If we hit this, it is a programming error.
+        isc_throw(Unexpected,
+                  "Invalid Subnet4 cast in Subnet4ConfigParser::parse");
+    }
+
+    // Set relay information if it was parsed
+    if (relay_info_) {
+        sn4ptr->setRelayInfo(*relay_info_);
+    }
+
+    // Parse Host Reservations for this subnet if any.
+    ConstElementPtr reservations = subnet->get("reservations");
+    if (reservations) {
+        HostCollection hosts;
+        HostReservationsListParser<HostReservationParser4> parser;
+        parser.parse(subnet_->getID(), reservations, hosts);
+        for (auto h = hosts.begin(); h != hosts.end(); ++h) {
+            CfgMgr::instance().getStagingCfg()->getCfgHosts()->add(*h);
+        }
+    }
+
+    return (sn4ptr);
+}
+
+void
+Subnet4ConfigParser::initSubnet(data::ConstElementPtr params,
+                                asiolink::IOAddress addr, uint8_t len) {
+    // The renew-timer and rebind-timer are optional. If not set, the
+    // option 58 and 59 will not be sent to a client. In this case the
+    // client will use default values based on the valid-lifetime.
+    Triplet<uint32_t> t1 = getInteger(params, "renew-timer");
+    Triplet<uint32_t> t2 = getInteger(params, "rebind-timer");
+
+    // The valid-lifetime is mandatory. It may be specified for a
+    // particular subnet. If not, the global value should be present.
+    // If there is no global value, exception is thrown.
+    Triplet<uint32_t> valid = getInteger(params, "valid-lifetime");
+
+    // Subnet ID is optional. If it is not supplied the value of 0 is used,
+    // which means autogenerate. The value was inserted earlier by calling
+    // SimpleParser4::setAllDefaults.
+    SubnetID subnet_id = static_cast<SubnetID>(getInteger(params, "id"));
+
+    stringstream s;
+    s << addr << "/" << static_cast<int>(len) << " with params: ";
+    // t1 and t2 are optional may be not specified.
+    if (!t1.unspecified()) {
+        s << "t1=" << t1 << ", ";
+    }
+    if (!t2.unspecified()) {
+        s << "t2=" << t2 << ", ";
+    }
+    s <<"valid-lifetime=" << valid;
+
+    LOG_INFO(dhcpsrv_logger, DHCPSRV_CFGMGR_NEW_SUBNET4).arg(s.str());
+
+    Subnet4Ptr subnet4(new Subnet4(addr, len, t1, t2, valid, subnet_id));
+    subnet_ = subnet4;
+
+    // Set the match-client-id value for the subnet. It is always present.
+    // If not explicitly specified, the default value was filled in when
+    // SimpleParser4::setAllDefaults was called.
+    bool match_client_id = getBoolean(params, "match-client-id");
+    subnet4->setMatchClientId(match_client_id);
+
+    // Set next-server. The default value is 0.0.0.0. Nevertheless, the
+    // user could have messed that up by specifying incorrect value.
+    // To avoid using 0.0.0.0, user can specify "".
+    string next_server;
+    try {
+        next_server = getString(params, "next-server");
+        if (!next_server.empty()) {
+            subnet4->setSiaddr(IOAddress(next_server));
+        }
+    } catch (...) {
+        ConstElementPtr next = params->get("next-server");
+        string pos;
+        if (next) {
+            pos = next->getPosition().str();
+        } else {
+            pos = params->getPosition().str();
+        }
+        isc_throw(DhcpConfigError, "invalid parameter next-server : "
+                  << next_server << "(" << pos << ")");
+    }
+
+    // 4o6 specific parameter: 4o6-interface. If not explicitly specified,
+    // it will have the default value of "".
+    string iface4o6 = getString(params, "4o6-interface");
+    if (!iface4o6.empty()) {
+        subnet4->get4o6().setIface4o6(iface4o6);
+        subnet4->get4o6().enabled(true);
+    }
+
+    // 4o6 specific parameter: 4o6-subnet. If not explicitly specified, it
+    // will have the default value of "".
+    string subnet4o6 = getString(params, "4o6-subnet");
+    if (!subnet4o6.empty()) {
+        size_t slash = subnet4o6.find("/");
+        if (slash == std::string::npos) {
+            isc_throw(DhcpConfigError, "Missing / in the 4o6-subnet parameter:"
+                      << subnet4o6 << ", expected format: prefix6/length");
+        }
+        string prefix = subnet4o6.substr(0, slash);
+        string lenstr = subnet4o6.substr(slash + 1);
+
+        uint8_t len = 128;
+        try {
+            len = boost::lexical_cast<unsigned int>(lenstr.c_str());
+        } catch (const boost::bad_lexical_cast &) {
+            isc_throw(DhcpConfigError, "Invalid prefix length specified in "
+                      "4o6-subnet parameter: " << subnet4o6 << ", expected 0..128 value");
+        }
+        subnet4->get4o6().setSubnet4o6(IOAddress(prefix), len);
+        subnet4->get4o6().enabled(true);
+    }
+
+    // Try 4o6 specific parameter: 4o6-interface-id
+    std::string ifaceid = getString(params, "4o6-interface-id");
+    if (!ifaceid.empty()) {
+        OptionBuffer tmp(ifaceid.begin(), ifaceid.end());
+        OptionPtr opt(new Option(Option::V6, D6O_INTERFACE_ID, tmp));
+        subnet4->get4o6().setInterfaceId(opt);
+        subnet4->get4o6().enabled(true);
+    }
+
+    /// client-class processing is now generic and handled in the common
+    /// code (see isc::data::SubnetConfigParser::createSubnet)
+}
+
+//**************************** Subnets4ListConfigParser **********************
+
+size_t
+Subnets4ListConfigParser::parse(SrvConfigPtr cfg, ConstElementPtr subnets_list) {
+    size_t cnt = 0;
+    BOOST_FOREACH(ConstElementPtr subnet_json, subnets_list->listValue()) {
+
+        Subnet4ConfigParser parser;
+        Subnet4Ptr subnet = parser.parse(subnet_json);
+        if (subnet) {
+
+            // Adding a subnet to the Configuration Manager may fail if the
+            // subnet id is invalid (duplicate). Thus, we catch exceptions
+            // here to append a position in the configuration string.
+            try {
+                cfg->getCfgSubnets4()->add(subnet);
+                cnt++;
+            } catch (const std::exception& ex) {
+                isc_throw(DhcpConfigError, ex.what() << " ("
+                          << subnet_json->getPosition() << ")");
+            }
+        }
+    }
+    return (cnt);
+}
+
+//**************************** Pool6Parser *********************************
+
+PoolPtr
+Pool6Parser::poolMaker (IOAddress &addr, uint32_t len, int32_t ptype)
+{
+    return (PoolPtr(new Pool6(static_cast<isc::dhcp::Lease::Type>
+                              (ptype), addr, len)));
+}
+
+PoolPtr
+Pool6Parser::poolMaker (IOAddress &min, IOAddress &max, int32_t ptype)
+{
+    return (PoolPtr(new Pool6(static_cast<isc::dhcp::Lease::Type>
+                              (ptype), min, max)));
+}
+
+
+//**************************** Pool6ListParser ***************************
+
+void
+Pools6ListParser::parse(PoolStoragePtr pools, ConstElementPtr pools_list) {
+    BOOST_FOREACH(ConstElementPtr pool, pools_list->listValue()) {
+        Pool6Parser parser;
+        parser.parse(pools, pool, AF_INET6);
+    }
+}
+
+//**************************** PdPoolParser ******************************
+
+PdPoolParser::PdPoolParser() : options_(new CfgOption()) {
+}
+
+void
+PdPoolParser::parse(PoolStoragePtr pools, ConstElementPtr pd_pool_) {
+    std::string addr_str = getString(pd_pool_, "prefix");
+
+    uint8_t prefix_len = getUint8(pd_pool_, "prefix-len");
+
+    uint8_t delegated_len = getUint8(pd_pool_, "delegated-len");
+
+    std::string excluded_prefix_str = "::";
+    if (pd_pool_->contains("excluded-prefix")) {
+        excluded_prefix_str = getString(pd_pool_, "excluded-prefix");
+    }
+
+    uint8_t excluded_prefix_len = 0;
+    if (pd_pool_->contains("excluded-prefix-len")) {
+        excluded_prefix_len = getUint8(pd_pool_, "excluded-prefix-len");
+    }
+
+    ConstElementPtr option_data = pd_pool_->get("option-data");
+    if (option_data) {
+        OptionDataListParser opts_parser(AF_INET6);
+        opts_parser.parse(options_, option_data);
+    }
+                    
+    ConstElementPtr user_context = pd_pool_->get("user-context");
+    if (user_context) {
+        user_context_ = user_context;
+    }
+
+    // Check the pool parameters. It will throw an exception if any
+    // of the required parameters are invalid.
+    try {
+        // Attempt to construct the local pool.
+        pool_.reset(new Pool6(IOAddress(addr_str),
+                              prefix_len,
+                              delegated_len,
+                              IOAddress(excluded_prefix_str),
+                              excluded_prefix_len));
+        // Merge options specified for a pool into pool configuration.
+        options_->copyTo(*pool_->getCfgOption());
+    } catch (const std::exception& ex) {
+        // Some parameters don't exist or are invalid. Since we are not
+        // aware whether they don't exist or are invalid, let's append
+        // the position of the pool map element.
+        isc_throw(isc::dhcp::DhcpConfigError, ex.what()
+                  << " (" << pd_pool_->getPosition() << ")");
+    }
+
+    if (user_context_) {
+        pool_->setUserContext(user_context_);
+    }
+
+    // Add the local pool to the external storage ptr.
+    pools->push_back(pool_);
+}
+
+//**************************** PdPoolsListParser ************************
+
+void
+PdPoolsListParser::parse(PoolStoragePtr pools, ConstElementPtr pd_pool_list) {
+    // Loop through the list of pd pools.
+    BOOST_FOREACH(ConstElementPtr pd_pool, pd_pool_list->listValue()) {
+        PdPoolParser parser;
+        parser.parse(pools, pd_pool);
+    }
+}
+
+//**************************** Subnet6ConfigParser ***********************
+
+Subnet6ConfigParser::Subnet6ConfigParser()
+    :SubnetConfigParser(AF_INET6) {
+}
+
+Subnet6Ptr
+Subnet6ConfigParser::parse(ConstElementPtr subnet) {
+    /// Parse all pools first.
+    ConstElementPtr pools = subnet->get("pools");
+    if (pools) {
+        Pools6ListParser parser;
+        parser.parse(pools_, pools);
+    }
+    ConstElementPtr pd_pools = subnet->get("pd-pools");
+    if (pd_pools) {
+        PdPoolsListParser parser;
+        parser.parse(pools_, pd_pools);
+    }
+
+    SubnetPtr generic = SubnetConfigParser::parse(subnet);
+
+    if (!generic) {
+        isc_throw(DhcpConfigError,
+                  "Failed to create an IPv6 subnet (" <<
+                  subnet->getPosition() << ")");
+    }
+
+    Subnet6Ptr sn6ptr = boost::dynamic_pointer_cast<Subnet6>(subnet_);
+    if (!sn6ptr) {
+        // If we hit this, it is a programming error.
+        isc_throw(Unexpected,
+                  "Invalid Subnet6 cast in Subnet6ConfigParser::parse");
+    }
+
+    // Set relay information if it was provided
+    if (relay_info_) {
+        sn6ptr->setRelayInfo(*relay_info_);
+    }
+
+
+    // Parse Host Reservations for this subnet if any.
+    ConstElementPtr reservations = subnet->get("reservations");
+    if (reservations) {
+        HostCollection hosts;
+        HostReservationsListParser<HostReservationParser6> parser;
+        parser.parse(subnet_->getID(), reservations, hosts);
+        for (auto h = hosts.begin(); h != hosts.end(); ++h) {
+            CfgMgr::instance().getStagingCfg()->getCfgHosts()->add(*h);
+        }
+    }
+
+    return (sn6ptr);
+}
+
+void
+Subnet6ConfigParser::duplicate_option_warning(uint32_t code,
+                                              asiolink::IOAddress& addr) {
+    LOG_WARN(dhcpsrv_logger, DHCPSRV_CFGMGR_OPTION_DUPLICATE)
+        .arg(code).arg(addr.toText());
+}
+
+void
+Subnet6ConfigParser::initSubnet(data::ConstElementPtr params,
+                                asiolink::IOAddress addr, uint8_t len) {
+    // Get all 'time' parameters using inheritance.
+    // If the subnet-specific value is defined then use it, else
+    // use the global value. The global value must always be
+    // present. If it is not, it is an internal error and exception
+    // is thrown.
+    Triplet<uint32_t> t1 = getInteger(params, "renew-timer");
+    Triplet<uint32_t> t2 = getInteger(params, "rebind-timer");
+    Triplet<uint32_t> pref = getInteger(params, "preferred-lifetime");
+    Triplet<uint32_t> valid = getInteger(params, "valid-lifetime");
+
+    // Subnet ID is optional. If it is not supplied the value of 0 is used,
+    // which means autogenerate. The value was inserted earlier by calling
+    // SimpleParser6::setAllDefaults.
+    SubnetID subnet_id = static_cast<SubnetID>(getInteger(params, "id"));
+
+    // We want to log whether rapid-commit is enabled, so we get this
+    // before the actual subnet creation.
+    bool rapid_commit = getBoolean(params, "rapid-commit");
+
+    std::ostringstream output;
+    output << addr << "/" << static_cast<int>(len)
+           << " with params t1=" << t1 << ", t2="
+           << t2 << ", preferred-lifetime=" << pref
+           << ", valid-lifetime=" << valid
+           << ", rapid-commit is " << (rapid_commit ? "enabled" : "disabled");
+
+
+    LOG_INFO(dhcpsrv_logger, DHCPSRV_CFGMGR_NEW_SUBNET4).arg(output.str());
+
+    // Create a new subnet.
+    Subnet6* subnet6 = new Subnet6(addr, len, t1, t2, pref, valid,
+                                       subnet_id);
+    subnet_.reset(subnet6);
+
+    // Enable or disable Rapid Commit option support for the subnet.
+    subnet6->setRapidCommit(rapid_commit);
+
+    // Get interface-id option content. For now we support string
+    // representation only
+    std::string ifaceid = getString(params, "interface-id");
+    std::string iface = getString(params, "interface");
+
+    // Specifying both interface for locally reachable subnets and
+    // interface id for relays is mutually exclusive. Need to test for
+    // this condition.
+    if (!ifaceid.empty() && !iface.empty()) {
+        isc_throw(isc::dhcp::DhcpConfigError,
+                  "parser error: interface (defined for locally reachable "
+                  "subnets) and interface-id (defined for subnets reachable"
+                  " via relays) cannot be defined at the same time for "
+                  "subnet " << addr << "/" << (int)len << "("
+                  << params->getPosition() << ")");
+    }
+
+    // Configure interface-id for remote interfaces, if defined
+    if (!ifaceid.empty()) {
+        OptionBuffer tmp(ifaceid.begin(), ifaceid.end());
+        OptionPtr opt(new Option(Option::V6, D6O_INTERFACE_ID, tmp));
+        subnet6->setInterfaceId(opt);
+    }
+
+    /// client-class processing is now generic and handled in the common
+    /// code (see isc::data::SubnetConfigParser::createSubnet)
+}
+
+//**************************** Subnet6ListConfigParser ********************
+
+size_t
+Subnets6ListConfigParser::parse(SrvConfigPtr cfg, ConstElementPtr subnets_list) {
+    size_t cnt = 0;
+    BOOST_FOREACH(ConstElementPtr subnet_json, subnets_list->listValue()) {
+
+        Subnet6ConfigParser parser;
+        Subnet6Ptr subnet = parser.parse(subnet_json);
+
+        // Adding a subnet to the Configuration Manager may fail if the
+        // subnet id is invalid (duplicate). Thus, we catch exceptions
+        // here to append a position in the configuration string.
+        try {
+            cfg->getCfgSubnets6()->add(subnet);
+            cnt++;
+        } catch (const std::exception& ex) {
+            isc_throw(DhcpConfigError, ex.what() << " ("
+                      << subnet_json->getPosition() << ")");
+        }
+    }
+    return (cnt);
+}
+
+
 //**************************** D2ClientConfigParser **********************
 //**************************** D2ClientConfigParser **********************
 
 
 dhcp_ddns::NameChangeProtocol
 dhcp_ddns::NameChangeProtocol

+ 274 - 158
src/lib/dhcpsrv/parsers/dhcp_parsers.h

@@ -341,164 +341,6 @@ public:
     void parse(SrvConfig& srv_cfg, isc::data::ConstElementPtr value);
     void parse(SrvConfig& srv_cfg, isc::data::ConstElementPtr value);
 };
 };
 
 
-
-/// @brief Parser for option data value.
-///
-/// This parser parses configuration entries that specify value of
-/// a single option. These entries include option name, option code
-/// and data carried by the option. The option data can be specified
-/// in one of the two available formats: binary value represented as
-/// a string of hexadecimal digits or a list of comma separated values.
-/// The format being used is controlled by csv-format configuration
-/// parameter. When setting this value to True, the latter format is
-/// used. The subsequent values in the CSV format apply to relevant
-/// option data fields in the configured option. For example the
-/// configuration: "data" : "192.168.2.0, 56, hello world" can be
-/// used to set values for the option comprising IPv4 address,
-/// integer and string data field. Note that order matters. If the
-/// order of values does not match the order of data fields within
-/// an option the configuration will not be accepted. If parsing
-/// is successful then an instance of an option is created and
-/// added to the storage provided by the calling class.
-class OptionDataParser : public isc::data::SimpleParser {
-public:
-    /// @brief Constructor.
-    ///
-    /// @param address_family Address family: @c AF_INET or @c AF_INET6.
-    explicit OptionDataParser(const uint16_t address_family);
-
-    /// @brief Parses ElementPtr containing option definition
-    ///
-    /// This method parses ElementPtr containing the option definition,
-    /// instantiates the option for it and then returns a pair
-    /// of option descriptor (that holds that new option) and
-    /// a string that specifies the option space.
-    ///
-    /// Note: ElementPtr is expected to contain all fields. If your
-    /// ElementPtr does not have them, please use
-    /// @ref isc::data::SimpleParser::setDefaults to fill the missing fields
-    /// with default values.
-    ///
-    /// @param single_option ElementPtr containing option definition
-    /// @return Option object wrapped in option description and an option
-    ///         space
-    std::pair<OptionDescriptor, std::string>
-    parse(isc::data::ConstElementPtr single_option);
-private:
-
-    /// @brief Finds an option definition within an option space
-    ///
-    /// Given an option space and an option code, find the corresponding
-    /// option definition within the option definition storage.
-    ///
-    /// @param option_space name of the parameter option space
-    /// @param search_key an option code or name to be used to lookup the
-    /// option definition.
-    /// @tparam A numeric type for searching using an option code or the
-    /// string for searching using the option name.
-    ///
-    /// @return OptionDefinitionPtr of the option definition or an
-    /// empty OptionDefinitionPtr if not found.
-    /// @throw DhcpConfigError if the option space requested is not valid
-    /// for this server.
-    template<typename SearchKey>
-    OptionDefinitionPtr findOptionDefinition(const std::string& option_space,
-                                             const SearchKey& search_key) const;
-
-    /// @brief Create option instance.
-    ///
-    /// Creates an instance of an option and adds it to the provided
-    /// options storage. If the option data parsed by \ref build function
-    /// are invalid or insufficient this function emits an exception.
-    ///
-    /// @param option_data An element holding data for a single option being
-    /// created.
-    ///
-    /// @return created option descriptor
-    ///
-    /// @throw DhcpConfigError if parameters provided in the configuration
-    /// are invalid.
-    std::pair<OptionDescriptor, std::string>
-    createOption(isc::data::ConstElementPtr option_data);
-
-    /// @brief Retrieves parsed option code as an optional value.
-    ///
-    /// @param parent A data element holding full option data configuration.
-    ///
-    /// @return Option code, possibly unspecified.
-    /// @throw DhcpConfigError if option code is invalid.
-    util::OptionalValue<uint32_t>
-    extractCode(data::ConstElementPtr parent) const;
-
-    /// @brief Retrieves parsed option name as an optional value.
-    ///
-    /// @param parent A data element holding full option data configuration.
-    ///
-    /// @return Option name, possibly unspecified.
-    /// @throw DhcpConfigError if option name is invalid.
-    util::OptionalValue<std::string>
-    extractName(data::ConstElementPtr parent) const;
-
-    /// @brief Retrieves csv-format parameter as an optional value.
-    ///
-    /// @return Value of the csv-format parameter, possibly unspecified.
-    util::OptionalValue<bool> extractCSVFormat(data::ConstElementPtr parent) const;
-
-    /// @brief Retrieves option data as a string.
-    ///
-    /// @param parent A data element holding full option data configuration.
-    /// @return Option data as a string. It will return empty string if
-    /// option data is unspecified.
-    std::string extractData(data::ConstElementPtr parent) const;
-
-    /// @brief Retrieves option space name.
-    ///
-    /// If option space name is not specified in the configuration the
-    /// 'dhcp4' or 'dhcp6' option space name is returned, depending on
-    /// the universe specified in the parser context.
-    ///
-    /// @param parent A data element holding full option data configuration.
-    ///
-    /// @return Option space name.
-    std::string extractSpace(data::ConstElementPtr parent) const;
-
-    /// @brief Retrieves persistent/always-send parameter as an optional value.
-    ///
-    /// @return Value of the persistent parameter, possibly unspecified.
-    util::OptionalValue<bool> extractPersistent(data::ConstElementPtr parent) const;
-
-    /// @brief Address family: @c AF_INET or @c AF_INET6.
-    uint16_t address_family_;
-};
-
-/// @brief Parser for option data values within a subnet.
-///
-/// This parser iterates over all entries that define options
-/// data for a particular subnet and creates a collection of options.
-/// If parsing is successful, all these options are added to the Subnet
-/// object.
-class OptionDataListParser : public isc::data::SimpleParser {
-public:
-    /// @brief Constructor.
-    ///
-    /// @param address_family Address family: @c AF_INET or AF_INET6
-    explicit OptionDataListParser(const uint16_t address_family);
-
-    /// @brief Parses a list of options, instantiates them and stores in cfg
-    ///
-    /// This method expects to get a list of options in option_data_list,
-    /// iterates over them, creates option objects, wraps them with
-    /// option descriptor and stores in specified cfg.
-    ///
-    /// @param cfg created options will be stored here
-    /// @param option_data_list configuration that describes the options
-    void parse(const CfgOptionPtr& cfg,
-               isc::data::ConstElementPtr option_data_list);
-private:
-    /// @brief Address family: @c AF_INET or @c AF_INET6
-    uint16_t address_family_;
-};
-
 typedef std::pair<isc::dhcp::OptionDefinitionPtr, std::string> OptionDefinitionTuple;
 typedef std::pair<isc::dhcp::OptionDefinitionPtr, std::string> OptionDefinitionTuple;
 
 
 /// @brief Parser for a single option definition.
 /// @brief Parser for a single option definition.
@@ -590,6 +432,37 @@ protected:
                               int32_t ptype = 0) = 0;
                               int32_t ptype = 0) = 0;
 };
 };
 
 
+/// @brief Parser for IPv4 pool definitions.
+///
+/// This is the IPv4 derivation of the PoolParser class and handles pool
+/// definitions, i.e. a list of entries of one of two syntaxes: min-max and
+/// prefix/len for IPv4 pools. Pool4 objects are created and stored in chosen
+/// PoolStorage container.
+///
+/// It is useful for parsing Dhcp4/subnet4[X]/pool parameters.
+class Pool4Parser : public PoolParser {
+protected:
+    /// @brief Creates a Pool4 object given a IPv4 prefix and the prefix length.
+    ///
+    /// @param addr is the IPv4 prefix of the pool.
+    /// @param len is the prefix length.
+    /// @param ignored dummy parameter to provide symmetry between the
+    /// PoolParser derivations. The V6 derivation requires a third value.
+    /// @return returns a PoolPtr to the new Pool4 object.
+    PoolPtr poolMaker (asiolink::IOAddress &addr, uint32_t len,
+                       int32_t ignored);
+
+    /// @brief Creates a Pool4 object given starting and ending IPv4 addresses.
+    ///
+    /// @param min is the first IPv4 address in the pool.
+    /// @param max is the last IPv4 address in the pool.
+    /// @param ignored dummy parameter to provide symmetry between the
+    /// PoolParser derivations. The V6 derivation requires a third value.
+    /// @return returns a PoolPtr to the new Pool4 object.
+    PoolPtr poolMaker (asiolink::IOAddress &min, asiolink::IOAddress &max,
+                       int32_t ignored);
+};
+
 /// @brief Parser for a list of pools
 /// @brief Parser for a list of pools
 ///
 ///
 /// This parser parses a list pools. Each element on that list gets its own
 /// This parser parses a list pools. Each element on that list gets its own
@@ -613,6 +486,20 @@ public:
                        isc::data::ConstElementPtr pools_list) = 0;
                        isc::data::ConstElementPtr pools_list) = 0;
 };
 };
 
 
+/// @brief Specialization of the pool list parser for DHCPv4
+class Pools4ListParser : PoolsListParser {
+public:
+
+    /// @brief parses the actual structure
+    ///
+    /// This method parses the actual list of pools.
+    ///
+    /// @param pools storage container in which to store the parsed pool.
+    /// @param pools_list a list of pool structures
+    /// @throw isc::dhcp::DhcpConfigError when pool parsing fails
+    void parse(PoolStoragePtr pools, data::ConstElementPtr pools_list);
+};
+
 /// @brief parser for additional relay information
 /// @brief parser for additional relay information
 ///
 ///
 /// This concrete parser handles RelayInfo structure definitions.
 /// This concrete parser handles RelayInfo structure definitions.
@@ -735,6 +622,235 @@ protected:
     CfgOptionPtr options_;
     CfgOptionPtr options_;
 };
 };
 
 
+/// @anchor Subnet4ConfigParser
+/// @brief This class parses a single IPv4 subnet.
+///
+/// This is the IPv4 derivation of the SubnetConfigParser class and it parses
+/// the whole subnet definition. It creates parsersfor received configuration
+/// parameters as needed.
+class Subnet4ConfigParser : public SubnetConfigParser {
+public:
+    /// @brief Constructor
+    ///
+    /// stores global scope parameters, options, option definitions.
+    Subnet4ConfigParser();
+
+    /// @brief Parses a single IPv4 subnet configuration and adds to the
+    /// Configuration Manager.
+    ///
+    /// @param subnet A new subnet being configured.
+    /// @return a pointer to created Subnet4 object
+    Subnet4Ptr parse(data::ConstElementPtr subnet);
+
+protected:
+
+    /// @brief Instantiates the IPv4 Subnet based on a given IPv4 address
+    /// and prefix length.
+    ///
+    /// @param params Data structure describing a subnet.
+    /// @param addr is IPv4 address of the subnet.
+    /// @param len is the prefix length
+    void initSubnet(data::ConstElementPtr params,
+                    asiolink::IOAddress addr, uint8_t len);
+};
+
+/// @brief this class parses list of DHCP4 subnets
+///
+/// This is a wrapper parser that handles the whole list of Subnet4
+/// definitions. It iterates over all entries and creates Subnet4ConfigParser
+/// for each entry.
+class Subnets4ListConfigParser : public isc::data::SimpleParser {
+public:
+
+    /// @brief parses contents of the list
+    ///
+    /// Iterates over all entries on the list, parses its content
+    /// (by instantiating Subnet6ConfigParser) and adds to specified
+    /// configuration.
+    ///
+    /// @param cfg Pointer to server configuration.
+    /// @param subnets_list pointer to a list of IPv4 subnets
+    /// @return number of subnets created
+    size_t parse(SrvConfigPtr cfg, data::ConstElementPtr subnets_list);
+};
+
+/// @brief Parser for IPv6 pool definitions.
+///
+/// This is the IPv6 derivation of the PoolParser class and handles pool
+/// definitions, i.e. a list of entries of one of two syntaxes: min-max and
+/// prefix/len for IPv6 pools. Pool6 objects are created and stored in chosen
+/// PoolStorage container.
+///
+/// It is useful for parsing Dhcp6/subnet6[X]/pool parameters.
+class Pool6Parser : public PoolParser {
+protected:
+    /// @brief Creates a Pool6 object given a IPv6 prefix and the prefix length.
+    ///
+    /// @param addr is the IPv6 prefix of the pool.
+    /// @param len is the prefix length.
+    /// @param ptype is the type of IPv6 pool (Pool::PoolType). Note this is
+    /// passed in as an int32_t and cast to PoolType to accommodate a
+    /// polymorphic interface.
+    /// @return returns a PoolPtr to the new Pool4 object.
+    PoolPtr poolMaker (asiolink::IOAddress &addr, uint32_t len, int32_t ptype);
+
+    /// @brief Creates a Pool6 object given starting and ending IPv6 addresses.
+    ///
+    /// @param min is the first IPv6 address in the pool.
+    /// @param max is the last IPv6 address in the pool.
+    /// @param ptype is the type of IPv6 pool (Pool::PoolType). Note this is
+    /// passed in as an int32_t and cast to PoolType to accommodate a
+    /// polymorphic interface.
+    /// @return returns a PoolPtr to the new Pool4 object.
+    PoolPtr poolMaker (asiolink::IOAddress &min, asiolink::IOAddress &max,
+                       int32_t ptype);
+};
+
+/// @brief Specialization of the pool list parser for DHCPv6
+class Pools6ListParser : PoolsListParser {
+public:
+
+    /// @brief parses the actual structure
+    ///
+    /// This method parses the actual list of pools.
+    ///
+    /// @param pools storage container in which to store the parsed pool.
+    /// @param pools_list a list of pool structures
+    /// @throw isc::dhcp::DhcpConfigError when pool parsing fails
+    void parse(PoolStoragePtr pools, data::ConstElementPtr pools_list);
+};
+
+/// @brief Parser for IPv6 prefix delegation definitions.
+///
+/// This class handles prefix delegation pool definitions for IPv6 subnets
+/// Pool6 objects are created and stored in the given PoolStorage container.
+///
+/// PdPool definitions currently support three elements: prefix, prefix-len,
+/// and delegated-len, as shown in the example JSON text below:
+///
+/// @code
+///
+/// {
+///     "prefix": "2001:db8:1::",
+///     "prefix-len": 64,
+///     "delegated-len": 128
+/// }
+/// @endcode
+///
+class PdPoolParser : public isc::data::SimpleParser {
+public:
+
+    /// @brief Constructor.
+    ///
+    PdPoolParser();
+
+    /// @brief Builds a prefix delegation pool from the given configuration
+    ///
+    /// This function parses configuration entries and creates an instance
+    /// of a dhcp::Pool6 configured for prefix delegation.
+    ///
+    /// @param pools storage container in which to store the parsed pool.
+    /// @param pd_pool_ pointer to an element that holds configuration entries
+    /// that define a prefix delegation pool.
+    ///
+    /// @throw DhcpConfigError if configuration parsing fails.
+    void parse(PoolStoragePtr pools, data::ConstElementPtr pd_pool_);
+
+private:
+
+    /// Pointer to the created pool object.
+    isc::dhcp::Pool6Ptr pool_;
+
+    /// A storage for pool specific option values.
+    CfgOptionPtr options_;
+
+    isc::data::ConstElementPtr user_context_;
+};
+
+/// @brief Parser for a list of prefix delegation pools.
+///
+/// This parser iterates over a list of prefix delegation pool entries and
+/// creates pool instances for each one. If the parsing is successful, the
+/// collection of pools is committed to the provided storage.
+class PdPoolsListParser : public PoolsListParser {
+public:
+
+    /// @brief Parse configuration entries.
+    ///
+    /// This function parses configuration entries and creates instances
+    /// of prefix delegation pools .
+    ///
+    /// @param storage is the pool storage in which to store the parsed
+    /// @param pd_pool_list pointer to an element that holds entries
+    /// that define a prefix delegation pool.
+    ///
+    /// @throw DhcpConfigError if configuration parsing fails.
+    void parse(PoolStoragePtr pools, data::ConstElementPtr pd_pool_list);
+};
+
+/// @anchor Subnet6ConfigParser
+/// @brief This class parses a single IPv6 subnet.
+///
+/// This is the IPv6 derivation of the SubnetConfigParser class and it parses
+/// the whole subnet definition. It creates parsersfor received configuration
+/// parameters as needed.
+class Subnet6ConfigParser : public SubnetConfigParser {
+public:
+
+    /// @brief Constructor
+    ///
+    /// stores global scope parameters, options, option definitions.
+    Subnet6ConfigParser();
+
+    /// @brief Parses a single IPv6 subnet configuration and adds to the
+    /// Configuration Manager.
+    ///
+    /// @param subnet A new subnet being configured.
+    /// @return a pointer to created Subnet6 object
+    Subnet6Ptr parse(data::ConstElementPtr subnet);
+
+protected:
+    /// @brief Issues a DHCP6 server specific warning regarding duplicate subnet
+    /// options.
+    ///
+    /// @param code is the numeric option code of the duplicate option
+    /// @param addr is the subnet address
+    /// @todo A means to know the correct logger and perhaps a common
+    /// message would allow this message to be emitted by the base class.
+    virtual void duplicate_option_warning(uint32_t code,
+                                         asiolink::IOAddress& addr);
+
+    /// @brief Instantiates the IPv6 Subnet based on a given IPv6 address
+    /// and prefix length.
+    ///
+    /// @param params Data structure describing a subnet.
+    /// @param addr is IPv6 prefix of the subnet.
+    /// @param len is the prefix length
+    void initSubnet(isc::data::ConstElementPtr params,
+                    isc::asiolink::IOAddress addr, uint8_t len);
+};
+
+
+/// @brief this class parses a list of DHCP6 subnets
+///
+/// This is a wrapper parser that handles the whole list of Subnet6
+/// definitions. It iterates over all entries and creates Subnet6ConfigParser
+/// for each entry.
+class Subnets6ListConfigParser : public isc::data::SimpleParser {
+public:
+
+    /// @brief parses contents of the list
+    ///
+    /// Iterates over all entries on the list, parses its content
+    /// (by instantiating Subnet6ConfigParser) and adds to specified
+    /// configuration.
+    ///
+    /// @param cfg configuration (parsed subnets will be stored here)
+    /// @param subnets_list pointer to a list of IPv6 subnets
+    /// @throw DhcpConfigError if CfgMgr rejects the subnet (e.g. subnet-id is a duplicate)
+    size_t parse(SrvConfigPtr cfg, data::ConstElementPtr subnets_list);
+};
+
 /// @brief Parser for  D2ClientConfig
 /// @brief Parser for  D2ClientConfig
 ///
 ///
 /// This class parses the configuration element "dhcp-ddns" common to the
 /// This class parses the configuration element "dhcp-ddns" common to the

+ 4 - 4
src/lib/dhcpsrv/parsers/host_reservation_parser.cc

@@ -7,8 +7,8 @@
 #include <config.h>
 #include <config.h>
 #include <asiolink/io_address.h>
 #include <asiolink/io_address.h>
 #include <dhcpsrv/cfgmgr.h>
 #include <dhcpsrv/cfgmgr.h>
-#include <dhcpsrv/parsers/dhcp_parsers.h>
 #include <dhcpsrv/parsers/host_reservation_parser.h>
 #include <dhcpsrv/parsers/host_reservation_parser.h>
+#include <dhcpsrv/parsers/option_data_parser.h>
 #include <boost/foreach.hpp>
 #include <boost/foreach.hpp>
 #include <boost/lexical_cast.hpp>
 #include <boost/lexical_cast.hpp>
 #include <algorithm>
 #include <algorithm>
@@ -113,7 +113,7 @@ HostReservationParser::parseInternal(const SubnetID&,
     try {
     try {
         // Gather those parameters that are common for both IPv4 and IPv6
         // Gather those parameters that are common for both IPv4 and IPv6
         // reservations.
         // reservations.
-        BOOST_FOREACH(ConfigPair element, reservation_data->mapValue()) {
+        BOOST_FOREACH(auto element, reservation_data->mapValue()) {
             // Check if we support this parameter.
             // Check if we support this parameter.
             if (!isSupportedParameter(element.first)) {
             if (!isSupportedParameter(element.first)) {
                 isc_throw(DhcpConfigError, "unsupported configuration"
                 isc_throw(DhcpConfigError, "unsupported configuration"
@@ -183,7 +183,7 @@ HostReservationParser4::parseInternal(const SubnetID& subnet_id,
 
 
     host->setIPv4SubnetID(subnet_id);
     host->setIPv4SubnetID(subnet_id);
 
 
-    BOOST_FOREACH(ConfigPair element, reservation_data->mapValue()) {
+    BOOST_FOREACH(auto element, reservation_data->mapValue()) {
         // For 'option-data' element we will use another parser which
         // For 'option-data' element we will use another parser which
         // already returns errors with position appended, so don't
         // already returns errors with position appended, so don't
         // surround it with try-catch.
         // surround it with try-catch.
@@ -242,7 +242,7 @@ HostReservationParser6::parseInternal(const SubnetID& subnet_id,
 
 
     host->setIPv6SubnetID(subnet_id);
     host->setIPv6SubnetID(subnet_id);
 
 
-    BOOST_FOREACH(ConfigPair element, reservation_data->mapValue()) {
+    BOOST_FOREACH(auto element, reservation_data->mapValue()) {
         // Parse option values. Note that the configuration option parser
         // Parse option values. Note that the configuration option parser
         // returns errors with position information appended, so there is no
         // returns errors with position information appended, so there is no
         // need to surround it with try-clause (and rethrow with position
         // need to surround it with try-clause (and rethrow with position

+ 369 - 0
src/lib/dhcpsrv/parsers/option_data_parser.cc

@@ -0,0 +1,369 @@
+// Copyright (C) 2017 Internet Systems Consortium, Inc. ("ISC")
+//
+// This Source Code Form is subject to the terms of the Mozilla Public
+// License, v. 2.0. If a copy of the MPL was not distributed with this
+// file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+#include <exceptions/exceptions.h>
+#include <dhcp/libdhcp++.h>
+#include <dhcp/option_definition.h>
+#include <dhcp/option_space.h>
+#include <dhcpsrv/cfgmgr.h>
+#include <dhcpsrv/parsers/option_data_parser.h>
+#include <util/encode/hex.h>
+#include <util/strutil.h>
+#include <boost/foreach.hpp>
+#include <limits>
+#include <vector>
+
+using namespace isc::data;
+using namespace isc::util;
+
+namespace isc {
+namespace dhcp {
+
+// **************************** OptionDataParser *************************
+
+OptionDataParser::OptionDataParser(const uint16_t address_family)
+    : address_family_(address_family) {
+}
+
+std::pair<OptionDescriptor, std::string>
+OptionDataParser::parse(isc::data::ConstElementPtr single_option) {
+
+    // Try to create the option instance.
+    std::pair<OptionDescriptor, std::string> opt = createOption(single_option);
+
+    if (!opt.first.option_) {
+        isc_throw(isc::InvalidOperation,
+            "parser logic error: no option has been configured and"
+            " thus there is nothing to commit. Has build() been called?");
+    }
+
+    return (opt);
+}
+
+OptionalValue<uint32_t>
+OptionDataParser::extractCode(ConstElementPtr parent) const {
+    uint32_t code;
+    try {
+        code = getInteger(parent, "code");
+
+    } catch (const std::exception&) {
+        // The code parameter was not found. Return an unspecified
+        // value.
+        return (OptionalValue<uint32_t>());
+    }
+
+    if (code == 0) {
+        isc_throw(DhcpConfigError, "option code must not be zero "
+                  "(" << getPosition("code", parent) << ")");
+
+    } else if (address_family_ == AF_INET &&
+               code > std::numeric_limits<uint8_t>::max()) {
+        isc_throw(DhcpConfigError, "invalid option code '" << code
+                << "', it must not be greater than '"
+                  << static_cast<int>(std::numeric_limits<uint8_t>::max())
+                  << "' (" << getPosition("code", parent)
+                  << ")");
+
+    } else if (address_family_ == AF_INET6 &&
+               code > std::numeric_limits<uint16_t>::max()) {
+        isc_throw(DhcpConfigError, "invalid option code '" << code
+                << "', it must not exceed '"
+                  << std::numeric_limits<uint16_t>::max()
+                  << "' (" << getPosition("code", parent)
+                  << ")");
+
+    }
+
+    return (OptionalValue<uint32_t>(code, OptionalValueState(true)));
+}
+
+OptionalValue<std::string>
+OptionDataParser::extractName(ConstElementPtr parent) const {
+    std::string name;
+    try {
+        name = getString(parent, "name");
+
+    } catch (...) {
+        return (OptionalValue<std::string>());
+    }
+
+    if (name.find(" ") != std::string::npos) {
+        isc_throw(DhcpConfigError, "invalid option name '" << name
+                  << "', space character is not allowed ("
+                  << getPosition("name", parent) << ")");
+    }
+
+    return (OptionalValue<std::string>(name, OptionalValueState(true)));
+}
+
+std::string
+OptionDataParser::extractData(ConstElementPtr parent) const {
+    std::string data;
+    try {
+        data = getString(parent, "data");
+
+    } catch (...) {
+        // The "data" parameter was not found. Return an empty value.
+        return (data);
+    }
+
+    return (data);
+}
+
+OptionalValue<bool>
+OptionDataParser::extractCSVFormat(ConstElementPtr parent) const {
+    bool csv_format = true;
+    try {
+        csv_format = getBoolean(parent, "csv-format");
+
+    } catch (...) {
+        return (OptionalValue<bool>(csv_format));
+    }
+
+    return (OptionalValue<bool>(csv_format, OptionalValueState(true)));
+}
+
+std::string
+OptionDataParser::extractSpace(ConstElementPtr parent) const {
+    std::string space = address_family_ == AF_INET ?
+        DHCP4_OPTION_SPACE : DHCP6_OPTION_SPACE;
+    try {
+        space = getString(parent, "space");
+
+    } catch (...) {
+        return (space);
+    }
+
+    try {
+        if (!OptionSpace::validateName(space)) {
+            isc_throw(DhcpConfigError, "invalid option space name '"
+                      << space << "'");
+        }
+
+        if ((space == DHCP4_OPTION_SPACE) && (address_family_ == AF_INET6)) {
+            isc_throw(DhcpConfigError, "'" << DHCP4_OPTION_SPACE
+                      << "' option space name is reserved for DHCPv4 server");
+
+        } else if ((space == DHCP6_OPTION_SPACE) &&
+                   (address_family_ == AF_INET)) {
+            isc_throw(DhcpConfigError, "'" << DHCP6_OPTION_SPACE
+                      << "' option space name is reserved for DHCPv6 server");
+        }
+
+    } catch (std::exception& ex) {
+        // Append position of the option space parameter.
+        isc_throw(DhcpConfigError, ex.what() << " ("
+                  << getPosition("space", parent) << ")");
+    }
+
+    return (space);
+}
+
+OptionalValue<bool>
+OptionDataParser::extractPersistent(ConstElementPtr parent) const {
+    bool persist = false;
+    try {
+        persist = getBoolean(parent, "always-send");
+
+    } catch (...) {
+        return (OptionalValue<bool>(persist));
+    }
+
+    return (OptionalValue<bool>(persist, OptionalValueState(true)));
+}
+
+template<typename SearchKey>
+OptionDefinitionPtr
+OptionDataParser::findOptionDefinition(const std::string& option_space,
+                                       const SearchKey& search_key) const {
+    OptionDefinitionPtr def = LibDHCP::getOptionDef(option_space, search_key);
+
+    if (!def) {
+        // Check if this is a vendor-option. If it is, get vendor-specific
+        // definition.
+        uint32_t vendor_id = LibDHCP::optionSpaceToVendorId(option_space);
+        if (vendor_id) {
+            const Option::Universe u = address_family_ == AF_INET ?
+                Option::V4 : Option::V6;
+            def = LibDHCP::getVendorOptionDef(u, vendor_id, search_key);
+        }
+    }
+
+    if (!def) {
+        // Check if this is an option specified by a user. We used to
+        // check that in the staging configuration, but when the configuration
+        // changes are caused by a command the staging configuration doesn't
+        // exist. What is always available is the container holding runtime
+        // option definitions in LibDHCP. It holds option definitions from
+        // the staging configuration in case of the full reconfiguration or
+        // the definitions from the current configuration in case there is
+        // no staging configuration (after configuration commit). In other
+        // words, runtime options are always the ones that we need here.
+        def = LibDHCP::getRuntimeOptionDef(option_space, search_key);
+    }
+
+    return (def);
+}
+
+std::pair<OptionDescriptor, std::string>
+OptionDataParser::createOption(ConstElementPtr option_data) {
+    const Option::Universe universe = address_family_ == AF_INET ?
+        Option::V4 : Option::V6;
+
+    OptionalValue<uint32_t> code_param =  extractCode(option_data);
+    OptionalValue<std::string> name_param = extractName(option_data);
+    OptionalValue<bool> csv_format_param = extractCSVFormat(option_data);
+    OptionalValue<bool> persist_param = extractPersistent(option_data);
+    std::string data_param = extractData(option_data);
+    std::string space_param = extractSpace(option_data);
+
+    // Require that option code or option name is specified.
+    if (!code_param.isSpecified() && !name_param.isSpecified()) {
+        isc_throw(DhcpConfigError, "option data configuration requires one of"
+                  " 'code' or 'name' parameters to be specified"
+                  << " (" << option_data->getPosition() << ")");
+    }
+
+    // Try to find a corresponding option definition using option code or
+    // option name.
+    OptionDefinitionPtr def = code_param.isSpecified() ?
+        findOptionDefinition(space_param, code_param) :
+        findOptionDefinition(space_param, name_param);
+
+    // If there is no definition, the user must not explicitly enable the
+    // use of csv-format.
+    if (!def) {
+        // If explicitly requested that the CSV format is to be used,
+        // the option definition is a must.
+        if (csv_format_param.isSpecified() && csv_format_param) {
+            isc_throw(DhcpConfigError, "definition for the option '"
+                      << space_param << "." << name_param
+                      << "' having code '" << code_param
+                      << "' does not exist ("
+                      << getPosition("name", option_data)
+                      << ")");
+
+        // If there is no option definition and the option code is not specified
+        // we have no means to find the option code.
+        } else if (name_param.isSpecified() && !code_param.isSpecified()) {
+            isc_throw(DhcpConfigError, "definition for the option '"
+                      << space_param << "." << name_param
+                      << "' does not exist ("
+                      << getPosition("name", option_data)
+                      << ")");
+        }
+    }
+
+    // Transform string of hexadecimal digits into binary format.
+    std::vector<uint8_t> binary;
+    std::vector<std::string> data_tokens;
+
+    // If the definition is available and csv-format hasn't been explicitly
+    // disabled, we will parse the data as comma separated values.
+    if (def && (!csv_format_param.isSpecified() || csv_format_param)) {
+        // If the option data is specified as a string of comma
+        // separated values then we need to split this string into
+        // individual values - each value will be used to initialize
+        // one data field of an option.
+        // It is the only usage of the escape option: this allows
+        // to embed commas in individual values and to return
+        // for instance a string value with embedded commas.
+        data_tokens = isc::util::str::tokens(data_param, ",", true);
+
+    } else {
+        // Otherwise, the option data is specified as a string of
+        // hexadecimal digits that we have to turn into binary format.
+        try {
+            // The decodeHex function expects that the string contains an
+            // even number of digits. If we don't meet this requirement,
+            // we have to insert a leading 0.
+            if (!data_param.empty() && ((data_param.length() % 2) != 0)) {
+                data_param = data_param.insert(0, "0");
+            }
+            util::encode::decodeHex(data_param, binary);
+        } catch (...) {
+            isc_throw(DhcpConfigError, "option data is not a valid"
+                      << " string of hexadecimal digits: " << data_param
+                      << " ("
+                      << getPosition("data", option_data)
+                      << ")");
+        }
+    }
+
+    OptionPtr option;
+    OptionDescriptor desc(false);
+
+    if (!def) {
+        // @todo We have a limited set of option definitions initialized at
+        // the moment.  In the future we want to initialize option definitions
+        // for all options.  Consequently an error will be issued if an option
+        // definition does not exist for a particular option code. For now it is
+        // ok to create generic option if definition does not exist.
+        OptionPtr option(new Option(universe, static_cast<uint16_t>(code_param),
+                                    binary));
+
+        desc.option_ = option;
+        desc.persistent_ = persist_param.isSpecified() && persist_param;
+    } else {
+
+        // Option name is specified it should match the name in the definition.
+        if (name_param.isSpecified() && (def->getName() != name_param.get())) {
+            isc_throw(DhcpConfigError, "specified option name '"
+                      << name_param << "' does not match the "
+                      << "option definition: '" << space_param
+                      << "." << def->getName() << "' ("
+                      << getPosition("name", option_data)
+                      << ")");
+        }
+
+        // Option definition has been found so let's use it to create
+        // an instance of our option.
+        try {
+            bool use_csv = !csv_format_param.isSpecified() || csv_format_param;
+            OptionPtr option = use_csv ?
+                def->optionFactory(universe, def->getCode(), data_tokens) :
+                def->optionFactory(universe, def->getCode(), binary);
+            desc.option_ = option;
+            desc.persistent_ = persist_param.isSpecified() && persist_param;
+            if (use_csv) {
+                desc.formatted_value_ = data_param;
+            }
+        } catch (const isc::Exception& ex) {
+            isc_throw(DhcpConfigError, "option data does not match"
+                      << " option definition (space: " << space_param
+                      << ", code: " << def->getCode() << "): "
+                      << ex.what() << " ("
+                      << getPosition("data", option_data)
+                      << ")");
+        }
+    }
+
+    // All went good, so we can set the option space name.
+    return make_pair(desc, space_param);
+}
+
+// **************************** OptionDataListParser *************************
+OptionDataListParser::OptionDataListParser(//const std::string&,
+                                           //const CfgOptionPtr& cfg,
+                                           const uint16_t address_family)
+    : address_family_(address_family) {
+}
+
+
+void OptionDataListParser::parse(const CfgOptionPtr& cfg,
+                                 isc::data::ConstElementPtr option_data_list) {
+    OptionDataParser option_parser(address_family_);
+    BOOST_FOREACH(ConstElementPtr data, option_data_list->listValue()) {
+        std::pair<OptionDescriptor, std::string> option =
+            option_parser.parse(data);
+        // Use the option description to keep the formatted value
+        cfg->add(option.first, option.second);
+        cfg->encapsulate();
+    }
+}
+
+} // end of namespace isc::dhcp
+} // end of namespace isc

+ 182 - 0
src/lib/dhcpsrv/parsers/option_data_parser.h

@@ -0,0 +1,182 @@
+// Copyright (C) 2017 Internet Systems Consortium, Inc. ("ISC")
+//
+// This Source Code Form is subject to the terms of the Mozilla Public
+// License, v. 2.0. If a copy of the MPL was not distributed with this
+// file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+#ifndef OPTION_DATA_PARSER_H
+#define OPTION_DATA_PARSER_H
+
+#include <cc/data.h>
+#include <cc/simple_parser.h>
+#include <dhcpsrv/cfg_option.h>
+#include <util/optional_value.h>
+#include <cstdint>
+#include <string>
+#include <utility>
+
+namespace isc {
+namespace dhcp {
+
+/// @brief Parser for option data value.
+///
+/// This parser parses configuration entries that specify value of
+/// a single option. These entries include option name, option code
+/// and data carried by the option. The option data can be specified
+/// in one of the two available formats: binary value represented as
+/// a string of hexadecimal digits or a list of comma separated values.
+/// The format being used is controlled by csv-format configuration
+/// parameter. When setting this value to True, the latter format is
+/// used. The subsequent values in the CSV format apply to relevant
+/// option data fields in the configured option. For example the
+/// configuration: "data" : "192.168.2.0, 56, hello world" can be
+/// used to set values for the option comprising IPv4 address,
+/// integer and string data field. Note that order matters. If the
+/// order of values does not match the order of data fields within
+/// an option the configuration will not be accepted. If parsing
+/// is successful then an instance of an option is created and
+/// added to the storage provided by the calling class.
+class OptionDataParser : public isc::data::SimpleParser {
+public:
+    /// @brief Constructor.
+    ///
+    /// @param address_family Address family: @c AF_INET or @c AF_INET6.
+    explicit OptionDataParser(const uint16_t address_family);
+
+    /// @brief Parses ElementPtr containing option definition
+    ///
+    /// This method parses ElementPtr containing the option definition,
+    /// instantiates the option for it and then returns a pair
+    /// of option descriptor (that holds that new option) and
+    /// a string that specifies the option space.
+    ///
+    /// Note: ElementPtr is expected to contain all fields. If your
+    /// ElementPtr does not have them, please use
+    /// @ref isc::data::SimpleParser::setDefaults to fill the missing fields
+    /// with default values.
+    ///
+    /// @param single_option ElementPtr containing option definition
+    /// @return Option object wrapped in option description and an option
+    ///         space
+    std::pair<OptionDescriptor, std::string>
+    parse(isc::data::ConstElementPtr single_option);
+private:
+
+    /// @brief Finds an option definition within an option space
+    ///
+    /// Given an option space and an option code, find the corresponding
+    /// option definition within the option definition storage.
+    ///
+    /// @param option_space name of the parameter option space
+    /// @param search_key an option code or name to be used to lookup the
+    /// option definition.
+    /// @tparam A numeric type for searching using an option code or the
+    /// string for searching using the option name.
+    ///
+    /// @return OptionDefinitionPtr of the option definition or an
+    /// empty OptionDefinitionPtr if not found.
+    /// @throw DhcpConfigError if the option space requested is not valid
+    /// for this server.
+    template<typename SearchKey>
+    OptionDefinitionPtr findOptionDefinition(const std::string& option_space,
+                                             const SearchKey& search_key) const;
+
+    /// @brief Create option instance.
+    ///
+    /// Creates an instance of an option and adds it to the provided
+    /// options storage. If the option data parsed by \ref build function
+    /// are invalid or insufficient this function emits an exception.
+    ///
+    /// @param option_data An element holding data for a single option being
+    /// created.
+    ///
+    /// @return created option descriptor
+    ///
+    /// @throw DhcpConfigError if parameters provided in the configuration
+    /// are invalid.
+    std::pair<OptionDescriptor, std::string>
+    createOption(isc::data::ConstElementPtr option_data);
+
+    /// @brief Retrieves parsed option code as an optional value.
+    ///
+    /// @param parent A data element holding full option data configuration.
+    ///
+    /// @return Option code, possibly unspecified.
+    /// @throw DhcpConfigError if option code is invalid.
+    util::OptionalValue<uint32_t>
+    extractCode(data::ConstElementPtr parent) const;
+
+    /// @brief Retrieves parsed option name as an optional value.
+    ///
+    /// @param parent A data element holding full option data configuration.
+    ///
+    /// @return Option name, possibly unspecified.
+    /// @throw DhcpConfigError if option name is invalid.
+    util::OptionalValue<std::string>
+    extractName(data::ConstElementPtr parent) const;
+
+    /// @brief Retrieves csv-format parameter as an optional value.
+    ///
+    /// @return Value of the csv-format parameter, possibly unspecified.
+    util::OptionalValue<bool> extractCSVFormat(data::ConstElementPtr parent) const;
+
+    /// @brief Retrieves option data as a string.
+    ///
+    /// @param parent A data element holding full option data configuration.
+    /// @return Option data as a string. It will return empty string if
+    /// option data is unspecified.
+    std::string extractData(data::ConstElementPtr parent) const;
+
+    /// @brief Retrieves option space name.
+    ///
+    /// If option space name is not specified in the configuration the
+    /// 'dhcp4' or 'dhcp6' option space name is returned, depending on
+    /// the universe specified in the parser context.
+    ///
+    /// @param parent A data element holding full option data configuration.
+    ///
+    /// @return Option space name.
+    std::string extractSpace(data::ConstElementPtr parent) const;
+
+    /// @brief Retrieves persistent/always-send parameter as an optional value.
+    ///
+    /// @return Value of the persistent parameter, possibly unspecified.
+    util::OptionalValue<bool> extractPersistent(data::ConstElementPtr parent) const;
+
+    /// @brief Address family: @c AF_INET or @c AF_INET6.
+    uint16_t address_family_;
+};
+
+/// @brief Parser for option data values within a subnet.
+///
+/// This parser iterates over all entries that define options
+/// data for a particular subnet and creates a collection of options.
+/// If parsing is successful, all these options are added to the Subnet
+/// object.
+class OptionDataListParser : public isc::data::SimpleParser {
+public:
+    /// @brief Constructor.
+    ///
+    /// @param address_family Address family: @c AF_INET or AF_INET6
+    explicit OptionDataListParser(const uint16_t address_family);
+
+    /// @brief Parses a list of options, instantiates them and stores in cfg
+    ///
+    /// This method expects to get a list of options in option_data_list,
+    /// iterates over them, creates option objects, wraps them with
+    /// option descriptor and stores in specified cfg.
+    ///
+    /// @param cfg created options will be stored here
+    /// @param option_data_list configuration that describes the options
+    void parse(const CfgOptionPtr& cfg,
+               isc::data::ConstElementPtr option_data_list);
+private:
+    /// @brief Address family: @c AF_INET or @c AF_INET6
+    uint16_t address_family_;
+};
+
+
+} // end of namespace isc::dhcp
+} // end of namespace isc
+
+#endif // OPTION_DATA_PARSER_H

+ 1 - 1
src/bin/dhcp4/simple_parser4.cc

@@ -4,7 +4,7 @@
 // License, v. 2.0. If a copy of the MPL was not distributed with this
 // License, v. 2.0. If a copy of the MPL was not distributed with this
 // file, You can obtain one at http://mozilla.org/MPL/2.0/.
 // file, You can obtain one at http://mozilla.org/MPL/2.0/.
 
 
-#include <dhcp4/simple_parser4.h>
+#include <dhcpsrv/parsers/simple_parser4.h>
 #include <cc/data.h>
 #include <cc/data.h>
 #include <boost/foreach.hpp>
 #include <boost/foreach.hpp>
 
 

src/bin/dhcp4/simple_parser4.h → src/lib/dhcpsrv/parsers/simple_parser4.h


+ 1 - 1
src/bin/dhcp6/simple_parser6.cc

@@ -4,8 +4,8 @@
 // License, v. 2.0. If a copy of the MPL was not distributed with this
 // License, v. 2.0. If a copy of the MPL was not distributed with this
 // file, You can obtain one at http://mozilla.org/MPL/2.0/.
 // file, You can obtain one at http://mozilla.org/MPL/2.0/.
 
 
-#include <dhcp6/simple_parser6.h>
 #include <cc/data.h>
 #include <cc/data.h>
+#include <dhcpsrv/parsers/simple_parser6.h>
 #include <boost/foreach.hpp>
 #include <boost/foreach.hpp>
 
 
 using namespace isc::data;
 using namespace isc::data;

src/bin/dhcp6/simple_parser6.h → src/lib/dhcpsrv/parsers/simple_parser6.h


+ 28 - 0
src/lib/dhcpsrv/tests/cfg_subnets4_unittest.cc

@@ -74,6 +74,34 @@ TEST(CfgSubnets4Test, getSpecificSubnet) {
     EXPECT_FALSE(cfg.getByPrefix("10.20.30.0/29"));
     EXPECT_FALSE(cfg.getByPrefix("10.20.30.0/29"));
 }
 }
 
 
+// This test verifies that a single subnet can be removed from the configuration.
+TEST(CfgSubnets4Test, deleteSubnet) {
+    CfgSubnets4 cfg;
+
+    // Create 3 subnets.
+    Subnet4Ptr subnet1(new Subnet4(IOAddress("192.0.2.0"),
+                                   26, 1, 2, 3, SubnetID(5)));
+    Subnet4Ptr subnet2(new Subnet4(IOAddress("192.0.3.0"),
+                                   26, 1, 2, 3, SubnetID(8)));
+    Subnet4Ptr subnet3(new Subnet4(IOAddress("192.0.4.0"),
+                                   26, 1, 2, 3, SubnetID(10)));
+
+    ASSERT_NO_THROW(cfg.add(subnet1));
+    ASSERT_NO_THROW(cfg.add(subnet2));
+    ASSERT_NO_THROW(cfg.add(subnet3));
+
+    // There should be three subnets.
+    ASSERT_EQ(3, cfg.getAll()->size());
+    // We're going to remove the subnet #2. Let's make sure it exists before
+    // we remove it.
+    ASSERT_TRUE(cfg.getByPrefix("192.0.3.0/26"));
+
+    // Remove the subnet and make sure it is gone.
+    ASSERT_NO_THROW(cfg.del(subnet2));
+    ASSERT_EQ(2, cfg.getAll()->size());
+    EXPECT_FALSE(cfg.getByPrefix("192.0.3.0/26"));
+}
+
 // This test verifies that it is possible to retrieve a subnet using an
 // This test verifies that it is possible to retrieve a subnet using an
 // IP address.
 // IP address.
 TEST(CfgSubnets4Test, selectSubnetByCiaddr) {
 TEST(CfgSubnets4Test, selectSubnetByCiaddr) {

+ 25 - 0
src/lib/dhcpsrv/tests/cfg_subnets6_unittest.cc

@@ -83,6 +83,31 @@ TEST(CfgSubnets6Test, getSpecificSubnet) {
     EXPECT_FALSE(cfg.getByPrefix("3000::/16"));
     EXPECT_FALSE(cfg.getByPrefix("3000::/16"));
 }
 }
 
 
+// This test verifies that a single subnet can be removed from the configuration.
+TEST(CfgSubnets6Test, deleteSubnet) {
+    CfgSubnets6 cfg;
+
+    // Create 3 subnets.
+    Subnet6Ptr subnet1(new Subnet6(IOAddress("2001:db8:1::"), 48, 1, 2, 3, 4));
+    Subnet6Ptr subnet2(new Subnet6(IOAddress("2001:db8:2::"), 48, 1, 2, 3, 4));
+    Subnet6Ptr subnet3(new Subnet6(IOAddress("2001:db8:3::"), 48, 1, 2, 3, 4));
+
+    ASSERT_NO_THROW(cfg.add(subnet1));
+    ASSERT_NO_THROW(cfg.add(subnet2));
+    ASSERT_NO_THROW(cfg.add(subnet3));
+
+    // There should be three subnets.
+    ASSERT_EQ(3, cfg.getAll()->size());
+    // We're going to remove the subnet #2. Let's make sure it exists before
+    // we remove it.
+    ASSERT_TRUE(cfg.getByPrefix("2001:db8:2::/48"));
+
+    // Remove the subnet and make sure it is gone.
+    ASSERT_NO_THROW(cfg.del(subnet2));
+    ASSERT_EQ(2, cfg.getAll()->size());
+    EXPECT_FALSE(cfg.getByPrefix("2001:db8:2::/48"));
+}
+
 // This test checks that the subnet can be selected using a relay agent's
 // This test checks that the subnet can be selected using a relay agent's
 // link address.
 // link address.
 TEST(CfgSubnets6Test, selectSubnetByRelayAddress) {
 TEST(CfgSubnets6Test, selectSubnetByRelayAddress) {

+ 1 - 0
src/lib/dhcpsrv/tests/dhcp_parsers_unittest.cc

@@ -18,6 +18,7 @@
 #include <dhcpsrv/subnet.h>
 #include <dhcpsrv/subnet.h>
 #include <dhcpsrv/cfg_mac_source.h>
 #include <dhcpsrv/cfg_mac_source.h>
 #include <dhcpsrv/parsers/dhcp_parsers.h>
 #include <dhcpsrv/parsers/dhcp_parsers.h>
+#include <dhcpsrv/parsers/option_data_parser.h>
 #include <dhcpsrv/tests/test_libraries.h>
 #include <dhcpsrv/tests/test_libraries.h>
 #include <dhcpsrv/testutils/config_result_check.h>
 #include <dhcpsrv/testutils/config_result_check.h>
 #include <exceptions/exceptions.h>
 #include <exceptions/exceptions.h>