From: Piotrek Zadroga Date: Thu, 4 May 2023 21:02:38 +0000 (+0200) Subject: [#2536] addressed review comments X-Git-Tag: Kea-2.3.8~167 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=db5ee838cc695fee8d6734ad86cb3603627ff4d3;p=thirdparty%2Fkea.git [#2536] addressed review comments --- diff --git a/doc/examples/kea4/all-options.json b/doc/examples/kea4/all-options.json index 49dbbf9fad..e9b4dd5786 100644 --- a/doc/examples/kea4/all-options.json +++ b/doc/examples/kea4/all-options.json @@ -1491,7 +1491,96 @@ "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 diff --git a/doc/examples/kea6/all-options.json b/doc/examples/kea6/all-options.json index 477800dbb8..ba9fea9dc4 100644 --- a/doc/examples/kea6/all-options.json +++ b/doc/examples/kea6/all-options.json @@ -1734,7 +1734,70 @@ "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. diff --git a/doc/sphinx/arm/dhcp4-srv.rst b/doc/sphinx/arm/dhcp4-srv.rst index b768a424dc..d15e67f12e 100644 --- a/doc/sphinx/arm/dhcp4-srv.rst +++ b/doc/sphinx/arm/dhcp4-srv.rst @@ -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, | | | diff --git a/doc/sphinx/arm/dhcp6-srv.rst b/doc/sphinx/arm/dhcp6-srv.rst index c74dd9aaf6..a7f04932ca 100644 --- a/doc/sphinx/arm/dhcp6-srv.rst +++ b/doc/sphinx/arm/dhcp6-srv.rst @@ -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 diff --git a/src/lib/dhcp/dhcp4.h b/src/lib/dhcp/dhcp4.h index 1a320667a8..eb4b1d2a08 100644 --- a/src/lib/dhcp/dhcp4.h +++ b/src/lib/dhcp/dhcp4.h @@ -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 */ diff --git a/src/lib/dhcp/dhcp6.h b/src/lib/dhcp/dhcp6.h index fe22236cf3..bb89c20a87 100644 --- a/src/lib/dhcp/dhcp6.h +++ b/src/lib/dhcp/dhcp6.h @@ -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 */ }; /* diff --git a/src/lib/dhcp/option4_dnr.cc b/src/lib/dhcp/option4_dnr.cc index 4a625e8cc2..2b5a774553 100644 --- a/src/lib/dhcp/option4_dnr.cc +++ b/src/lib/dhcp/option4_dnr.cc @@ -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); } diff --git a/src/lib/dhcp/option4_dnr.h b/src/lib/dhcp/option4_dnr.h index ae8e43c620..53b08bbb01 100644 --- a/src/lib/dhcp/option4_dnr.h +++ b/src/lib/dhcp/option4_dnr.h @@ -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. diff --git a/src/lib/dhcp/option6_dnr.h b/src/lib/dhcp/option6_dnr.h index b157adafca..6cf38e579a 100644 --- a/src/lib/dhcp/option6_dnr.h +++ b/src/lib/dhcp/option6_dnr.h @@ -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) {} diff --git a/src/lib/dhcp/std_option_defs.h b/src/lib/dhcp/std_option_defs.h index 5669ea24db..ccd3ae1cce 100644 --- a/src/lib/dhcp/std_option_defs.h +++ b/src/lib/dhcp/std_option_defs.h @@ -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. /// diff --git a/src/lib/dhcp/tests/option_definition_unittest.cc b/src/lib/dhcp/tests/option_definition_unittest.cc index d05229334a..e19523f7b0 100644 --- a/src/lib/dhcp/tests/option_definition_unittest.cc +++ b/src/lib/dhcp/tests/option_definition_unittest.cc @@ -13,6 +13,8 @@ #include #include #include +#include +#include #include #include #include @@ -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 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(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 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(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 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(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 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(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.