]> git.ipfire.org Git - thirdparty/kea.git/commitdiff
[#2536] addressed review comments
authorPiotrek Zadroga <piotrek@isc.org>
Thu, 4 May 2023 21:02:38 +0000 (23:02 +0200)
committerPiotrek Zadroga <piotrek@isc.org>
Thu, 4 May 2023 21:18:04 +0000 (23:18 +0200)
doc/examples/kea4/all-options.json
doc/examples/kea6/all-options.json
doc/sphinx/arm/dhcp4-srv.rst
doc/sphinx/arm/dhcp6-srv.rst
src/lib/dhcp/dhcp4.h
src/lib/dhcp/dhcp6.h
src/lib/dhcp/option4_dnr.cc
src/lib/dhcp/option4_dnr.h
src/lib/dhcp/option6_dnr.h
src/lib/dhcp/std_option_defs.h
src/lib/dhcp/tests/option_definition_unittest.cc

index 49dbbf9fad93137f93b3daa3713b136b30904bf3..e9b4dd578612a746696d5c424e28b99b123a8780 100644 (file)
         "name": "v4-portparams"
       },
 
-      // Option codes 161-209 are unassigned.
+      // Option codes 160-161 are unassigned.
+
+      /*
+       0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5
+      +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+      | OPTION_V4_DNR |     Length    |
+      +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+      ~      DNR Instance Data #1     ~
+      +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+   ---
+      .              ...              .    |
+      +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ optional
+      ~      DNR Instance Data #n     ~    |
+      +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+   ---
+
+      DNR Instance Data Format:
+       0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5
+      +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+      |    DNR Instance Data Length   |
+      +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+      |       Service Priority        |
+      +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+      |   ADN Length  |               |
+      +-+-+-+-+-+-+-+-+               |
+      ~  authentication-domain-name   ~
+      +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+      |  Addr Length  |               |
+      +-+-+-+-+-+-+-+-+               |
+      ~        IPv4 Address(es)       ~
+      |               +-+-+-+-+-+-+-+-+
+      |               |               |
+      +-+-+-+-+-+-+-+-+               |
+      ~Service Parameters (SvcParams) ~
+      |                               |
+      +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+
+      Code:  OPTION_V4_DNR (162).
+
+      Length:  Indicates the length of the enclosed data in octets.
+
+      DNR Instance Data:  Includes the configuration data of an encrypted
+         DNS resolver. When several encrypted DNS resolvers are to be included, the "DNR
+         Instance Data" field is repeated.
+
+      DNR Instance Data Length:  Length of all following data in octets.
+         This field is set to ('ADN Length' + 3) when only an ADN is
+         provided for a DNR instance.
+
+      Service Priority:  The priority of this instance compared to other
+         DNR instances.  This 16-bit unsigned integer is interpreted
+         following the rules specified in Section 2.4.1 of
+         [I-D.ietf-dnsop-svcb-https].
+
+      ADN Length:  Length of the authentication-domain-name in octets.
+
+      authentication-domain-name (variable length):  The authentication
+         domain name of the encrypted DNS resolver.  This field is
+         formatted as specified in Section 10 of [RFC8415].
+
+      Addr Length:  Length of included IPv4 addresses in octets.  When
+         present, it MUST be a multiple of 4.
+
+      IPv4 Address(es) (variable length):  Indicates one or more IPv4
+         addresses to reach the encrypted DNS resolver.  Both private and
+         public IPv4 addresses can be included in this field.
+
+      Service Parameters (SvcParams) (variable length):  Specifies a set of
+         service parameters that are encoded following the rules in
+         Section 2.1 of [I-D.ietf-dnsop-svcb-https].
+         The length of this field is ('DNR Instance Data Length' - 4 - 'ADN
+         Length' - 'Addr Length').
+
+      Note that "Addr Length", "IPv4 Address(es)", and "Service Parameters
+      (SvcParams)" fields are not present if the ADN-only mode is used.
+      */
+      // Type: uint16, uint16, uint8, FQDN, binary
+      {
+        // only one DNR instance ADN only mode - IP address(es) and SvcParams are missing on purpose.
+        "code": 162,
+        "name": "v4-dnr",
+        "data": "26, 1234, 23, example.some.host.org., " // please notice comma and space at the end put on purpose
+                                                         // this means empty last type of the record - binary type
+      },
+      {
+        // 2 DNR instances with IP address(es) and SvcParams included as binary type.
+        "code": 162,
+        "name": "v4-dnr",           // addresses len=8   |  IPv4 1   |  IPv4 2   |             SvcParams "key1=val1 key2=val2"            |Len2 |Prio2|  | ADN with Len=21=15hex myhost1.example.com                    |  |  IPv4 1   |  IPv4 2   |             SvcParams "key3=val3 key4=val4"            |
+        "data": "54, 3234, 23, example.some.host.org., 08 c0 a8 00 01 c0 a8 00 02 6b 65 79 31 3d 76 61 6c 31 20 6b 65 79 32 3d 76 61 6c 32 00 34 10 e1 15 07 6D 79 68 6F 73 74 31 07 65 78 61 6D 70 6C 65 03 63 6F 6D 00 08 c0 a9 00 01 c0 a9 00 02 6b 65 79 33 3d 76 61 6c 33 20 6b 65 79 34 3d 76 61 6c 34"
+      },
+
+      // Option codes 163-209 are unassigned.
 
       /*
       0                   1                   2                   3
index 477800dbb8c0aa81888049370a6395a00d66c652..ba9fea9dc40f83750f1f2a187f532ac7fba8d052 100644 (file)
         "name": "ipv6-address-andsf"
       },
 
-      // Option codes 144-65535 are unassigned.
+      /*
+       0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+      +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+      |       Option-code             |         Option-length         |
+      +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+      |       Service Priority        |         ADN Length            |
+      +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+      ~                   authentication-domain-name                  ~
+      +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+      |         Addr Length           |                               |
+      +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+                               |
+      ~                        ipv6-address(es)                       ~
+      |                               +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+      |                               |                               |
+      +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+                               |
+      ~                 Service Parameters (SvcParams)                ~
+      +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+
+      Option-code:  OPTION_V6_DNR (144)
+
+      Option-length:  Length of the enclosed data in octets.  The option
+         length is ('ADN Length' + 4) when only an ADN is included in the
+         option.
+
+      Service Priority:  The priority of this OPTION_V6_DNR instance
+         compared to other instances.  This 16-bit unsigned integer is
+         interpreted following the rules specified in Section 2.4.1 of
+         [I-D.ietf-dnsop-svcb-https].
+
+      ADN Length:  Length of the authentication-domain-name field in
+         octets.
+
+      authentication-domain-name (variable length):  A fully qualified
+         domain name of the encrypted DNS resolver.  This field is
+         formatted as specified in Section 10 of [RFC8415].
+
+      Addr Length:  Length of enclosed IPv6 addresses in octets.  When
+         present, it MUST be a multiple of 16.
+
+      ipv6-address(es) (variable length):  Indicates one or more IPv6
+         addresses to reach the encrypted DNS resolver.  An address can be
+         link-local, ULA, or GUA.
+
+      Service Parameters (SvcParams) (variable length):  Specifies a set of
+         service parameters that are encoded following the rules in
+         Section 2.1 of [I-D.ietf-dnsop-svcb-https].
+      */
+      // Type: uint16, uint16, FQDN, binary
+      {
+        // DNR ADN only mode - IP address(es) and SvcParams are missing on purpose.
+        "code": 144,
+        "name": "v6-dnr",
+        "data": "1234, 23, example.some.host.org., " // please notice comma and space at the end put on purpose
+                                                     // this means empty last type of the record - binary type
+
+      },
+      {
+        // DNR with IP address(es) and SvcParams included as binary type.
+        "code": 144,
+        "name": "v6-dnr",          // addresses len=32  |                                     1st IPv6  |                                     2nd IPv6  |             SvcParams "key1=val1 key2=val2"            |
+        "data": "3234, 23, example.some.host.org., 00 20 20 01 0d b8 00 01 00 00 00 00 00 00 de ad be ef ff 02 00 00 00 00 00 00 00 00 00 00 fa ce b0 0c 6b 65 79 31 3d 76 61 6c 31 20 6b 65 79 32 3d 76 61 6c 32"
+      },
+
+      // Option codes 145-65535 are unassigned.
 
       /*                         Custom option data                           */
       // See "option-def" below for the definitions.
