Browse Source

[4088] Finished merge of trac4091 from master

Francis Dupont 9 years ago
parent
commit
11bc4b3cfc
46 changed files with 1576 additions and 483 deletions
  1. 4 1
      AUTHORS
  2. 9 0
      ChangeLog
  3. 8 8
      doc/guide/dhcp4-srv.xml
  4. 12 1
      doc/guide/dhcp6-srv.xml
  5. 10 0
      doc/guide/logging.xml
  6. 45 2
      src/bin/dhcp4/json_config_parser.cc
  7. 272 156
      src/bin/dhcp4/tests/config_parser_unittest.cc
  8. 2 2
      src/bin/dhcp4/tests/ctrl_dhcp4_srv_unittest.cc
  9. 1 4
      src/bin/dhcp4/tests/decline_unittest.cc
  10. 9 36
      src/bin/dhcp4/tests/dora_unittest.cc
  11. 2 8
      src/bin/dhcp4/tests/fqdn_unittest.cc
  12. 8 32
      src/bin/dhcp4/tests/inform_unittest.cc
  13. 1 1
      src/bin/dhcp4/tests/kea_controller_unittest.cc
  14. 1 4
      src/bin/dhcp4/tests/release_unittest.cc
  15. 2 2
      src/bin/dhcp6/json_config_parser.cc
  16. 41 165
      src/bin/dhcp6/tests/config_parser_unittest.cc
  17. 2 2
      src/bin/dhcp6/tests/ctrl_dhcp6_srv_unittest.cc
  18. 4 18
      src/bin/dhcp6/tests/dhcp6_srv_unittest.cc
  19. 1 1
      src/bin/dhcp6/tests/kea_controller_unittest.cc
  20. 1 0
      src/lib/dhcp/Makefile.am
  21. 15 8
      src/lib/dhcp/dhcp6.h
  22. 11 0
      src/lib/dhcp/libdhcp++.cc
  23. 3 0
      src/lib/dhcp/libdhcp++.h
  24. 4 4
      src/lib/dhcp/pkt.cc
  25. 8 0
      src/lib/dhcp/pkt4.h
  26. 60 0
      src/lib/dhcp/pkt4o6.cc
  27. 83 0
      src/lib/dhcp/pkt4o6.h
  28. 10 0
      src/lib/dhcp/pkt6.cc
  29. 16 0
      src/lib/dhcp/std_option_defs.h
  30. 1 0
      src/lib/dhcp/tests/Makefile.am
  31. 6 0
      src/lib/dhcp/tests/libdhcp++_unittest.cc
  32. 106 0
      src/lib/dhcp/tests/pkt4o6_unittest.cc
  33. 8 0
      src/lib/dhcp/tests/pkt6_unittest.cc
  34. 1 1
      src/lib/dhcpsrv/parsers/dhcp_parsers.cc
  35. 2 1
      src/lib/dhcpsrv/parsers/dhcp_parsers.h
  36. 88 0
      src/lib/dhcpsrv/subnet.h
  37. 109 18
      src/lib/dhcpsrv/tests/dhcp_parsers_unittest.cc
  38. 10 2
      src/lib/eval/Makefile.am
  39. 26 0
      src/lib/eval/eval_log.cc
  40. 49 0
      src/lib/eval/eval_log.h
  41. 5 0
      src/lib/eval/eval_messages.mes
  42. 5 2
      src/lib/eval/tests/Makefile.am
  43. 0 0
      src/lib/eval/tests/run_unittests.cc
  44. 320 0
      src/lib/eval/tests/token_unittest.cc
  45. 111 0
      src/lib/eval/token.cc
  46. 84 4
      src/lib/eval/token.h

+ 4 - 1
AUTHORS

@@ -76,12 +76,15 @@ We have received the following contributions:
  - David Gutierrez Rueda, CERN
  - David Gutierrez Rueda, CERN
    2014-12: Support for client link-address option in DHCPv6 (RFC6939)
    2014-12: Support for client link-address option in DHCPv6 (RFC6939)
 
 
- - Adam Kalmus, Gdank University of Technology
+ - Adam Kalmus, Gdansk University of Technology
    2014-12: Extract MAC address from DUID-LL and DUID-LLT types
    2014-12: Extract MAC address from DUID-LL and DUID-LLT types
    2015-01: Extract MAC address from remote-id
    2015-01: Extract MAC address from remote-id
    2015-05: MySQL schema extended to cover host reservation
    2015-05: MySQL schema extended to cover host reservation
    2015-10: Common MySQL Connector Pool
    2015-10: Common MySQL Connector Pool
 
 
+ - Jinmei Tatuya
+   2015-10: Pkt4o6 class improvements
+
 Kea uses log4cplus (http://sourceforge.net/projects/log4cplus/) for logging,
 Kea uses log4cplus (http://sourceforge.net/projects/log4cplus/) for logging,
 Boost (http://www.boost.org/) library for almost everything, and can use Botan
 Boost (http://www.boost.org/) library for almost everything, and can use Botan
 (http://botan.randombit.net/) or OpenSSL (https://www.openssl.org/) for
 (http://botan.randombit.net/) or OpenSSL (https://www.openssl.org/) for

+ 9 - 0
ChangeLog

@@ -1,3 +1,12 @@
+1043.	[func]		fdupont
+	Implemented support for hex strings in client classification.
+	(Trac #4091, git 406153af95404adb96296df09ec6033b484586e3)
+
+1042.	[doc]		fdupont
+	User Guide: parameters having default values may be omitted in the
+	option definitions.
+	(Trac #3927, git c7460e849258ec77cf1215a2baf840d98f1ab77b)
+
 1041.	[func]		tomek
 1041.	[func]		tomek
 	A new library, libkea-eval has been edded. It is not functional
 	A new library, libkea-eval has been edded. It is not functional
 	yet, but its purpose is to provide a generic expression
 	yet, but its purpose is to provide a generic expression

+ 8 - 8
doc/guide/dhcp4-srv.xml

@@ -1160,14 +1160,14 @@ It is merely echoed by the server
       should be left blank.  Note that the above set of comments define the
       should be left blank.  Note that the above set of comments define the
       format of the new option and do not set its values.
       format of the new option and do not set its values.
       </para>
       </para>
-      <note>
+
-        <para>
+      <para>The <command>name</command>, <command>code</command> and
-          In the current release the default values are not propagated to the
+      <command>type</command> parameters are required, all others are
-          parser when the new configuration is being set. Therefore, all
+      optional. The <command>array</command> default value is
-          parameters must be specified at all times, even if their values are
+      <command>false</command>. The <command>record-types</command>
-          left blank.
+      and <command>encapsulate</command> default values are blank
-        </para>
+      (i.e. ""). The default <command>space</command> is "dhcp4".
-      </note>
+      </para>
 
 
       <para>Once the new option format is defined, its value is set
       <para>Once the new option format is defined, its value is set
       in the same way as for a standard option. For example the following
       in the same way as for a standard option. For example the following

+ 12 - 1
doc/guide/dhcp6-srv.xml

@@ -961,6 +961,8 @@ temporarily override a list of interface names and listen on all interfaces.
 <row><entry>erp-local-domain-name</entry><entry>65</entry><entry>fqdn</entry><entry>false</entry></row>
 <row><entry>erp-local-domain-name</entry><entry>65</entry><entry>fqdn</entry><entry>false</entry></row>
 <row><entry>rsoo</entry><entry>66</entry><entry>empty</entry><entry>false</entry></row>
 <row><entry>rsoo</entry><entry>66</entry><entry>empty</entry><entry>false</entry></row>
 <row><entry>client-linklayer-addr</entry><entry>79</entry><entry>binary</entry><entry>false</entry></row>
 <row><entry>client-linklayer-addr</entry><entry>79</entry><entry>binary</entry><entry>false</entry></row>
+<!-- <row><entry>dhcpv4-message</entry><entry>87</entry><entry>binary</entry><entry>false</entry></row> -->
+<row><entry>dhcp4o6-server-addr</entry><entry>88</entry><entry>ipv6-address</entry><entry>true</entry></row>
         </tbody>
         </tbody>
         </tgroup>
         </tgroup>
       </table>
       </table>
@@ -1022,6 +1024,15 @@ temporarily override a list of interface names and listen on all interfaces.
       set of comments define the format of the new option and do not set its
       set of comments define the format of the new option and do not set its
       values.
       values.
       </para>
       </para>
+
+      <para>The <command>name</command>, <command>code</command> and
+      <command>type</command> parameters are required, all others are
+      optional. The <command>array</command> default value is
+      <command>false</command>. The <command>record-types</command>
+      and <command>encapsulate</command> default values are blank
+      (i.e. ""). The default <command>space</command> is "dhcp6".
+      </para>
+
       <para>Once the new option format is defined, its value is set
       <para>Once the new option format is defined, its value is set
       in the same way as for a standard option. For example the following
       in the same way as for a standard option. For example the following
       commands set a global value that applies to all subnets.
       commands set a global value that applies to all subnets.
@@ -1059,7 +1070,7 @@ temporarily override a list of interface names and listen on all interfaces.
             "space": "dhcp6",
             "space": "dhcp6",
             "type": "record",
             "type": "record",
             "array": false,
             "array": false,
-            "record-types": "ipv4-address, uint16, boolean, string",
+            "record-types": "ipv6-address, uint16, boolean, string",
             "encapsulate": ""</userinput>
             "encapsulate": ""</userinput>
         }, ...
         }, ...
     ],
     ],

+ 10 - 0
doc/guide/logging.xml

@@ -220,6 +220,11 @@
             logger for the libdhcpsrv library.</simpara>
             logger for the libdhcpsrv library.</simpara>
           </listitem>
           </listitem>
           <listitem>
           <listitem>
+            <simpara><command>kea-dhcp4.eval</command> - this logger is used
+            to log messages relating to the client classification expression
+            evaluation code.</simpara>
+          </listitem>
+          <listitem>
             <simpara><command>kea-dhcp4.hooks</command> - this logger is used
             <simpara><command>kea-dhcp4.hooks</command> - this logger is used
             to log messages related to management of hooks libraries, e.g.
             to log messages related to management of hooks libraries, e.g.
             registration and deregistration of the libraries, and to the
             registration and deregistration of the libraries, and to the
@@ -303,6 +308,11 @@
             logger for the libdhcpsrv library.</simpara>
             logger for the libdhcpsrv library.</simpara>
           </listitem>
           </listitem>
           <listitem>
           <listitem>
+            <simpara><command>kea-dhcp6.eval</command> - this logger is used
+            to log messages relating to the client classification expression
+            evaluation code.</simpara>
+          </listitem>
+          <listitem>
             <simpara><command>kea-dhcp6.hooks</command> - this logger is used
             <simpara><command>kea-dhcp6.hooks</command> - this logger is used
             to log messages related to management of hooks libraries, e.g.
             to log messages related to management of hooks libraries, e.g.
             registration and deregistration of the libraries, and to the
             registration and deregistration of the libraries, and to the

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

@@ -200,6 +200,12 @@ protected:
             parser = new OptionDataListParser(config_id, options_, AF_INET);
             parser = new OptionDataListParser(config_id, options_, AF_INET);
         } else if (config_id.compare("match-client-id") == 0) {
         } else if (config_id.compare("match-client-id") == 0) {
             parser = new BooleanParser(config_id, boolean_values_);
             parser = new BooleanParser(config_id, boolean_values_);
+        } else if (config_id.compare("4o6-subnet") == 0) {
+            parser = new StringParser(config_id, string_values_);
+        } else if (config_id.compare("4o6-interface") == 0) {
+            parser = new StringParser(config_id, string_values_);
+        } else if (config_id.compare("4o6-interface-id") == 0) {
+            parser = new StringParser(config_id, string_values_);
         } else {
         } else {
             isc_throw(NotImplemented, "unsupported parameter: " << config_id);
             isc_throw(NotImplemented, "unsupported parameter: " << config_id);
         }
         }
@@ -305,6 +311,43 @@ protected:
                       << ")");
                       << ")");
         }
         }
 
 
