From: Thomas Markwalder Date: Fri, 12 Jun 2020 15:42:00 +0000 (-0400) Subject: [#1235] Updated v6 option defintions, made current server_id accessible X-Git-Tag: Kea-1.7.9~48 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=b567101b8087b405ff27d6d559331fe02a70f965;p=thirdparty%2Fkea.git [#1235] Updated v6 option defintions, made current server_id accessible src/lib/dhcp/libdhcp++.cc Added LQ_QUERY_OPTION_SPACE to encapuslate options conveyed with D6O_LQ_QUERY options src/lib/dhcp/std_option_defs.h Revamped defintions D6O_LQ_QUERY adn D6O_CLIENT_DATA src/lib/dhcp/tests/libdhcp++_unittest.cc Updated unit tests src/lib/dhcp/tests/pkt6_unittest.cc TEST_F(Pkt6Test, lqQueryOption) TEST_F(Pkt6Test, clientDataOption) TEST_F(Pkt6Test, relayDataOption) - new tests src/lib/dhcpsrv/cfg_duid.* CfgDUID::create() - modified to retain the created DUID so it can be retrieved in places other than than Dhcp6Srv instances, such as hook callouts. src/lib/dhcpsrv/tests/cfg_duid_unittest.cc Updated unit tests. --- diff --git a/src/lib/dhcp/libdhcp++.cc b/src/lib/dhcp/libdhcp++.cc index 6a6ba1344b..994e7a638a 100644 --- a/src/lib/dhcp/libdhcp++.cc +++ b/src/lib/dhcp/libdhcp++.cc @@ -50,6 +50,7 @@ const OptionDefParamsEncapsulation OPTION_DEF_PARAMS[] = { { LW_V6_OPTION_DEFINITIONS, LW_V6_OPTION_DEFINITIONS_SIZE, LW_V6_OPTION_SPACE }, { V4V6_RULE_OPTION_DEFINITIONS, V4V6_RULE_OPTION_DEFINITIONS_SIZE, V4V6_RULE_OPTION_SPACE }, { V4V6_BIND_OPTION_DEFINITIONS, V4V6_BIND_OPTION_DEFINITIONS_SIZE, V4V6_BIND_OPTION_SPACE }, + { LQ_QUERY_OPTION_DEFINITIONS, LQ_QUERY_OPTION_DEFINITIONS_SIZE, LQ_QUERY_OPTION_SPACE }, { LAST_RESORT_V4_OPTION_DEFINITIONS, LAST_RESORT_V4_OPTION_DEFINITIONS_SIZE, LAST_RESORT_V4_OPTION_SPACE }, { NULL, 0, "" } }; diff --git a/src/lib/dhcp/std_option_defs.h b/src/lib/dhcp/std_option_defs.h index eb25fb9826..a08434a68f 100644 --- a/src/lib/dhcp/std_option_defs.h +++ b/src/lib/dhcp/std_option_defs.h @@ -27,6 +27,7 @@ #define DHCP_AGENT_OPTION_SPACE "dhcp-agent-options-space" #define VENDOR_OPTION_SPACE "vendor-opts-space" #define VENDOR_ENCAPSULATED_OPTION_SPACE "vendor-encapsulated-options-space" +#define LQ_QUERY_OPTION_SPACE "lq-query-option-space" // NOTE: // When adding a new space, make sure you also update @@ -430,8 +431,9 @@ const OptionDefParams STANDARD_V6_OPTION_DEFINITIONS[] = { NO_RECORD_DEF, "" }, { "ero", D6O_ERO, OPT_UINT16_TYPE, true, NO_RECORD_DEF, "" }, { "lq-query", D6O_LQ_QUERY, OPT_RECORD_TYPE, false, - RECORD_DEF(LQ_QUERY_RECORDS), "" }, - { "client-data", D6O_CLIENT_DATA, OPT_EMPTY_TYPE, false, NO_RECORD_DEF, "" }, + RECORD_DEF(LQ_QUERY_RECORDS), LQ_QUERY_OPTION_SPACE }, + { "client-data", D6O_CLIENT_DATA, OPT_EMPTY_TYPE, false, NO_RECORD_DEF, + DHCP6_OPTION_SPACE }, { "clt-time", D6O_CLT_TIME, OPT_UINT32_TYPE, false, NO_RECORD_DEF, "" }, { "lq-relay-data", D6O_LQ_RELAY_DATA, OPT_RECORD_TYPE, false, RECORD_DEF(LQ_RELAY_DATA_RECORDS), "" }, @@ -563,6 +565,18 @@ const int V4V6_BIND_OPTION_DEFINITIONS_SIZE = sizeof(V4V6_BIND_OPTION_DEFINITIONS) / sizeof(V4V6_BIND_OPTION_DEFINITIONS[0]); +/// @brief LQ_QUERY suboption definitions (v6) +const OptionDefParams LQ_QUERY_OPTION_DEFINITIONS[] = { + { "clientid", D6O_CLIENTID, OPT_BINARY_TYPE, false, NO_RECORD_DEF, ""}, + { "iaaddr", D6O_IAADDR, OPT_RECORD_TYPE, false, RECORD_DEF(IAADDR_RECORDS), ""}, + { "oro", D6O_ORO, OPT_UINT16_TYPE, true, NO_RECORD_DEF, "" } +}; + +const int LQ_QUERY_OPTION_DEFINITIONS_SIZE = + sizeof(LQ_QUERY_OPTION_DEFINITIONS) / + sizeof(LQ_QUERY_OPTION_DEFINITIONS[0]); + + } // namespace } // namespace dhcp diff --git a/src/lib/dhcp/tests/libdhcp++_unittest.cc b/src/lib/dhcp/tests/libdhcp++_unittest.cc index 95f0540de0..a803536d8c 100644 --- a/src/lib/dhcp/tests/libdhcp++_unittest.cc +++ b/src/lib/dhcp/tests/libdhcp++_unittest.cc @@ -274,7 +274,8 @@ private: << " is invalid"; // Check that the valid encapsulated option space name // has been specified. - EXPECT_EQ(encapsulates, def->getEncapsulatedSpace()); + EXPECT_EQ(encapsulates, def->getEncapsulatedSpace()) << + "opt name: " << def->getName(); OptionPtr option; // Create the option. ASSERT_NO_THROW(option = def->optionFactory(u, code, begin, end)) @@ -1924,10 +1925,10 @@ TEST_F(LibDhcpTest, stdOptionDefs6) { typeid(OptionIntArray)); LibDhcpTest::testStdOptionDefs6(D6O_LQ_QUERY, begin, end, - typeid(OptionCustom)); + typeid(OptionCustom), LQ_QUERY_OPTION_SPACE); LibDhcpTest::testStdOptionDefs6(D6O_CLIENT_DATA, begin, end, - typeid(Option)); + typeid(OptionCustom), DHCP6_OPTION_SPACE); LibDhcpTest::testStdOptionDefs6(D6O_CLT_TIME, begin, begin + 4, typeid(OptionInt)); diff --git a/src/lib/dhcp/tests/pkt6_unittest.cc b/src/lib/dhcp/tests/pkt6_unittest.cc index 5775d2ce92..a81ea86dd3 100644 --- a/src/lib/dhcp/tests/pkt6_unittest.cc +++ b/src/lib/dhcp/tests/pkt6_unittest.cc @@ -1,4 +1,4 @@ -// Copyright (C) 2011-2019 Internet Systems Consortium, Inc. ("ISC") +// Copyright (C) 2011-2020 Internet Systems Consortium, Inc. ("ISC") // // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this @@ -11,6 +11,8 @@ #include #include #include +#include +#include #include #include #include @@ -1812,4 +1814,248 @@ TEST_F(Pkt6Test, testSkipThisOptionError) { EXPECT_EQ("def", opstr->getValue()); } +// This test verifies that LQ_QUERY_OPTIONs can be created, packed, +// and unpacked correctly. +TEST_F(Pkt6Test, lqQueryOption) { + + OptionDefinitionPtr def = LibDHCP::getOptionDef(DHCP6_OPTION_SPACE, D6O_LQ_QUERY); + ASSERT_TRUE(def) << "D6O_LQ_QUERY is not undefined"; + + OptionCustomPtr lq_option(new OptionCustom(*def, Option::V6)); + ASSERT_TRUE(lq_option); + + // Add query type (77 is technically not valid but better visually). + uint8_t orig_type = 77; + ASSERT_NO_THROW_LOG(lq_option->writeInteger(77,0)); + + // Add query link address + IOAddress orig_link("2001:db8::1"); + ASSERT_NO_THROW_LOG(lq_option->writeAddress(orig_link, 1)); + + // Now add supported sub-options: D6O_IAADR, D6O_CLIENTID, and D6O_ORO + // We are ingoring the fact that a query containing both a D6O_IAADDR + // and a D6O_CLIENTID is not technically valid. We only care that the + // sub options will pack and unpack. + + // Add a D6O_IAADDR option + Option6IAAddrPtr orig_iaaddr(new Option6IAAddr(D6O_IAADDR, IOAddress("2001:db8::2"), 0, 0)); + ASSERT_TRUE(orig_iaaddr); + ASSERT_NO_THROW_LOG(lq_option->addOption(orig_iaaddr)); + + // Add a D6O_CLIENTID option + DuidPtr duid(new DUID(DUID::fromText("0102020202030303030303"))); + OptionPtr orig_clientid(new Option(Option::V6, D6O_CLIENTID, OptionBuffer( + duid->getDuid().begin(), duid->getDuid().end()))); + ASSERT_NO_THROW_LOG(lq_option->addOption(orig_clientid)); + + // Add a D6O_ORO option + OptionUint16ArrayPtr orig_oro(new OptionUint16Array(Option::V6, D6O_ORO)); + ASSERT_TRUE(orig_oro); + orig_oro->addValue(1234); + ASSERT_NO_THROW_LOG(lq_option->addOption(orig_oro)); + + // Now let's create a packet to which to add our new lq_option. + Pkt6Ptr orig(new Pkt6(DHCPV6_LEASEQUERY, 0x2312)); + orig->addOption(lq_option); + ASSERT_NO_THROW_LOG(orig->pack()); + + // Now create second packet,based on assembled data from the first one + Pkt6Ptr clone(new Pkt6(static_cast + (orig->getBuffer().getData()), + orig->getBuffer().getLength())); + + // Unpack it. + ASSERT_NO_THROW_LOG(clone->unpack()); + + // We should be able to find our query option. + OptionPtr opt; + opt = clone->getOption(D6O_LQ_QUERY); + ASSERT_TRUE(opt); + OptionCustomPtr clone_query = boost::dynamic_pointer_cast(opt); + ASSERT_TRUE(clone_query); + + // Verify the query type is right. + uint8_t clone_type; + ASSERT_NO_THROW_LOG(clone_type = clone_query->readInteger(0)); + EXPECT_EQ(orig_type, clone_type); + + // Verify the query link address is right. + IOAddress clone_link("::"); + ASSERT_NO_THROW_LOG(clone_link = clone_query->readAddress(1)); + EXPECT_EQ(orig_link, clone_link); + + // Verify the suboptions. + + // Verify the D6O_IAADDR option + opt = clone_query->getOption(D6O_IAADDR); + ASSERT_TRUE(opt); + Option6IAAddrPtr clone_iaaddr = boost::dynamic_pointer_cast(opt); + ASSERT_TRUE(clone_iaaddr); + EXPECT_TRUE(clone_iaaddr->equals(*orig_iaaddr)); + + // Verify the D6O_CLIENTID option + opt = clone_query->getOption(D6O_CLIENTID); + ASSERT_TRUE(opt); + EXPECT_TRUE(opt->equals(*orig_clientid)); + + // Verify the D6O_ORO option + opt = clone_query->getOption(D6O_ORO); + ASSERT_TRUE(opt); + OptionUint16ArrayPtr clone_oro = boost::dynamic_pointer_cast(opt); + ASSERT_TRUE(clone_oro); + EXPECT_TRUE(clone_oro->equals(*orig_oro)); +} + +// This test verifies that D6O_CLIENT_DATA options can be created, packed, +// and unpacked correctly. +TEST_F(Pkt6Test, clientDataOption) { + + OptionDefinitionPtr def = LibDHCP::getOptionDef(DHCP6_OPTION_SPACE, D6O_CLIENT_DATA); + ASSERT_TRUE(def) << "D6O_CLIENT_DATA is not undefined"; + + OptionCustomPtr cd_option(new OptionCustom(*def, Option::V6)); + ASSERT_TRUE(cd_option); + + // Now add supported sub-options: D6O_CLIENTID, D6O_IAADR, D6O_IAAPREFIX, + // and D6O_CLTT + + // Add a D6O_CLIENTID option + DuidPtr duid(new DUID(DUID::fromText("0102020202030303030303"))); + OptionPtr orig_clientid(new Option(Option::V6, D6O_CLIENTID, OptionBuffer( + duid->getDuid().begin(), duid->getDuid().end()))); + ASSERT_NO_THROW_LOG(cd_option->addOption(orig_clientid)); + + // Add a D6O_IAADDR option + Option6IAAddrPtr orig_iaaddr1(new Option6IAAddr(D6O_IAADDR, IOAddress("2001:db8::1"), 0, 0)); + ASSERT_TRUE(orig_iaaddr1); + ASSERT_NO_THROW_LOG(cd_option->addOption(orig_iaaddr1)); + + // Add another D6O_IAADDR option + Option6IAAddrPtr orig_iaaddr2(new Option6IAAddr(D6O_IAADDR, IOAddress("2001:db8::2"), 0, 0)); + ASSERT_TRUE(orig_iaaddr2); + ASSERT_NO_THROW_LOG(cd_option->addOption(orig_iaaddr2)); + + // Add a D6O_IAPREFIX option + Option6IAAddrPtr orig_iaprefix1(new Option6IAPrefix(D6O_IAPREFIX, IOAddress("2001:db8:1::"), 64, 0, 0)); + ASSERT_TRUE(orig_iaprefix1); + ASSERT_NO_THROW_LOG(cd_option->addOption(orig_iaprefix1)); + + // Add another D6O_IAPREFIX option + Option6IAAddrPtr orig_iaprefix2(new Option6IAPrefix(D6O_IAPREFIX, IOAddress("2001:db8:2::"), 64, 0, 0)); + ASSERT_TRUE(orig_iaprefix2); + ASSERT_NO_THROW_LOG(cd_option->addOption(orig_iaprefix2)); + + // Add a D6O_CLT_TIME option + OptionUint32Ptr orig_cltt(new OptionInt(Option::V6, D6O_CLT_TIME, 4000)); + ASSERT_TRUE(orig_cltt); + ASSERT_NO_THROW_LOG(cd_option->addOption(orig_cltt)); + + // Now let's create a packet to which to add our new client data option. + Pkt6Ptr orig(new Pkt6(DHCPV6_LEASEQUERY_REPLY, 0x2312)); + orig->addOption(cd_option); + ASSERT_NO_THROW_LOG(orig->pack()); + + // Now create second packet,based on assembled data from the first one + Pkt6Ptr clone(new Pkt6(static_cast + (orig->getBuffer().getData()), + orig->getBuffer().getLength())); + + // Unpack it. + ASSERT_NO_THROW_LOG(clone->unpack()); + + // We should be able to find our client data option. + OptionPtr opt; + opt = clone->getOption(D6O_CLIENT_DATA); + ASSERT_TRUE(opt); + OptionCustomPtr clone_cd_option = boost::dynamic_pointer_cast(opt); + ASSERT_TRUE(clone_cd_option); + + // Verify the suboptions. + opt = clone_cd_option->getOption(D6O_CLIENTID); + ASSERT_TRUE(opt); + EXPECT_TRUE(opt->equals(*orig_clientid)); + + // Verify the first address option + opt = clone_cd_option->getOption(D6O_IAADDR); + ASSERT_TRUE(opt); + Option6IAAddrPtr clone_iaaddr = boost::dynamic_pointer_cast(opt); + ASSERT_TRUE(clone_iaaddr); + EXPECT_TRUE(clone_iaaddr->equals(*orig_iaaddr1)); + + // Verify the second address option. + opt = clone_cd_option->getOption(D6O_IAADDR); + ASSERT_TRUE(opt); + clone_iaaddr = boost::dynamic_pointer_cast(opt); + ASSERT_TRUE(clone_iaaddr); + EXPECT_TRUE(clone_iaaddr->equals(*orig_iaaddr2)); + + // Verify the first prefix option. + opt = clone_cd_option->getOption(D6O_IAPREFIX); + ASSERT_TRUE(opt); + Option6IAPrefixPtr clone_iaprefix = boost::dynamic_pointer_cast(opt); + ASSERT_TRUE(clone_iaprefix); + EXPECT_TRUE(clone_iaprefix->equals(*orig_iaprefix1)); + + // Verify the second prefix option. + opt = clone_cd_option->getOption(D6O_IAPREFIX); + ASSERT_TRUE(opt); + clone_iaprefix = boost::dynamic_pointer_cast(opt); + ASSERT_TRUE(clone_iaprefix); + EXPECT_TRUE(clone_iaprefix->equals(*orig_iaprefix2)); + + // Verify the CLT option. + opt = clone_cd_option->getOption(D6O_CLT_TIME); + ASSERT_TRUE(opt); + OptionUint32Ptr clone_cltt = boost::dynamic_pointer_cast(opt); + ASSERT_TRUE(clone_cltt); + EXPECT_TRUE(clone_cltt->equals(*orig_cltt)); +} + +// This test verifies that D6O_LQ_RELAY_DATA options can be created, packed, +// and unpacked correctly. +TEST_F(Pkt6Test, relayDataOption) { + OptionDefinitionPtr def = LibDHCP::getOptionDef(DHCP6_OPTION_SPACE, D6O_LQ_RELAY_DATA); + ASSERT_TRUE(def) << "D6O_LQ_RELAY_DATA is not undefined"; + + OptionCustomPtr rd_option(new OptionCustom(*def, Option::V6)); + ASSERT_TRUE(rd_option); + + // Write out the peer address. + IOAddress orig_address("2001:db8::1"); + rd_option->writeAddress(orig_address, 0); + + // Write out the binary data (in real life this is a RELAY_FORW message) + std::vectororig_data({ 01,02,03,04,05,06 }); + rd_option->writeBinary(orig_data, 1); + + // Now let's create a packet to which to add our new relay data option. + Pkt6Ptr orig(new Pkt6(DHCPV6_LEASEQUERY_REPLY, 0x2312)); + orig->addOption(rd_option); + ASSERT_NO_THROW_LOG(orig->pack()); + + // Now create second packet,based on assembled data from the first one + Pkt6Ptr clone(new Pkt6(static_cast + (orig->getBuffer().getData()), + orig->getBuffer().getLength())); + // Unpack it. + ASSERT_NO_THROW_LOG(clone->unpack()); + + // We should be able to find our client data option. + OptionPtr opt; + opt = clone->getOption(D6O_LQ_RELAY_DATA); + ASSERT_TRUE(opt); + OptionCustomPtr clone_rd_option = boost::dynamic_pointer_cast(opt); + ASSERT_TRUE(clone_rd_option); + + // Verify the address field. + IOAddress clone_addr("::"); + ASSERT_NO_THROW_LOG(clone_addr = clone_rd_option->readAddress(0)); + EXPECT_EQ(orig_address, clone_addr); + + // Verify the binary field + OptionBuffer clone_data; + ASSERT_NO_THROW_LOG(clone_data = clone_rd_option->readBinary(1)); + EXPECT_EQ(orig_data, clone_data); +} + } diff --git a/src/lib/dhcpsrv/cfg_duid.cc b/src/lib/dhcpsrv/cfg_duid.cc index 2da19d6198..90b344f457 100644 --- a/src/lib/dhcpsrv/cfg_duid.cc +++ b/src/lib/dhcpsrv/cfg_duid.cc @@ -52,7 +52,7 @@ CfgDUID::setIdentifier(const std::string& identifier_as_hex) { } DuidPtr -CfgDUID::create(const std::string& duid_file_path) const { +CfgDUID::create(const std::string& duid_file_path) { // Use DUID factory to create a DUID instance. DUIDFactory factory(persist() ? duid_file_path : ""); @@ -73,7 +73,8 @@ CfgDUID::create(const std::string& duid_file_path) const { } // Return generated DUID. - return (factory.get()); + current_duid_ = factory.get(); + return (current_duid_); } ElementPtr diff --git a/src/lib/dhcpsrv/cfg_duid.h b/src/lib/dhcpsrv/cfg_duid.h index 70554443d1..91fea0cc8a 100644 --- a/src/lib/dhcpsrv/cfg_duid.h +++ b/src/lib/dhcpsrv/cfg_duid.h @@ -1,4 +1,4 @@ -// Copyright (C) 2015-2018 Internet Systems Consortium, Inc. ("ISC") +// Copyright (C) 2015-2020 Internet Systems Consortium, Inc. ("ISC") // // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this @@ -114,13 +114,20 @@ public: /// /// @param duid_file_path Absolute path to a DUID file. /// @return Pointer to an instance of new DUID. - DuidPtr create(const std::string& duid_file_path) const; + DuidPtr create(const std::string& duid_file_path); /// @brief Unparse a configuration object /// /// @return a pointer to unparsed configuration virtual isc::data::ElementPtr toElement() const; + /// @brief Fetches the duid created by @ref create() + /// @return a pointer to created duid. Pointer will + /// empty if the duid has not yet been created. + const DuidPtr getCurrentDuid() const { + return (current_duid_); + } + private: /// @brief DUID type. @@ -141,6 +148,8 @@ private: /// @brief Boolean flag which indicates if server identifier should /// be stored on the disk. bool persist_; + + DuidPtr current_duid_; }; /// @name Pointers to the @c CfgDUID objects. diff --git a/src/lib/dhcpsrv/tests/cfg_duid_unittest.cc b/src/lib/dhcpsrv/tests/cfg_duid_unittest.cc index 73c6af1781..6b061f6f28 100644 --- a/src/lib/dhcpsrv/tests/cfg_duid_unittest.cc +++ b/src/lib/dhcpsrv/tests/cfg_duid_unittest.cc @@ -194,6 +194,11 @@ TEST_F(CfgDUIDTest, createLLT) { // Verify that the DUID file has been created. EXPECT_TRUE(fileExists(absolutePath(DUID_FILE_NAME))); + + // Verifiy getCurrentDuid() returns the value created. + DuidPtr current_duid = cfg.getCurrentDuid(); + ASSERT_TRUE(current_duid); + EXPECT_EQ(*current_duid, *duid); } // This method checks that the DUID-EN can be created from the @@ -214,6 +219,11 @@ TEST_F(CfgDUIDTest, createEN) { // Verify that the DUID file has been created. EXPECT_TRUE(fileExists(absolutePath(DUID_FILE_NAME))); + + // Verifiy getCurrentDuid() returns the value created. + DuidPtr current_duid = cfg.getCurrentDuid(); + ASSERT_TRUE(current_duid); + EXPECT_EQ(*current_duid, *duid); } // This method checks that the DUID-LL can be created from the @@ -234,6 +244,11 @@ TEST_F(CfgDUIDTest, createLL) { // Verify that the DUID file has been created. EXPECT_TRUE(fileExists(absolutePath(DUID_FILE_NAME))); + + // Verifiy getCurrentDuid() returns the value created. + DuidPtr current_duid = cfg.getCurrentDuid(); + ASSERT_TRUE(current_duid); + EXPECT_EQ(*current_duid, *duid); } // This test verifies that it is possible to disable storing @@ -255,6 +270,11 @@ TEST_F(CfgDUIDTest, createDisableWrite) { // DUID persistence is disabled so there should be no DUID file. EXPECT_FALSE(fileExists(absolutePath(DUID_FILE_NAME))); + + // Verifiy getCurrentDuid() returns the value created. + DuidPtr current_duid = cfg.getCurrentDuid(); + ASSERT_TRUE(current_duid); + EXPECT_EQ(*current_duid, *duid); } } // end of anonymous namespace