index b768a424dc75a3a527f3271b3df213c6b334136d..d15e67f12e9b1217fb4bde6f1540179df51a5ff2 100644 (file)
@@ -1986,7 +1986,8 @@ types are given in :ref:`dhcp-types`.
    +----------------------------------------+------+---------------------------+-------------+-------------+
    | v4-portparams                          | 159  | record (uint8, psid)      | false       | false       |
    +----------------------------------------+------+---------------------------+-------------+-------------+
-   | v4-dnr                                 | 162  | binary                    | false       | false       |
+   | v4-dnr                                 | 162  | record (uint16, uint16,   | false       | false       |
+   |                                        |      | uint8, fqdn, binary)      |             |             |
    +----------------------------------------+------+---------------------------+-------------+-------------+
    | option-6rd                             | 212  | record (uint8, uint8,     | true        | false       |
    |                                        |      | ipv6-address,             |             |             |
index c74dd9aaf6bedb1e63f4095fb279986677c9789d..a7f04932ca8594826b48be24e9dd285ec744f3a4 100644 (file)
@@ -1769,7 +1769,8 @@ types are given in :ref:`dhcp-types`.
    | ipv6-address-andsf       | 143             | ipv6-address    | true            |
    +--------------------------+-----------------+-----------------+-----------------+
    | v6-dnr                   | 144             | record (uint16, | false           |
-   |                          |                 | uint16, binary) |                 |
+   |                          |                 | uint16, fqdn,   |                 |
+   |                          |                 | binary)         |                 |
    +--------------------------+-----------------+-----------------+-----------------+
 
 Options marked with (1) have option definitions, but the logic behind