+        // Try 4o6 specific parameter: 4o6-interface
+        string iface4o6 = string_values_->getOptionalParam("4o6-interface", "");
+        if (!iface4o6.empty()) {
+            subnet4->get4o6().setIface4o6(iface4o6);
+            subnet4->get4o6().enabled(true);
+        }
+
+        // Try 4o6 specific parameter: 4o6-subnet
+        string subnet4o6 = string_values_->getOptionalParam("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 paramter: 4o6-interface-id
+        std::string ifaceid = string_values_->getOptionalParam("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);
+        }
 
 
         // Try setting up client class (if specified)
         // Try setting up client class (if specified)
         try {
         try {
@@ -373,8 +416,8 @@ namespace dhcp {
 /// @return parser for specified global DHCPv4 parameter
 /// @return parser for specified global DHCPv4 parameter
 /// @throw NotImplemented if trying to create a parser for unknown
 /// @throw NotImplemented if trying to create a parser for unknown
 /// config element
 /// config element
-    DhcpConfigParser* createGlobalDhcp4ConfigParser(const std::string& config_id,
+DhcpConfigParser* createGlobalDhcp4ConfigParser(const std::string& config_id,
-                                                    ConstElementPtr element) {
+                                                ConstElementPtr element) {
     DhcpConfigParser* parser = NULL;
     DhcpConfigParser* parser = NULL;
     if ((config_id.compare("valid-lifetime") == 0)  ||
     if ((config_id.compare("valid-lifetime") == 0)  ||
         (config_id.compare("renew-timer") == 0)  ||
         (config_id.compare("renew-timer") == 0)  ||

+ 272 - 156
src/bin/dhcp4/tests/config_parser_unittest.cc

@@ -1330,10 +1330,7 @@ TEST_F(Dhcp4ParserTest, optionDefIpv4Address) {
         "      \"name\": \"foo\","
         "      \"name\": \"foo\","
         "      \"code\": 100,"
         "      \"code\": 100,"
         "      \"type\": \"ipv4-address\","
         "      \"type\": \"ipv4-address\","
-        "      \"array\": False,"
+        "      \"space\": \"isc\""
-        "      \"record-types\": \"\","
-        "      \"space\": \"isc\","
-        "      \"encapsulate\": \"\""
         "  } ]"
         "  } ]"
         "}";
         "}";
     ElementPtr json = Element::fromJSON(config);
     ElementPtr json = Element::fromJSON(config);
@@ -1372,10 +1369,8 @@ TEST_F(Dhcp4ParserTest, optionDefRecord) {
         "      \"name\": \"foo\","
         "      \"name\": \"foo\","
         "      \"code\": 100,"
         "      \"code\": 100,"
         "      \"type\": \"record\","
         "      \"type\": \"record\","
-        "      \"array\": False,"
         "      \"record-types\": \"uint16, ipv4-address, ipv6-address, string\","
         "      \"record-types\": \"uint16, ipv4-address, ipv6-address, string\","
-        "      \"space\": \"isc\","
+        "      \"space\": \"isc\""
-        "      \"encapsulate\": \"\""
         "  } ]"
         "  } ]"
         "}";
         "}";
     ElementPtr json = Element::fromJSON(config);
     ElementPtr json = Element::fromJSON(config);
@@ -1422,19 +1417,13 @@ TEST_F(Dhcp4ParserTest, optionDefMultiple) {
         "      \"name\": \"foo\","
         "      \"name\": \"foo\","
         "      \"code\": 100,"
         "      \"code\": 100,"
         "      \"type\": \"uint32\","
         "      \"type\": \"uint32\","
-        "      \"array\": False,"
+        "      \"space\": \"isc\""
-        "      \"record-types\": \"\","
-        "      \"space\": \"isc\","
-        "      \"encapsulate\": \"\""
         "  },"
         "  },"
         "  {"
         "  {"
         "      \"name\": \"foo-2\","
         "      \"name\": \"foo-2\","
         "      \"code\": 101,"
         "      \"code\": 101,"
         "      \"type\": \"ipv4-address\","
         "      \"type\": \"ipv4-address\","
-        "      \"array\": False,"
+        "      \"space\": \"isc\""
-        "      \"record-types\": \"\","
-        "      \"space\": \"isc\","
-        "      \"encapsulate\": \"\""
         "  } ]"
         "  } ]"
         "}";
         "}";
     ElementPtr json = Element::fromJSON(config);
     ElementPtr json = Element::fromJSON(config);
@@ -1488,19 +1477,13 @@ TEST_F(Dhcp4ParserTest, optionDefDuplicate) {
         "      \"name\": \"foo\","
         "      \"name\": \"foo\","
         "      \"code\": 100,"
         "      \"code\": 100,"
         "      \"type\": \"uint32\","
         "      \"type\": \"uint32\","
-        "      \"array\": False,"
+        "      \"space\": \"isc\""
-        "      \"record-types\": \"\","
-        "      \"space\": \"isc\","
-        "      \"encapsulate\": \"\""
         "  },"
         "  },"
         "  {"
         "  {"
         "      \"name\": \"foo-2\","
         "      \"name\": \"foo-2\","
         "      \"code\": 100,"
         "      \"code\": 100,"
         "      \"type\": \"ipv4-address\","
         "      \"type\": \"ipv4-address\","
-        "      \"array\": False,"
+        "      \"space\": \"isc\""
-        "      \"record-types\": \"\","
-        "      \"space\": \"isc\","
-        "      \"encapsulate\": \"\""
         "  } ]"
         "  } ]"
         "}";
         "}";
     ElementPtr json = Element::fromJSON(config);
     ElementPtr json = Element::fromJSON(config);
@@ -1529,9 +1512,7 @@ TEST_F(Dhcp4ParserTest, optionDefArray) {
         "      \"code\": 100,"
         "      \"code\": 100,"
         "      \"type\": \"uint32\","
         "      \"type\": \"uint32\","
         "      \"array\": True,"
         "      \"array\": True,"
-        "      \"record-types\": \"\","
+        "      \"space\": \"isc\""
-        "      \"space\": \"isc\","
-        "      \"encapsulate\": \"\""
         "  } ]"
         "  } ]"
         "}";
         "}";
     ElementPtr json = Element::fromJSON(config);
     ElementPtr json = Element::fromJSON(config);
@@ -1571,8 +1552,6 @@ TEST_F(Dhcp4ParserTest, optionDefEncapsulate) {
         "      \"name\": \"foo\","
         "      \"name\": \"foo\","
         "      \"code\": 100,"
         "      \"code\": 100,"
         "      \"type\": \"uint32\","
         "      \"type\": \"uint32\","
-        "      \"array\": False,"
-        "      \"record-types\": \"\","
         "      \"space\": \"isc\","
         "      \"space\": \"isc\","
         "      \"encapsulate\": \"sub-opts-space\""
         "      \"encapsulate\": \"sub-opts-space\""
         "  } ]"
         "  } ]"
@@ -1613,10 +1592,7 @@ TEST_F(Dhcp4ParserTest, optionDefInvalidName) {
         "      \"name\": \"invalid%name\","
         "      \"name\": \"invalid%name\","
         "      \"code\": 100,"
         "      \"code\": 100,"
         "      \"type\": \"string\","
         "      \"type\": \"string\","
-        "      \"array\": False,"
+        "      \"space\": \"isc\""
-        "      \"record-types\": \"\","
-        "      \"space\": \"isc\","
-        "      \"encapsulate\": \"\""
         "  } ]"
         "  } ]"
         "}";
         "}";
     ElementPtr json = Element::fromJSON(config);
     ElementPtr json = Element::fromJSON(config);
@@ -1640,10 +1616,7 @@ TEST_F(Dhcp4ParserTest, optionDefInvalidType) {
         "      \"name\": \"foo\","
         "      \"name\": \"foo\","
         "      \"code\": 100,"
         "      \"code\": 100,"
         "      \"type\": \"sting\","
         "      \"type\": \"sting\","
-        "      \"array\": False,"
+        "      \"space\": \"isc\""
-        "      \"record-types\": \"\","
-        "      \"space\": \"isc\","
-        "      \"encapsulate\": \"\""
         "  } ]"
         "  } ]"
         "}";
         "}";
     ElementPtr json = Element::fromJSON(config);
     ElementPtr json = Element::fromJSON(config);
@@ -1667,10 +1640,8 @@ TEST_F(Dhcp4ParserTest, optionDefInvalidRecordType) {
         "      \"name\": \"foo\","
         "      \"name\": \"foo\","
         "      \"code\": 100,"
         "      \"code\": 100,"
         "      \"type\": \"record\","
         "      \"type\": \"record\","
-        "      \"array\": False,"
         "      \"record-types\": \"uint32,uint8,sting\","
         "      \"record-types\": \"uint32,uint8,sting\","
-        "      \"space\": \"isc\","
+        "      \"space\": \"isc\""
-        "      \"encapsulate\": \"\""
         "  } ]"
         "  } ]"
         "}";
         "}";
     ElementPtr json = Element::fromJSON(config);
     ElementPtr json = Element::fromJSON(config);
@@ -1694,8 +1665,6 @@ TEST_F(Dhcp4ParserTest, optionDefInvalidEncapsulatedSpace) {
         "      \"name\": \"foo\","
         "      \"name\": \"foo\","
         "      \"code\": 100,"
         "      \"code\": 100,"
         "      \"type\": \"uint32\","
         "      \"type\": \"uint32\","
-        "      \"array\": False,"
-        "      \"record-types\": \"\","
         "      \"space\": \"isc\","
         "      \"space\": \"isc\","
         "      \"encapsulate\": \"invalid%space%name\""
         "      \"encapsulate\": \"invalid%space%name\""
         "  } ]"
         "  } ]"
@@ -1724,7 +1693,6 @@ TEST_F(Dhcp4ParserTest, optionDefEncapsulatedSpaceAndArray) {
         "      \"code\": 100,"
         "      \"code\": 100,"
         "      \"type\": \"uint32\","
         "      \"type\": \"uint32\","
         "      \"array\": True,"
         "      \"array\": True,"
-        "      \"record-types\": \"\","
         "      \"space\": \"isc\","
         "      \"space\": \"isc\","
         "      \"encapsulate\": \"valid-space-name\""
         "      \"encapsulate\": \"valid-space-name\""
         "  } ]"
         "  } ]"
@@ -1750,8 +1718,6 @@ TEST_F(Dhcp4ParserTest, optionDefEncapsulateOwnSpace) {
         "      \"name\": \"foo\","
         "      \"name\": \"foo\","
         "      \"code\": 100,"
         "      \"code\": 100,"
         "      \"type\": \"uint32\","
         "      \"type\": \"uint32\","
-        "      \"array\": False,"
-        "      \"record-types\": \"\","
         "      \"space\": \"isc\","
         "      \"space\": \"isc\","
         "      \"encapsulate\": \"isc\""
         "      \"encapsulate\": \"isc\""
         "  } ]"
         "  } ]"
@@ -1781,10 +1747,7 @@ TEST_F(Dhcp4ParserTest, optionStandardDefOverride) {
         "      \"name\": \"foo\","
         "      \"name\": \"foo\","
         "      \"code\": 109,"
         "      \"code\": 109,"
         "      \"type\": \"string\","
         "      \"type\": \"string\","
-        "      \"array\": False,"
+        "      \"space\": \"dhcp4\""
-        "      \"record-types\": \"\","
-        "      \"space\": \"dhcp4\","
-        "      \"encapsulate\": \"\""
         "  } ]"
         "  } ]"
         "}";
         "}";
     ElementPtr json = Element::fromJSON(config);
     ElementPtr json = Element::fromJSON(config);
@@ -1818,10 +1781,7 @@ TEST_F(Dhcp4ParserTest, optionStandardDefOverride) {
         "      \"name\": \"routers\","
         "      \"name\": \"routers\","
         "      \"code\": 3,"
         "      \"code\": 3,"
         "      \"type\": \"ipv4-address\","
         "      \"type\": \"ipv4-address\","
-        "      \"array\": False,"
+        "      \"space\": \"dhcp4\""
-        "      \"record-types\": \"\","
-        "      \"space\": \"dhcp4\","
-        "      \"encapsulate\": \"\""
         "  } ]"
         "  } ]"
         "}";
         "}";
     json = Element::fromJSON(config);
     json = Element::fromJSON(config);
@@ -1843,10 +1803,7 @@ TEST_F(Dhcp4ParserTest, optionStandardDefOverride) {
         "      \"name\": \"nis-server-addr\","
         "      \"name\": \"nis-server-addr\","
         "      \"code\": 65,"
         "      \"code\": 65,"
         "      \"type\": \"ipv4-address\","
         "      \"type\": \"ipv4-address\","
-        "      \"array\": False,"
+        "      \"space\": \"dhcp4\""
-        "      \"record-types\": \"\","
-        "      \"space\": \"dhcp4\","
-        "      \"encapsulate\": \"\""
         "  } ]"
         "  } ]"
         "}";
         "}";
     json = Element::fromJSON(config);
     json = Element::fromJSON(config);
@@ -1879,15 +1836,11 @@ TEST_F(Dhcp4ParserTest, optionDataDefaults) {
         "\"renew-timer\": 1000,"
         "\"renew-timer\": 1000,"
         "\"option-data\": [ {"
         "\"option-data\": [ {"
         "    \"name\": \"dhcp-message\","
         "    \"name\": \"dhcp-message\","
-        "    \"space\": \"dhcp4\","
-        "    \"code\": 56,"
         "    \"data\": \"ABCDEF0105\","
         "    \"data\": \"ABCDEF0105\","
         "    \"csv-format\": False"
         "    \"csv-format\": False"
         " },"
         " },"
         " {"
         " {"
         "    \"name\": \"default-ip-ttl\","
         "    \"name\": \"default-ip-ttl\","
-        "    \"space\": \"dhcp4\","
-        "    \"code\": 23,"
         "    \"data\": \"01\","
         "    \"data\": \"01\","
         "    \"csv-format\": False"
         "    \"csv-format\": False"
         " } ],"
         " } ],"
@@ -1952,26 +1905,19 @@ TEST_F(Dhcp4ParserTest, optionDataTwoSpaces) {
         "\"renew-timer\": 1000,"
         "\"renew-timer\": 1000,"
         "\"option-data\": [ {"
         "\"option-data\": [ {"
         "    \"name\": \"dhcp-message\","
         "    \"name\": \"dhcp-message\","
-        "    \"space\": \"dhcp4\","
-        "    \"code\": 56,"
         "    \"data\": \"ABCDEF0105\","
         "    \"data\": \"ABCDEF0105\","
         "    \"csv-format\": False"
         "    \"csv-format\": False"
         " },"
         " },"
         " {"
         " {"
         "    \"name\": \"foo\","
         "    \"name\": \"foo\","
         "    \"space\": \"isc\","
         "    \"space\": \"isc\","
-        "    \"code\": 56,"
+        "    \"data\": \"1234\""
-        "    \"data\": \"1234\","
-        "    \"csv-format\": True"
         " } ],"
         " } ],"
         "\"option-def\": [ {"
         "\"option-def\": [ {"
         "    \"name\": \"foo\","
         "    \"name\": \"foo\","
         "    \"code\": 56,"
         "    \"code\": 56,"
         "    \"type\": \"uint32\","
         "    \"type\": \"uint32\","
-        "    \"array\": False,"
+        "    \"space\": \"isc\""
-        "    \"record-types\": \"\","
-        "    \"space\": \"isc\","
-        "    \"encapsulate\": \"\""
         " } ],"
         " } ],"
         "\"subnet4\": [ { "
         "\"subnet4\": [ { "
         "    \"pools\": [ { \"pool\": \"192.0.2.1 - 192.0.2.100\" } ],"
         "    \"pools\": [ { \"pool\": \"192.0.2.1 - 192.0.2.100\" } ],"
@@ -2033,34 +1979,24 @@ TEST_F(Dhcp4ParserTest, optionDataEncapsulate) {
         "\"option-data\": [ {"
         "\"option-data\": [ {"
         "    \"name\": \"foo\","
         "    \"name\": \"foo\","
         "    \"space\": \"isc\","
         "    \"space\": \"isc\","
-        "    \"code\": 1,"
+        "    \"data\": \"1234\""
-        "    \"data\": \"1234\","
-        "    \"csv-format\": True"
         " },"
         " },"
         " {"
         " {"
         "    \"name\": \"foo2\","
         "    \"name\": \"foo2\","
         "    \"space\": \"isc\","
         "    \"space\": \"isc\","
-        "    \"code\": 2,"
+        "    \"data\": \"192.168.2.1\""
-        "    \"data\": \"192.168.2.1\","
-        "    \"csv-format\": True"
         " } ],"
         " } ],"
         "\"option-def\": [ {"
         "\"option-def\": [ {"
         "    \"name\": \"foo\","
         "    \"name\": \"foo\","
         "    \"code\": 1,"
         "    \"code\": 1,"
         "    \"type\": \"uint32\","
         "    \"type\": \"uint32\","
-        "    \"array\": False,"
+        "    \"space\": \"isc\""
-        "    \"record-types\": \"\","
-        "    \"space\": \"isc\","
-        "    \"encapsulate\": \"\""
         " },"
         " },"
         " {"
         " {"
         "    \"name\": \"foo2\","
         "    \"name\": \"foo2\","
         "    \"code\": 2,"
         "    \"code\": 2,"
         "    \"type\": \"ipv4-address\","
         "    \"type\": \"ipv4-address\","
-        "    \"array\": False,"
+        "    \"space\": \"isc\""
-        "    \"record-types\": \"\","
-        "    \"space\": \"isc\","
-        "    \"encapsulate\": \"\""
         " } ]"
         " } ]"
         "}";
         "}";
 
 
@@ -2084,31 +2020,22 @@ TEST_F(Dhcp4ParserTest, optionDataEncapsulate) {
         "\"renew-timer\": 1000,"
         "\"renew-timer\": 1000,"
         "\"option-data\": [ {"
         "\"option-data\": [ {"
         "    \"name\": \"base-option\","
         "    \"name\": \"base-option\","
-        "    \"space\": \"dhcp4\","
+        "    \"data\": \"11\""
-        "    \"code\": 222,"
-        "    \"data\": \"11\","
-        "    \"csv-format\": True"
         " },"
         " },"
         " {"
         " {"
         "    \"name\": \"foo\","
         "    \"name\": \"foo\","
         "    \"space\": \"isc\","
         "    \"space\": \"isc\","
-        "    \"code\": 1,"
+        "    \"data\": \"1234\""
-        "    \"data\": \"1234\","
-        "    \"csv-format\": True"
         " },"
         " },"
         " {"
         " {"
         "    \"name\": \"foo2\","
         "    \"name\": \"foo2\","
         "    \"space\": \"isc\","
         "    \"space\": \"isc\","
-        "    \"code\": 2,"
+        "    \"data\": \"192.168.2.1\""
-        "    \"data\": \"192.168.2.1\","
-        "    \"csv-format\": True"
         " } ],"
         " } ],"
         "\"option-def\": [ {"
         "\"option-def\": [ {"
         "    \"name\": \"base-option\","
         "    \"name\": \"base-option\","
         "    \"code\": 222,"
         "    \"code\": 222,"
         "    \"type\": \"uint8\","
         "    \"type\": \"uint8\","
-        "    \"array\": False,"
-        "    \"record-types\": \"\","
         "    \"space\": \"dhcp4\","
         "    \"space\": \"dhcp4\","
         "    \"encapsulate\": \"isc\""
         "    \"encapsulate\": \"isc\""
         "},"
         "},"
@@ -2116,19 +2043,13 @@ TEST_F(Dhcp4ParserTest, optionDataEncapsulate) {
         "    \"name\": \"foo\","
         "    \"name\": \"foo\","
         "    \"code\": 1,"
         "    \"code\": 1,"
         "    \"type\": \"uint32\","
         "    \"type\": \"uint32\","
-        "    \"array\": False,"
+        "    \"space\": \"isc\""
-        "    \"record-types\": \"\","
-        "    \"space\": \"isc\","
-        "    \"encapsulate\": \"\""
         " },"
         " },"
         " {"
         " {"
         "    \"name\": \"foo2\","
         "    \"name\": \"foo2\","
         "    \"code\": 2,"
         "    \"code\": 2,"
         "    \"type\": \"ipv4-address\","
         "    \"type\": \"ipv4-address\","
-        "    \"array\": False,"
+        "    \"space\": \"isc\""
-        "    \"record-types\": \"\","
-        "    \"space\": \"isc\","
-        "    \"encapsulate\": \"\""
         " } ],"
         " } ],"
         "\"subnet4\": [ { "
         "\"subnet4\": [ { "
         "    \"pools\": [ { \"pool\": \"192.0.2.1 - 192.0.2.100\" } ],"
         "    \"pools\": [ { \"pool\": \"192.0.2.1 - 192.0.2.100\" } ],"
@@ -2180,8 +2101,6 @@ TEST_F(Dhcp4ParserTest, optionDataInSingleSubnet) {
         "\"renew-timer\": 1000, "
         "\"renew-timer\": 1000, "
         "\"option-data\": [ {"
         "\"option-data\": [ {"
         "      \"name\": \"dhcp-message\","
         "      \"name\": \"dhcp-message\","
-        "      \"space\": \"dhcp4\","
-        "      \"code\": 56,"
         "      \"data\": \"AB\","
         "      \"data\": \"AB\","
         "      \"csv-format\": False"
         "      \"csv-format\": False"
         " } ],"
         " } ],"
@@ -2190,15 +2109,11 @@ TEST_F(Dhcp4ParserTest, optionDataInSingleSubnet) {
         "    \"subnet\": \"192.0.2.0/24\", "
         "    \"subnet\": \"192.0.2.0/24\", "
         "    \"option-data\": [ {"
         "    \"option-data\": [ {"
         "          \"name\": \"dhcp-message\","
         "          \"name\": \"dhcp-message\","
-        "          \"space\": \"dhcp4\","
-        "          \"code\": 56,"
         "          \"data\": \"ABCDEF0105\","
         "          \"data\": \"ABCDEF0105\","
         "          \"csv-format\": False"
         "          \"csv-format\": False"
         "        },"
         "        },"
         "        {"
         "        {"
         "          \"name\": \"default-ip-ttl\","
         "          \"name\": \"default-ip-ttl\","
-        "          \"space\": \"dhcp4\","
-        "          \"code\": 23,"
         "          \"data\": \"01\","
         "          \"data\": \"01\","
         "          \"csv-format\": False"
         "          \"csv-format\": False"
         "        } ]"
         "        } ]"
@@ -2337,8 +2252,6 @@ TEST_F(Dhcp4ParserTest, optionDataInMultipleSubnets) {
         "    \"subnet\": \"192.0.2.0/24\", "
         "    \"subnet\": \"192.0.2.0/24\", "
         "    \"option-data\": [ {"
         "    \"option-data\": [ {"
         "          \"name\": \"dhcp-message\","
         "          \"name\": \"dhcp-message\","
-        "          \"space\": \"dhcp4\","
-        "          \"code\": 56,"
         "          \"data\": \"0102030405060708090A\","
         "          \"data\": \"0102030405060708090A\","
         "          \"csv-format\": False"
         "          \"csv-format\": False"
         "        } ]"
         "        } ]"
@@ -2348,8 +2261,6 @@ TEST_F(Dhcp4ParserTest, optionDataInMultipleSubnets) {
         "    \"subnet\": \"192.0.3.0/24\", "
         "    \"subnet\": \"192.0.3.0/24\", "
         "    \"option-data\": [ {"
         "    \"option-data\": [ {"
         "          \"name\": \"default-ip-ttl\","
         "          \"name\": \"default-ip-ttl\","
-        "          \"space\": \"dhcp4\","
-        "          \"code\": 23,"
         "          \"data\": \"FF\","
         "          \"data\": \"FF\","
         "          \"csv-format\": False"
         "          \"csv-format\": False"
         "        } ]"
         "        } ]"
@@ -2610,34 +2521,24 @@ TEST_F(Dhcp4ParserTest, stdOptionDataEncapsulate) {
         "\"option-data\": [ {"
         "\"option-data\": [ {"
         "    \"name\": \"foo\","
         "    \"name\": \"foo\","
         "    \"space\": \"vendor-encapsulated-options-space\","
         "    \"space\": \"vendor-encapsulated-options-space\","
-        "    \"code\": 1,"
+        "    \"data\": \"1234\""
-        "    \"data\": \"1234\","
-        "    \"csv-format\": True"
         " },"
         " },"
         " {"
         " {"
         "    \"name\": \"foo2\","
         "    \"name\": \"foo2\","
         "    \"space\": \"vendor-encapsulated-options-space\","
         "    \"space\": \"vendor-encapsulated-options-space\","
-        "    \"code\": 2,"
+        "    \"data\": \"192.168.2.1\""
-        "    \"data\": \"192.168.2.1\","
-        "    \"csv-format\": True"
         " } ],"
         " } ],"
         "\"option-def\": [ {"
         "\"option-def\": [ {"
         "    \"name\": \"foo\","
         "    \"name\": \"foo\","
         "    \"code\": 1,"
         "    \"code\": 1,"
         "    \"type\": \"uint32\","
         "    \"type\": \"uint32\","
-        "    \"array\": False,"
+        "    \"space\": \"vendor-encapsulated-options-space\""
-        "    \"record-types\": \"\","
-        "    \"space\": \"vendor-encapsulated-options-space\","
-        "    \"encapsulate\": \"\""
         " },"
         " },"
         " {"
         " {"
         "    \"name\": \"foo2\","
         "    \"name\": \"foo2\","
         "    \"code\": 2,"
         "    \"code\": 2,"
         "    \"type\": \"ipv4-address\","
         "    \"type\": \"ipv4-address\","
-        "    \"array\": False,"
+        "    \"space\": \"vendor-encapsulated-options-space\""
-        "    \"record-types\": \"\","
-        "    \"space\": \"vendor-encapsulated-options-space\","
-        "    \"encapsulate\": \"\""
         " } ]"
         " } ]"
         "}";
         "}";
 
 
@@ -2665,17 +2566,12 @@ TEST_F(Dhcp4ParserTest, stdOptionDataEncapsulate) {
         "\"renew-timer\": 1000,"
         "\"renew-timer\": 1000,"
         "\"option-data\": [ {"
         "\"option-data\": [ {"
         "    \"name\": \"vendor-encapsulated-options\","
         "    \"name\": \"vendor-encapsulated-options\","
-        "    \"space\": \"dhcp4\","
-        "    \"code\": 43,"
-        "    \"data\": \"\","
         "    \"csv-format\": False"
         "    \"csv-format\": False"
         " },"
         " },"
         " {"
         " {"
         "    \"name\": \"foo\","
         "    \"name\": \"foo\","
         "    \"space\": \"vendor-encapsulated-options-space\","
         "    \"space\": \"vendor-encapsulated-options-space\","
-        "    \"code\": 1,"
+        "    \"data\": \"1234\""
-        "    \"data\": \"1234\","
-        "    \"csv-format\": True"
         " },"
         " },"
         " {"
         " {"
         "    \"name\": \"foo2\","
         "    \"name\": \"foo2\","
@@ -2688,19 +2584,13 @@ TEST_F(Dhcp4ParserTest, stdOptionDataEncapsulate) {
         "    \"name\": \"foo\","
         "    \"name\": \"foo\","
         "    \"code\": 1,"
         "    \"code\": 1,"
         "    \"type\": \"uint32\","
         "    \"type\": \"uint32\","
-        "    \"array\": False,"
+        "    \"space\": \"vendor-encapsulated-options-space\""
-        "    \"record-types\": \"\","
-        "    \"space\": \"vendor-encapsulated-options-space\","
-        "    \"encapsulate\": \"\""
         " },"
         " },"
         " {"
         " {"
         "    \"name\": \"foo2\","
         "    \"name\": \"foo2\","
         "    \"code\": 2,"
         "    \"code\": 2,"
         "    \"type\": \"ipv4-address\","
         "    \"type\": \"ipv4-address\","
-        "    \"array\": False,"
+        "    \"space\": \"vendor-encapsulated-options-space\""
-        "    \"record-types\": \"\","
-        "    \"space\": \"vendor-encapsulated-options-space\","
-        "    \"encapsulate\": \"\""
         " } ],"
         " } ],"
         "\"subnet4\": [ { "
         "\"subnet4\": [ { "
         "    \"pools\": [ { \"pool\": \"192.0.2.1 - 192.0.2.100\" } ],"
         "    \"pools\": [ { \"pool\": \"192.0.2.1 - 192.0.2.100\" } ],"
@@ -2834,17 +2724,13 @@ TEST_F(Dhcp4ParserTest, vendorOptionsCsv) {
         "    \"name\": \"foo\","
         "    \"name\": \"foo\","
         "    \"space\": \"vendor-4491\","
         "    \"space\": \"vendor-4491\","
         "    \"code\": 100,"
         "    \"code\": 100,"
-        "    \"data\": \"this is a string vendor-opt\","
+        "    \"data\": \"this is a string vendor-opt\""
-        "    \"csv-format\": True"
         " } ],"
         " } ],"
         "\"option-def\": [ {"
         "\"option-def\": [ {"
         "    \"name\": \"foo\","
         "    \"name\": \"foo\","
         "    \"code\": 100,"
         "    \"code\": 100,"
         "    \"type\": \"string\","
         "    \"type\": \"string\","
-        "    \"array\": False,"
+        "    \"space\": \"vendor-4491\""
-        "    \"record-types\": \"\","
-        "    \"space\": \"vendor-4491\","
-        "    \"encapsulate\": \"\""
         " } ],"
         " } ],"
         "\"subnet4\": [ { "
         "\"subnet4\": [ { "
         "    \"pools\": [ { \"pool\":  \"192.0.2.1 - 192.0.2.100\" } ],"
         "    \"pools\": [ { \"pool\":  \"192.0.2.1 - 192.0.2.100\" } ],"
@@ -2911,26 +2797,19 @@ buildHooksLibrariesConfig(const std::vector<std::string>& libraries) {
         "\"renew-timer\": 1000,"
         "\"renew-timer\": 1000,"
         "\"option-data\": [ {"
         "\"option-data\": [ {"
         "    \"name\": \"dhcp-message\","
         "    \"name\": \"dhcp-message\","
-        "    \"space\": \"dhcp4\","
-        "    \"code\": 56,"
         "    \"data\": \"ABCDEF0105\","
         "    \"data\": \"ABCDEF0105\","
         "    \"csv-format\": False"
         "    \"csv-format\": False"
         " },"
         " },"
         " {"
         " {"
         "    \"name\": \"foo\","
         "    \"name\": \"foo\","
         "    \"space\": \"isc\","
         "    \"space\": \"isc\","
-        "    \"code\": 56,"
+        "    \"data\": \"1234\""
-        "    \"data\": \"1234\","
-        "    \"csv-format\": True"
         " } ],"
         " } ],"
         "\"option-def\": [ {"
         "\"option-def\": [ {"
         "    \"name\": \"foo\","
         "    \"name\": \"foo\","
         "    \"code\": 56,"
         "    \"code\": 56,"
         "    \"type\": \"uint32\","
         "    \"type\": \"uint32\","
-        "    \"array\": False,"
+        "    \"space\": \"isc\""
-        "    \"record-types\": \"\","
-        "    \"space\": \"isc\","
-        "    \"encapsulate\": \"\""
         " } ],"
         " } ],"
         "\"subnet4\": [ { "
         "\"subnet4\": [ { "
         "    \"pools\": [ { \"pool\": \"192.0.2.1 - 192.0.2.100\" } ],"
         "    \"pools\": [ { \"pool\": \"192.0.2.1 - 192.0.2.100\" } ],"
@@ -3782,4 +3661,241 @@ TEST_F(Dhcp4ParserTest, expiredLeasesProcessingError) {
     EXPECT_TRUE(errorContainsPosition(status, "<string>"));
     EXPECT_TRUE(errorContainsPosition(status, "<string>"));
 }
 }
 
 
+
+// Checks if the DHCPv4 is able to parse the configuration without 4o6 parameters
+// and does not set 4o6 fields at all.
+TEST_F(Dhcp4ParserTest, 4o6default) {
+
+    ConstElementPtr status;
+
+    // Just a plain v4 config (no 4o6 parameters)
+    string config = "{ " + genIfaceConfig() + "," +
+        "\"rebind-timer\": 2000, "
+        "\"renew-timer\": 1000, "
+        "\"subnet4\": [ { "
+        "    \"pools\": [ { \"pool\": \"192.0.2.1 - 192.0.2.100\" } ],"
+        "    \"subnet\": \"192.0.2.0/24\" } ],"
+        "\"valid-lifetime\": 4000 }";
+
+    ElementPtr json = Element::fromJSON(config);
+
+    EXPECT_NO_THROW(status = configureDhcp4Server(*srv_, json));
+
+    // check if returned status is OK
+    checkResult(status, 0);
+
+    // Now check if the configuration was indeed handled and we have
+    // expected pool configured.
+    Subnet4Ptr subnet = CfgMgr::instance().getStagingCfg()->
+        getCfgSubnets4()->selectSubnet(IOAddress("192.0.2.200"));
+    ASSERT_TRUE(subnet);
+
+    Cfg4o6& dhcp4o6 = subnet->get4o6();
+    EXPECT_FALSE(dhcp4o6.enabled());
+}
+
+// Checks if the DHCPv4 is able to parse the configuration with 4o6 subnet
+// defined.
+TEST_F(Dhcp4ParserTest, 4o6subnet) {
+
+    ConstElementPtr status;
+
+    // Just a plain v4 config (no 4o6 parameters)
+    string config = "{ " + genIfaceConfig() + "," +
+        "\"rebind-timer\": 2000, "
+        "\"renew-timer\": 1000, "
+        "\"subnet4\": [ { "
+        "    \"pools\": [ { \"pool\": \"192.0.2.1 - 192.0.2.100\" } ],"
+        "    \"subnet\": \"192.0.2.0/24\","
+        "    \"4o6-subnet\": \"2001:db8::123/45\" } ],"
+        "\"valid-lifetime\": 4000 }";
+
+    ElementPtr json = Element::fromJSON(config);
+
+    EXPECT_NO_THROW(status = configureDhcp4Server(*srv_, json));
+
+    // check if returned status is OK
+    checkResult(status, 0);
+
+    // Now check if the configuration was indeed handled and we have
+    // expected pool configured.
+    Subnet4Ptr subnet = CfgMgr::instance().getStagingCfg()->
+        getCfgSubnets4()->selectSubnet(IOAddress("192.0.2.200"));
+    ASSERT_TRUE(subnet);
+
+    Cfg4o6& dhcp4o6 = subnet->get4o6();
+    EXPECT_TRUE(dhcp4o6.enabled());
+    EXPECT_EQ(IOAddress("2001:db8::123"), dhcp4o6.getSubnet4o6().first);
+    EXPECT_EQ(45, dhcp4o6.getSubnet4o6().second);
+}
+
+// Checks if the DHCPv4 is able to parse the configuration with 4o6 subnet
+// defined.
+TEST_F(Dhcp4ParserTest, 4o6subnetBogus) {
+
+    ConstElementPtr status;
+
+    // Just a plain v4 config (no 4o6 parameters)
+    string config[] = {
+        // Bogus configuration 1: missing / in subnet
+        "{ " + genIfaceConfig() + "," +
+        "\"rebind-timer\": 2000, "
+        "\"renew-timer\": 1000, "
+        "\"subnet4\": [ { "
+        "    \"pools\": [ { \"pool\": \"192.0.2.1 - 192.0.2.100\" } ],"
+        "    \"subnet\": \"192.0.2.0/24\","
+        "    \"4o6-subnet\": \"2001:db8::123\" } ],"
+        "\"valid-lifetime\": 4000 }",
+
+        // Bogus configuration 2: incorrect address
+                "{ " + genIfaceConfig() + "," +
+        "\"rebind-timer\": 2000, "
+        "\"renew-timer\": 1000, "
+        "\"subnet4\": [ { "
+        "    \"pools\": [ { \"pool\": \"192.0.2.1 - 192.0.2.100\" } ],"
+        "    \"subnet\": \"192.0.2.0/24\","
+        "    \"4o6-subnet\": \"2001:db8:bogus/45\" } ],"
+        "\"valid-lifetime\": 4000 }",
+
+        // Bogus configuration 3: incorrect prefix lenght
+        "{ " + genIfaceConfig() + "," +
+        "\"rebind-timer\": 2000, "
+        "\"renew-timer\": 1000, "
+        "\"subnet4\": [ { "
+        "    \"pools\": [ { \"pool\": \"192.0.2.1 - 192.0.2.100\" } ],"
+        "    \"subnet\": \"192.0.2.0/24\","
+        "    \"4o6-subnet\": \"2001:db8::123/200\" } ],"
+        "\"valid-lifetime\": 4000 }"
+    };
+
+    ElementPtr json1 = Element::fromJSON(config[0]);
+    ElementPtr json2 = Element::fromJSON(config[0]);
+    ElementPtr json3 = Element::fromJSON(config[0]);
+
+    // Check that the first config is rejected.
+    EXPECT_NO_THROW(status = configureDhcp4Server(*srv_, json1));
+    checkResult(status, 1);
+
+    // Check that the second config is rejected.
+    EXPECT_NO_THROW(status = configureDhcp4Server(*srv_, json2));
+    checkResult(status, 1);
+
+    // Check that the third config is rejected.
+    EXPECT_NO_THROW(status = configureDhcp4Server(*srv_, json3));
+    checkResult(status, 1);
+}
+
+
+// Checks if the DHCPv4 is able to parse the configuration with 4o6 network
+// interface defined.
+TEST_F(Dhcp4ParserTest, 4o6iface) {
+
+    ConstElementPtr status;
+
+    // Just a plain v4 config (no 4o6 parameters)
+    string config = "{ " + genIfaceConfig() + "," +
+        "\"rebind-timer\": 2000, "
+        "\"renew-timer\": 1000, "
+        "\"subnet4\": [ { "
+        "    \"pools\": [ { \"pool\": \"192.0.2.1 - 192.0.2.100\" } ],"
+        "    \"subnet\": \"192.0.2.0/24\","
+        "    \"4o6-interface\": \"ethX\" } ],"
+        "\"valid-lifetime\": 4000 }";
+
+    ElementPtr json = Element::fromJSON(config);
+
+    EXPECT_NO_THROW(status = configureDhcp4Server(*srv_, json));
+
+    // check if returned status is OK
+    checkResult(status, 0);
+
+    // Now check if the configuration was indeed handled and we have
+    // expected pool configured.
+    Subnet4Ptr subnet = CfgMgr::instance().getStagingCfg()->
+        getCfgSubnets4()->selectSubnet(IOAddress("192.0.2.200"));
+    ASSERT_TRUE(subnet);
+
+    Cfg4o6& dhcp4o6 = subnet->get4o6();
+    EXPECT_TRUE(dhcp4o6.enabled());
+    EXPECT_EQ("ethX", dhcp4o6.getIface4o6());
+}
+
+// Checks if the DHCPv4 is able to parse the configuration with both 4o6 network
+// interface and v6 subnet defined.
+TEST_F(Dhcp4ParserTest, 4o6subnetIface) {
+
+    ConstElementPtr status;
+
+    // Just a plain v4 config (no 4o6 parameters)
+    string config = "{ " + genIfaceConfig() + "," +
+        "\"rebind-timer\": 2000, "
+        "\"renew-timer\": 1000, "
+        "\"subnet4\": [ { "
+        "    \"pools\": [ { \"pool\": \"192.0.2.1 - 192.0.2.100\" } ],"
+        "    \"subnet\": \"192.0.2.0/24\","
+        "    \"4o6-subnet\": \"2001:db8::543/21\","
+        "    \"4o6-interface\": \"ethX\" } ],"
+        "\"valid-lifetime\": 4000 }";
+
+    ElementPtr json = Element::fromJSON(config);
+
+    EXPECT_NO_THROW(status = configureDhcp4Server(*srv_, json));
+
+    // check if returned status is OK
+    checkResult(status, 0);
+
+    // Now check if the configuration was indeed handled and we have
+    // expected subnet configured...
+    Subnet4Ptr subnet = CfgMgr::instance().getStagingCfg()->
+        getCfgSubnets4()->selectSubnet(IOAddress("192.0.2.200"));
+    ASSERT_TRUE(subnet);
+
+    // ... and that subnet has 4o6 network interface specified.
+    Cfg4o6& dhcp4o6 = subnet->get4o6();
+    EXPECT_TRUE(dhcp4o6.enabled());
+    EXPECT_EQ(IOAddress("2001:db8::543"), dhcp4o6.getSubnet4o6().first);
+    EXPECT_EQ(21, dhcp4o6.getSubnet4o6().second);
+    EXPECT_EQ("ethX", dhcp4o6.getIface4o6());
+}
+
+// Checks if the DHCPv4 is able to parse the configuration with 4o6 network
+// interface-id.
+TEST_F(Dhcp4ParserTest, 4o6subnetInterfaceId) {
+
+    ConstElementPtr status;
+
+    // Just a plain v4 config (no 4o6 parameters)
+    string config = "{ " + genIfaceConfig() + "," +
+        "\"rebind-timer\": 2000, "
+        "\"renew-timer\": 1000, "
+        "\"subnet4\": [ { "
+        "    \"pools\": [ { \"pool\": \"192.0.2.1 - 192.0.2.100\" } ],"
+        "    \"subnet\": \"192.0.2.0/24\","
+        "    \"4o6-interface-id\": \"vlan123\" } ],"
+        "\"valid-lifetime\": 4000 }";
+
+    ElementPtr json = Element::fromJSON(config);
+
+    EXPECT_NO_THROW(status = configureDhcp4Server(*srv_, json));
+
+    // check if returned status is OK
+    checkResult(status, 0);
+
+    // Now check if the configuration was indeed handled and we have
+    // expected 4o6-interface-id configured.
+    Subnet4Ptr subnet = CfgMgr::instance().getStagingCfg()->
+        getCfgSubnets4()->selectSubnet(IOAddress("192.0.2.200"));
+    ASSERT_TRUE(subnet);
+
+    Cfg4o6& dhcp4o6 = subnet->get4o6();
+    EXPECT_TRUE(dhcp4o6.enabled());
+    OptionPtr ifaceid = dhcp4o6.getInterfaceId();
+    ASSERT_TRUE(ifaceid);
+
+    vector<uint8_t> data = ifaceid->getData();
+    EXPECT_EQ(7, data.size());
+    const char *exp = "vlan123";
+    EXPECT_EQ(0, memcmp(&data[0], exp, data.size()));
+}
+
 }
 }

+ 2 - 2
src/bin/dhcp4/tests/ctrl_dhcp4_srv_unittest.cc

@@ -361,7 +361,7 @@ TEST_F(CtrlChannelDhcpv4SrvTest, controlLeasesReclaim) {
                                 time(NULL) - 100, SubnetID(1)));
                                 time(NULL) - 100, SubnetID(1)));
 
 
     // Add leases to the database.
     // Add leases to the database.
-    LeaseMgr& lease_mgr = LeaseMgrFactory().instance();
+    LeaseMgr& lease_mgr = LeaseMgrFactory::instance();
     ASSERT_NO_THROW(lease_mgr.addLease(lease0));
     ASSERT_NO_THROW(lease_mgr.addLease(lease0));
     ASSERT_NO_THROW(lease_mgr.addLease(lease1));
     ASSERT_NO_THROW(lease_mgr.addLease(lease1));
 
 
@@ -419,7 +419,7 @@ TEST_F(CtrlChannelDhcpv4SrvTest, controlLeasesReclaimRemove) {
                                 time(NULL) - 100, SubnetID(1)));
                                 time(NULL) - 100, SubnetID(1)));
 
 
     // Add leases to the database.
     // Add leases to the database.
-    LeaseMgr& lease_mgr = LeaseMgrFactory().instance();
+    LeaseMgr& lease_mgr = LeaseMgrFactory::instance();
     ASSERT_NO_THROW(lease_mgr.addLease(lease0));
     ASSERT_NO_THROW(lease_mgr.addLease(lease0));
     ASSERT_NO_THROW(lease_mgr.addLease(lease1));
     ASSERT_NO_THROW(lease_mgr.addLease(lease1));
 
 

+ 1 - 4
src/bin/dhcp4/tests/decline_unittest.cc

@@ -53,10 +53,7 @@ const char* DECLINE_CONFIGS[] = {
         "    \"pools\": [ { \"pool\": \"10.0.0.10-10.0.0.100\" } ],"
         "    \"pools\": [ { \"pool\": \"10.0.0.10-10.0.0.100\" } ],"
         "    \"option-data\": [ {"
         "    \"option-data\": [ {"
         "        \"name\": \"routers\","
         "        \"name\": \"routers\","
-        "        \"code\": 3,"
+        "        \"data\": \"10.0.0.200,10.0.0.201\""
-        "        \"data\": \"10.0.0.200,10.0.0.201\","
-        "        \"csv-format\": true,"
-        "        \"space\": \"dhcp4\""
         "    } ]"
         "    } ]"
         " } ]"
         " } ]"
     "}"
     "}"

+ 9 - 36
src/bin/dhcp4/tests/dora_unittest.cc

@@ -77,31 +77,19 @@ const char* DORA_CONFIGS[] = {
         "    \"pools\": [ { \"pool\": \"10.0.0.10-10.0.0.100\" } ],"
         "    \"pools\": [ { \"pool\": \"10.0.0.10-10.0.0.100\" } ],"
         "    \"option-data\": [ {"
         "    \"option-data\": [ {"
         "        \"name\": \"routers\","
         "        \"name\": \"routers\","
-        "        \"code\": 3,"
+        "        \"data\": \"10.0.0.200,10.0.0.201\""
-        "        \"data\": \"10.0.0.200,10.0.0.201\","
-        "        \"csv-format\": true,"
-        "        \"space\": \"dhcp4\""
         "    },"
         "    },"
         "    {"
         "    {"
         "        \"name\": \"domain-name-servers\","
         "        \"name\": \"domain-name-servers\","
-        "        \"code\": 6,"
+        "        \"data\": \"10.0.0.202,10.0.0.203\""
-        "        \"data\": \"10.0.0.202,10.0.0.203\","
-        "        \"csv-format\": true,"
-        "        \"space\": \"dhcp4\""
         "    },"
         "    },"
         "    {"
         "    {"
         "        \"name\": \"log-servers\","
         "        \"name\": \"log-servers\","
-        "        \"code\": 7,"
+        "        \"data\": \"10.0.0.200,10.0.0.201\""
-        "        \"data\": \"10.0.0.200,10.0.0.201\","
-        "        \"csv-format\": true,"
-        "        \"space\": \"dhcp4\""
         "    },"
         "    },"
         "    {"
         "    {"
         "        \"name\": \"cookie-servers\","
         "        \"name\": \"cookie-servers\","
-        "        \"code\": 8,"
+        "        \"data\": \"10.0.0.202,10.0.0.203\""
-        "        \"data\": \"10.0.0.202,10.0.0.203\","
-        "        \"csv-format\": true,"
-        "        \"space\": \"dhcp4\""
         "    } ]"
         "    } ]"
         " } ]"
         " } ]"
     "}",
     "}",
@@ -116,31 +104,19 @@ const char* DORA_CONFIGS[] = {
         "    \"subnet\": \"192.0.2.0/24\", "
         "    \"subnet\": \"192.0.2.0/24\", "
         "    \"option-data\": [ {"
         "    \"option-data\": [ {"
         "        \"name\": \"routers\","
         "        \"name\": \"routers\","
-        "        \"code\": 3,"
+        "        \"data\": \"192.0.2.200,192.0.2.201\""
-        "        \"data\": \"192.0.2.200,192.0.2.201\","
-        "        \"csv-format\": true,"
-        "        \"space\": \"dhcp4\""
         "    },"
         "    },"
         "    {"
         "    {"
         "        \"name\": \"domain-name-servers\","
         "        \"name\": \"domain-name-servers\","
-        "        \"code\": 6,"
+        "        \"data\": \"192.0.2.202,192.0.2.203\""
-        "        \"data\": \"192.0.2.202,192.0.2.203\","
-        "        \"csv-format\": true,"
-        "        \"space\": \"dhcp4\""
         "    },"
         "    },"
         "    {"
         "    {"
         "        \"name\": \"log-servers\","
         "        \"name\": \"log-servers\","
-        "        \"code\": 7,"
+        "        \"data\": \"10.0.0.200,10.0.0.201\""
-        "        \"data\": \"10.0.0.200,10.0.0.201\","
-        "        \"csv-format\": true,"
-        "        \"space\": \"dhcp4\""
         "    },"
         "    },"
         "    {"
         "    {"
         "        \"name\": \"cookie-servers\","
         "        \"name\": \"cookie-servers\","
-        "        \"code\": 8,"
+        "        \"data\": \"10.0.0.202,10.0.0.203\""
-        "        \"data\": \"10.0.0.202,10.0.0.203\","
-        "        \"csv-format\": true,"
-        "        \"space\": \"dhcp4\""
         "    } ]"
         "    } ]"
         " } ]"
         " } ]"
     "}",
     "}",
@@ -174,10 +150,7 @@ const char* DORA_CONFIGS[] = {
         "    \"pools\": [ { \"pool\": \"10.0.0.10-10.0.0.100\" } ],"
         "    \"pools\": [ { \"pool\": \"10.0.0.10-10.0.0.100\" } ],"
         "    \"option-data\": [ {"
         "    \"option-data\": [ {"
         "        \"name\": \"routers\","
         "        \"name\": \"routers\","
-        "        \"code\": 3,"
+        "        \"data\": \"10.0.0.200,10.0.0.201\""
-        "        \"data\": \"10.0.0.200,10.0.0.201\","
-        "        \"csv-format\": true,"
-        "        \"space\": \"dhcp4\""
         "    } ]"
         "    } ]"
         " } ]"
         " } ]"
     "}",
     "}",

+ 2 - 8
src/bin/dhcp4/tests/fqdn_unittest.cc

@@ -47,10 +47,7 @@ const char* CONFIGS[] = {
         "    \"pools\": [ { \"pool\": \"10.0.0.10-10.0.0.100\" } ],"
         "    \"pools\": [ { \"pool\": \"10.0.0.10-10.0.0.100\" } ],"
         "    \"option-data\": [ {"
         "    \"option-data\": [ {"
         "        \"name\": \"routers\","
         "        \"name\": \"routers\","
-        "        \"code\": 3,"
+        "        \"data\": \"10.0.0.200,10.0.0.201\""
-        "        \"data\": \"10.0.0.200,10.0.0.201\","
-        "        \"csv-format\": true,"
-        "        \"space\": \"dhcp4\""
         "    } ],"
         "    } ],"
         "    \"reservations\": ["
         "    \"reservations\": ["
         "       {"
         "       {"
@@ -74,10 +71,7 @@ const char* CONFIGS[] = {
         "    \"pools\": [ { \"pool\": \"10.0.0.10-10.0.0.100\" } ],"
         "    \"pools\": [ { \"pool\": \"10.0.0.10-10.0.0.100\" } ],"
         "    \"option-data\": [ {"
         "    \"option-data\": [ {"
         "        \"name\": \"routers\","
         "        \"name\": \"routers\","
-        "        \"code\": 3,"
+        "        \"data\": \"10.0.0.200,10.0.0.201\""
-        "        \"data\": \"10.0.0.200,10.0.0.201\","
-        "        \"csv-format\": true,"
-        "        \"space\": \"dhcp4\""
         "    } ],"
         "    } ],"
         "    \"reservations\": ["
         "    \"reservations\": ["
         "       {"
         "       {"

+ 8 - 32
src/bin/dhcp4/tests/inform_unittest.cc

@@ -58,31 +58,19 @@ const char* INFORM_CONFIGS[] = {
         "    \"pools\": [ { \"pool\": \"10.0.0.10-10.0.0.100\" } ],"
         "    \"pools\": [ { \"pool\": \"10.0.0.10-10.0.0.100\" } ],"
         "    \"option-data\": [ {"
         "    \"option-data\": [ {"
         "        \"name\": \"routers\","
         "        \"name\": \"routers\","
-        "        \"code\": 3,"
+        "        \"data\": \"10.0.0.200,10.0.0.201\""
-        "        \"data\": \"10.0.0.200,10.0.0.201\","
-        "        \"csv-format\": true,"
-        "        \"space\": \"dhcp4\""
         "    },"
         "    },"
         "    {"
         "    {"
         "        \"name\": \"domain-name-servers\","
         "        \"name\": \"domain-name-servers\","
-        "        \"code\": 6,"
+        "        \"data\": \"10.0.0.202,10.0.0.203\""
-        "        \"data\": \"10.0.0.202,10.0.0.203\","
-        "        \"csv-format\": true,"
-        "        \"space\": \"dhcp4\""
         "    },"
         "    },"
         "    {"
         "    {"
         "        \"name\": \"log-servers\","
         "        \"name\": \"log-servers\","
-        "        \"code\": 7,"
+        "        \"data\": \"10.0.0.200,10.0.0.201\""
-        "        \"data\": \"10.0.0.200,10.0.0.201\","
-        "        \"csv-format\": true,"
-        "        \"space\": \"dhcp4\""
         "    },"
         "    },"
         "    {"
         "    {"
         "        \"name\": \"cookie-servers\","
         "        \"name\": \"cookie-servers\","
-        "        \"code\": 8,"
+        "        \"data\": \"10.0.0.202,10.0.0.203\""
-        "        \"data\": \"10.0.0.202,10.0.0.203\","
-        "        \"csv-format\": true,"
-        "        \"space\": \"dhcp4\""
         "    } ]"
         "    } ]"
         " } ]"
         " } ]"
     "}",
     "}",
@@ -96,31 +84,19 @@ const char* INFORM_CONFIGS[] = {
         "    \"subnet\": \"192.0.2.0/24\", "
         "    \"subnet\": \"192.0.2.0/24\", "
         "    \"option-data\": [ {"
         "    \"option-data\": [ {"
         "        \"name\": \"routers\","
         "        \"name\": \"routers\","
-        "        \"code\": 3,"
+        "        \"data\": \"192.0.2.200,192.0.2.201\""
-        "        \"data\": \"192.0.2.200,192.0.2.201\","
-        "        \"csv-format\": true,"
-        "        \"space\": \"dhcp4\""
         "    },"
         "    },"
         "    {"
         "    {"
         "        \"name\": \"domain-name-servers\","
         "        \"name\": \"domain-name-servers\","
-        "        \"code\": 6,"
+        "        \"data\": \"192.0.2.202,192.0.2.203\""
-        "        \"data\": \"192.0.2.202,192.0.2.203\","
-        "        \"csv-format\": true,"
-        "        \"space\": \"dhcp4\""
         "    },"
         "    },"
         "    {"
         "    {"
         "        \"name\": \"log-servers\","
         "        \"name\": \"log-servers\","
-        "        \"code\": 7,"
+        "        \"data\": \"10.0.0.200,10.0.0.201\""
-        "        \"data\": \"10.0.0.200,10.0.0.201\","
-        "        \"csv-format\": true,"
-        "        \"space\": \"dhcp4\""
         "    },"
         "    },"
         "    {"
         "    {"
         "        \"name\": \"cookie-servers\","
         "        \"name\": \"cookie-servers\","
-        "        \"code\": 8,"
+        "        \"data\": \"10.0.0.202,10.0.0.203\""
-        "        \"data\": \"10.0.0.202,10.0.0.203\","
-        "        \"csv-format\": true,"
-        "        \"space\": \"dhcp4\""
         "    } ]"
         "    } ]"
         " } ]"
         " } ]"
     "}"
     "}"

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

@@ -377,7 +377,7 @@ TEST_F(JSONFileBackendTest, timers) {
     lease_reclaimed->state_ = Lease4::STATE_EXPIRED_RECLAIMED;
     lease_reclaimed->state_ = Lease4::STATE_EXPIRED_RECLAIMED;
 
 
     // Add leases to the database.
     // Add leases to the database.
-    LeaseMgr& lease_mgr = LeaseMgrFactory().instance();
+    LeaseMgr& lease_mgr = LeaseMgrFactory::instance();
     ASSERT_NO_THROW(lease_mgr.addLease(lease_expired));
     ASSERT_NO_THROW(lease_mgr.addLease(lease_expired));
     ASSERT_NO_THROW(lease_mgr.addLease(lease_reclaimed));
     ASSERT_NO_THROW(lease_mgr.addLease(lease_reclaimed));
 
 

+ 1 - 4
src/bin/dhcp4/tests/release_unittest.cc

@@ -53,10 +53,7 @@ const char* RELEASE_CONFIGS[] = {
         "    \"pools\": [ { \"pool\": \"10.0.0.10-10.0.0.100\" } ],"
         "    \"pools\": [ { \"pool\": \"10.0.0.10-10.0.0.100\" } ],"
         "    \"option-data\": [ {"
         "    \"option-data\": [ {"
         "        \"name\": \"routers\","
         "        \"name\": \"routers\","
-        "        \"code\": 3,"
+        "        \"data\": \"10.0.0.200,10.0.0.201\""
-        "        \"data\": \"10.0.0.200,10.0.0.201\","
-        "        \"csv-format\": true,"
-        "        \"space\": \"dhcp4\""
         "    } ]"
         "    } ]"
         " } ]"
         " } ]"
     "}"
     "}"

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

@@ -665,8 +665,8 @@ namespace dhcp {
 /// @return parser for specified global DHCPv6 parameter
 /// @return parser for specified global DHCPv6 parameter
 /// @throw NotImplemented if trying to create a parser for unknown config
 /// @throw NotImplemented if trying to create a parser for unknown config
 /// element
 /// element
-    DhcpConfigParser* createGlobal6DhcpConfigParser(const std::string& config_id,
+DhcpConfigParser* createGlobal6DhcpConfigParser(const std::string& config_id,
-                                                    ConstElementPtr element) {
+                                                ConstElementPtr element) {
     DhcpConfigParser* parser = NULL;
     DhcpConfigParser* parser = NULL;
     if ((config_id.compare("preferred-lifetime") == 0)  ||
     if ((config_id.compare("preferred-lifetime") == 0)  ||
         (config_id.compare("valid-lifetime") == 0)  ||
         (config_id.compare("valid-lifetime") == 0)  ||

+ 41 - 165
src/bin/dhcp6/tests/config_parser_unittest.cc

@@ -205,10 +205,7 @@ public:
             "  \"name\": \"bool-option\","
             "  \"name\": \"bool-option\","
             "  \"code\": 1000,"
             "  \"code\": 1000,"
             "  \"type\": \"boolean\","
             "  \"type\": \"boolean\","
-            "  \"array\": False,"
+            "  \"space\": \"dhcp6\""
-            "  \"record-types\": \"\","
-            "  \"space\": \"dhcp6\","
-            "  \"encapsulate\": \"\""
             "} ],"
             "} ],"
             "\"subnet6\": [ { "
             "\"subnet6\": [ { "
             "    \"pools\": [ { \"pool\": \"2001:db8:1::/80\" } ],"
             "    \"pools\": [ { \"pool\": \"2001:db8:1::/80\" } ],"
@@ -1572,10 +1569,7 @@ TEST_F(Dhcp6ParserTest, optionDefIpv6Address) {
         "      \"name\": \"foo\","
         "      \"name\": \"foo\","
         "      \"code\": 100,"
         "      \"code\": 100,"
         "      \"type\": \"ipv6-address\","
         "      \"type\": \"ipv6-address\","
-        "      \"array\": False,"
+        "      \"space\": \"isc\""
-        "      \"record-types\": \"\","
-        "      \"space\": \"isc\","
-        "      \"encapsulate\": \"\""
         "  } ]"
         "  } ]"
         "}";
         "}";
     ElementPtr json = Element::fromJSON(config);
     ElementPtr json = Element::fromJSON(config);
@@ -1612,10 +1606,8 @@ TEST_F(Dhcp6ParserTest, optionDefRecord) {
         "      \"name\": \"foo\","
         "      \"name\": \"foo\","
         "      \"code\": 100,"
         "      \"code\": 100,"
         "      \"type\": \"record\","
         "      \"type\": \"record\","
-        "      \"array\": False,"
         "      \"record-types\": \"uint16, ipv4-address, ipv6-address, string\","
         "      \"record-types\": \"uint16, ipv4-address, ipv6-address, string\","
-        "      \"space\": \"isc\","
+        "      \"space\": \"isc\""
-        "      \"encapsulate\": \"\""
         "  } ]"
         "  } ]"
         "}";
         "}";
     ElementPtr json = Element::fromJSON(config);
     ElementPtr json = Element::fromJSON(config);
@@ -1661,19 +1653,13 @@ TEST_F(Dhcp6ParserTest, optionDefMultiple) {
         "      \"name\": \"foo\","
         "      \"name\": \"foo\","
         "      \"code\": 100,"
         "      \"code\": 100,"
         "      \"type\": \"uint32\","
         "      \"type\": \"uint32\","
-        "      \"array\": False,"
+        "      \"space\": \"isc\""
-        "      \"record-types\": \"\","
-        "      \"space\": \"isc\","
-        "      \"encapsulate\": \"\""
         "  },"
         "  },"
         "  {"
         "  {"
         "      \"name\": \"foo-2\","
         "      \"name\": \"foo-2\","
         "      \"code\": 101,"
         "      \"code\": 101,"
         "      \"type\": \"ipv4-address\","
         "      \"type\": \"ipv4-address\","
-        "      \"array\": False,"
+        "      \"space\": \"isc\""
-        "      \"record-types\": \"\","
-        "      \"space\": \"isc\","
-        "      \"encapsulate\": \"\""
         "  } ]"
         "  } ]"
         "}";
         "}";
     ElementPtr json = Element::fromJSON(config);
     ElementPtr json = Element::fromJSON(config);
@@ -1725,19 +1711,13 @@ TEST_F(Dhcp6ParserTest, optionDefDuplicate) {
         "      \"name\": \"foo\","
         "      \"name\": \"foo\","
         "      \"code\": 100,"
         "      \"code\": 100,"
         "      \"type\": \"uint32\","
         "      \"type\": \"uint32\","
-        "      \"array\": False,"
+        "      \"space\": \"isc\""
-        "      \"record-types\": \"\","
-        "      \"space\": \"isc\","
-        "      \"encapsulate\": \"\""
         "  },"
         "  },"
         "  {"
         "  {"
         "      \"name\": \"foo-2\","
         "      \"name\": \"foo-2\","
         "      \"code\": 100,"
         "      \"code\": 100,"
         "      \"type\": \"ipv4-address\","
         "      \"type\": \"ipv4-address\","
-        "      \"array\": False,"
+        "      \"space\": \"isc\""
-        "      \"record-types\": \"\","
-        "      \"space\": \"isc\","
-        "      \"encapsulate\": \"\""
         "  } ]"
         "  } ]"
         "}";
         "}";
     ElementPtr json = Element::fromJSON(config);
     ElementPtr json = Element::fromJSON(config);
@@ -1766,9 +1746,7 @@ TEST_F(Dhcp6ParserTest, optionDefArray) {
         "      \"code\": 100,"
         "      \"code\": 100,"
         "      \"type\": \"uint32\","
         "      \"type\": \"uint32\","
         "      \"array\": True,"
         "      \"array\": True,"
-        "      \"record-types\": \"\","
+        "      \"space\": \"isc\""
-        "      \"space\": \"isc\","
-        "      \"encapsulate\": \"\""
         "  } ]"
         "  } ]"
         "}";
         "}";
     ElementPtr json = Element::fromJSON(config);
     ElementPtr json = Element::fromJSON(config);
@@ -1806,8 +1784,6 @@ TEST_F(Dhcp6ParserTest, optionDefEncapsulate) {
         "      \"name\": \"foo\","
         "      \"name\": \"foo\","
         "      \"code\": 100,"
         "      \"code\": 100,"
         "      \"type\": \"uint32\","
         "      \"type\": \"uint32\","
-        "      \"array\": False,"
-        "      \"record-types\": \"\","
         "      \"space\": \"isc\","
         "      \"space\": \"isc\","
         "      \"encapsulate\": \"sub-opts-space\""
         "      \"encapsulate\": \"sub-opts-space\""
         "  } ]"
         "  } ]"
@@ -1847,10 +1823,7 @@ TEST_F(Dhcp6ParserTest, optionDefInvalidName) {
         "      \"name\": \"invalid%name\","
         "      \"name\": \"invalid%name\","
         "      \"code\": 100,"
         "      \"code\": 100,"
         "      \"type\": \"string\","
         "      \"type\": \"string\","
-        "      \"array\": False,"
+        "      \"space\": \"isc\""
-        "      \"record-types\": \"\","
-        "      \"space\": \"isc\","
-        "      \"encapsulate\": \"\""
         "  } ]"
         "  } ]"
         "}";
         "}";
     ElementPtr json = Element::fromJSON(config);
     ElementPtr json = Element::fromJSON(config);
@@ -1874,10 +1847,7 @@ TEST_F(Dhcp6ParserTest, optionDefInvalidType) {
         "      \"name\": \"foo\","
         "      \"name\": \"foo\","
         "      \"code\": 100,"
         "      \"code\": 100,"
         "      \"type\": \"sting\","
         "      \"type\": \"sting\","
-        "      \"array\": False,"
+        "      \"space\": \"isc\""
-        "      \"record-types\": \"\","
-        "      \"space\": \"isc\","
-        "      \"encapsulate\": \"\""
         "  } ]"
         "  } ]"
         "}";
         "}";
     ElementPtr json = Element::fromJSON(config);
     ElementPtr json = Element::fromJSON(config);
@@ -1901,10 +1871,8 @@ TEST_F(Dhcp6ParserTest, optionDefInvalidRecordType) {
         "      \"name\": \"foo\","
         "      \"name\": \"foo\","
         "      \"code\": 100,"
         "      \"code\": 100,"
         "      \"type\": \"record\","
         "      \"type\": \"record\","
-        "      \"array\": False,"
         "      \"record-types\": \"uint32,uint8,sting\","
         "      \"record-types\": \"uint32,uint8,sting\","
-        "      \"space\": \"isc\","
+        "      \"space\": \"isc\""
-        "      \"encapsulate\": \"\""
         "  } ]"
         "  } ]"
         "}";
         "}";
     ElementPtr json = Element::fromJSON(config);
     ElementPtr json = Element::fromJSON(config);
@@ -1928,8 +1896,6 @@ TEST_F(Dhcp6ParserTest, optionDefInvalidEncapsulatedSpace) {
         "      \"name\": \"foo\","
         "      \"name\": \"foo\","
         "      \"code\": 100,"
         "      \"code\": 100,"
         "      \"type\": \"uint32\","
         "      \"type\": \"uint32\","
-        "      \"array\": False,"
-        "      \"record-types\": \"\","
         "      \"space\": \"isc\","
         "      \"space\": \"isc\","
         "      \"encapsulate\": \"invalid%space%name\""
         "      \"encapsulate\": \"invalid%space%name\""
         "  } ]"
         "  } ]"
@@ -1958,7 +1924,6 @@ TEST_F(Dhcp6ParserTest, optionDefEncapsulatedSpaceAndArray) {
         "      \"code\": 100,"
         "      \"code\": 100,"
         "      \"type\": \"uint32\","
         "      \"type\": \"uint32\","
         "      \"array\": True,"
         "      \"array\": True,"
-        "      \"record-types\": \"\","
         "      \"space\": \"isc\","
         "      \"space\": \"isc\","
         "      \"encapsulate\": \"valid-space-name\""
         "      \"encapsulate\": \"valid-space-name\""
         "  } ]"
         "  } ]"
@@ -1984,8 +1949,6 @@ TEST_F(Dhcp6ParserTest, optionDefEncapsulateOwnSpace) {
         "      \"name\": \"foo\","
         "      \"name\": \"foo\","
         "      \"code\": 100,"
         "      \"code\": 100,"
         "      \"type\": \"uint32\","
         "      \"type\": \"uint32\","
-        "      \"array\": False,"
-        "      \"record-types\": \"\","
         "      \"space\": \"isc\","
         "      \"space\": \"isc\","
         "      \"encapsulate\": \"isc\""
         "      \"encapsulate\": \"isc\""
         "  } ]"
         "  } ]"
@@ -2016,10 +1979,7 @@ TEST_F(Dhcp6ParserTest, optionStandardDefOverride) {
         "      \"name\": \"foo\","
         "      \"name\": \"foo\","
         "      \"code\": 100,"
         "      \"code\": 100,"
         "      \"type\": \"string\","
         "      \"type\": \"string\","
-        "      \"array\": False,"
+        "      \"space\": \"dhcp6\""
-        "      \"record-types\": \"\","
-        "      \"space\": \"dhcp6\","
-        "      \"encapsulate\": \"\""
         "  } ]"
         "  } ]"
         "}";
         "}";
     ElementPtr json = Element::fromJSON(config);
     ElementPtr json = Element::fromJSON(config);
@@ -2053,10 +2013,7 @@ TEST_F(Dhcp6ParserTest, optionStandardDefOverride) {
         "      \"name\": \"foo\","
         "      \"name\": \"foo\","
         "      \"code\": 3,"
         "      \"code\": 3,"
         "      \"type\": \"string\","
         "      \"type\": \"string\","
-        "      \"array\": False,"
+        "      \"space\": \"dhcp6\""
-        "      \"record-types\": \"\","
-        "      \"space\": \"dhcp6\","
-        "      \"encapsulate\": \"\""
         "  } ]"
         "  } ]"
         "}";
         "}";
     json = Element::fromJSON(config);
     json = Element::fromJSON(config);
@@ -2078,10 +2035,7 @@ TEST_F(Dhcp6ParserTest, optionStandardDefOverride) {
         "      \"name\": \"geolocation\","
         "      \"name\": \"geolocation\","
         "      \"code\": 63,"
         "      \"code\": 63,"
         "      \"type\": \"string\","
         "      \"type\": \"string\","
-        "      \"array\": False,"
+        "      \"space\": \"dhcp6\""
-        "      \"record-types\": \"\","
-        "      \"space\": \"dhcp6\","
-        "      \"encapsulate\": \"\""
         "  } ]"
         "  } ]"
         "}";
         "}";
     json = Element::fromJSON(config);
     json = Element::fromJSON(config);
@@ -2114,17 +2068,12 @@ TEST_F(Dhcp6ParserTest, optionDataDefaults) {
         "\"renew-timer\": 1000,"
         "\"renew-timer\": 1000,"
         "\"option-data\": [ {"
         "\"option-data\": [ {"
         "    \"name\": \"subscriber-id\","
         "    \"name\": \"subscriber-id\","
-        "    \"space\": \"dhcp6\","
-        "    \"code\": 38,"
         "    \"data\": \"ABCDEF0105\","
         "    \"data\": \"ABCDEF0105\","
         "    \"csv-format\": False"
         "    \"csv-format\": False"
         " },"
         " },"
         " {"
         " {"
         "    \"name\": \"preference\","
         "    \"name\": \"preference\","
-        "    \"space\": \"dhcp6\","
+        "    \"data\": \"01\""
-        "    \"code\": 7,"
-        "    \"data\": \"01\","
-        "    \"csv-format\": True"
         " } ],"
         " } ],"
         "\"subnet6\": [ { "
         "\"subnet6\": [ { "
         "    \"pools\": [ { \"pool\": \"2001:db8:1::/80\" } ],"
         "    \"pools\": [ { \"pool\": \"2001:db8:1::/80\" } ],"
@@ -2196,26 +2145,19 @@ TEST_F(Dhcp6ParserTest, optionDataTwoSpaces) {
         "\"renew-timer\": 1000,"
         "\"renew-timer\": 1000,"
         "\"option-data\": [ {"
         "\"option-data\": [ {"
         "    \"name\": \"subscriber-id\","
         "    \"name\": \"subscriber-id\","
-        "    \"space\": \"dhcp6\","
-        "    \"code\": 38,"
         "    \"data\": \"ABCDEF0105\","
         "    \"data\": \"ABCDEF0105\","
         "    \"csv-format\": False"
         "    \"csv-format\": False"
         " },"
         " },"
         " {"
         " {"
         "    \"name\": \"foo\","
         "    \"name\": \"foo\","
         "    \"space\": \"isc\","
         "    \"space\": \"isc\","
-        "    \"code\": 38,"
+        "    \"data\": \"1234\""
-        "    \"data\": \"1234\","
-        "    \"csv-format\": True"
         " } ],"
         " } ],"
         "\"option-def\": [ {"
         "\"option-def\": [ {"
         "    \"name\": \"foo\","
         "    \"name\": \"foo\","
         "    \"code\": 38,"
         "    \"code\": 38,"
         "    \"type\": \"uint32\","
         "    \"type\": \"uint32\","
-        "    \"array\": False,"
+        "    \"space\": \"isc\""
-        "    \"record-types\": \"\","
-        "    \"space\": \"isc\","
-        "    \"encapsulate\": \"\""
         " } ],"
         " } ],"
         "\"subnet6\": [ { "
         "\"subnet6\": [ { "
         "    \"pools\": [ { \"pool\": \"2001:db8:1::/80\" } ],"
         "    \"pools\": [ { \"pool\": \"2001:db8:1::/80\" } ],"
@@ -2278,34 +2220,24 @@ TEST_F(Dhcp6ParserTest, optionDataEncapsulate) {
         "\"option-data\": [ {"
         "\"option-data\": [ {"
         "    \"name\": \"foo\","
         "    \"name\": \"foo\","
         "    \"space\": \"isc\","
         "    \"space\": \"isc\","
-        "    \"code\": 110,"
+        "    \"data\": \"1234\""
-        "    \"data\": \"1234\","
-        "    \"csv-format\": True"
         " },"
         " },"
         " {"
         " {"
         "    \"name\": \"foo2\","
         "    \"name\": \"foo2\","
         "    \"space\": \"isc\","
         "    \"space\": \"isc\","
-        "    \"code\": 111,"
+        "    \"data\": \"192.168.2.1\""
-        "    \"data\": \"192.168.2.1\","
-        "    \"csv-format\": True"
         " } ],"
         " } ],"
         "\"option-def\": [ {"
         "\"option-def\": [ {"
         "    \"name\": \"foo\","
         "    \"name\": \"foo\","
         "    \"code\": 110,"
         "    \"code\": 110,"
         "    \"type\": \"uint32\","
         "    \"type\": \"uint32\","
-        "    \"array\": False,"
+        "    \"space\": \"isc\""
-        "    \"record-types\": \"\","
-        "    \"space\": \"isc\","
-        "    \"encapsulate\": \"\""
         " },"
         " },"
         " {"
         " {"
         "    \"name\": \"foo2\","
         "    \"name\": \"foo2\","
         "    \"code\": 111,"
         "    \"code\": 111,"
         "    \"type\": \"ipv4-address\","
         "    \"type\": \"ipv4-address\","
-        "    \"array\": False,"
+        "    \"space\": \"isc\""
-        "    \"record-types\": \"\","
-        "    \"space\": \"isc\","
-        "    \"encapsulate\": \"\""
         " } ]"
         " } ]"
         "}";
         "}";
 
 
@@ -2330,31 +2262,22 @@ TEST_F(Dhcp6ParserTest, optionDataEncapsulate) {
         "\"renew-timer\": 1000,"
         "\"renew-timer\": 1000,"
         "\"option-data\": [ {"
         "\"option-data\": [ {"
         "    \"name\": \"base-option\","
         "    \"name\": \"base-option\","
-        "    \"space\": \"dhcp6\","
+        "    \"data\": \"11\""
-        "    \"code\": 100,"
-        "    \"data\": \"11\","
-        "    \"csv-format\": True"
         " },"
         " },"
         " {"
         " {"
         "    \"name\": \"foo\","
         "    \"name\": \"foo\","
         "    \"space\": \"isc\","
         "    \"space\": \"isc\","
-        "    \"code\": 110,"
+        "    \"data\": \"1234\""
-        "    \"data\": \"1234\","
-        "    \"csv-format\": True"
         " },"
         " },"
         " {"
         " {"
         "    \"name\": \"foo2\","
         "    \"name\": \"foo2\","
         "    \"space\": \"isc\","
         "    \"space\": \"isc\","
-        "    \"code\": 111,"
+        "    \"data\": \"192.168.2.1\""
-        "    \"data\": \"192.168.2.1\","
-        "    \"csv-format\": True"
         " } ],"
         " } ],"
         "\"option-def\": [ {"
         "\"option-def\": [ {"
         "    \"name\": \"base-option\","
         "    \"name\": \"base-option\","
         "    \"code\": 100,"
         "    \"code\": 100,"
         "    \"type\": \"uint8\","
         "    \"type\": \"uint8\","
-        "    \"array\": False,"
-        "    \"record-types\": \"\","
         "    \"space\": \"dhcp6\","
         "    \"space\": \"dhcp6\","
         "    \"encapsulate\": \"isc\""
         "    \"encapsulate\": \"isc\""
         "},"
         "},"
@@ -2362,19 +2285,13 @@ TEST_F(Dhcp6ParserTest, optionDataEncapsulate) {
         "    \"name\": \"foo\","
         "    \"name\": \"foo\","
         "    \"code\": 110,"
         "    \"code\": 110,"
         "    \"type\": \"uint32\","
         "    \"type\": \"uint32\","
-        "    \"array\": False,"
+        "    \"space\": \"isc\""
-        "    \"record-types\": \"\","
-        "    \"space\": \"isc\","
-        "    \"encapsulate\": \"\""
         " },"
         " },"
         " {"
         " {"
         "    \"name\": \"foo2\","
         "    \"name\": \"foo2\","
         "    \"code\": 111,"
         "    \"code\": 111,"
         "    \"type\": \"ipv4-address\","
         "    \"type\": \"ipv4-address\","
-        "    \"array\": False,"
+        "    \"space\": \"isc\""
-        "    \"record-types\": \"\","
-        "    \"space\": \"isc\","
-        "    \"encapsulate\": \"\""
         " } ],"
         " } ],"
         "\"subnet6\": [ { "
         "\"subnet6\": [ { "
         "    \"pools\": [ { \"pool\": \"2001:db8:1::/80\" } ],"
         "    \"pools\": [ { \"pool\": \"2001:db8:1::/80\" } ],"
@@ -2429,8 +2346,6 @@ TEST_F(Dhcp6ParserTest, optionDataInMultipleSubnets) {
         "    \"subnet\": \"2001:db8:1::/64\", "
         "    \"subnet\": \"2001:db8:1::/64\", "
         "    \"option-data\": [ {"
         "    \"option-data\": [ {"
         "          \"name\": \"subscriber-id\","
         "          \"name\": \"subscriber-id\","
-        "          \"space\": \"dhcp6\","
-        "          \"code\": 38,"
         "          \"data\": \"0102030405060708090A\","
         "          \"data\": \"0102030405060708090A\","
         "          \"csv-format\": False"
         "          \"csv-format\": False"
         "        } ]"
         "        } ]"
@@ -2440,8 +2355,6 @@ TEST_F(Dhcp6ParserTest, optionDataInMultipleSubnets) {
         "    \"subnet\": \"2001:db8:2::/64\", "
         "    \"subnet\": \"2001:db8:2::/64\", "
         "    \"option-data\": [ {"
         "    \"option-data\": [ {"
         "          \"name\": \"user-class\","
         "          \"name\": \"user-class\","
-        "          \"space\": \"dhcp6\","
-        "          \"code\": 15,"
         "          \"data\": \"FFFEFDFCFB\","
         "          \"data\": \"FFFEFDFCFB\","
         "          \"csv-format\": False"
         "          \"csv-format\": False"
         "        } ]"
         "        } ]"
@@ -2804,17 +2717,13 @@ TEST_F(Dhcp6ParserTest, vendorOptionsCsv) {
         "    \"name\": \"foo\","
         "    \"name\": \"foo\","
         "    \"space\": \"vendor-4491\","
         "    \"space\": \"vendor-4491\","
         "    \"code\": 100,"
         "    \"code\": 100,"
-        "    \"data\": \"this is a string vendor-opt\","
+        "    \"data\": \"this is a string vendor-opt\""
-        "    \"csv-format\": True"
         " } ],"
         " } ],"
         "\"option-def\": [ {"
         "\"option-def\": [ {"
         "    \"name\": \"foo\","
         "    \"name\": \"foo\","
         "    \"code\": 100,"
         "    \"code\": 100,"
         "    \"type\": \"string\","
         "    \"type\": \"string\","
-        "    \"array\": False,"
+        "    \"space\": \"vendor-4491\""
-        "    \"record-types\": \"\","
-        "    \"space\": \"vendor-4491\","
-        "    \"encapsulate\": \"\""
         " } ],"
         " } ],"
         "\"subnet6\": [ { "
         "\"subnet6\": [ { "
         "    \"pools\": [ { \"pool\": \"2001:db8:1::/80\" } ],"
         "    \"pools\": [ { \"pool\": \"2001:db8:1::/80\" } ],"
@@ -2869,34 +2778,24 @@ TEST_F(Dhcp6ParserTest, DISABLED_stdOptionDataEncapsulate) {
         "\"option-data\": [ {"
         "\"option-data\": [ {"
         "    \"name\": \"foo\","
         "    \"name\": \"foo\","
         "    \"space\": \"vendor-opts-space\","
         "    \"space\": \"vendor-opts-space\","
-        "    \"code\": 110,"
+        "    \"data\": \"1234\""
-        "    \"data\": \"1234\","
-        "    \"csv-format\": True"
         " },"
         " },"
         " {"
         " {"
         "    \"name\": \"foo2\","
         "    \"name\": \"foo2\","
         "    \"space\": \"vendor-opts-space\","
         "    \"space\": \"vendor-opts-space\","
-        "    \"code\": 111,"
+        "    \"data\": \"192.168.2.1\""
-        "    \"data\": \"192.168.2.1\","
-        "    \"csv-format\": True"
         " } ],"
         " } ],"
         "\"option-def\": [ {"
         "\"option-def\": [ {"
         "    \"name\": \"foo\","
         "    \"name\": \"foo\","
         "    \"code\": 110,"
         "    \"code\": 110,"
         "    \"type\": \"uint32\","
         "    \"type\": \"uint32\","
-        "    \"array\": False,"
+        "    \"space\": \"vendor-opts-space\""
-        "    \"record-types\": \"\","
-        "    \"space\": \"vendor-opts-space\","
-        "    \"encapsulate\": \"\""
         " },"
         " },"
         " {"
         " {"
         "    \"name\": \"foo2\","
         "    \"name\": \"foo2\","
         "    \"code\": 111,"
         "    \"code\": 111,"
         "    \"type\": \"ipv4-address\","
         "    \"type\": \"ipv4-address\","
-        "    \"array\": False,"
+        "    \"space\": \"vendor-opts-space\""
-        "    \"record-types\": \"\","
-        "    \"space\": \"vendor-opts-space\","
-        "    \"encapsulate\": \"\""
         " } ]"
         " } ]"
         "}";
         "}";
 
 
@@ -2923,42 +2822,29 @@ TEST_F(Dhcp6ParserTest, DISABLED_stdOptionDataEncapsulate) {
         "\"renew-timer\": 1000,"
         "\"renew-timer\": 1000,"
         "\"option-data\": [ {"
         "\"option-data\": [ {"
         "    \"name\": \"vendor-opts\","
         "    \"name\": \"vendor-opts\","
-        "    \"space\": \"dhcp6\","
+        "    \"data\": \"1234\""
-        "    \"code\": 17,"
-        "    \"data\": \"1234\","
-        "    \"csv-format\": True"
         " },"
         " },"
         " {"
         " {"
         "    \"name\": \"foo\","
         "    \"name\": \"foo\","
         "    \"space\": \"vendor-opts-space\","
         "    \"space\": \"vendor-opts-space\","
-        "    \"code\": 110,"
+        "    \"data\": \"1234\""
-        "    \"data\": \"1234\","
-        "    \"csv-format\": True"
         " },"
         " },"
         " {"
         " {"
         "    \"name\": \"foo2\","
         "    \"name\": \"foo2\","
         "    \"space\": \"vendor-opts-space\","
         "    \"space\": \"vendor-opts-space\","
-        "    \"code\": 111,"
+        "    \"data\": \"192.168.2.1\""
-        "    \"data\": \"192.168.2.1\","
-        "    \"csv-format\": True"
         " } ],"
         " } ],"
         "\"option-def\": [ {"
         "\"option-def\": [ {"
         "    \"name\": \"foo\","
         "    \"name\": \"foo\","
         "    \"code\": 110,"
         "    \"code\": 110,"
         "    \"type\": \"uint32\","
         "    \"type\": \"uint32\","
-        "    \"array\": False,"
+        "    \"space\": \"vendor-opts-space\""
-        "    \"record-types\": \"\","
-        "    \"space\": \"vendor-opts-space\","
-        "    \"encapsulate\": \"\""
         " },"
         " },"
         " {"
         " {"
         "    \"name\": \"foo2\","
         "    \"name\": \"foo2\","
         "    \"code\": 111,"
         "    \"code\": 111,"
         "    \"type\": \"ipv4-address\","
         "    \"type\": \"ipv4-address\","
-        "    \"array\": False,"
+        "    \"space\": \"vendor-opts-space\""
-        "    \"record-types\": \"\","
-        "    \"space\": \"vendor-opts-space\","
-        "    \"encapsulate\": \"\""
         " } ],"
         " } ],"
         "\"subnet6\": [ { "
         "\"subnet6\": [ { "
         "    \"pools\": [ { \"pool\": \"2001:db8:1::/80\" } ],"
         "    \"pools\": [ { \"pool\": \"2001:db8:1::/80\" } ],"
@@ -3049,34 +2935,24 @@ buildHooksLibrariesConfig(const std::vector<std::string>& libraries) {
         "\"option-data\": [ {"
         "\"option-data\": [ {"
         "    \"name\": \"foo\","
         "    \"name\": \"foo\","
         "    \"space\": \"vendor-opts-space\","
         "    \"space\": \"vendor-opts-space\","
-        "    \"code\": 110,"
+        "    \"data\": \"1234\""
-        "    \"data\": \"1234\","
-        "    \"csv-format\": True"
         " },"
         " },"
         " {"
         " {"
         "    \"name\": \"foo2\","
         "    \"name\": \"foo2\","
         "    \"space\": \"vendor-opts-space\","
         "    \"space\": \"vendor-opts-space\","
-        "    \"code\": 111,"
+        "    \"data\": \"192.168.2.1\""
-        "    \"data\": \"192.168.2.1\","
-        "    \"csv-format\": True"
         " } ],"
         " } ],"
         "\"option-def\": [ {"
         "\"option-def\": [ {"
         "    \"name\": \"foo\","
         "    \"name\": \"foo\","
         "    \"code\": 110,"
         "    \"code\": 110,"
         "    \"type\": \"uint32\","
         "    \"type\": \"uint32\","
-        "    \"array\": False,"
+        "    \"space\": \"vendor-opts-space\""
-        "    \"record-types\": \"\","
-        "    \"space\": \"vendor-opts-space\","
-        "    \"encapsulate\": \"\""
         " },"
         " },"
         " {"
         " {"
         "    \"name\": \"foo2\","
         "    \"name\": \"foo2\","
         "    \"code\": 111,"
         "    \"code\": 111,"
         "    \"type\": \"ipv4-address\","
         "    \"type\": \"ipv4-address\","
-        "    \"array\": False,"
+        "    \"space\": \"vendor-opts-space\""
-        "    \"record-types\": \"\","
-        "    \"space\": \"vendor-opts-space\","
-        "    \"encapsulate\": \"\""
         " } ]"
         " } ]"
         "}");
         "}");
 
 

+ 2 - 2
src/bin/dhcp6/tests/ctrl_dhcp6_srv_unittest.cc

@@ -436,7 +436,7 @@ TEST_F(CtrlChannelDhcpv6SrvTest, controlLeasesReclaim) {
     lease1->cltt_ = time(NULL) - 100;
     lease1->cltt_ = time(NULL) - 100;
 
 
     // Add leases to the database.
     // Add leases to the database.
-    LeaseMgr& lease_mgr = LeaseMgrFactory().instance();
+    LeaseMgr& lease_mgr = LeaseMgrFactory::instance();
     ASSERT_NO_THROW(lease_mgr.addLease(lease0));
     ASSERT_NO_THROW(lease_mgr.addLease(lease0));
     ASSERT_NO_THROW(lease_mgr.addLease(lease1));
     ASSERT_NO_THROW(lease_mgr.addLease(lease1));
 
 
@@ -498,7 +498,7 @@ TEST_F(CtrlChannelDhcpv6SrvTest, controlLeasesReclaimRemove) {
     lease1->cltt_ = time(NULL) - 100;
     lease1->cltt_ = time(NULL) - 100;
 
 
     // Add leases to the database.
     // Add leases to the database.
-    LeaseMgr& lease_mgr = LeaseMgrFactory().instance();
+    LeaseMgr& lease_mgr = LeaseMgrFactory::instance();
     ASSERT_NO_THROW(lease_mgr.addLease(lease0));
     ASSERT_NO_THROW(lease_mgr.addLease(lease0));
     ASSERT_NO_THROW(lease_mgr.addLease(lease1));
     ASSERT_NO_THROW(lease_mgr.addLease(lease1));
 
 

+ 4 - 18
src/bin/dhcp6/tests/dhcp6_srv_unittest.cc

@@ -300,15 +300,10 @@ TEST_F(Dhcpv6SrvTest, advertiseOptions) {
         "    \"interface\": \"eth0\", "
         "    \"interface\": \"eth0\", "
         "    \"option-data\": [ {"
         "    \"option-data\": [ {"
         "          \"name\": \"dns-servers\","
         "          \"name\": \"dns-servers\","
-        "          \"space\": \"dhcp6\","
+        "          \"data\": \"2001:db8:1234:FFFF::1, 2001:db8:1234:FFFF::2\""
-        "          \"code\": 23,"
-        "          \"data\": \"2001:db8:1234:FFFF::1, 2001:db8:1234:FFFF::2\","
-        "          \"csv-format\": True"
         "        },"
         "        },"
         "        {"
         "        {"
         "          \"name\": \"subscriber-id\","
         "          \"name\": \"subscriber-id\","
-        "          \"space\": \"dhcp6\","
-        "          \"code\": 38,"
         "          \"data\": \"1234\","
         "          \"data\": \"1234\","
         "          \"csv-format\": False"
         "          \"csv-format\": False"
         "        } ]"
         "        } ]"
@@ -1615,17 +1610,12 @@ TEST_F(Dhcpv6SrvTest, vendorOptionsORO) {
         "        \"name\": \"config-file\","
         "        \"name\": \"config-file\","
         "        \"code\": 33,"
         "        \"code\": 33,"
         "        \"type\": \"string\","
         "        \"type\": \"string\","
-        "        \"array\": False,"
+        "        \"space\": \"vendor-4491\""
-        "        \"record-types\": \"\","
-        "        \"space\": \"vendor-4491\","
-        "        \"encapsulate\": \"\""
         "     } ],"
         "     } ],"
         "    \"option-data\": [ {"
         "    \"option-data\": [ {"
         "          \"name\": \"config-file\","
         "          \"name\": \"config-file\","
         "          \"space\": \"vendor-4491\","
         "          \"space\": \"vendor-4491\","
-        "          \"code\": 33,"
+        "          \"data\": \"normal_erouter_v6.cm\""
-        "          \"data\": \"normal_erouter_v6.cm\","
-        "          \"csv-format\": True"
         "        }],"
         "        }],"
         "\"subnet6\": [ { "
         "\"subnet6\": [ { "
         "    \"pools\": [ { \"pool\": \"2001:db8:1::/64\" } ],"
         "    \"pools\": [ { \"pool\": \"2001:db8:1::/64\" } ],"
@@ -2305,11 +2295,7 @@ TEST_F(Dhcpv6SrvTest, rsooOverride) {
         "    \"option-def\": [ {"
         "    \"option-def\": [ {"
         "      \"name\": \"foo\","
         "      \"name\": \"foo\","
         "      \"code\": 120,"
         "      \"code\": 120,"
-        "      \"type\": \"binary\","
+        "      \"type\": \"binary\""
-        "      \"array\": False,"
-        "      \"record-types\": \"\","
-        "      \"space\": \"dhcp6\","
-        "      \"encapsulate\": \"\""
         "    } ],"
         "    } ],"
         "    \"option-data\": [ {"
         "    \"option-data\": [ {"
         "      \"code\": 120,"
         "      \"code\": 120,"

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

@@ -321,7 +321,7 @@ TEST_F(JSONFileBackendTest, timers) {
     lease_reclaimed->state_ = Lease6::STATE_EXPIRED_RECLAIMED;
     lease_reclaimed->state_ = Lease6::STATE_EXPIRED_RECLAIMED;
 
 
     // Add leases to the database.
     // Add leases to the database.
-    LeaseMgr& lease_mgr = LeaseMgrFactory().instance();
+    LeaseMgr& lease_mgr = LeaseMgrFactory::instance();
     ASSERT_NO_THROW(lease_mgr.addLease(lease_expired));
     ASSERT_NO_THROW(lease_mgr.addLease(lease_expired));
     ASSERT_NO_THROW(lease_mgr.addLease(lease_reclaimed));
     ASSERT_NO_THROW(lease_mgr.addLease(lease_reclaimed));
 
 

+ 1 - 0
src/lib/dhcp/Makefile.am

@@ -48,6 +48,7 @@ libkea_dhcp___la_SOURCES += protocol_util.cc protocol_util.h
 libkea_dhcp___la_SOURCES += pkt.cc pkt.h
 libkea_dhcp___la_SOURCES += pkt.cc pkt.h
 libkea_dhcp___la_SOURCES += pkt6.cc pkt6.h
 libkea_dhcp___la_SOURCES += pkt6.cc pkt6.h
 libkea_dhcp___la_SOURCES += pkt4.cc pkt4.h
 libkea_dhcp___la_SOURCES += pkt4.cc pkt4.h
+libkea_dhcp___la_SOURCES += pkt4o6.cc pkt4o6.h
 libkea_dhcp___la_SOURCES += pkt_filter.h pkt_filter.cc
 libkea_dhcp___la_SOURCES += pkt_filter.h pkt_filter.cc
 libkea_dhcp___la_SOURCES += pkt_filter6.h pkt_filter6.cc
 libkea_dhcp___la_SOURCES += pkt_filter6.h pkt_filter6.cc
 libkea_dhcp___la_SOURCES += pkt_filter_inet.cc pkt_filter_inet.h
 libkea_dhcp___la_SOURCES += pkt_filter_inet.cc pkt_filter_inet.h

+ 15 - 8
src/lib/dhcp/dhcp6.h

@@ -110,8 +110,8 @@
 //#define D6O_ADDRSEL                             84 /* RFC7078 */
 //#define D6O_ADDRSEL                             84 /* RFC7078 */
 //#define D6O_ADDRSEL_TABLE                       85 /* RFC7078 */
 //#define D6O_ADDRSEL_TABLE                       85 /* RFC7078 */
 //#define D6O_V6_PCP_SERVER                       86 /* RFC7291 */
 //#define D6O_V6_PCP_SERVER                       86 /* RFC7291 */
-//#define D6O_DHCPV4_MSG                          87 /* RFC7341 */
+#define D6O_DHCPV4_MSG                          87 /* RFC7341 */
-//#define D6O_DHCPV4_O_DHCPV6_SERVER              88 /* RFC7341 */
+#define D6O_DHCPV4_O_DHCPV6_SERVER              88 /* RFC7341 */
 //#define D6O_S46_RULE                            89 /* RFC7598 */
 //#define D6O_S46_RULE                            89 /* RFC7598 */
 //#define D6O_S46_BR                              90 /* RFC7598 */
 //#define D6O_S46_BR                              90 /* RFC7598 */
 //#define D6O_S46_DMR                             91 /* RFC7598 */
 //#define D6O_S46_DMR                             91 /* RFC7598 */
@@ -123,10 +123,9 @@
 //#define D6O_4RD                                 97 /* RFC7600 */
 //#define D6O_4RD                                 97 /* RFC7600 */
 //#define D6O_4RD_MAP_RULE                        98 /* RFC7600 */
 //#define D6O_4RD_MAP_RULE                        98 /* RFC7600 */
 //#define D6O_4RD_NON_MAP_RULE                    99 /* RFC7600 */
 //#define D6O_4RD_NON_MAP_RULE                    99 /* RFC7600 */
-/* draft-ietf-dhc-dhcpv6-active-leasequery-04 */
+//#define D6O_LQ_BASE_TIME                       100 /* RFC7653 */
-//#define D6O_LQ_BASE_TIME                       100
+//#define D6O_LQ_START_TIME                      101 /* RFC7653 */
-//#define D6O_LQ_START_TIME                      101
+//#define D6O_LQ_END_TIME                        102 /* RFC7653 */
-//#define D6O_LQ_END_TIME                        102
 /* 103-142 unassigned */
 /* 103-142 unassigned */
 //#define D6O_IPV6_ADDRESS_ANDSF                 143 /* RFC6153 */
 //#define D6O_IPV6_ADDRESS_ANDSF                 143 /* RFC6153 */
 
 
@@ -195,8 +194,8 @@
 //#define DHCPV6_RECONFIGURE_REQUEST 18
 //#define DHCPV6_RECONFIGURE_REQUEST 18
 //#define DHCPV6_RECONFIGURE_REPLY   19
 //#define DHCPV6_RECONFIGURE_REPLY   19
 /* RFC 7341 */
 /* RFC 7341 */
-//#define DHCPV6_DHCPV4_QUERY        20
+#define DHCPV6_DHCPV4_QUERY        20
-//#define DHCPV6_DHCPV4_RESPONSE     21
+#define DHCPV6_DHCPV4_RESPONSE     21
 /* draft-ietf-dhc-dhcpv6-active-leasequery-04 */
 /* draft-ietf-dhc-dhcpv6-active-leasequery-04 */
 //#define DHCPV6_ACTIVELEASEQUERY    22
 //#define DHCPV6_ACTIVELEASEQUERY    22
 //#define DHCPV6_STARTTLS            23
 //#define DHCPV6_STARTTLS            23
@@ -223,6 +222,11 @@ extern const int dhcpv6_type_name_max;
 // Taken from http://www.iana.org/assignments/enterprise-numbers
 // Taken from http://www.iana.org/assignments/enterprise-numbers
 #define ENTERPRISE_ID_ISC 2495
 #define ENTERPRISE_ID_ISC 2495
 
 
+/* DHCPv4-over-DHCPv6 (RFC 7341) inter-process communication. These are option
+   codes for the ISC vendor specific options used in 4o6 */
+#define ISC_V6_4O6_INTERFACE                 60000
+#define ISC_V6_4O6_SRC_ADDRESS               60001
+
 /* Offsets into IA_*'s where Option spaces commence.  */
 /* Offsets into IA_*'s where Option spaces commence.  */
 #define IA_NA_OFFSET 12 /* IAID, T1, T2, all 4 octets each */
 #define IA_NA_OFFSET 12 /* IAID, T1, T2, all 4 octets each */
 #define IA_TA_OFFSET  4 /* IAID only, 4 octets */
 #define IA_TA_OFFSET  4 /* IAID only, 4 octets */
@@ -298,4 +302,7 @@ extern const int dhcpv6_type_name_max;
 #define IRT_DEFAULT     86400
 #define IRT_DEFAULT     86400
 #define IRT_MINIMUM     600
 #define IRT_MINIMUM     600
 
 
+/* DHCPv4-query message flags (see RFC7341) */
+#define DHCPV4_QUERY_FLAGS_UNICAST (1 << 23)
+
 #endif /* DHCP6_H */
 #endif /* DHCP6_H */

+ 11 - 0
src/lib/dhcp/libdhcp++.cc

@@ -110,6 +110,11 @@ LibDHCP::getVendorOption6Defs(const uint32_t vendor_id) {
         initVendorOptsDocsis6();
         initVendorOptsDocsis6();
     }
     }
 
 
+    if (vendor_id == ENTERPRISE_ID_ISC &&
+        vendor6_defs_.find(ENTERPRISE_ID_ISC) == vendor6_defs_.end()) {
+        initVendorOptsIsc6();
+    }
+
     VendorOptionDefContainers::const_iterator def = vendor6_defs_.find(vendor_id);
     VendorOptionDefContainers::const_iterator def = vendor6_defs_.find(vendor_id);
     if (def == vendor6_defs_.end()) {
     if (def == vendor6_defs_.end()) {
         // No such vendor-id space
         // No such vendor-id space
@@ -737,6 +742,12 @@ LibDHCP::initVendorOptsDocsis6() {
     initOptionSpace(vendor6_defs_[VENDOR_ID_CABLE_LABS], DOCSIS3_V6_DEFS, DOCSIS3_V6_DEFS_SIZE);
     initOptionSpace(vendor6_defs_[VENDOR_ID_CABLE_LABS], DOCSIS3_V6_DEFS, DOCSIS3_V6_DEFS_SIZE);
 }
 }
 
 
+void
+LibDHCP::initVendorOptsIsc6() {
+    vendor6_defs_[ENTERPRISE_ID_ISC] = OptionDefContainer();
+    initOptionSpace(vendor6_defs_[ENTERPRISE_ID_ISC], ISC_V6_DEFS, ISC_V6_DEFS_SIZE);
+}
+
 void initOptionSpace(OptionDefContainer& defs,
 void initOptionSpace(OptionDefContainer& defs,
                      const OptionDefParams* params,
                      const OptionDefParams* params,
                      size_t params_size) {
                      size_t params_size) {

+ 3 - 0
src/lib/dhcp/libdhcp++.h

@@ -281,6 +281,9 @@ private:
 
 
     static void initVendorOptsDocsis6();
     static void initVendorOptsDocsis6();
 
 
+    /// Initialize private DHCPv6 option definitions.
+    static void initVendorOptsIsc6();
+
     /// pointers to factories that produce DHCPv6 options
     /// pointers to factories that produce DHCPv6 options
     static FactoryMap v4factories_;
     static FactoryMap v4factories_;
 
 

+ 4 - 4
src/lib/dhcp/pkt.cc

@@ -50,10 +50,10 @@ Pkt::Pkt(const uint8_t* buf, uint32_t len, const isc::asiolink::IOAddress& local
 {
 {
 
 
     if (len != 0) {
     if (len != 0) {
-	if (buf == NULL) {
+        if (buf == NULL) {
-	    isc_throw(InvalidParameter, "data buffer passed to Pkt is NULL");
+            isc_throw(InvalidParameter, "data buffer passed to Pkt is NULL");
-	}
+        }
-	data_.resize(len);
+        data_.resize(len);
         memcpy(&data_[0], buf, len);
         memcpy(&data_[0], buf, len);
     }
     }
 }
 }

+ 8 - 0
src/lib/dhcp/pkt4.h

@@ -375,6 +375,14 @@ public:
     /// (true) or non-relayed (false).
     /// (true) or non-relayed (false).
     bool isRelayed() const;
     bool isRelayed() const;
 
 
+    /// @brief Checks if a DHCPv4 message has beeb transported over DHCPv6
+    ///
+    /// @return Boolean value which indicates whether the message is
+    /// transported over DHCPv6 (true) or native DHCPv4 (false)
+    virtual bool isDhcp4o6() const {
+        return (false);
+    }
+
 private:
 private:
 
 
     /// @brief Generic method that validates and sets HW address.
     /// @brief Generic method that validates and sets HW address.

+ 60 - 0
src/lib/dhcp/pkt4o6.cc

@@ -0,0 +1,60 @@
+// Copyright (C) 2015 Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#include <config.h>
+
+#include <dhcp/dhcp6.h>
+#include <dhcp/option.h>
+#include <dhcp/pkt4o6.h>
+#include <exceptions/exceptions.h>
+#include <util/buffer.h>
+
+using namespace isc::asiolink;
+using namespace isc::dhcp;
+using namespace isc::util;
+using namespace std;
+
+namespace isc {
+namespace dhcp {
+
+Pkt4o6::Pkt4o6(const OptionBuffer& pkt4, const Pkt6Ptr& pkt6)
+    :Pkt4(&pkt4[0], pkt4.size()), pkt6_(pkt6)
+{
+    static_cast<void>(pkt6->delOption(D6O_DHCPV4_MSG));
+    setIface(pkt6->getIface());
+    setIndex(pkt6->getIndex());
+    setRemoteAddr(pkt6->getRemoteAddr());
+}
+
+Pkt4o6::Pkt4o6(const Pkt4Ptr& pkt4, const Pkt6Ptr& pkt6)
+    :Pkt4(*pkt4), pkt6_(pkt6) {
+}
+
+void Pkt4o6::pack() {
+    // Convert wire-format Pkt4 data in the form of OptionBuffer.
+    Pkt4::pack();
+    OutputBuffer& buf = getBuffer();
+    const uint8_t* ptr = static_cast<const uint8_t*>(buf.getData());
+    OptionBuffer msg(ptr, ptr + buf.getLength());
+
+    // Build the DHCPv4 Message option for the DHCPv6 message, and pack the
+    // entire stuff.
+    OptionPtr dhcp4_msg(new Option(Option::V6, D6O_DHCPV4_MSG, msg));
+    pkt6_->addOption(dhcp4_msg);
+    pkt6_->pack();
+}
+
+} // end of namespace isc::dhcp
+
+} // end of namespace isc

+ 83 - 0
src/lib/dhcp/pkt4o6.h

@@ -0,0 +1,83 @@
+// Copyright (C) 2015 Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#ifndef PKT4O6_H
+#define PKT4O6_H
+
+#include <dhcp/pkt4.h>
+#include <dhcp/pkt6.h>
+
+#include <boost/shared_ptr.hpp>
+
+namespace isc {
+
+namespace dhcp {
+
+/// @brief Represents DHCPv4-over-DHCPv6 packet
+///
+/// This class derives from @c Pkt4 in order to be handled by
+/// the DHCPv4 server code. It includes a shared pointer to the
+/// DHCPv6 message too.
+///
+/// This is an implementation of the DHCPv4-query/response DHCPv6 messages
+/// defined in RFC 7341 (http://ietf.org/rfc/rfc7341.txt).
+/// See also http://kea.isc.org/wiki/Dhcp4o6Design for design discussions.
+class Pkt4o6 : public Pkt4 {
+public:
+
+    /// @brief Constructor, used in message reception.
+    ///
+    /// @param pkt4 Content of the DHCPv4-message option
+    /// @param pkt6 encapsulating unpacked DHCPv6 message
+    /// the DHCPv4 message option will be removed
+    Pkt4o6(const OptionBuffer& pkt4, const Pkt6Ptr& pkt6);
+
+    /// @brief Constructor, used in replying to a message
+    ///
+    /// @param pkt4 DHCPv4 message
+    /// @param pkt6 DHCPv6 message
+    Pkt4o6(const Pkt4Ptr& pkt4, const Pkt6Ptr& pkt6);
+
+    /// @brief Returns encapsulating DHCPv6 message
+    Pkt6Ptr getPkt6() const { return (pkt6_); }
+
+    /// @brief Prepares on-wire format of DHCPv4-over-DHCPv6 packet.
+    ///
+    /// Calls pack() on both DHCPv4 and DHCPv6 parts
+    /// Inserts the DHCPv4-message option
+    /// @ref Pkt4::pack and @ref Pkt6::pack
+    virtual void pack();
+
+    /// @brief Checks if a DHCPv4 message has been transported over DHCPv6
+    ///
+    /// @return Boolean value which indicates whether the message is
+    /// transported over DHCPv6 (true) or native DHCPv4 (false)
+    virtual bool isDhcp4o6() const {
+        return (true);
+    }
+
+private:
+    /// Encapsulating DHCPv6 message
+    Pkt6Ptr pkt6_;
+
+}; // Pkt4o6 class
+
+/// @brief A pointer to Pkt4o6 object.
+typedef boost::shared_ptr<Pkt4o6> Pkt4o6Ptr;
+
+} // isc::dhcp namespace
+
+} // isc namespace
+
+#endif

+ 10 - 0
src/lib/dhcp/pkt6.cc

@@ -289,6 +289,8 @@ Pkt6::unpackUDP() {
     case DHCPV6_DECLINE:
     case DHCPV6_DECLINE:
     case DHCPV6_RECONFIGURE:
     case DHCPV6_RECONFIGURE:
     case DHCPV6_INFORMATION_REQUEST:
     case DHCPV6_INFORMATION_REQUEST:
+    case DHCPV6_DHCPV4_QUERY:
+    case DHCPV6_DHCPV4_RESPONSE:
     default: // assume that uknown messages are not using relay format
     default: // assume that uknown messages are not using relay format
         {
         {
             return (unpackMsg(data_.begin(), data_.end()));
             return (unpackMsg(data_.begin(), data_.end()));
@@ -586,6 +588,8 @@ Pkt6::getName(const uint8_t type) {
     static const char* REPLY = "REPLY";
     static const char* REPLY = "REPLY";
     static const char* REQUEST = "REQUEST";
     static const char* REQUEST = "REQUEST";
     static const char* SOLICIT = "SOLICIT";
     static const char* SOLICIT = "SOLICIT";
+    static const char* DHCPV4_QUERY = "DHCPV4_QUERY";
+    static const char* DHCPV4_RESPONSE = "DHCPV4_RESPONSE";
     static const char* UNKNOWN = "UNKNOWN";
     static const char* UNKNOWN = "UNKNOWN";
 
 
     switch (type) {
     switch (type) {
@@ -634,6 +638,12 @@ Pkt6::getName(const uint8_t type) {
     case DHCPV6_SOLICIT:
     case DHCPV6_SOLICIT:
         return (SOLICIT);
         return (SOLICIT);
 
 
+    case DHCPV6_DHCPV4_QUERY:
+        return (DHCPV4_QUERY);
+
+    case DHCPV6_DHCPV4_RESPONSE:
+        return (DHCPV4_RESPONSE);
+
     default:
     default:
         ;
         ;
     }
     }

+ 16 - 0
src/lib/dhcp/std_option_defs.h

@@ -353,6 +353,9 @@ const OptionDefParams OPTION_DEF_PARAMS6[] = {
     { "rsoo", D6O_RSOO, OPT_EMPTY_TYPE, false, NO_RECORD_DEF, "rsoo-opts" },
     { "rsoo", D6O_RSOO, OPT_EMPTY_TYPE, false, NO_RECORD_DEF, "rsoo-opts" },
     { "client-linklayer-addr", D6O_CLIENT_LINKLAYER_ADDR, OPT_BINARY_TYPE, false,
     { "client-linklayer-addr", D6O_CLIENT_LINKLAYER_ADDR, OPT_BINARY_TYPE, false,
       NO_RECORD_DEF, "" },
       NO_RECORD_DEF, "" },
+    { "dhcpv4-message", D6O_DHCPV4_MSG, OPT_BINARY_TYPE, false, NO_RECORD_DEF, "" },
+    { "dhcp4o6-server-addr", D6O_DHCPV4_O_DHCPV6_SERVER, OPT_IPV6_ADDRESS_TYPE, true,
+      NO_RECORD_DEF, "" },
     { "public-key", D6O_PUBLIC_KEY, OPT_BINARY_TYPE, false,
     { "public-key", D6O_PUBLIC_KEY, OPT_BINARY_TYPE, false,
       NO_RECORD_DEF, "" },
       NO_RECORD_DEF, "" },
     { "certificate", D6O_CERTIFICATE, OPT_BINARY_TYPE, false,
     { "certificate", D6O_CERTIFICATE, OPT_BINARY_TYPE, false,
@@ -371,6 +374,19 @@ const OptionDefParams OPTION_DEF_PARAMS6[] = {
 const int OPTION_DEF_PARAMS_SIZE6  =
 const int OPTION_DEF_PARAMS_SIZE6  =
     sizeof(OPTION_DEF_PARAMS6) / sizeof(OPTION_DEF_PARAMS6[0]);
     sizeof(OPTION_DEF_PARAMS6) / sizeof(OPTION_DEF_PARAMS6[0]);
 
 
+/// @brief Definitions of vendor-specific DHCPv6 options, defined by ISC.
+/// 4o6-* options are used for inter-process communication. For details, see
+/// http://kea.isc.org/wiki/Dhcp4o6Design
+///
+/// @todo: As those options are defined by ISC, they do not belong in std_option_defs.h.
+///        We need to move them to a separate file, e.g. isc_option_defs.h
+const OptionDefParams ISC_V6_DEFS[] = {
+    { "4o6-interface", ISC_V6_4O6_INTERFACE, OPT_STRING_TYPE, false, NO_RECORD_DEF, "" },
+    { "4o6-source-address", ISC_V6_4O6_SRC_ADDRESS, OPT_IPV6_ADDRESS_TYPE, false, NO_RECORD_DEF, "" }
+};
+
+const int ISC_V6_DEFS_SIZE = sizeof(ISC_V6_DEFS) / sizeof(OptionDefParams);
+
 } // unnamed namespace
 } // unnamed namespace
 
 
 } // namespace dhcp
 } // namespace dhcp

+ 1 - 0
src/lib/dhcp/tests/Makefile.am

@@ -74,6 +74,7 @@ libdhcp___unittests_SOURCES += option_vendor_class_unittest.cc
 libdhcp___unittests_SOURCES  += pkt_captures4.cc pkt_captures6.cc pkt_captures.h
 libdhcp___unittests_SOURCES  += pkt_captures4.cc pkt_captures6.cc pkt_captures.h
 libdhcp___unittests_SOURCES += pkt4_unittest.cc
 libdhcp___unittests_SOURCES += pkt4_unittest.cc
 libdhcp___unittests_SOURCES += pkt6_unittest.cc
 libdhcp___unittests_SOURCES += pkt6_unittest.cc
+libdhcp___unittests_SOURCES += pkt4o6_unittest.cc
 libdhcp___unittests_SOURCES += pkt_filter_unittest.cc
 libdhcp___unittests_SOURCES += pkt_filter_unittest.cc
 libdhcp___unittests_SOURCES += pkt_filter_inet_unittest.cc
 libdhcp___unittests_SOURCES += pkt_filter_inet_unittest.cc
 libdhcp___unittests_SOURCES += pkt_filter_inet6_unittest.cc
 libdhcp___unittests_SOURCES += pkt_filter_inet6_unittest.cc

+ 6 - 0
src/lib/dhcp/tests/libdhcp++_unittest.cc

@@ -1200,6 +1200,12 @@ TEST_F(LibDhcpTest, stdOptionDefs6) {
                                     fqdn_buf.begin(), fqdn_buf.end(),
                                     fqdn_buf.begin(), fqdn_buf.end(),
                                     typeid(OptionCustom));
                                     typeid(OptionCustom));
 
 
+    LibDhcpTest::testStdOptionDefs6(D6O_DHCPV4_MSG, begin, end,
+                                    typeid(Option));
+
+    LibDhcpTest::testStdOptionDefs6(D6O_DHCPV4_O_DHCPV6_SERVER, begin, end,
+                                    typeid(Option6AddrLst));
+
     LibDhcpTest::testStdOptionDefs6(D6O_PUBLIC_KEY, begin, end,
     LibDhcpTest::testStdOptionDefs6(D6O_PUBLIC_KEY, begin, end,
                                     typeid(Option));
                                     typeid(Option));
 
 

+ 106 - 0
src/lib/dhcp/tests/pkt4o6_unittest.cc

@@ -0,0 +1,106 @@
+// Copyright (C) 2015 Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#include <config.h>
+
+#include <dhcp/dhcp6.h>
+#include <dhcp/option.h>
+#include <dhcp/pkt4.h>
+#include <dhcp/pkt6.h>
+#include <dhcp/pkt4o6.h>
+
+#include <boost/scoped_ptr.hpp>
+
+#include <gtest/gtest.h>
+
+using namespace isc::dhcp;
+
+namespace {
+
+/// @brief A Fixture class dedicated to testing of the Pkt4o6 class that
+///        represents a DHCPv4-over-DHCPv6 packet.
+class Pkt4o6Test : public ::testing::Test {
+protected:
+    Pkt4o6Test() :
+        data6_(6, 0),
+        pkt6_(new Pkt6(&data6_[0], data6_.size())),
+        pkt4_(new Pkt4(DHCPDISCOVER, 0x12345678))
+    {
+        pkt4_->pack();
+        const uint8_t* cp = static_cast<const uint8_t*>(
+            pkt4_->getBuffer().getData());
+        buffer4_.assign(cp, cp + pkt4_->getBuffer().getLength());
+    }
+
+protected:
+    // commonly used test data
+    const std::vector<uint8_t> data6_; // data for Pkt6 (content unimportant)
+    Pkt6Ptr pkt6_;                     // DHCPv6 message for 4o6
+    Pkt4Ptr pkt4_;                     // DHCPv4 message for 4o6
+    OptionBuffer buffer4_;             // wire-format data buffer of pkt4_
+};
+
+// This test verifies that the constructors are working as expected.
+TEST_F(Pkt4o6Test, construct) {
+    // Construct 4o6 packet, unpack the data to examine it
+    boost::scoped_ptr<Pkt4o6> pkt4o6(new Pkt4o6(buffer4_, pkt6_));
+    pkt4o6->unpack();
+    // Inspect its internal to confirm it's built as expected.  We also test
+    // isDhcp4o6() here.
+    EXPECT_TRUE(pkt4o6->isDhcp4o6());
+    EXPECT_EQ(pkt6_, pkt4o6->getPkt6());
+    EXPECT_EQ(DHCPDISCOVER, pkt4o6->getType());
+
+    // Same check for the other constructor.  It relies on the internal
+    // behavior of Pkt4's copy constructor, so we need to first unpack pkt4.
+    pkt4_.reset(new Pkt4(&buffer4_[0], buffer4_.size()));
+    pkt4_->unpack();
+    pkt4o6.reset(new Pkt4o6(pkt4_, pkt6_));
+    EXPECT_TRUE(pkt4o6->isDhcp4o6());
+    EXPECT_EQ(pkt6_, pkt4o6->getPkt6());
+    EXPECT_EQ(DHCPDISCOVER, pkt4o6->getType());
+}
+
+// This test verifies that the pack() method handles the building
+// process correctly.
+TEST_F(Pkt4o6Test, pack) {
+    // prepare unpacked DHCPv4 packet (see the note in constructor test)
+    pkt4_.reset(new Pkt4(&buffer4_[0], buffer4_.size()));
+    pkt4_->unpack();
+
+    // Construct 4o6 packet to be tested and pack the data.
+    Pkt4o6 pkt4o6(pkt4_, pkt6_);
+    pkt4o6.pack();
+
+    // The packed data should be:
+    // 4-byte DHCPv6 message header
+    // 4-byte header part of DHCPv4 message option
+    // Raw DHCPv4 message (data stored in buffer4_)
+    EXPECT_EQ(4 + 4 + buffer4_.size(),
+              pkt4o6.getPkt6()->getBuffer().getLength());
+
+    // Check the DHCPv4 message option content (Pkt4o6 class is not responsible
+    // for making it valid, so we won't examine it)
+    const uint8_t* cp = static_cast<const uint8_t*>(
+        pkt4o6.getPkt6()->getBuffer().getData());
+    EXPECT_EQ(0, cp[4]);
+    EXPECT_EQ(D6O_DHCPV4_MSG, cp[5]);
+    EXPECT_EQ((buffer4_.size() >> 8) & 0xff, cp[6]);
+    EXPECT_EQ(buffer4_.size() & 0xff, cp[7]);
+    EXPECT_EQ(0, memcmp(&cp[8], &buffer4_[0], buffer4_.size()));
+}
+
+/// @todo: Add a test that handles actual DHCP4o6 traffic capture
+///        once we get it. We should add the capture to pkt_captures{4,6}.cc
+}

+ 8 - 0
src/lib/dhcp/tests/pkt6_unittest.cc

@@ -594,6 +594,14 @@ TEST_F(Pkt6Test, getName) {
             EXPECT_STREQ("DECLINE", Pkt6::getName(type));
             EXPECT_STREQ("DECLINE", Pkt6::getName(type));
             break;
             break;
 
 
+        case DHCPV6_DHCPV4_QUERY:
+            EXPECT_STREQ("DHCPV4_QUERY", Pkt6::getName(type));
+            break;
+
+        case DHCPV6_DHCPV4_RESPONSE:
+            EXPECT_STREQ("DHCPV4_RESPONSE", Pkt6::getName(type));
+            break;
+
         case DHCPV6_INFORMATION_REQUEST:
         case DHCPV6_INFORMATION_REQUEST:
             EXPECT_STREQ("INFORMATION_REQUEST",
             EXPECT_STREQ("INFORMATION_REQUEST",
                          Pkt6::getName(type));
                          Pkt6::getName(type));

+ 1 - 1
src/lib/dhcpsrv/parsers/dhcp_parsers.cc

@@ -593,7 +593,7 @@ OptionDataParser::createOption(ConstElementPtr option_data) {
             // The decodeHex function expects that the string contains an
             // The decodeHex function expects that the string contains an
             // even number of digits. If we don't meet this requirement,
             // even number of digits. If we don't meet this requirement,
             // we have to insert a leading 0.
             // we have to insert a leading 0.
-            if (!data_param.empty() && data_param.length() % 2) {
+            if (!data_param.empty() && ((data_param.length() % 2) != 0)) {
                 data_param = data_param.insert(0, "0");
                 data_param = data_param.insert(0, "0");
             }
             }
             util::encode::decodeHex(data_param, binary);
             util::encode::decodeHex(data_param, binary);

+ 2 - 1
src/lib/dhcpsrv/parsers/dhcp_parsers.h

@@ -1,4 +1,4 @@
-// Copyright (C) 2013-2014 Internet Systems Consortium, Inc. ("ISC")
+// Copyright (C) 2013-2015 Internet Systems Consortium, Inc. ("ISC")
 //
 //
 // Permission to use, copy, modify, and/or distribute this software for any
 // Permission to use, copy, modify, and/or distribute this software for any
 // purpose with or without fee is hereby granted, provided that the above
 // purpose with or without fee is hereby granted, provided that the above
@@ -745,6 +745,7 @@ private:
 
 
     /// Instance of option definition being created by this parser.
     /// Instance of option definition being created by this parser.
     OptionDefinitionPtr option_definition_;
     OptionDefinitionPtr option_definition_;
+
     /// Name of the space the option definition belongs to.
     /// Name of the space the option definition belongs to.
     std::string option_space_name_;
     std::string option_space_name_;
 
 

+ 88 - 0
src/lib/dhcpsrv/subnet.h

@@ -507,6 +507,83 @@ private:
 /// @brief A generic pointer to either Subnet4 or Subnet6 object
 /// @brief A generic pointer to either Subnet4 or Subnet6 object
 typedef boost::shared_ptr<Subnet> SubnetPtr;
 typedef boost::shared_ptr<Subnet> SubnetPtr;
 
 
+/// @brief This structure contains information about DHCP4o6 (RFC7341)
+///
+/// DHCP4o6 is completely optional. If it is not enabled, this structure
+/// does not contain any information.
+struct Cfg4o6 {
+
+    /// the default constructor.
+    ///
+    /// Initializes fields to their default value.
+    Cfg4o6()
+    :enabled_(false), subnet4o6_(std::make_pair(asiolink::IOAddress("::"), 128u)) {
+    }
+
+    /// @brief Returns whether the DHCP4o6 is enabled or not.
+    /// @return true if enabled
+    bool enabled() const {
+        return (enabled_);
+    }
+
+    /// @brief Sets the DHCP4o6 enabled status.
+    /// @param enabled specifies if the DHCP4o6 should be enabled or not
+    void enabled(bool enabled) {
+        enabled_ = enabled;
+    }
+
+    /// @brief Returns the DHCP4o6 interface.
+    /// @return value of the 4o6-interface parameter.
+    std::string getIface4o6() const {
+        return (iface4o6_);
+    }
+
+    /// @brief Sets the 4o6-interface.
+    /// @param iface name of the network interface the 4o6 traffic is received on
+    void setIface4o6(const std::string& iface) {
+        iface4o6_ = iface;
+    }
+
+    /// @brief Returns prefix/len for the IPv6 subnet.
+    /// @return prefix/length pair
+    std::pair<asiolink::IOAddress, uint8_t> getSubnet4o6() const {
+        return (subnet4o6_);
+    }
+
+    /// @brief Sets the prefix/length information (content of the 4o6-subnet).
+    /// @param subnet IOAddress that represents a prefix
+    /// @param prefix specifies prefix length
+    void setSubnet4o6(const asiolink::IOAddress& subnet, uint8_t prefix) {
+        subnet4o6_ = std::make_pair(subnet, prefix);
+    }
+
+    /// @brief Returns the interface-id.
+    /// @return the option representing interface-id (or NULL)
+    OptionPtr getInterfaceId() const {
+        return (interface_id_);
+    }
+
+    /// @brief Sets the interface-id
+    /// @param opt option to be used as interface-id match
+    void setInterfaceId(const OptionPtr& opt) {
+        interface_id_ = opt;
+    }
+
+private:
+
+    /// Specifies if 4o6 is enabled on this subnet.
+    bool enabled_;
+
+    /// Specifies the network interface used as v4 subnet selector.
+    std::string iface4o6_;
+
+    /// Specifies the IPv6 subnet used for v4 subnet selection.
+    std::pair<asiolink::IOAddress, uint8_t> subnet4o6_;
+
+    /// Specifies the v6 interface-id used for v4 subnet selection.
+    OptionPtr interface_id_;
+};
+
 /// @brief A configuration holder for IPv4 subnet.
 /// @brief A configuration holder for IPv4 subnet.
 ///
 ///
 /// This class represents an IPv4 subnet.
 /// This class represents an IPv4 subnet.
@@ -559,6 +636,14 @@ public:
         return (match_client_id_);
         return (match_client_id_);
     }
     }
 
 
+    /// @brief Returns DHCP4o6 configuration parameters.
+    ///
+    /// This structure is always available. If the 4o6 is not enabled, its
+    /// enabled_ field will be set to false.
+    Cfg4o6& get4o6() {
+        return (dhcp4o6_);
+    }
+
 private:
 private:
 
 
     /// @brief Returns default address for pool selection
     /// @brief Returns default address for pool selection
@@ -581,6 +666,9 @@ private:
     /// @brief Should server use client identifiers for client lease
     /// @brief Should server use client identifiers for client lease
     /// lookup.
     /// lookup.
     bool match_client_id_;
     bool match_client_id_;
+
+    /// @brief All the information related to DHCP4o6
+    Cfg4o6 dhcp4o6_;
 };
 };
 
 
 /// @brief A pointer to a @c Subnet4 object
 /// @brief A pointer to a @c Subnet4 object

+ 109 - 18
src/lib/dhcpsrv/tests/dhcp_parsers_unittest.cc

@@ -458,10 +458,10 @@ public:
     std::string error_text_;
     std::string error_text_;
 };
 };
 
 
-/// @brief Check Basic parsing of option definitions.
+/// @brief Check basic parsing of option definitions.
 ///
 ///
 /// Note that this tests basic operation of the OptionDefinitionListParser and
 /// Note that this tests basic operation of the OptionDefinitionListParser and
-/// OptionDefinitionParser.  It uses a simple configuration consisting of one
+/// OptionDefinitionParser.  It uses a simple configuration consisting of
 /// one definition and verifies that it is parsed and committed to storage
 /// one definition and verifies that it is parsed and committed to storage
 /// correctly.
 /// correctly.
 TEST_F(ParseConfigTest, basicOptionDefTest) {
 TEST_F(ParseConfigTest, basicOptionDefTest) {
@@ -481,7 +481,7 @@ TEST_F(ParseConfigTest, basicOptionDefTest) {
 
 
     // Verify that the configuration string parses.
     // Verify that the configuration string parses.
     int rcode = parseConfiguration(config);
     int rcode = parseConfiguration(config);
-    ASSERT_TRUE(rcode == 0);
+    ASSERT_EQ(0, rcode);
 
 
 
 
     // Verify that the option definition can be retrieved.
     // Verify that the option definition can be retrieved.
@@ -497,7 +497,73 @@ TEST_F(ParseConfigTest, basicOptionDefTest) {
     EXPECT_TRUE(def->getEncapsulatedSpace().empty());
     EXPECT_TRUE(def->getEncapsulatedSpace().empty());
 }
 }
 
 
-/// @brief Check Basic parsing of options.
+/// @brief Check minimal parsing of option definitions.
+///
+/// Same than basic but without optional parameters set to their default.
+TEST_F(ParseConfigTest, minimalOptionDefTest) {
+
+    // Configuration string.
+    std::string config =
+        "{ \"option-def\": [ {"
+        "      \"name\": \"foo\","
+        "      \"code\": 100,"
+        "      \"type\": \"ipv4-address\","
+        "      \"space\": \"isc\""
+        "  } ]"
+        "}";
+
+    // Verify that the configuration string parses.
+    int rcode = parseConfiguration(config);
+    ASSERT_EQ(0, rcode);
+
+
+    // Verify that the option definition can be retrieved.
+    OptionDefinitionPtr def =
+        CfgMgr::instance().getStagingCfg()->getCfgOptionDef()->get("isc", 100);
+    ASSERT_TRUE(def);
+
+    // Verify that the option definition is correct.
+    EXPECT_EQ("foo", def->getName());
+    EXPECT_EQ(100, def->getCode());
+    EXPECT_FALSE(def->getArrayType());
+    EXPECT_EQ(OPT_IPV4_ADDRESS_TYPE, def->getType());
+    EXPECT_TRUE(def->getEncapsulatedSpace().empty());
+}
+
+/// @brief Check parsing of option definitions using default dhcp6 space.
+///
+/// Same than minimal but using the fact the default universe is V6
+/// so the default space is dhcp6
+TEST_F(ParseConfigTest, defaultSpaceOptionDefTest) {
+
+    // Configuration string.
+    std::string config =
+        "{ \"option-def\": [ {"
+        "      \"name\": \"foo\","
+        "      \"code\": 10000,"
+        "      \"type\": \"ipv6-address\""
+        "  } ]"
+        "}";
+
+    // Verify that the configuration string parses.
+    int rcode = parseConfiguration(config);
+    ASSERT_EQ(0, rcode);
+
+
+    // Verify that the option definition can be retrieved.
+    OptionDefinitionPtr def =
+        CfgMgr::instance().getStagingCfg()->getCfgOptionDef()->get("dhcp6", 10000);
+    ASSERT_TRUE(def);
+
+    // Verify that the option definition is correct.
+    EXPECT_EQ("foo", def->getName());
+    EXPECT_EQ(10000, def->getCode());
+    EXPECT_FALSE(def->getArrayType());
+    EXPECT_EQ(OPT_IPV6_ADDRESS_TYPE, def->getType());
+    EXPECT_TRUE(def->getEncapsulatedSpace().empty());
+}
+
+/// @brief Check basic parsing of options.
 ///
 ///
 /// Note that this tests basic operation of the OptionDataListParser and
 /// Note that this tests basic operation of the OptionDataListParser and
 /// OptionDataParser.  It uses a simple configuration consisting of one
 /// OptionDataParser.  It uses a simple configuration consisting of one
@@ -511,10 +577,7 @@ TEST_F(ParseConfigTest, basicOptionDataTest) {
         "      \"name\": \"foo\","
         "      \"name\": \"foo\","
         "      \"code\": 100,"
         "      \"code\": 100,"
         "      \"type\": \"ipv4-address\","
         "      \"type\": \"ipv4-address\","
-        "      \"array\": False,"
+        "      \"space\": \"isc\""
-        "      \"record-types\": \"\","
-        "      \"space\": \"isc\","
-        "      \"encapsulate\": \"\""
         " } ], "
         " } ], "
         " \"option-data\": [ {"
         " \"option-data\": [ {"
         "    \"name\": \"foo\","
         "    \"name\": \"foo\","
@@ -527,13 +590,47 @@ TEST_F(ParseConfigTest, basicOptionDataTest) {
 
 
     // Verify that the configuration string parses.
     // Verify that the configuration string parses.
     int rcode = parseConfiguration(config);
     int rcode = parseConfiguration(config);
-    ASSERT_TRUE(rcode == 0);
+    ASSERT_EQ(0, rcode);
 
 
     // Verify that the option can be retrieved.
     // Verify that the option can be retrieved.
     OptionPtr opt_ptr = getOptionPtr("isc", 100);
     OptionPtr opt_ptr = getOptionPtr("isc", 100);
     ASSERT_TRUE(opt_ptr);
     ASSERT_TRUE(opt_ptr);
 
 
-    // Verify that the option definition is correct.
+    // Verify that the option data is correct.
+    std::string val = "type=00100, len=00004: 192.0.2.0 (ipv4-address)";
+
+    EXPECT_EQ(val, opt_ptr->toText());
+}
+
+/// @brief Check minimal parsing of options.
+///
+/// Same than basic but without optional parameters set to their default.
+TEST_F(ParseConfigTest, minimalOptionDataTest) {
+
+    // Configuration string.
+    std::string config =
+        "{ \"option-def\": [ {"
+        "      \"name\": \"foo\","
+        "      \"code\": 100,"
+        "      \"type\": \"ipv4-address\","
+        "      \"space\": \"isc\""
+        " } ], "
+        " \"option-data\": [ {"
+        "    \"name\": \"foo\","
+        "    \"space\": \"isc\","
+        "    \"data\": \"192.0.2.0\""
+        " } ]"
+        "}";
+
+    // Verify that the configuration string parses.
+    int rcode = parseConfiguration(config);
+    ASSERT_EQ(0, rcode);
+
+    // Verify that the option can be retrieved.
+    OptionPtr opt_ptr = getOptionPtr("isc", 100);
+    ASSERT_TRUE(opt_ptr);
+
+    // Verify that the option data is correct.
     std::string val = "type=00100, len=00004: 192.0.2.0 (ipv4-address)";
     std::string val = "type=00100, len=00004: 192.0.2.0 (ipv4-address)";
 
 
     EXPECT_EQ(val, opt_ptr->toText());
     EXPECT_EQ(val, opt_ptr->toText());
@@ -690,7 +787,6 @@ TEST_F(ParseConfigTest, optionDataNoName) {
         "{ \"option-data\": [ {"
         "{ \"option-data\": [ {"
         "    \"space\": \"dhcp6\","
         "    \"space\": \"dhcp6\","
         "    \"code\": 23,"
         "    \"code\": 23,"
-        "    \"csv-format\": True,"
         "    \"data\": \"2001:db8:1::1\""
         "    \"data\": \"2001:db8:1::1\""
         " } ]"
         " } ]"
         "}";
         "}";
@@ -711,7 +807,6 @@ TEST_F(ParseConfigTest, optionDataNoCode) {
         "{ \"option-data\": [ {"
         "{ \"option-data\": [ {"
         "    \"space\": \"dhcp6\","
         "    \"space\": \"dhcp6\","
         "    \"name\": \"dns-servers\","
         "    \"name\": \"dns-servers\","
-        "    \"csv-format\": True,"
         "    \"data\": \"2001:db8:1::1\""
         "    \"data\": \"2001:db8:1::1\""
         " } ]"
         " } ]"
         "}";
         "}";
@@ -772,9 +867,7 @@ TEST_F(ParseConfigTest, optionDataMinimalWithOptionDef) {
         "      \"code\": 2345,"
         "      \"code\": 2345,"
         "      \"type\": \"ipv6-address\","
         "      \"type\": \"ipv6-address\","
         "      \"array\": True,"
         "      \"array\": True,"
-        "      \"record-types\": \"\","
+        "      \"space\": \"dhcp6\""
-        "      \"space\": \"dhcp6\","
-        "      \"encapsulate\": \"\""
         "  } ],"
         "  } ],"
         "  \"option-data\": [ {"
         "  \"option-data\": [ {"
         "    \"name\": \"foo-name\","
         "    \"name\": \"foo-name\","
@@ -800,9 +893,7 @@ TEST_F(ParseConfigTest, optionDataMinimalWithOptionDef) {
         "      \"code\": 2345,"
         "      \"code\": 2345,"
         "      \"type\": \"ipv6-address\","
         "      \"type\": \"ipv6-address\","
         "      \"array\": True,"
         "      \"array\": True,"
-        "      \"record-types\": \"\","
+        "      \"space\": \"dhcp6\""
-        "      \"space\": \"dhcp6\","
-        "      \"encapsulate\": \"\""
         "  } ],"
         "  } ],"
         "  \"option-data\": [ {"
         "  \"option-data\": [ {"
         "    \"code\": 2345,"
         "    \"code\": 2345,"

+ 10 - 2
src/lib/eval/Makefile.am

@@ -12,18 +12,25 @@ AM_CXXFLAGS += $(WARNING_NO_MISSING_FIELD_INITIALIZERS_CFLAG)
 
 
 lib_LTLIBRARIES = libkea-eval.la
 lib_LTLIBRARIES = libkea-eval.la
 libkea_eval_la_SOURCES  =
 libkea_eval_la_SOURCES  =
+libkea_eval_la_SOURCES += eval_log.cc eval_log.h
 libkea_eval_la_SOURCES += token.cc token.h
 libkea_eval_la_SOURCES += token.cc token.h
 
 
 libkea_eval_la_SOURCES += parser.cc parser.h
 libkea_eval_la_SOURCES += parser.cc parser.h
 libkea_eval_la_SOURCES += lexer.cc
 libkea_eval_la_SOURCES += lexer.cc
 libkea_eval_la_SOURCES += eval_context.cc
 libkea_eval_la_SOURCES += eval_context.cc
 
 
+nodist_libkea_eval_la_SOURCES = eval_messages.h eval_messages.cc
+
 libkea_eval_la_CXXFLAGS = $(AM_CXXFLAGS)
 libkea_eval_la_CXXFLAGS = $(AM_CXXFLAGS)
 libkea_eval_la_CPPFLAGS = $(AM_CPPFLAGS)
 libkea_eval_la_CPPFLAGS = $(AM_CPPFLAGS)
 libkea_eval_la_LIBADD   = $(top_builddir)/src/lib/exceptions/libkea-exceptions.la
 libkea_eval_la_LIBADD   = $(top_builddir)/src/lib/exceptions/libkea-exceptions.la
 libkea_eval_la_LIBADD  += $(top_builddir)/src/lib/dhcp/libkea-dhcp++.la
 libkea_eval_la_LIBADD  += $(top_builddir)/src/lib/dhcp/libkea-dhcp++.la
+libkea_eval_la_LIBADD  += $(top_builddir)/src/lib/log/libkea-log.la
+libkea_eval_la_LIBADD  += $(top_builddir)/src/lib/util/libkea-util.la
+libkea_eval_la_LIBADD  += $(LOG4CPLUS_LIBS) $(CRYPTO_LIBS)
+
 libkea_eval_la_LDFLAGS  = -no-undefined -version-info 3:0:0
 libkea_eval_la_LDFLAGS  = -no-undefined -version-info 3:0:0
-libkea_eval_la_LDFLAGS += $(LOG4CPLUS_LIBS) $(CRYPTO_LDFLAGS)
+libkea_eval_la_LDFLAGS += $(CRYPTO_LDFLAGS)
 
 
 EXTRA_DIST  = eval.dox
 EXTRA_DIST  = eval.dox
 EXTRA_DIST += eval_messages.mes
 EXTRA_DIST += eval_messages.mes
@@ -33,6 +40,7 @@ eval_messages.h eval_messages.cc: s-messages
 
 
 s-messages: eval_messages.mes
 s-messages: eval_messages.mes
 	$(top_builddir)/src/lib/log/compiler/message $(top_srcdir)/src/lib/eval/eval_messages.mes
 	$(top_builddir)/src/lib/log/compiler/message $(top_srcdir)/src/lib/eval/eval_messages.mes
+	touch $@
 
 
 # Tell Automake that the eval_messages.{cc,h} source files are created in the
 # Tell Automake that the eval_messages.{cc,h} source files are created in the
 # build process, so it must create these before doing anything else. Although
 # build process, so it must create these before doing anything else. Although
@@ -43,7 +51,7 @@ s-messages: eval_messages.mes
 # first.
 # first.
 BUILT_SOURCES = eval_messages.h eval_messages.cc
 BUILT_SOURCES = eval_messages.h eval_messages.cc
 
 
-CLEANFILES = eval_messages.h eval_messages.cc
+CLEANFILES = eval_messages.h eval_messages.cc s-messages
 
 
 # If we want to get rid of all flex/bison generated files, we need to use
 # If we want to get rid of all flex/bison generated files, we need to use
 # make maintainer-clean. The proper way to introduce custom commands for
 # make maintainer-clean. The proper way to introduce custom commands for

+ 26 - 0
src/lib/eval/eval_log.cc

@@ -0,0 +1,26 @@
+// Copyright (C) 2015  Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+/// Defines the logger used by the Eval (classification) code
+
+#include <eval/eval_log.h>
+
+namespace isc {
+namespace dhcp {
+
+isc::log::Logger eval_logger("eval");
+
+} // namespace dhcp
+} // namespace isc
+

+ 49 - 0
src/lib/eval/eval_log.h

@@ -0,0 +1,49 @@
+// Copyright (C) 2015  Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#ifndef EVAL_LOG_H
+#define EVAL_LOG_H
+
+#include <log/macros.h>
+#include <eval/eval_messages.h>
+
+namespace isc {
+namespace dhcp {
+
+/// @brief Eval debug Logging levels
+///
+/// Defines the levels used to output debug messages in the eval (classification) code.
+/// Note that higher numbers equate to more verbose (and detailed) output.
+
+// The first level traces normal operations,
+const int EVAL_DBG_TRACE = DBGLVL_TRACE_BASIC;
+
+// The next level traces each call to hook code.
+const int EVAL_DBG_CALLS = DBGLVL_TRACE_BASIC_DATA;
+
+// Additional information on the calls.  Report each call to a callout (even
+// if there are multiple callouts on a hook) and each status return.
+const int EVAL_DBG_EXTENDED_CALLS = DBGLVL_TRACE_DETAIL_DATA;
+
+/// @brief Eval Logger
+///
+/// Define the logger used to log messages.  We could define it in multiple
+/// modules, but defining in a single module and linking to it saves time and
+/// space.
+extern isc::log::Logger eval_logger;
+
+} // namespace dhcp
+} // namespace isc
+
+#endif // EVAL_LOG_H

+ 5 - 0
src/lib/eval/eval_messages.mes

@@ -18,3 +18,8 @@ $NAMESPACE isc::dhcp
 This debug message indicates that the expression has been evaluated
 This debug message indicates that the expression has been evaluated
 to said value. This message is mostly useful during debugging of the
 to said value. This message is mostly useful during debugging of the
 client classification expressions.
 client classification expressions.
+
+% EVAL_SUBSTRING_BAD_PARAM_CONVERSION starting %1, length %2
+This debug message indicates that the parameter for the starting postion
+or length of the substring couldn't be converted to an integer.  In this
+case the substring routine returns an empty string.

+ 5 - 2
src/lib/eval/tests/Makefile.am

@@ -2,6 +2,8 @@ SUBDIRS = .
 
 
 AM_CPPFLAGS = -I$(top_builddir)/src/lib -I$(top_srcdir)/src/lib
 AM_CPPFLAGS = -I$(top_builddir)/src/lib -I$(top_srcdir)/src/lib
 AM_CPPFLAGS += $(BOOST_INCLUDES)
 AM_CPPFLAGS += $(BOOST_INCLUDES)
+AM_CPPFLAGS += -DLOGGING_SPEC_FILE=\"$(abs_top_srcdir)/src/lib/dhcpsrv/logging.spec\"
+
 AM_CXXFLAGS = $(KEA_CXXFLAGS)
 AM_CXXFLAGS = $(KEA_CXXFLAGS)
 
 
 # Some versions of GCC warn about some versions of Boost regarding
 # Some versions of GCC warn about some versions of Boost regarding
@@ -24,8 +26,9 @@ if HAVE_GTEST
 
 
 TESTS += libeval_unittests
 TESTS += libeval_unittests
 
 
-libeval_unittests_SOURCES  = token_unittest.cc main.cc
+libeval_unittests_SOURCES  = context_unittest.cc
-libeval_unittests_SOURCES += context_unittest.cc
+libeval_unittests_SOURCES += token_unittest.cc
+libeval_unittests_SOURCES += run_unittests.cc
 libeval_unittests_CXXFLAGS = $(AM_CXXFLAGS)
 libeval_unittests_CXXFLAGS = $(AM_CXXFLAGS)
 libeval_unittests_CPPFLAGS = $(AM_CPPFLAGS) $(GTEST_INCLUDES)
 libeval_unittests_CPPFLAGS = $(AM_CPPFLAGS) $(GTEST_INCLUDES)
 libeval_unittests_LDFLAGS  = $(AM_LDFLAGS) $(CRYPTO_LDFLAGS) $(GTEST_LDFLAGS)
 libeval_unittests_LDFLAGS  = $(AM_LDFLAGS) $(CRYPTO_LDFLAGS) $(GTEST_LDFLAGS)

src/lib/eval/tests/main.cc → src/lib/eval/tests/run_unittests.cc


+ 320 - 0
src/lib/eval/tests/token_unittest.cc

@@ -24,6 +24,8 @@
 #include <boost/scoped_ptr.hpp>
 #include <boost/scoped_ptr.hpp>
 #include <gtest/gtest.h>
 #include <gtest/gtest.h>
 
 
+#include <arpa/inet.h>
+
 using namespace std;
 using namespace std;
 using namespace isc::dhcp;
 using namespace isc::dhcp;
 
 
@@ -60,6 +62,40 @@ public:
     OptionPtr option_str4_; ///< A string option for DHCPv4
     OptionPtr option_str4_; ///< A string option for DHCPv4
     OptionPtr option_str6_; ///< A string option for DHCPv6
     OptionPtr option_str6_; ///< A string option for DHCPv6
 
 
+
+    /// @brief Verify that the substring eval works properly
+    ///
+    /// This function takes the parameters and sets up the value
+    /// stack then executes the eval and checks the results.
+    ///
+    /// @param test_string The string to operate on
+    /// @param test_start The postion to start when getting a substring
+    /// @param test_length The length of the substring to get
+    /// @param result_string The expected result of the eval
+    void verifySubstringEval(const std::string& test_string,
+                             const std::string& test_start,
+                             const std::string& test_length,
+                             const std::string& result_string) {
+
+        // create the token
+        ASSERT_NO_THROW(t_.reset(new TokenSubstring()));
+
+        // push values on stack
+        values_.push(test_string);
+        values_.push(test_start);
+        values_.push(test_length);
+
+        // evaluate the token
+        EXPECT_NO_THROW(t_->evaluate(*pkt4_, values_));
+
+        // verify results
+        ASSERT_EQ(1, values_.size());
+        EXPECT_EQ(result_string, values_.top());
+
+        // remove result
+        values_.pop();
+    }
+
     /// @todo: Add more option types here
     /// @todo: Add more option types here
 };
 };
 
 
@@ -93,6 +129,116 @@ TEST_F(TokenTest, string6) {
     EXPECT_EQ("foo", values_.top());
     EXPECT_EQ("foo", values_.top());
 }
 }
 
 
+// This simple test checks that a TokenHexString, representing a constant
+// string coded in hexadecimal, can be used in Pkt4 evaluation.
+// (The actual packet is not used)
+TEST_F(TokenTest, hexstring4) {
+    TokenPtr empty;
+    TokenPtr bad;
+    TokenPtr nodigit;
+    TokenPtr baddigit;
+    TokenPtr bell;
+    TokenPtr foo;
+    TokenPtr cookie;
+
+    // Store constant empty hexstring "" ("") in the TokenHexString object.
+    ASSERT_NO_THROW(empty.reset(new TokenHexString("")));
+    // Store bad encoded hexstring "0abc" ("").
+    ASSERT_NO_THROW(bad.reset(new TokenHexString("0abc")));
+    // Store hexstring with no digits "0x" ("").
+    ASSERT_NO_THROW(nodigit.reset(new TokenHexString("0x")));
+    // Store hexstring with a bad hexdigit "0xxabc" ("").
+    ASSERT_NO_THROW(baddigit.reset(new TokenHexString("0xxabc")));
+    // Store hexstring with an odd number of hexdigits "0x7" ("\a").
+    ASSERT_NO_THROW(bell.reset(new TokenHexString("0x7")));
+    // Store constant hexstring "0x666f6f" ("foo").
+    ASSERT_NO_THROW(foo.reset(new TokenHexString("0x666f6f")));
+    // Store constant hexstring "0x63825363" (DHCP_OPTIONS_COOKIE).
+    ASSERT_NO_THROW(cookie.reset(new TokenHexString("0x63825363")));
+
+    // Make sure that tokens can be evaluated without exceptions.
+    ASSERT_NO_THROW(empty->evaluate(*pkt4_, values_));
+    ASSERT_NO_THROW(bad->evaluate(*pkt4_, values_));
+    ASSERT_NO_THROW(nodigit->evaluate(*pkt4_, values_));
+    ASSERT_NO_THROW(baddigit->evaluate(*pkt4_, values_));
+    ASSERT_NO_THROW(bell->evaluate(*pkt4_, values_));
+    ASSERT_NO_THROW(foo->evaluate(*pkt4_, values_));
+    ASSERT_NO_THROW(cookie->evaluate(*pkt4_, values_));
+
+    // Check that the evaluation put its value on the values stack.
+    ASSERT_EQ(7, values_.size());
+    uint32_t expected = htonl(DHCP_OPTIONS_COOKIE);
+    EXPECT_EQ(4, values_.top().size());
+    EXPECT_EQ(0, memcmp(&expected, &values_.top()[0], 4));
+    values_.pop();
+    EXPECT_EQ("foo", values_.top());
+    values_.pop();
+    EXPECT_EQ("\a", values_.top());
+    values_.pop();
+    EXPECT_EQ("", values_.top());
+    values_.pop();
+    EXPECT_EQ("", values_.top());
+    values_.pop();
+    EXPECT_EQ("", values_.top());
+    values_.pop();
+    EXPECT_EQ("", values_.top());
+}
+
+// This simple test checks that a TokenHexString, representing a constant
+// string coded in hexadecimal, can be used in Pkt6 evaluation.
+// (The actual packet is not used)
+TEST_F(TokenTest, hexstring6) {
+    TokenPtr empty;
+    TokenPtr bad;
+    TokenPtr nodigit;
+    TokenPtr baddigit;
+    TokenPtr bell;
+    TokenPtr foo;
+    TokenPtr cookie;
+
+    // Store constant empty hexstring "" ("") in the TokenHexString object.
+    ASSERT_NO_THROW(empty.reset(new TokenHexString("")));
+    // Store bad encoded hexstring "0abc" ("").
+    ASSERT_NO_THROW(bad.reset(new TokenHexString("0abc")));
+    // Store hexstring with no digits "0x" ("").
+    ASSERT_NO_THROW(nodigit.reset(new TokenHexString("0x")));
+    // Store hexstring with a bad hexdigit "0xxabc" ("").
+    ASSERT_NO_THROW(baddigit.reset(new TokenHexString("0xxabc")));
+    // Store hexstring with an odd number of hexdigits "0x7" ("\a").
+    ASSERT_NO_THROW(bell.reset(new TokenHexString("0x7")));
+    // Store constant hexstring "0x666f6f" ("foo").
+    ASSERT_NO_THROW(foo.reset(new TokenHexString("0x666f6f")));
+    // Store constant hexstring "0x63825363" (DHCP_OPTIONS_COOKIE).
+    ASSERT_NO_THROW(cookie.reset(new TokenHexString("0x63825363")));
+
+    // Make sure that tokens can be evaluated without exceptions.
+    ASSERT_NO_THROW(empty->evaluate(*pkt6_, values_));
+    ASSERT_NO_THROW(bad->evaluate(*pkt6_, values_));
+    ASSERT_NO_THROW(nodigit->evaluate(*pkt6_, values_));
+    ASSERT_NO_THROW(baddigit->evaluate(*pkt6_, values_));
+    ASSERT_NO_THROW(bell->evaluate(*pkt6_, values_));
+    ASSERT_NO_THROW(foo->evaluate(*pkt6_, values_));
+    ASSERT_NO_THROW(cookie->evaluate(*pkt6_, values_));
+
+    // Check that the evaluation put its value on the values stack.
+    ASSERT_EQ(7, values_.size());
+    uint32_t expected = htonl(DHCP_OPTIONS_COOKIE);
+    EXPECT_EQ(4, values_.top().size());
+    EXPECT_EQ(0, memcmp(&expected, &values_.top()[0], 4));
+    values_.pop();
+    EXPECT_EQ("foo", values_.top());
+    values_.pop();
+    EXPECT_EQ("\a", values_.top());
+    values_.pop();
+    EXPECT_EQ("", values_.top());
+    values_.pop();
+    EXPECT_EQ("", values_.top());
+    values_.pop();
+    EXPECT_EQ("", values_.top());
+    values_.pop();
+    EXPECT_EQ("", values_.top());
+}
+
 // This test checks if a token representing an option value is able to extract
 // This test checks if a token representing an option value is able to extract
 // the option from an IPv4 packet and properly store the option's value.
 // the option from an IPv4 packet and properly store the option's value.
 TEST_F(TokenTest, optionString4) {
 TEST_F(TokenTest, optionString4) {
@@ -197,3 +343,177 @@ TEST_F(TokenTest, optionEqualTrue) {
 }
 }
 
 
 };
 };
+
+// This test checks if an a token representing a substring request
+// throws an exception if there aren't enough values on the stack.
+// The stack from the top is: length, start, string.
+// The actual packet is not used.
+TEST_F(TokenTest, substringNotEnoughValues) {
+    ASSERT_NO_THROW(t_.reset(new TokenSubstring()));
+
+    // Subsring requires three values on the stack, try
+    // with 0, 1 and 2 all should thorw an exception
+    EXPECT_THROW(t_->evaluate(*pkt4_, values_), EvalBadStack);
+
+    values_.push("");
+    EXPECT_THROW(t_->evaluate(*pkt4_, values_), EvalBadStack);
+
+    values_.push("0");
+    EXPECT_THROW(t_->evaluate(*pkt4_, values_), EvalBadStack);
+
+    // Three should work
+    values_.push("0");
+    EXPECT_NO_THROW(t_->evaluate(*pkt4_, values_));
+
+    // As we had an empty string to start with we should have an empty
+    // one after the evaluate
+    ASSERT_EQ(1, values_.size());
+    EXPECT_EQ("", values_.top());
+}
+
+// Test getting the whole string in different ways
+TEST_F(TokenTest, substringWholeString) {
+    // Get the whole string
+    verifySubstringEval("foobar", "0", "6", "foobar");
+
+    // Get the whole string with "all"
+    verifySubstringEval("foobar", "0", "all", "foobar");
+
+    // Get the whole string with an extra long number
+    verifySubstringEval("foobar", "0", "123456", "foobar");
+
+    // Get the whole string counting from the back
+    verifySubstringEval("foobar", "-6", "all", "foobar");
+}
+
+// Test getting a suffix, in this case the last 3 characters
+TEST_F(TokenTest, substringTrailer) {
+    verifySubstringEval("foobar", "3", "3", "bar");
+    verifySubstringEval("foobar", "3", "all", "bar");
+    verifySubstringEval("foobar", "-3", "all", "bar");
+    verifySubstringEval("foobar", "-3", "123", "bar");
+}
+
+// Test getting the middle of the string in different ways
+TEST_F(TokenTest, substringMiddle) {
+    verifySubstringEval("foobar", "1", "4", "ooba");
+    verifySubstringEval("foobar", "-5", "4", "ooba");
+    verifySubstringEval("foobar", "-1", "-4", "ooba");
+    verifySubstringEval("foobar", "5", "-4", "ooba");
+}
+
+// Test getting the last letter in different ways
+TEST_F(TokenTest, substringLastLetter) {
+    verifySubstringEval("foobar", "5", "all", "r");
+    verifySubstringEval("foobar", "5", "1", "r");
+    verifySubstringEval("foobar", "5", "5", "r");
+    verifySubstringEval("foobar", "-1", "all", "r");
+    verifySubstringEval("foobar", "-1", "1", "r");
+    verifySubstringEval("foobar", "-1", "5", "r");
+}
+
+// Test we get only what is available if we ask for a longer string
+TEST_F(TokenTest, substringLength) {
+    // Test off the front
+    verifySubstringEval("foobar", "0", "-4", "");
+    verifySubstringEval("foobar", "1", "-4", "f");
+    verifySubstringEval("foobar", "2", "-4", "fo");
+    verifySubstringEval("foobar", "3", "-4", "foo");
+
+    // and the back
+    verifySubstringEval("foobar", "3", "4", "bar");
+    verifySubstringEval("foobar", "4", "4", "ar");
+    verifySubstringEval("foobar", "5", "4", "r");
+    verifySubstringEval("foobar", "6", "4", "");
+}
+
+// Test that we get nothing if the starting postion is out of the string
+TEST_F(TokenTest, substringStartingPosition) {
+    // Off the front
+    verifySubstringEval("foobar", "-7", "1", "");
+    verifySubstringEval("foobar", "-7", "-11", "");
+    verifySubstringEval("foobar", "-7", "all", "");
+
+    // and the back
+    verifySubstringEval("foobar", "6", "1", "");
+    verifySubstringEval("foobar", "6", "-11", "");
+    verifySubstringEval("foobar", "6", "all", "");
+}
+
+// Check what happens if we use strings that aren't numbers for start or length
+// We should return the empty string
+TEST_F(TokenTest, substringBadParams) {
+    verifySubstringEval("foobar", "0ick", "all", "");
+    verifySubstringEval("foobar", "ick0", "all", "");
+    verifySubstringEval("foobar", "ick", "all", "");
+    verifySubstringEval("foobar", "0", "ick", "");
+    verifySubstringEval("foobar", "0", "0ick", "");
+    verifySubstringEval("foobar", "0", "ick0", "");
+    verifySubstringEval("foobar", "0", "allaboard", "");
+}
+
+// lastly check that we don't get anything if the string is empty or
+// we don't ask for any characters from it.
+TEST_F(TokenTest, substringReturnEmpty) {
+    verifySubstringEval("", "0", "all", "");
+    verifySubstringEval("foobar", "0", "0", "");
+}
+
+// Check if we can use the substring and equal tokens together
+// We put the result on the stack first then the substring values
+// then evaluate the substring which should leave the original
+// result on the bottom with the substring result on next.
+// Evaulating the equals should produce true for the first
+// and false for the second.
+// throws an exception if there aren't enough values on the stack.
+// The stack from the top is: length, start, string.
+// The actual packet is not used.
+TEST_F(TokenTest, substringEquals) {
+    TokenPtr tequal;
+
+    ASSERT_NO_THROW(t_.reset(new TokenSubstring()));
+    ASSERT_NO_THROW(tequal.reset(new TokenEqual()));
+
+    // The final expected value
+    values_.push("ooba");
+
+    // The substring values
+    // Subsring requires three values on the stack, try
+    // with 0, 1 and 2 all should thorw an exception
+    values_.push("foobar");
+    values_.push("1");
+    values_.push("4");
+    EXPECT_NO_THROW(t_->evaluate(*pkt4_, values_));
+
+    // we should have two values on the stack
+    ASSERT_EQ(2, values_.size());
+
+    // next the equals eval
+    EXPECT_NO_THROW(tequal->evaluate(*pkt4_, values_));
+    ASSERT_EQ(1, values_.size());
+    EXPECT_EQ("true", values_.top());
+
+    // get rid of the result
+    values_.pop();
+
+    // and try it again but with a bad final value
+    // The final expected value
+    values_.push("foob");
+
+    // The substring values
+    // Subsring requires three values on the stack, try
+    // with 0, 1 and 2 all should thorw an exception
+    values_.push("foobar");
+    values_.push("1");
+    values_.push("4");
+    EXPECT_NO_THROW(t_->evaluate(*pkt4_, values_));
+
+    // we should have two values on the stack
+    ASSERT_EQ(2, values_.size());
+
+    // next the equals eval
+    EXPECT_NO_THROW(tequal->evaluate(*pkt4_, values_));
+    ASSERT_EQ(1, values_.size());
+    EXPECT_EQ("false", values_.top());
+
+}

+ 111 - 0
src/lib/eval/token.cc

@@ -13,6 +13,10 @@
 // PERFORMANCE OF THIS SOFTWARE.
 // PERFORMANCE OF THIS SOFTWARE.
 
 
 #include <eval/token.h>
 #include <eval/token.h>
+#include <eval/eval_log.h>
+#include <util/encode/hex.h>
+#include <boost/lexical_cast.hpp>
+#include <cstring>
 #include <string>
 #include <string>
 
 
 using namespace isc::dhcp;
 using namespace isc::dhcp;
@@ -24,6 +28,40 @@ TokenString::evaluate(const Pkt& /*pkt*/, ValueStack& values) {
     values.push(value_);
     values.push(value_);
 }
 }
 
 
+TokenHexString::TokenHexString(const string& str) : value_("") {
+    // Check string starts "0x" or "0x" and has at least one additional character.
+    if ((str.size() < 3) ||
+        (str[0] != '0') ||
+        ((str[1] != 'x') && (str[1] != 'X'))) {
+        return;
+    }
+    string digits = str.substr(2);
+
+    // Transform string of hexadecimal digits into binary format
+    vector<uint8_t> binary;
+    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 ((digits.length() % 2) != 0) {
+            digits = digits.insert(0, "0");
+        }
+        util::encode::decodeHex(digits, binary);
+    } catch (...) {
+        return;
+    }
+
+    // Convert to a string (note that binary.size() cannot be 0)
+    value_.resize(binary.size());
+    memmove(&value_[0], &binary[0], binary.size());
+}
+
+void
+TokenHexString::evaluate(const Pkt& /*pkt*/, ValueStack& values) {
+    // Literals only push, nothing to pop
+    values.push(value_);
+}
+
 void
 void
 TokenOption::evaluate(const Pkt& pkt, ValueStack& values) {
 TokenOption::evaluate(const Pkt& pkt, ValueStack& values) {
     OptionPtr opt = pkt.getOption(option_code_);
     OptionPtr opt = pkt.getOption(option_code_);
@@ -53,3 +91,76 @@ TokenEqual::evaluate(const Pkt& /*pkt*/, ValueStack& values) {
     else
     else
         values.push("false");
         values.push("false");
 }
 }
+
+void
+TokenSubstring::evaluate(const Pkt& /*pkt*/, ValueStack& values) {
+
+    if (values.size() < 3) {
+        isc_throw(EvalBadStack, "Incorrect stack order. Expected at least "
+                  "3 values for substring operator, got " << values.size());
+    }
+
+    string len_str = values.top();
+    values.pop();
+    string start_str = values.top();
+    values.pop();
+    string string_str = values.top();
+    values.pop();
+
+    // If we have no string to start with we push an empty string and leave
+    if (string_str.empty()) {
+        values.push("");
+        return;
+    }
+
+    // Convert the starting position and length from strings to numbers
+    // the length may also be "all" in which case simply make it the
+    // length of the string.
+    // If we have a problem push an empty string and leave
+    int start_pos;
+    int length;
+    try {
+        start_pos = boost::lexical_cast<int>(start_str);
+        if (len_str == "all") {
+            length = string_str.length();
+        } else {
+            length = boost::lexical_cast<int>(len_str);
+        }
+    } catch (const boost::bad_lexical_cast&) {
+        LOG_DEBUG(eval_logger, EVAL_DBG_TRACE,
+                  EVAL_SUBSTRING_BAD_PARAM_CONVERSION)
+            .arg(start_str)
+            .arg(len_str);
+
+        values.push("");
+        return;
+    }
+
+    const int string_length = string_str.length();
+    // If the starting postion is outside of the string push an
+    // empty string and leave
+    if ((start_pos < -string_length) || (start_pos >= string_length)) {
+        values.push("");
+        return;
+    }
+
+    // Adjust the values to be something for substr.  We first figure out
+    // the starting postion, then update it and the length to get the
+    // characters before or after it depending on the sign of length
+    if (start_pos < 0) {
+        start_pos = string_length + start_pos;
+    }
+
+    if (length < 0) {
+        length = -length;
+        if (length <=  start_pos){
+            start_pos -= length;
+        } else {
+            length = start_pos;
+            start_pos = 0;
+        }
+    }
+
+    // and finally get the substring
+    values.push(string_str.substr(start_pos, length));
+}

+ 84 - 4
src/lib/eval/token.h

@@ -56,7 +56,7 @@ public:
 /// - option[123] (a token that extracts value of option 123)
 /// - option[123] (a token that extracts value of option 123)
 /// - == (an operator that compares two other tokens)
 /// - == (an operator that compares two other tokens)
 /// - substring(a,b,c) (an operator that takes three arguments: a string,
 /// - substring(a,b,c) (an operator that takes three arguments: a string,
-///   first and last character)
+///   first character and length)
 class Token {
 class Token {
 public:
 public:
 
 
@@ -101,6 +101,31 @@ protected:
     std::string value_; ///< Constant value
     std::string value_; ///< Constant value
 };
 };
 
 
+/// @brief Token representing a constant string in hexadecimal format
+///
+/// This token holds value of a constant string giving in an hexadecimal
+/// format, for instance 0x666f6f is "foo"
+class TokenHexString : public Token {
+public:
+    /// Value is set during token construction.
+    ///
+    /// @param str constant string to be represented
+    /// (must be "0x" or "0X" followed by a string of hexadecimal digits
+    /// or decoding will fail)
+    TokenHexString(const std::string& str);
+
+    /// @brief Token evaluation (puts value of the constant string on
+    /// the stack after decoding or an empty string if decoding fails
+    /// (note it should not if the parser is correct)
+    ///
+    /// @param pkt (ignored)
+    /// @param values (represented string will be pushed here)
+    void evaluate(const Pkt& pkt, ValueStack& values);
+
+protected:
+    std::string value_; ///< Constant value
+};
+
 /// @brief Token that represents a value of an option
 /// @brief Token that represents a value of an option
 ///
 ///
 /// This represents a reference to a given option, e.g. in the expression
 /// This represents a reference to a given option, e.g. in the expression
@@ -160,10 +185,65 @@ public:
     /// either "true" or "false". It requires at least two parameters to be
     /// either "true" or "false". It requires at least two parameters to be
     /// present on stack.
     /// present on stack.
     ///
     ///
-    /// @throw EvalBadStack if there's less than 2 values on stack
+    /// @throw EvalBadStack if there are less than 2 values on stack
+    ///
+    /// @param pkt (unused)
+    /// @param values - stack of values (2 arguments will be popped, 1 result
+    ///        will be pushed)
+    void evaluate(const Pkt& pkt, ValueStack& values);
+};
+
+/// @brief Token that represents the substring operator (returns a portion
+/// of the supplied string)
+///
+/// This token represents substring(str, start, len)  An operator that takes three
+/// arguments: a string, the first character and the length.
+class TokenSubstring : public Token {
+public:
+    /// @brief Constructor (does nothing)
+    TokenSubstring() {}
+
+    /// @brief Extract a substring from a string
+    ///
+    /// Evaluation does not use packet information.  It requires at least
+    /// three values to be present on the stack.  It will consume the top
+    /// three values on the stack as parameters and push the resulting substring
+    /// onto the stack.  From the top it expects the values on the stack as:
+    /// -  len
+    /// -  start
+    /// -  str
+    ///
+    /// str is the string to extract a substring from.  If it is empty, an empty
+    /// string is pushed onto the value stack.
+    ///
+    /// start is the postion from which the code starts extracting the substring.
+    /// 0 is the first character and a negative number starts from the end, with
+    /// -1 being the last character.  If the starting point is outside of the
+    /// original string an empty string is pushed onto the value stack.
+    ///
+    /// length is the number of characters from the string to extract.
+    /// "all" means all remaining characters from start to the end of string.
+    /// A negative number means to go from start towards the beginning of
+    /// the string, but doesn't include start.
+    /// If length is longer than the remaining portion of string
+    /// then the entire remaining portion is placed on the value stack.
+    ///
+    /// The following examples all use the base string "foobar", the first number
+    /// is the starting position and the second is the length.  Note that
+    /// a negative length only selects which characters to extract it does not
+    /// indicate an attempt to reverse the string.
+    /// -  0, all => "foobar"
+    /// -  0,  6  => "foobar"
+    /// -  0,  4  => "foob"
+    /// -  2, all => "obar"
+    /// -  2,  6  => "obar"
+    /// - -1, all => "r"
+    /// - -1, -4  => "ooba"
+    ///
+    /// @throw EvalBadStack if there are less than 3 values on stack
     ///
     ///
-    /// @brief pkt (unused)
+    /// @param pkt (unused)
-    /// @brief values - stack of values (2 arguments will be poped, 1 result
+    /// @param values - stack of values (3 arguments will be popped, 1 result
     ///        will be pushed)
     ///        will be pushed)
     void evaluate(const Pkt& pkt, ValueStack& values);
     void evaluate(const Pkt& pkt, ValueStack& values);
 };
 };