From: Marcin Siodelski Date: Fri, 10 Jul 2015 09:23:27 +0000 (+0200) Subject: [3947] Implemented basic test for the RFC7550 Renew. X-Git-Tag: trac3238_base~6^2~14 X-Git-Url: http://git.ipfire.org/gitweb/gitweb.cgi?a=commitdiff_plain;h=fe3d24975c85fbc4957f0f75f5303f4c069bbb5e;p=thirdparty%2Fkea.git [3947] Implemented basic test for the RFC7550 Renew. This test checks that the client can request allocation of the prefix when it renews the existing IA_NA binding. The DHCPv6 test client had to be extended to faciliate this. --- diff --git a/src/bin/dhcp6/tests/Makefile.am b/src/bin/dhcp6/tests/Makefile.am index 2d444bc2c1..21dabfcdf3 100644 --- a/src/bin/dhcp6/tests/Makefile.am +++ b/src/bin/dhcp6/tests/Makefile.am @@ -83,6 +83,7 @@ dhcp6_unittests_SOURCES += marker_file.cc dhcp6_unittests_SOURCES += ctrl_dhcp6_srv_unittest.cc dhcp6_unittests_SOURCES += dhcp6_client.cc dhcp6_client.h dhcp6_unittests_SOURCES += rebind_unittest.cc +dhcp6_unittests_SOURCES += renew_unittest.cc dhcp6_unittests_SOURCES += sarr_unittest.cc dhcp6_unittests_SOURCES += config_parser_unittest.cc dhcp6_unittests_SOURCES += confirm_unittest.cc diff --git a/src/bin/dhcp6/tests/dhcp6_client.cc b/src/bin/dhcp6/tests/dhcp6_client.cc index 9de4755c8b..d8b985fb72 100644 --- a/src/bin/dhcp6/tests/dhcp6_client.cc +++ b/src/bin/dhcp6/tests/dhcp6_client.cc @@ -23,6 +23,7 @@ #include #include #include +#include #include #include #include @@ -209,18 +210,71 @@ Dhcp6Client::appendFQDN() { } } +void +Dhcp6Client::appendRequestedIAs(const Pkt6Ptr& query) const { + if (use_na_) { + conditionallyAppendRequestedIA(query, D6O_IA_NA, 1234); + } + + if (use_pd_) { + conditionallyAppendRequestedIA(query, D6O_IA_PD, 5678); + } +} + +void +Dhcp6Client::conditionallyAppendRequestedIA(const Pkt6Ptr& query, + const uint8_t ia_type, + const uint32_t iaid) const { + // Get existing options of the specified type. + OptionCollection options = query->getOptions(ia_type); + std::pair option_pair; + + // Check if the option we want to add is already present. + BOOST_FOREACH(option_pair, options) { + Option6IAPtr ia = boost::dynamic_pointer_cast(option_pair.second); + // This shouldn't happen. + if (!ia) { + isc_throw(Unexpected, "Dhcp6Client: IA option has an invalid C++ type;" + " this is a programming issue"); + } + // There is an option of the specific type already. If it has our + // IAID we return here, because we don't want to duplicate the IA. + // If IAID is different, we check other existing IAs. + if (ia->getIAID() == iaid) { + return; + } + } + + // If we're here, it means that there is no instance of our IA yet. + Option6IAPtr requested_ia(new Option6IA(ia_type, iaid)); + // Add prefix hint if specified. + if (prefix_hint_ && (ia_type == D6O_IA_PD)) { + requested_ia->addOption(prefix_hint_); + } + + query->addOption(requested_ia); +} + + void Dhcp6Client::copyIAs(const Pkt6Ptr& source, const Pkt6Ptr& dest) { typedef OptionCollection Opts; // Copy IA_NAs. Opts opts = source->getOptions(D6O_IA_NA); for (Opts::const_iterator opt = opts.begin(); opt != opts.end(); ++opt) { - dest->addOption(opt->second); + // Only copy the entire IA_NA if there is at lease one IA Address option. + if (opt->second->getOption(D6O_IAADDR)) { + dest->addOption(opt->second); + } } // Copy IA_PDs. opts = source->getOptions(D6O_IA_PD); for (Opts::const_iterator opt = opts.begin(); opt != opts.end(); ++opt) { - dest->addOption(opt->second); + // Only copy the entire IA_PD if there is at least one IA Prefix option + // in it. + if (opt->second->getOption(D6O_IAPREFIX)) { + dest->addOption(opt->second); + } } } @@ -298,17 +352,10 @@ Dhcp6Client::doSolicit() { if (forced_server_id_) { context_.query_->addOption(forced_server_id_); } - if (use_na_) { - context_.query_->addOption(Option6IAPtr(new Option6IA(D6O_IA_NA, - 1234))); - } - if (use_pd_) { - Option6IAPtr ia(new Option6IA(D6O_IA_PD, 5678)); - if (prefix_hint_) { - ia->addOption(prefix_hint_); - } - context_.query_->addOption(ia); - } + + // Append requested (empty) IAs. + appendRequestedIAs(context_.query_); + if (use_rapid_commit_) { context_.query_->addOption(OptionPtr(new Option(Option::V6, D6O_RAPID_COMMIT))); @@ -336,6 +383,7 @@ Dhcp6Client::doRequest() { query->addOption(forced_server_id_); } copyIAs(context_.response_, query); + appendRequestedIAs(query); // Add Client FQDN if configured. appendFQDN(); @@ -384,6 +432,10 @@ Dhcp6Client::doRenew() { query->addOption(context_.response_->getOption(D6O_SERVERID)); copyIAsFromLeases(query); + // During the Renew the client may request additional bindings per + // RFC7550. + appendRequestedIAs(query); + // Add Client FQDN if configured. appendFQDN(); @@ -401,6 +453,10 @@ Dhcp6Client::doRebind() { Pkt6Ptr query = createMsg(DHCPV6_REBIND); copyIAsFromLeases(query); + // During the Rebind the client may request additional bindings per + // RFC7550. + appendRequestedIAs(query); + // Add Client FQDN if configured. appendFQDN(); @@ -487,6 +543,18 @@ Dhcp6Client::getLeasesByIAID(const uint32_t iaid) const { return (leases); } +std::vector +Dhcp6Client::getLeasesByType(const Lease::Type& lease_type) const { + std::vector leases; + LeaseInfo lease_info; + BOOST_FOREACH(lease_info, config_.leases_) { + if (lease_info.lease_.type_ == lease_type) { + leases.push_back(lease_info.lease_); + } + } + return (leases); +} + void Dhcp6Client::setDUID(const std::string& str) { DUID d = DUID::fromText(str); diff --git a/src/bin/dhcp6/tests/dhcp6_client.h b/src/bin/dhcp6/tests/dhcp6_client.h index 35462321ce..a6171861f8 100644 --- a/src/bin/dhcp6/tests/dhcp6_client.h +++ b/src/bin/dhcp6/tests/dhcp6_client.h @@ -312,6 +312,13 @@ public: /// @return Vector containing leases for the IAID. std::vector getLeasesByIAID(const uint32_t iaid) const; + /// @brief Returns collection of leases by type. + /// + /// @param type Lease type: D6O_IA_NA or D6O_IA_PD. + /// + /// @return Vector containing leases of the specified type. + std::vector getLeasesByType(const Lease::Type& lease_type) const; + /// @brief Returns the value of the global status code for the last /// transaction. uint16_t getStatusCode() const { @@ -529,12 +536,35 @@ private: /// @param lease_info Structure holding new lease information. void applyLease(const LeaseInfo& lease_info); - /// @brief Includes CLient FQDN in the client's message. + /// @brief Includes Client FQDN in the client's message. /// /// This method checks if @c fqdn_ is specified and includes it in /// the client's message. void appendFQDN(); + /// @brief Includes IAs to be requested. + /// + /// This method checks if @c use_na_ and/or @c use_pd_ are specified and + /// includes appropriate IA types, if they are not already included. + /// + /// @param query Pointer to the client's message to which IAs should be + /// added. + void appendRequestedIAs(const Pkt6Ptr& query) const; + + /// @brief Include IA of the specified type if it doesn't exist yet. + /// + /// This methods includes an IA option of the specific type, and + /// having a given IAID to the query message, if this IA hasn't + /// been added yet. + /// + /// @param query Pointer to the client's message to which IA should be + /// added. + /// @param ia_type One of the D6O_IA_NA or D6O_IA_PD + /// @param iaid IAID of the IA. + void conditionallyAppendRequestedIA(const Pkt6Ptr& query, + const uint8_t ia_type, + const uint32_t iaid) const; + /// @brief Copy IA options from one message to another. /// /// This method copies IA_NA and IA_PD options from one message to another. diff --git a/src/bin/dhcp6/tests/dhcp6_message_test.h b/src/bin/dhcp6/tests/dhcp6_message_test.h index a3eeca4c7b..c885bc27b4 100644 --- a/src/bin/dhcp6/tests/dhcp6_message_test.h +++ b/src/bin/dhcp6/tests/dhcp6_message_test.h @@ -78,6 +78,7 @@ public: void requestLease(const std::string& config, const int subnets_num, Dhcp6Client& client); + protected: /// @brief Interface Manager's fake configuration control. diff --git a/src/bin/dhcp6/tests/renew_unittest.cc b/src/bin/dhcp6/tests/renew_unittest.cc new file mode 100644 index 0000000000..91f058980d --- /dev/null +++ b/src/bin/dhcp6/tests/renew_unittest.cc @@ -0,0 +1,141 @@ +// 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 +#include +#include +#include +#include +#include + +using namespace isc; +using namespace isc::asiolink; +using namespace isc::data; +using namespace isc::dhcp; +using namespace isc::dhcp::test; +using namespace isc::test; + +namespace { + +/// @brief Set of JSON configurations used throughout the Renew tests. +/// +/// - Configuration 0: +/// - only addresses (no prefixes) +/// - 2 subnets with 2001:db8:1::/64 and 2001:db8:2::64 +/// - 1 subnet for eth0 and 1 subnet for eth1 +/// +/// - Configuration 1: +/// - similar to Configuration 0 but different subnets +/// - pools configured: 2001:db8:3::/64 and 2001:db8:4::/64 +/// +/// - Configuration 2: +/// - similar to Configuration 0 and Configuration 1 +/// - pools configured: 3000:1::/64 and 3000:2::/64 +/// - this specific configuration is used by tests using relays +/// +const char* RENEW_CONFIGS[] = { +// Configuration 0 + "{ \"interfaces-config\": {" + " \"interfaces\": [ \"*\" ]" + "}," + "\"preferred-lifetime\": 3000," + "\"rebind-timer\": 2000, " + "\"renew-timer\": 1000, " + "\"subnet6\": [ { " + " \"pools\": [ { \"pool\": \"2001:db8:1::/64\" } ]," + " \"subnet\": \"2001:db8:1::/48\", " + " \"interface-id\": \"\"," + " \"interface\": \"eth0\"" + " } ]," + "\"valid-lifetime\": 4000 }", + +// Configuration 1 + "{ \"interfaces-config\": {" + " \"interfaces\": [ \"*\" ]" + "}," + "\"preferred-lifetime\": 3000," + "\"rebind-timer\": 2000, " + "\"renew-timer\": 1000, " + "\"subnet6\": [ { " + " \"pools\": [ { \"pool\": \"2001:db8:1::/64\" } ]," + " \"pd-pools\": [" + " { \"prefix\": \"3000::\", " + " \"prefix-len\": 72, " + " \"delegated-len\": 80" + " } ]," + " \"subnet\": \"2001:db8:1::/48\", " + " \"interface-id\": \"\"," + " \"interface\": \"eth0\"" + " } ]," + "\"valid-lifetime\": 4000 }" +}; + +/// @brief Test fixture class for testing Renew. +class RenewTest : public Dhcpv6MessageTest { +public: + + /// @brief Constructor. + /// + /// Sets up fake interfaces. + RenewTest() + : Dhcpv6MessageTest() { + } +}; + +// This test verifies that the client can request the prefix delegation +// while it is renewing an address lease. +TEST_F(RenewTest, requestPrefixInRenew) { + Dhcp6Client client; + + // Configure client to request IA_NA and IA_PD. + client.useNA(); + client.usePD(); + + // Configure the server with NA pools only. + ASSERT_NO_THROW(configure(RENEW_CONFIGS[0], *client.getServer())); + + // Perform 4-way exchange. + ASSERT_NO_THROW(client.doSARR()); + + // Simulate aging of leases. + client.fastFwdTime(1000); + + // Make sure that the client has acquired NA lease. + std::vector leases_client_na = client.getLeasesByType(Lease::TYPE_NA); + ASSERT_EQ(1, leases_client_na.size()); + + // The client should not acquire a PD lease. + std::vector leases_client_pd = client.getLeasesByType(Lease::TYPE_PD); + ASSERT_TRUE(leases_client_pd.empty()); + + // Reconfigure the server to use both NA and PD pools. + configure(RENEW_CONFIGS[1], *client.getServer()); + + // Send Renew message to the server, including IA_NA and requesting IA_PD. + ASSERT_NO_THROW(client.doRenew()); + + // Make sure that the client has acquired NA lease. + std::vector leases_client_na_renewed = client.getLeasesByType(Lease::TYPE_NA); + ASSERT_EQ(1, leases_client_na_renewed.size()); + + // The lease should have been renewed. + EXPECT_EQ(1000, leases_client_na_renewed[0].cltt_ - leases_client_na[0].cltt_); + + // The client should now also acquire a PD lease. + leases_client_pd = client.getLeasesByType(Lease::TYPE_PD); + ASSERT_EQ(1, leases_client_pd.size()); +} + + +} // end of anonymous namespace