index 1a320667a868b81305d16fbbae88b88475c4f4db..eb4b1d2a089d91db3da2aa18d10b75574e6356ac 100644 (file)
@@ -215,7 +215,7 @@ enum DHCPOptionType {
     // 160 used to be assigned in RFC7710, but was removed in RFC8910
     // The Captive Portal option now uses code 114.
 //  DHO_MUD_URL_V4                   = 161, /* RFC8520 */
-    DHO_V4_DNR                       = 162, /* RFC-ietf-add-dnr-13 */
+    DHO_V4_DNR                       = 162, /* RFC-ietf-add-dnr */
     // 163-209 are removed/unassigned
 //  DHO_PATH_PREFIX                  = 210, /* RFC5071 */
 //  DHO_REBOOT_TIME                  = 211, /* RFC5071 */
index fe22236cf301ebe73466538e631044c862f998ec..bb89c20a87c6fcc27af10e2778d2efaa59934ec9 100644 (file)
@@ -156,7 +156,7 @@ enum DHCPv6OptionType {
    D60_V6_SZTP_REDIRECT                   = 136, /* RFC8572 */
    // Option codes 137-142 are unassigned.
    D6O_IPV6_ADDRESS_ANDSF                 = 143, /* RFC6153 */
-   D6O_V6_DNR                             = 144 /* RFC-ietf-add-dnr-13 */
+   D6O_V6_DNR                             = 144 /* RFC-ietf-add-dnr */
 };
 
 /*
index 4a625e8cc2eba79665b2b5702468fffb6ac3dcf3..2b5a774553b636cdd979dfaf6ba849392fad5f09 100644 (file)
@@ -79,7 +79,7 @@ Option4Dnr::unpack(OptionBufferConstIter begin, OptionBufferConstIter end) {
         dnr_instance.unpackAddresses(begin, dnr_instance_end);
 
         // SvcParams (variable length) field is last.
-        dnr_instance.unpackSvcParams(begin, end);
+        dnr_instance.unpackSvcParams(begin, dnr_instance_end);
 
         addDnrInstance(dnr_instance);
     }
index ae8e43c620c65e9590576b3143ed40039773e183..53b08bbb01661be8fff55b1f41da9720650fbc52 100644 (file)
@@ -78,6 +78,11 @@ public:
     /// @param adn ADN FQDN
     /// @param ip_addresses Container of IP addresses
     /// @param svc_params Service Parameters
+    ///
+    /// @throw InvalidOptionDnrDomainName Thrown in case of any issue with parsing ADN
+    /// @throw InvalidOptionDnrSvcParams Thrown when @c checkSvcParams(from_wire_data) throws
+    /// @throw OutOfRange Thrown in case of no IP addresses found or when IP addresses length
+    /// is too big
     DnrInstance(Option::Universe universe,
                 uint16_t service_priority,
                 const std::string& adn,
@@ -93,6 +98,8 @@ public:
     /// @param universe either V4 or V6 Option universe
     /// @param service_priority Service priority
     /// @param adn ADN FQDN
+    ///
+    /// @throw InvalidOptionDnrDomainName Thrown in case of any issue with parsing ADN
     DnrInstance(Option::Universe universe, uint16_t service_priority, const std::string& adn);
 
     /// @brief Default destructor.
@@ -436,6 +443,11 @@ public:
     /// @param begin Iterator pointing to the beginning of the buffer holding an
     /// option.
     /// @param end Iterator pointing to the end of the buffer holding an option.
+    ///
+    /// @throw OutOfRange Thrown in case of truncated data. May be also thrown when
+    /// @c DnrInstance::unpackDnrInstanceDataLength(begin,end) throws.
+    /// @throw BadValue Thrown when @c DnrInstance::unpackAdn(begin,end) throws.
+    /// @throw InvalidOptionDnrDomainName Thrown when @c DnrInstance::unpackAdn(begin,end) throws.
     Option4Dnr(OptionBufferConstIter begin, OptionBufferConstIter end);
 
     /// @brief Constructor of the empty %Option.
index b157adafcaf618256d9c5533909a1756764f478e..6cf38e579ab1539038505f044e099ef77d7eca6d 100644 (file)
@@ -34,6 +34,10 @@ public:
     /// @param begin Iterator pointing to the beginning of the buffer holding an
     /// option.
     /// @param end Iterator pointing to the end of the buffer holding an option.
+    ///
+    /// @throw OutOfRange Thrown in case of truncated data.
+    /// @throw BadValue Thrown when @c DnrInstance::unpackAdn(begin,end) throws.
+    /// @throw InvalidOptionDnrDomainName Thrown when @c DnrInstance::unpackAdn(begin,end) throws.
     Option6Dnr(OptionBufferConstIter begin, OptionBufferConstIter end);
 
     /// @brief Constructor of the %Option with all fields from params.
@@ -46,6 +50,11 @@ public:
     /// @param adn ADN FQDN
     /// @param ip_addresses Container of IP addresses
     /// @param svc_params Service Parameters
+    ///
+    /// @throw InvalidOptionDnrDomainName Thrown in case of any issue with parsing ADN
+    /// @throw InvalidOptionDnrSvcParams Thrown when @c checkSvcParams(from_wire_data) throws
+    /// @throw OutOfRange Thrown in case of no IP addresses found or when IP addresses length
+    /// is too big
     Option6Dnr(const uint16_t service_priority,
                const std::string& adn,
                const Option6Dnr::AddressContainer& ip_addresses,
@@ -61,6 +70,8 @@ public:
     ///
     /// @param service_priority Service priority
     /// @param adn ADN FQDN
+    ///
+    /// @throw InvalidOptionDnrDomainName Thrown in case of any issue with parsing ADN
     Option6Dnr(const uint16_t service_priority, const std::string& adn)
         : Option(V6, D6O_V6_DNR), DnrInstance(V6, service_priority, adn) {}
 
index 5669ea24dbf77c6b381000221155cca0f2e8c38f..ccd3ae1ccefa0f5c1a732131aad10c4640746cc6 100644 (file)
@@ -107,6 +107,16 @@ RECORD_DECL(V4_PORTPARAMS_RECORDS, OPT_UINT8_TYPE, OPT_PSID_TYPE);
 RECORD_DECL(OPT_6RD_RECORDS, OPT_UINT8_TYPE, OPT_UINT8_TYPE,
             OPT_IPV6_ADDRESS_TYPE, OPT_IPV4_ADDRESS_TYPE);
 
+// RFC-draft-ietf-add-dnr DHCPv4 DNR option.
+//
+// DNR Instance Data Length (2 octets), Service Priority (2 octets),
+// ADN Length (1 octet), ADN FQDN.
+// Opaque data is represented here by the binary data field.
+// It may contain Addr Length (1 octet), IPv4 address(es), SvcParams,
+// and next DNR instances as binary data.
+RECORD_DECL(V4_DNR_RECORDS, OPT_UINT16_TYPE, OPT_UINT16_TYPE, OPT_UINT8_TYPE,
+            OPT_FQDN_TYPE, OPT_BINARY_TYPE);
+
 /// @brief Definitions of standard DHCPv4 options.
 const OptionDefParams STANDARD_V4_OPTION_DEFINITIONS[] = {
     { "subnet-mask", DHO_SUBNET_MASK, DHCP4_OPTION_SPACE,
@@ -366,8 +376,8 @@ const OptionDefParams STANDARD_V4_OPTION_DEFINITIONS[] = {
       OPT_UINT8_TYPE, false, NO_RECORD_DEF, "" },
     { "v4-portparams", DHO_V4_PORTPARAMS, DHCP4_OPTION_SPACE, OPT_RECORD_TYPE,
       false, RECORD_DEF(V4_PORTPARAMS_RECORDS), "" },
-    { "v4-dnr", DHO_V4_DNR, DHCP4_OPTION_SPACE, OPT_BINARY_TYPE,
-     false, NO_RECORD_DEF, "" },
+    { "v4-dnr", DHO_V4_DNR, DHCP4_OPTION_SPACE, OPT_RECORD_TYPE,
+     false, RECORD_DEF(V4_DNR_RECORDS), "" },
     { "option-6rd", DHO_6RD, DHCP4_OPTION_SPACE, OPT_RECORD_TYPE, true,
       RECORD_DEF(OPT_6RD_RECORDS), "" },
     { "v4-access-domain", DHO_V4_ACCESS_DOMAIN, DHCP4_OPTION_SPACE,
@@ -491,11 +501,10 @@ RECORD_DECL(CLIENT_NII_RECORDS, OPT_UINT8_TYPE, OPT_UINT8_TYPE, OPT_UINT8_TYPE);
 
 // RFC-draft-ietf-add-dnr DHCPv6 DNR option.
 //
-// Service Priority (2 octets), ADN Length (2 octets),
+// Service Priority (2 octets), ADN Length (2 octets), ADN FQDN.
 // Opaque data is represented here by the binary data field.
-// It must contain at least authentication-domain-name FQDN(s).
 // It may contain Addr Length (2 octets), IPv6 address(es), SvcParams.
-RECORD_DECL(V6_DNR_RECORDS, OPT_UINT16_TYPE, OPT_UINT16_TYPE, OPT_BINARY_TYPE);
+RECORD_DECL(V6_DNR_RECORDS, OPT_UINT16_TYPE, OPT_UINT16_TYPE, OPT_FQDN_TYPE, OPT_BINARY_TYPE);
 
 /// Standard DHCPv6 option definitions.
 ///
index d05229334a0e0731adc79be35252c96a245cd765..e19523f7b037f7fed902763b8e7e2b4b936be2fe 100644 (file)
@@ -13,6 +13,8 @@
 #include <dhcp/option6_addrlst.h>
 #include <dhcp/option6_ia.h>
 #include <dhcp/option6_iaaddr.h>
+#include <dhcp/option4_dnr.h>
+#include <dhcp/option6_dnr.h>
 #include <dhcp/option_custom.h>
 #include <dhcp/option_definition.h>
 #include <dhcp/option_int.h>
@@ -1840,6 +1842,227 @@ TEST_F(OptionDefinitionTest, tuple4ArrayOption143) {
     EXPECT_EQ("world", tuple2.getText());
 }
 
+// The purpose of this test is to verify that definition can be created
+// for option that comprises record of data. In this particular test
+// the V4-DNR option is used (code 162) in ADN only mode, only one DNR instance.
+// Option's fields are specified as a vector of strings.
+TEST_F(OptionDefinitionTest, recordOption4DnrAdnOnly) {
+    OptionDefinition opt_def("option-dnr", DHO_V4_DNR, DHCP4_OPTION_SPACE, "record", false);
+    opt_def.addRecordField(OPT_UINT16_TYPE);
+    opt_def.addRecordField(OPT_UINT16_TYPE);
+    opt_def.addRecordField(OPT_UINT8_TYPE);
+    opt_def.addRecordField(OPT_FQDN_TYPE);
+    opt_def.addRecordField(OPT_BINARY_TYPE);
+
+    OptionPtr option;
+
+    // Specify option's fields for ADN only mode.
+    std::vector<std::string> values;
+    values.push_back("26");                      // DNR instance data Len
+    values.push_back("1234");                    // service priority
+    values.push_back("23");                      // ADN Len
+    values.push_back("Example.Some.Host.Org.");  // ADN FQDN
+    values.push_back("");                        // leave empty Binary type
+
+    // Create an instance of this option using the definition.
+    ASSERT_NO_THROW(option = opt_def.optionFactory(Option::V4, DHO_V4_DNR, values););
+
+    // Make sure that the returned option class is correct.
+    const Option* optptr = option.get();
+    ASSERT_TRUE(optptr);
+    ASSERT_TRUE(typeid(*optptr) == typeid(Option4Dnr));
+
+    // Validate that option's fields were correctly parsed from strings.
+    Option4DnrPtr option_cast = boost::dynamic_pointer_cast<Option4Dnr>(option);
+
+    auto dnr_instances = option_cast->getDnrInstances();
+
+    // Only one DNR instance is expected.
+    ASSERT_EQ(1, dnr_instances.size());
+
+    DnrInstance& dnr = dnr_instances[0];
+    ASSERT_EQ(26, dnr.getDnrInstanceDataLength());
+    ASSERT_EQ(1234, dnr.getServicePriority());
+    ASSERT_EQ(true, dnr.isAdnOnlyMode());
+    ASSERT_EQ("example.some.host.org.", dnr.getAdnAsText());
+    ASSERT_EQ(23, dnr.getAdnLength());
+    ASSERT_EQ(0, dnr.getAddrLength());
+    ASSERT_EQ(0, dnr.getSvcParamsLength());
+}
+
+// The purpose of this test is to verify that definition can be created
+// for option that comprises record of data. In this particular test
+// the V4-DNR option is used (code 162) with ADN, IP addresses and Service
+// Parameters included. Option's fields are specified as a vector of strings.
+// Multiple DNR instances are configured in this test.
+TEST_F(OptionDefinitionTest, recordOption4Dnr) {
+    OptionDefinition opt_def("option-dnr", DHO_V4_DNR, DHCP4_OPTION_SPACE, "record", false);
+    opt_def.addRecordField(OPT_UINT16_TYPE);
+    opt_def.addRecordField(OPT_UINT16_TYPE);
+    opt_def.addRecordField(OPT_UINT8_TYPE);
+    opt_def.addRecordField(OPT_FQDN_TYPE);
+    opt_def.addRecordField(OPT_BINARY_TYPE);
+
+    OptionPtr option;
+
+    // Specify option's fields - multiple DNR instances.
+    std::vector<std::string> values;
+    values.push_back("54");                            // DNR instance #1 data Len
+    values.push_back("1234");                          // service priority
+    values.push_back("23");                            // ADN Len
+    values.push_back("Example.Some.Host.Org.");        // ADN FQDN
+    values.push_back("08 "                             // Addr Len
+                     "c0 a8 00 01"                     // IP 192.168.0.1
+                     "c0 a8 00 02"                     // IP 192.168.0.2
+                     "6b 65 79 31 3d 76 61 6c 31 20 "  // SvcParams "key1=val1 "
+                     "6b 65 79 32 3d 76 61 6c 32 "     // SvcParams "key2=val2"
+                     "00 34 "                          // DNR instance #2 data Len 52
+                     "10 e1 "                          // service priority 4321
+                     "15 "                             // ADN Len 21
+                     "07 6D 79 68 6F 73 74 31 "        // ADN FQDN myhost1.
+                     "07 65 78 61 6D 70 6C 65 "        // example.
+                     "03 63 6F 6D 00 "                 // com.
+                     "08 "                             // Addr Len 8
+                     "c0 a9 00 01"                     // IP 192.169.0.1
+                     "c0 a9 00 02"                     // IP 192.169.0.2
+                     "6b 65 79 33 3d 76 61 6c 33 20 "  // SvcParams "key3=val3 "
+                     "6b 65 79 34 3d 76 61 6c 34 "     // SvcParams "key4=val4"
+    );
+
+    // Create an instance of this option using the definition.
+    ASSERT_NO_THROW(option = opt_def.optionFactory(Option::V4, DHO_V4_DNR, values););
+
+    // Make sure that the returned option class is correct.
+    const Option* optptr = option.get();
+    ASSERT_TRUE(optptr);
+    ASSERT_TRUE(typeid(*optptr) == typeid(Option4Dnr));
+
+    // Validate that option's fields were correctly parsed from strings.
+    Option4DnrPtr option_cast = boost::dynamic_pointer_cast<Option4Dnr>(option);
+
+    auto dnr_instances = option_cast->getDnrInstances();
+
+    // Two DNR instances are expected.
+    ASSERT_EQ(2, dnr_instances.size());
+
+    // Let's check 1st DNR instance.
+    DnrInstance& dnr_1 = dnr_instances[0];
+    ASSERT_EQ(54, dnr_1.getDnrInstanceDataLength());
+    ASSERT_EQ(1234, dnr_1.getServicePriority());
+    ASSERT_EQ(false, dnr_1.isAdnOnlyMode());
+    ASSERT_EQ(23, dnr_1.getAdnLength());
+    ASSERT_EQ("example.some.host.org.", dnr_1.getAdnAsText());
+    ASSERT_EQ(8, dnr_1.getAddrLength());
+    ASSERT_EQ(19, dnr_1.getSvcParamsLength());
+    auto addresses_1 = dnr_1.getAddresses();
+    ASSERT_EQ(2, addresses_1.size());
+    ASSERT_EQ("192.168.0.1", addresses_1[0].toText());
+    ASSERT_EQ("192.168.0.2", addresses_1[1].toText());
+    ASSERT_EQ("key1=val1 key2=val2", dnr_1.getSvcParams());
+
+    // Let's check 2nd DNR instance.
+    DnrInstance& dnr_2 = dnr_instances[1];
+    ASSERT_EQ(52, dnr_2.getDnrInstanceDataLength());
+    ASSERT_EQ(4321, dnr_2.getServicePriority());
+    ASSERT_EQ(false, dnr_2.isAdnOnlyMode());
+    ASSERT_EQ(21, dnr_2.getAdnLength());
+    ASSERT_EQ("myhost1.example.com.", dnr_2.getAdnAsText());
+    ASSERT_EQ(8, dnr_2.getAddrLength());
+    ASSERT_EQ(19, dnr_2.getSvcParamsLength());
+    auto addresses_2 = dnr_2.getAddresses();
+    ASSERT_EQ(2, addresses_2.size());
+    ASSERT_EQ("192.169.0.1", addresses_2[0].toText());
+    ASSERT_EQ("192.169.0.2", addresses_2[1].toText());
+    ASSERT_EQ("key3=val3 key4=val4", dnr_2.getSvcParams());
+}
+
+// The purpose of this test is to verify that definition can be created
+// for option that comprises record of data. In this particular test
+// the V6-DNR option is used (code 144) in ADN only mode.
+// Option's fields are specified as a vector of strings.
+TEST_F(OptionDefinitionTest, recordOption6DnrAdnOnly) {
+    OptionDefinition opt_def("option-dnr", D6O_V6_DNR, DHCP6_OPTION_SPACE, "record", false);
+    opt_def.addRecordField(OPT_UINT16_TYPE);
+    opt_def.addRecordField(OPT_UINT16_TYPE);
+    opt_def.addRecordField(OPT_FQDN_TYPE);
+    opt_def.addRecordField(OPT_BINARY_TYPE);
+
+    OptionPtr option;
+
+    // Specify option's fields for ADN only mode.
+    std::vector<std::string> values;
+    values.push_back("1234");                    // service priority
+    values.push_back("23");                      // ADN Len
+    values.push_back("Example.Some.Host.Org.");  // ADN FQDN
+    values.push_back("");                        // leave empty Binary type
+
+    // Create an instance of this option using the definition.
+    ASSERT_NO_THROW(option = opt_def.optionFactory(Option::V6, D6O_V6_DNR, values););
+
+    // Make sure that the returned option class is correct.
+    const Option* optptr = option.get();
+    ASSERT_TRUE(optptr);
+    ASSERT_TRUE(typeid(*optptr) == typeid(Option6Dnr));
+
+    // Validate that option's fields were correctly parsed from strings.
+    Option6DnrPtr option_cast = boost::dynamic_pointer_cast<Option6Dnr>(option);
+
+    ASSERT_EQ(1234, option_cast->getServicePriority());
+    ASSERT_EQ(true, option_cast->isAdnOnlyMode());
+    ASSERT_EQ("example.some.host.org.", option_cast->getAdnAsText());
+    ASSERT_EQ(23, option_cast->getAdnLength());
+    ASSERT_EQ(0, option_cast->getAddrLength());
+    ASSERT_EQ(0, option_cast->getSvcParamsLength());
+}
+
+// The purpose of this test is to verify that definition can be created
+// for option that comprises record of data. In this particular test
+// the V6-DNR option is used (code 144) with ADN, IP addresses and Service
+// Parameters included. Option's fields are specified as a vector of strings.
+TEST_F(OptionDefinitionTest, recordOption6Dnr) {
+    OptionDefinition opt_def("option-dnr", D6O_V6_DNR, DHCP6_OPTION_SPACE, "record", false);
+    opt_def.addRecordField(OPT_UINT16_TYPE);
+    opt_def.addRecordField(OPT_UINT16_TYPE);
+    opt_def.addRecordField(OPT_FQDN_TYPE);
+    opt_def.addRecordField(OPT_BINARY_TYPE);
+
+    OptionPtr option;
+
+    // Specify option's fields: service priority, ADN, IP addresses and SvcParams.
+    std::vector<std::string> values;
+    values.push_back("1234");                                            // service priority
+    values.push_back("23");                                              // ADN Len
+    values.push_back("Example.Some.Host.Org.");                          // ADN FQDN
+    values.push_back("00 20 "                                            // Addr Len
+                     "20 01 0d b8 00 01 00 00 00 00 00 00 de ad be ef "  // IP 2001:db8:1::dead:beef
+                     "ff 02 00 00 00 00 00 00 00 00 00 00 fa ce b0 0c "  // IP ff02::face:b00c
+                     "6b 65 79 31 3d 76 61 6c 31 20 "                    // SvcParams "key1=val1 "
+                     "6b 65 79 32 3d 76 61 6c 32"                        // SvcParams "key2=val2"
+    );
+
+    // Create an instance of this option using the definition.
+    ASSERT_NO_THROW(option = opt_def.optionFactory(Option::V6, D6O_V6_DNR, values););
+
+    // Make sure that the returned option class is correct.
+    const Option* optptr = option.get();
+    ASSERT_TRUE(optptr);
+    ASSERT_TRUE(typeid(*optptr) == typeid(Option6Dnr));
+
+    // Validate that option's fields were correctly parsed from strings.
+    Option6DnrPtr option_cast = boost::dynamic_pointer_cast<Option6Dnr>(option);
+
+    ASSERT_EQ(1234, option_cast->getServicePriority());
+    ASSERT_EQ(false, option_cast->isAdnOnlyMode());
+    ASSERT_EQ("example.some.host.org.", option_cast->getAdnAsText());
+    ASSERT_EQ(23, option_cast->getAdnLength());
+    ASSERT_EQ(32, option_cast->getAddrLength());
+    auto addresses = option_cast->getAddresses();
+    ASSERT_EQ(2, addresses.size());
+    ASSERT_EQ("2001:db8:1::dead:beef", addresses[0].toText());
+    ASSERT_EQ("ff02::face:b00c", addresses[1].toText());
+    ASSERT_EQ("key1=val1 key2=val2", option_cast->getSvcParams());
+}
+
 // This test verifies that a definition of an option with an array
 // of DHCPv6 tuples can be created and that the instance of this option
 // can be created by specifying multiple DHCPv6 tuples in the textual format.