From 625cc0a45da9ee3356df3263d03a16eca5051b03 Mon Sep 17 00:00:00 2001 From: Marcin Siodelski Date: Wed, 1 Aug 2012 17:40:00 +0200 Subject: [PATCH 01/40] [1959] Created TestControl class, unit tests and main function. --- tests/tools/perfdhcp/Makefile.am | 28 +- tests/tools/perfdhcp/main.cc | 46 ++++ tests/tools/perfdhcp/test_control.cc | 239 ++++++++++++++++++ tests/tools/perfdhcp/test_control.h | 117 +++++++++ tests/tools/perfdhcp/tests/Makefile.am | 2 + .../perfdhcp/tests/test_control_unittest.cc | 67 +++++ 6 files changed, 486 insertions(+), 13 deletions(-) create mode 100644 tests/tools/perfdhcp/main.cc create mode 100644 tests/tools/perfdhcp/test_control.cc create mode 100644 tests/tools/perfdhcp/test_control.h create mode 100644 tests/tools/perfdhcp/tests/test_control_unittest.cc diff --git a/tests/tools/perfdhcp/Makefile.am b/tests/tools/perfdhcp/Makefile.am index 6ebc00f92a..e9593df056 100644 --- a/tests/tools/perfdhcp/Makefile.am +++ b/tests/tools/perfdhcp/Makefile.am @@ -18,23 +18,25 @@ if USE_STATIC_LINK AM_LDFLAGS += -static endif -lib_LTLIBRARIES = libperfdhcp++.la -libperfdhcp___la_SOURCES = command_options.cc command_options.h -libperfdhcp___la_SOURCES += localized_option.h -libperfdhcp___la_SOURCES += perf_pkt6.cc perf_pkt6.h -libperfdhcp___la_SOURCES += perf_pkt4.cc perf_pkt4.h -libperfdhcp___la_SOURCES += pkt_transform.cc pkt_transform.h +pkglibexec_PROGRAMS = perfdhcp2 +perfdhcp2_SOURCES = main.cc +perfdhcp2_SOURCES += command_options.cc command_options.h +perfdhcp2_SOURCES += localized_option.h +perfdhcp2_SOURCES += perf_pkt6.cc perf_pkt6.h +perfdhcp2_SOURCES += perf_pkt4.cc perf_pkt4.h +perfdhcp2_SOURCES += pkt_transform.cc pkt_transform.h +perfdhcp2_SOURCES += test_control.cc test_control.h -libperfdhcp___la_CXXFLAGS = $(AM_CXXFLAGS) +perfdhcp2_CXXFLAGS = $(AM_CXXFLAGS) if USE_CLANGPP # Disable unused parameter warning caused by some of the # Boost headers when compiling with clang. -libperfdhcp___la_CXXFLAGS += -Wno-unused-parameter +perfdhcp2_CXXFLAGS += -Wno-unused-parameter endif -libperfdhcp___la_LIBADD = $(top_builddir)/src/lib/exceptions/libexceptions.la -libperfdhcp___la_LIBADD += $(top_builddir)/src/lib/dhcp/libdhcp++.la -libperfdhcp___la_LIBADD += $(top_builddir)/src/lib/asiolink/libasiolink.la +perfdhcp2_LDADD = $(top_builddir)/src/lib/exceptions/libexceptions.la +perfdhcp2_LDADD += $(top_builddir)/src/lib/dhcp/libdhcp++.la +perfdhcp2_LDADD += $(top_builddir)/src/lib/asiolink/libasiolink.la -pkglibexec_PROGRAMS = perfdhcp -perfdhcp_SOURCES = perfdhcp.c +#pkglibexec_PROGRAMS = perfdhcp +#perfdhcp_SOURCES = perfdhcp.c diff --git a/tests/tools/perfdhcp/main.cc b/tests/tools/perfdhcp/main.cc new file mode 100644 index 0000000000..d70b8fae8d --- /dev/null +++ b/tests/tools/perfdhcp/main.cc @@ -0,0 +1,46 @@ +// Copyright (C) 2012 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 "test_control.h" +#include "command_options.h" + +using namespace isc::perfdhcp; + +int +main(int argc, char* argv[]) { + CommandOptions& command_options = CommandOptions::instance(); + try { + command_options.parse(argc, argv); + } catch(isc::Exception& e) { + std::cout << "Error parsing command line options: " + << e.what() << std::endl; + command_options.usage(); + return(1); + } + try{ + TestControl& test_control = TestControl::instance(); + test_control.run(); + } catch (isc::Exception& e) { + std::cout << "Error starting perfdhcp: " << e.what() << std::endl; + return(1); + } + return(0); +} + diff --git a/tests/tools/perfdhcp/test_control.cc b/tests/tools/perfdhcp/test_control.cc new file mode 100644 index 0000000000..728d4d58ec --- /dev/null +++ b/tests/tools/perfdhcp/test_control.cc @@ -0,0 +1,239 @@ +// Copyright (C) 2012 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 +#include +#include +#include "test_control.h" +#include "command_options.h" + +using namespace std; +using namespace boost; +using namespace boost::posix_time; +using namespace isc; +using namespace isc::dhcp; + +namespace isc { +namespace perfdhcp { + +TestControl& +TestControl::instance() { + static TestControl test_control; + return (test_control); +} + +TestControl::TestControl() : + send_due_(microsec_clock::universal_time()), + last_sent_(send_due_) { +} + +bool +TestControl::checkExitConditions() const { + CommandOptions& options = CommandOptions::instance(); + if ((options.getNumRequests().size() > 0) && + (sent_packets_0_ >= options.getNumRequests()[0])) { + return(true); + } else if ((options.getNumRequests().size() == 2) && + (sent_packets_1_ >= options.getNumRequests()[1])) { + return(true); + } + return(false); +} + +Pkt4* +TestControl::createDiscoverPkt4() { + const uint32_t transid = static_cast(random()); + Pkt4* pkt4 = new Pkt4(DHCPDISCOVER, transid); + + OptionBuffer opt_request_list_buf(); + // createRequestListBuffer4(opt_request_list_buf); + return NULL; +} + +OptionPtr +TestControl::factoryRequestList4(Option::Universe u, + uint16_t type, + const OptionBuffer& buf) +{ + const uint8_t buf_array[] = { + DHO_SUBNET_MASK, + DHO_BROADCAST_ADDRESS, + DHO_TIME_OFFSET, + DHO_ROUTERS, + DHO_DOMAIN_NAME, + DHO_DOMAIN_NAME_SERVERS, + DHO_HOST_NAME + }; + + OptionBuffer buf_with_options(buf_array, buf_array + sizeof(buf_array)); + Option* opt = new Option(u, type, buf); + opt->setData(buf_with_options.begin(), + buf_with_options.end()); + return OptionPtr(opt); +} + +uint64_t +TestControl::getNextExchangesNum() const { + CommandOptions& options = CommandOptions::instance(); + // Reset number of exchanges. + uint64_t due_exchanges = 0; + // Get current time. + ptime now(microsec_clock::universal_time()); + // The due time indicates when we should start sening next chunk + // of packets. If it is already due time, we should calculate + // how many packets to send. + if (now >= send_due_) { + // If rate is specified from the command line we have to + // synchornize with it. + if (options.getRate() != 0) { + time_period period(send_due_, now); + // Null condition should not occur because we + // have checked it in the first if statement but + // let's keep this check just in case. + if (period.is_null()) { + return (0); + } + time_duration duration = period.length(); + // due_factor indicates the number of seconds that + // sending next chunk of packets will take. + double due_factor = duration.fractional_seconds() / + time_duration::ticks_per_second(); + due_factor += duration.total_seconds(); + // Multiplying due_factor by expected rate gives the number + // of exchanges to be initiated. + due_exchanges = static_cast(due_factor * options.getRate()); + // We want to make sure that at least one packet goes out. + due_exchanges += 1; + // We should not exceed aggressivity as it could have been + // restricted from command line. + if (due_exchanges > options.getAggressivity()) { + due_exchanges = options.getAggressivity(); + } + } else { + // Rate is not specified so we rely on aggressivity + // which is the number of packets to be sent in + // one chunk. + due_exchanges = options.getAggressivity(); + } + return (due_exchanges); + } + return (0); +} + +void +TestControl::registerOptionFactories4() const { + static bool factories_registered = false; + if (!factories_registered) { + LibDHCP::OptionFactoryRegister(Option::V4, + DHO_DHCP_PARAMETER_REQUEST_LIST, + &TestControl::factoryRequestList4); + } + factories_registered = true; +} + +void +TestControl::registerOptionFactories6() const { + static bool factories_registered = false; + if (!factories_registered) { + } + factories_registered = true; +} + +void +TestControl::registerOptionFactories() const { + CommandOptions& options = CommandOptions::instance(); + switch(options.getIpVersion()) { + case 4: + registerOptionFactories4(); + break; + case 6: + registerOptionFactories6(); + break; + default: + isc_throw(InvalidOperation, "command line options have to be parsed " + "before DHCP option factories can be registered"); + } +} + +void +TestControl::run() { + sent_packets_0_ = 0; + sent_packets_1_ = 0; + CommandOptions& options = CommandOptions::instance(); + // Ip version is not set ONLY in case the command options + // where not parsed. This surely means that parse() function + // was not called prior to starting the test. This is fatal + // error. + if (options.getIpVersion() == 0) { + isc_throw(InvalidOperation, "command options must be parsed before running " + "a test"); + } + registerOptionFactories(); + uint64_t packets_sent = 0; + for (;;) { + updateSendDue(); + if (checkExitConditions()) { + break; + } + uint64_t packets_due = getNextExchangesNum(); + for (uint64_t i = packets_due; i > 0; --i) { + startExchange(); + ++packets_sent; + cout << "Packets sent " << packets_sent << endl; + } + } + +} + +void +TestControl::startExchange() { + ++sent_packets_0_; + last_sent_ = microsec_clock::universal_time(); +} + +void +TestControl::updateSendDue() { + // If default constructor was called, this should not happen but + // if somebody has changed default constructor it is better to + // keep this check. + if (last_sent_.is_not_a_date_time()) { + isc_throw(Unexpected, "time of last sent packet not initialized"); + } + // Get the expected exchange rate. + CommandOptions& options = CommandOptions::instance(); + int rate = options.getRate(); + // If rate was not specified we will wait just one clock tick to + // send next packet. This simulates best effort conditions. + long duration = 1; + if (rate != 0) { + // We use number of ticks instead of nanoseconds because + // nanosecond resolution may not be available on some + // machines. Number of ticks guarantees the highest possible + // timer resolution. + duration = time_duration::ticks_per_second() / rate; + } + // Calculate due time to initate next chunk of exchanges. + send_due_ = last_sent_ + time_duration(0, 0, 0, duration); +} + + +} // namespace perfdhcp +} // namespace isc diff --git a/tests/tools/perfdhcp/test_control.h b/tests/tools/perfdhcp/test_control.h new file mode 100644 index 0000000000..b1d7d27d32 --- /dev/null +++ b/tests/tools/perfdhcp/test_control.h @@ -0,0 +1,117 @@ +// Copyright (C) 2012 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 __TEST_CONTROL_H +#define __TEST_CONTROL_H + +#include +#include + +#include +#include + +#include +#include + +namespace isc { +namespace perfdhcp { + +/// \brief Test Control class. +/// +/// This class is responsible for running whole perfdhcp test. +/// +class TestControl : public boost::noncopyable { +public: + /// TestControl is a singleton class. This method returns reference + /// to its sole instance. + /// + /// \return the only existing instance of test control + static TestControl& instance(); + + /// Run performance test. + /// + /// Method runs whole performance test. Command line options must + /// be parsed prior to running this function. Othewise function will + /// throw exception. + /// + /// \throw isc::InvalidOperation if command line options are not parsed. + /// \throw isc::Unexpected if internal Test Controler error occured. + void run(); + +private: + + /// \brief Private default constructor. + /// + /// Default constructor is private as the object can be created + /// only via \ref instance method. + TestControl(); + + /// \brief Check if test exit condtitions fulfiled. + /// + /// Method checks if test exit conditions are fulfiled. + /// Exit conditions are checked periodically from the + /// main loop. Program should break the main loop when + /// this method returns true. It is calling function + /// responsibility to break main loop gracefully and + /// cleanup after test execution. + /// + /// \return true if any of the exit conditions is fulfiled. + bool checkExitConditions() const; + + dhcp::Pkt4* createDiscoverPkt4(); + + static dhcp::OptionPtr factoryRequestList4(dhcp::Option::Universe u, + uint16_t type, + const dhcp::OptionBuffer& buf); + + /// \brief Returns number of exchanges to be started. + /// + /// Method returns number of new exchanges to be started as soon + /// as possible to satisfy expected rate. Calculation used here + /// is based on current time, due time calculated with + /// \ref updateSendTime function and expected rate. + /// + /// \return number of exchanges to be started immediatelly. + uint64_t getNextExchangesNum() const; + + void registerOptionFactories4() const; + + void registerOptionFactories6() const; + + void registerOptionFactories() const; + + /// \brief Start new exchange of DHCP messages. + /// + void startExchange(); + + /// \brief Update due time to initiate next chunk of exchanges. + /// + /// Method updates due time to initiate next chunk of exchanges. + /// Function takes current time, last sent packet's time and + /// expected rate in its calculations. + void updateSendDue(); + + boost::posix_time::ptime send_due_; ///< Due time to initiate next chunk + ///< of exchanges. + boost::posix_time::ptime last_sent_; ///< Indicates when the last exchange + /// was initiated. + + uint64_t sent_packets_0_; + uint64_t sent_packets_1_; +}; + +} // namespace perfdhcp +} // namespace isc + +#endif // __COMMAND_OPTIONS_H diff --git a/tests/tools/perfdhcp/tests/Makefile.am b/tests/tools/perfdhcp/tests/Makefile.am index ab1251c5c9..000bab527b 100644 --- a/tests/tools/perfdhcp/tests/Makefile.am +++ b/tests/tools/perfdhcp/tests/Makefile.am @@ -21,10 +21,12 @@ run_unittests_SOURCES += command_options_unittest.cc run_unittests_SOURCES += perf_pkt6_unittest.cc run_unittests_SOURCES += perf_pkt4_unittest.cc run_unittests_SOURCES += localized_option_unittest.cc +run_unittests_SOURCES += test_control_unittest.cc run_unittests_SOURCES += $(top_builddir)/tests/tools/perfdhcp/command_options.cc run_unittests_SOURCES += $(top_builddir)/tests/tools/perfdhcp/pkt_transform.cc run_unittests_SOURCES += $(top_builddir)/tests/tools/perfdhcp/perf_pkt6.cc run_unittests_SOURCES += $(top_builddir)/tests/tools/perfdhcp/perf_pkt4.cc +run_unittests_SOURCES += $(top_builddir)/tests/tools/perfdhcp/test_control.cc run_unittests_CPPFLAGS = $(AM_CPPFLAGS) $(GTEST_INCLUDES) run_unittests_LDFLAGS = $(AM_LDFLAGS) $(GTEST_LDFLAGS) diff --git a/tests/tools/perfdhcp/tests/test_control_unittest.cc b/tests/tools/perfdhcp/tests/test_control_unittest.cc new file mode 100644 index 0000000000..7a66b03fb3 --- /dev/null +++ b/tests/tools/perfdhcp/tests/test_control_unittest.cc @@ -0,0 +1,67 @@ +// Copyright (C) 2012 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 "../test_control.h" +#include "../command_options.h" +#include "exceptions/exceptions.h" + +using namespace std; +using namespace isc; +using namespace isc::perfdhcp; + +/// \brief Test Fixture Class +/// +/// This test fixture class is used to perform +/// unit tests on perfdhcp TestControl class. +class TestControlTest : public virtual ::testing::Test +{ +public: + /// \brief Default Constructor + TestControlTest() { } + +protected: +}; + +TEST_F(TestControlTest, Run) { + // Get the instance of TestControl object. + TestControl& test_control = TestControl::instance(); + // Running test without parsing command line arguments is + // expected to cause exception. + EXPECT_THROW(test_control.run(), isc::InvalidOperation); + + // The command line is to run single test iteration and exit. + // We have to declare argv as const walk around the problem + // of deprecated conversion from string to char*. + const char* argv[] = { "perfdhcp", "-l", "eth0", "-r", "10", "-n", "1" }; + const int argc = sizeof(argv) / sizeof(argv[0]); + CommandOptions& options = CommandOptions::instance(); + + // const_cast is odd but it seems to be the most straight forward way + // to achive the goal. In the future we might think about creating + // a tokenizing function that would dynamically produce non-const + // argv value. + ASSERT_NO_THROW(options.parse(argc, const_cast(argv))); + EXPECT_NO_THROW(test_control.run()); + + // This is ok to run the test again with the same parameters. + // It may trigger exception if TestControl singleton is in + // invalid state after test run. We want to make sure it is + // safe to rerun the test. + EXPECT_NO_THROW(test_control.run()); +} From 8935b6716b3795bb889641a3127c2a701c6cac59 Mon Sep 17 00:00:00 2001 From: Marcin Siodelski Date: Wed, 1 Aug 2012 17:41:28 +0200 Subject: [PATCH 02/40] [1959] Added factory function to create instance of options. --- src/lib/dhcp/libdhcp++.cc | 26 ++++++++++ src/lib/dhcp/libdhcp++.h | 25 +++++++++- src/lib/dhcp/option.cc | 8 +++ src/lib/dhcp/option.h | 17 +++++++ src/lib/dhcp/tests/libdhcp++_unittest.cc | 63 ++++++++++++++++++++++++ 5 files changed, 137 insertions(+), 2 deletions(-) diff --git a/src/lib/dhcp/libdhcp++.cc b/src/lib/dhcp/libdhcp++.cc index c054a4b0a5..4ff07eac6a 100644 --- a/src/lib/dhcp/libdhcp++.cc +++ b/src/lib/dhcp/libdhcp++.cc @@ -15,6 +15,7 @@ #include #include #include +#include #include #include "config.h" #include @@ -34,6 +35,31 @@ std::map LibDHCP::v4factories_; std::map LibDHCP::v6factories_; +OptionPtr +LibDHCP::optionFactory(Option::Universe u, + uint16_t type, + const OptionBuffer& buf) { + FactoryMap::iterator it; + if (u == Option::V4) { + it = v4factories_.find(type); + if (it == v4factories_.end()) { + isc_throw(BadValue, "factory function not registered " + "for DHCP v4 option type " << type); + } + } else if (u == Option::V6) { + it = v6factories_.find(type); + if (it == v6factories_.end()) { + isc_throw(BadValue, "factory function not registered " + "for DHCPv6 option type " << type); + } + } else { + isc_throw(BadValue, "invalid universe specified (expected " + "Option::V4 or Option::V6"); + } + return(it->second(u, type, buf)); +} + + size_t LibDHCP::unpackOptions6(const OptionBuffer& buf, isc::dhcp::Option::OptionCollection& options) { size_t offset = 0; diff --git a/src/lib/dhcp/libdhcp++.h b/src/lib/dhcp/libdhcp++.h index c7935c8ff3..c959609c61 100644 --- a/src/lib/dhcp/libdhcp++.h +++ b/src/lib/dhcp/libdhcp++.h @@ -25,6 +25,27 @@ namespace dhcp { class LibDHCP { public: + + /// Map of factory functions. + typedef std::map FactoryMap; + + /// @brief Factory function to create instance of option. + /// + /// Factory method creates instance of specified option. The option + /// to be created has to have corresponding factory function + /// registered with \ref LibDHCP::OptionFactoryRegister. + /// + /// @param u universe of the option (V4 or V6) + /// @param type option-type + /// @param buf option-buffer + /// @throw isc::InvalidOperation if there is no factory function + /// registered for specified option type. + /// @return instance of option. + static isc::dhcp::OptionPtr optionFactory(isc::dhcp::Option::Universe u, + uint16_t type, + const OptionBuffer& buf); + + /// Builds collection of options. /// /// Builds raw (on-wire) data for provided collection of options. @@ -84,10 +105,10 @@ public: Option::Factory * factory); protected: /// pointers to factories that produce DHCPv6 options - static std::map v4factories_; + static FactoryMap v4factories_; /// pointers to factories that produce DHCPv6 options - static std::map v6factories_; + static FactoryMap v6factories_; }; } diff --git a/src/lib/dhcp/option.cc b/src/lib/dhcp/option.cc index 0c71606b6c..fb441f93d1 100644 --- a/src/lib/dhcp/option.cc +++ b/src/lib/dhcp/option.cc @@ -29,6 +29,14 @@ using namespace isc::util; namespace isc { namespace dhcp { +OptionPtr +Option::factory(Option::Universe u, + uint16_t type, + const OptionBuffer& buf) { + return(LibDHCP::optionFactory(u, type, buf)); +} + + Option::Option(Universe u, uint16_t type) :universe_(u), type_(type) { diff --git a/src/lib/dhcp/option.h b/src/lib/dhcp/option.h index 066296704e..051b15dd4e 100644 --- a/src/lib/dhcp/option.h +++ b/src/lib/dhcp/option.h @@ -66,6 +66,23 @@ public: /// @return a pointer to a created option object typedef OptionPtr Factory(Option::Universe u, uint16_t type, const OptionBuffer& buf); + /// @brief Factory function to create instance of option. + /// + /// Factory method creates instance of specified option. The option + /// to be created has to have corresponding factory function + /// registered with \ref LibDHCP::OptionFactoryRegister. + /// + /// @param u universe of the option (V4 or V6) + /// @param type option-type + /// @param buf option-buffer + /// @throw isc::InvalidOperation if there is no factory function + /// registered for specified option type. + /// @return instance of option. + static OptionPtr factory(Option::Universe u, + uint16_t type, + const OptionBuffer& buf); + + /// @brief ctor, used for options constructed, usually during transmission /// /// @param u option universe (DHCPv4 or DHCPv6) diff --git a/src/lib/dhcp/tests/libdhcp++_unittest.cc b/src/lib/dhcp/tests/libdhcp++_unittest.cc index 7e18be6889..ac758bed9c 100644 --- a/src/lib/dhcp/tests/libdhcp++_unittest.cc +++ b/src/lib/dhcp/tests/libdhcp++_unittest.cc @@ -18,6 +18,8 @@ #include #include #include +#include +#include #include #include "config.h" @@ -31,6 +33,19 @@ class LibDhcpTest : public ::testing::Test { public: LibDhcpTest() { } + + /// @brief Generic factory function to create any option. + /// + /// Generic factory function to create any option. + /// + /// @param u universe (V4 or V6) + /// @param type option-type + /// @param buf option-buffer + static OptionPtr genericOptionFactory(Option::Universe u, uint16_t type, + const OptionBuffer& buf) { + Option* option = new Option(u, type, buf); + return OptionPtr(option); + } }; static const uint8_t packed[] = { @@ -41,6 +56,54 @@ static const uint8_t packed[] = { 1, 1, 0, 1, 114 // opt5 (5 bytes) }; +TEST(LibDhcpTest, optionFactory) { + OptionBuffer buf; + // Factory functions for specific options must be registered before + // they can be used to create options instances. Otherwise exception + // is rised. + EXPECT_THROW(LibDHCP::optionFactory(Option::V4, DHO_SUBNET_MASK, buf), + isc::BadValue); + + // Let's register some factory functions (two v4 and one v6 function). + // Registration may trigger exception if function for the specified + // option has been registered already. + ASSERT_NO_THROW( + LibDHCP::OptionFactoryRegister(Option::V4, DHO_SUBNET_MASK, + &LibDhcpTest::genericOptionFactory); + ); + ASSERT_NO_THROW( + LibDHCP::OptionFactoryRegister(Option::V4, DHO_TIME_OFFSET, + &LibDhcpTest::genericOptionFactory); + ); + ASSERT_NO_THROW( + LibDHCP::OptionFactoryRegister(Option::V6, D6O_CLIENTID, + &LibDhcpTest::genericOptionFactory); + ); + + // Invoke factory functions for all options (check if registration + // was successful). + OptionPtr opt_subnet_mask; + opt_subnet_mask = LibDHCP::optionFactory(Option::V4, + DHO_SUBNET_MASK, + buf); + // Check if non-NULL DHO_SUBNET_MASK option pointer has been returned. + EXPECT_TRUE(opt_subnet_mask); + + OptionPtr opt_time_offset; + opt_time_offset = LibDHCP::optionFactory(Option::V4, + DHO_TIME_OFFSET, + buf); + // Check if non-NULL DHO_TIME_OFFSET option pointer has been returned. + EXPECT_TRUE(opt_time_offset); + + OptionPtr opt_clientid; + opt_clientid = LibDHCP::optionFactory(Option::V6, + D6O_CLIENTID, + buf); + // Check if non-NULL D6O_CLIENTID option pointer has been returned. + EXPECT_TRUE(opt_clientid); +} + TEST(LibDhcpTest, packOptions6) { OptionBuffer buf(512); isc::dhcp::Option::OptionCollection opts; // list of options From 1bb01f6f6ca354f10def4d35850a782d2ae348a0 Mon Sep 17 00:00:00 2001 From: Marcin Siodelski Date: Wed, 1 Aug 2012 18:08:05 +0200 Subject: [PATCH 03/40] [1959] Use factory function to create option's instance. --- src/lib/dhcp/option.h | 17 +++++++++++++++++ tests/tools/perfdhcp/test_control.cc | 19 +++++++++++-------- tests/tools/perfdhcp/test_control.h | 2 +- 3 files changed, 29 insertions(+), 9 deletions(-) diff --git a/src/lib/dhcp/option.h b/src/lib/dhcp/option.h index 051b15dd4e..37069d6df5 100644 --- a/src/lib/dhcp/option.h +++ b/src/lib/dhcp/option.h @@ -82,6 +82,23 @@ public: uint16_t type, const OptionBuffer& buf); + /// @brief Factory function to create instance of option. + /// + /// Factory method creates instance of specified option. The option + /// to be created has to have corresponding factory function + /// registered with \ref LibDHCP::OptionFactoryRegister. + /// This method creates empty \ref OptionBuffer object. Use this + /// factory function if it is not needed to pass custom buffer. + /// + /// @param u universe of the option (V4 or V6) + /// @param type option-type + /// @throw isc::InvalidOperation if there is no factory function + /// registered for specified option type. + /// @return instance of option. + static OptionPtr factory(Option::Universe u, uint16_t type) { + return factory(u, type, OptionBuffer()); + } + /// @brief ctor, used for options constructed, usually during transmission /// diff --git a/tests/tools/perfdhcp/test_control.cc b/tests/tools/perfdhcp/test_control.cc index 728d4d58ec..8072e75461 100644 --- a/tests/tools/perfdhcp/test_control.cc +++ b/tests/tools/perfdhcp/test_control.cc @@ -58,14 +58,18 @@ TestControl::checkExitConditions() const { return(false); } -Pkt4* -TestControl::createDiscoverPkt4() { +boost::shared_ptr +TestControl::createDiscoverPkt4() const { const uint32_t transid = static_cast(random()); - Pkt4* pkt4 = new Pkt4(DHCPDISCOVER, transid); + boost::shared_ptr pkt4(new Pkt4(DHCPDISCOVER, transid)); + if (!pkt4) { + isc_throw(isc::Unexpected, "failed to create DISCOVER packet"); + } - OptionBuffer opt_request_list_buf(); - // createRequestListBuffer4(opt_request_list_buf); - return NULL; + OptionPtr request_list_option = + Option::factory(Option::V4, DHO_DHCP_PARAMETER_REQUEST_LIST); + pkt4->addOption(request_list_option); + return pkt4; } OptionPtr @@ -85,8 +89,7 @@ TestControl::factoryRequestList4(Option::Universe u, OptionBuffer buf_with_options(buf_array, buf_array + sizeof(buf_array)); Option* opt = new Option(u, type, buf); - opt->setData(buf_with_options.begin(), - buf_with_options.end()); + opt->setData(buf_with_options.begin(), buf_with_options.end()); return OptionPtr(opt); } diff --git a/tests/tools/perfdhcp/test_control.h b/tests/tools/perfdhcp/test_control.h index b1d7d27d32..ce5aa4c4b6 100644 --- a/tests/tools/perfdhcp/test_control.h +++ b/tests/tools/perfdhcp/test_control.h @@ -69,7 +69,7 @@ private: /// \return true if any of the exit conditions is fulfiled. bool checkExitConditions() const; - dhcp::Pkt4* createDiscoverPkt4(); + boost::shared_ptr createDiscoverPkt4() const; static dhcp::OptionPtr factoryRequestList4(dhcp::Option::Universe u, uint16_t type, From 4ae9b5ea7462f0d2a7c87c8b3466ca100a310c74 Mon Sep 17 00:00:00 2001 From: Marcin Siodelski Date: Fri, 3 Aug 2012 12:46:21 +0200 Subject: [PATCH 04/40] [1959] Moved sockets_ member in IfaceMgr to protected scope. --- src/lib/dhcp/iface_mgr.cc | 46 ++++++++++++++++++++++----------------- src/lib/dhcp/iface_mgr.h | 21 +++++++++++++++--- 2 files changed, 44 insertions(+), 23 deletions(-) diff --git a/src/lib/dhcp/iface_mgr.cc b/src/lib/dhcp/iface_mgr.cc index ae975d0820..99a3108e9a 100644 --- a/src/lib/dhcp/iface_mgr.cc +++ b/src/lib/dhcp/iface_mgr.cc @@ -66,6 +66,16 @@ IfaceMgr::Iface::Iface(const std::string& name, int ifindex) memset(mac_, 0, sizeof(mac_)); } +void +IfaceMgr::Iface::closeSockets() { + for (SocketCollection::iterator sock = sockets_.begin(); + sock != sockets_.end(); ++sock) { + cout << "Closing socket " << sock->sockfd_ << endl; + close(sock->sockfd_); + } + sockets_.clear(); +} + std::string IfaceMgr::Iface::getFullName() const { ostringstream tmp; @@ -154,15 +164,8 @@ IfaceMgr::IfaceMgr() void IfaceMgr::closeSockets() { for (IfaceCollection::iterator iface = ifaces_.begin(); iface != ifaces_.end(); ++iface) { - - for (SocketCollection::iterator sock = iface->sockets_.begin(); - sock != iface->sockets_.end(); ++sock) { - cout << "Closing socket " << sock->sockfd_ << endl; - close(sock->sockfd_); - } - iface->sockets_.clear(); + iface->closeSockets(); } - } IfaceMgr::~IfaceMgr() { @@ -561,8 +564,7 @@ int IfaceMgr::openSocket6(Iface& iface, const IOAddress& addr, uint16_t port) { struct sockaddr_in6 addr6; memset(&addr6, 0, sizeof(addr6)); addr6.sin6_family = AF_INET6; - addr6.sin6_port = htons(port); - if (addr.toText() != "::1") + addr6.sin6_port = htons(port); if (addr.toText() != "::1") addr6.sin6_scope_id = if_nametoindex(iface.getName().c_str()); memcpy(&addr6.sin6_addr, @@ -740,7 +742,6 @@ IfaceMgr::send(const Pkt6Ptr& pkt) { bool IfaceMgr::send(const Pkt4Ptr& pkt) { - Iface* iface = getIface(pkt->getIface()); if (!iface) { isc_throw(BadValue, "Unable to send Pkt4. Invalid interface (" @@ -817,8 +818,9 @@ IfaceMgr::receive4(uint32_t timeout) { /// provided set to indicated which sockets have something to read. for (iface = ifaces_.begin(); iface != ifaces_.end(); ++iface) { - for (SocketCollection::const_iterator s = iface->sockets_.begin(); - s != iface->sockets_.end(); ++s) { + const SocketCollection& socket_collection = iface->getSockets(); + for (SocketCollection::const_iterator s = socket_collection.begin(); + s != socket_collection.end(); ++s) { // Only deal with IPv4 addresses. if (s->addr_.getFamily() == AF_INET) { @@ -881,8 +883,9 @@ IfaceMgr::receive4(uint32_t timeout) { // Let's find out which interface/socket has the data for (iface = ifaces_.begin(); iface != ifaces_.end(); ++iface) { - for (SocketCollection::const_iterator s = iface->sockets_.begin(); - s != iface->sockets_.end(); ++s) { + const SocketCollection& socket_collection = iface->getSockets(); + for (SocketCollection::const_iterator s = socket_collection.begin(); + s != socket_collection.end(); ++s) { if (FD_ISSET(s->sockfd_, &sockets)) { candidate = &(*s); break; @@ -1010,8 +1013,9 @@ Pkt6Ptr IfaceMgr::receive6() { IfaceCollection::const_iterator iface = ifaces_.begin(); const SocketInfo* candidate = 0; while (iface != ifaces_.end()) { - for (SocketCollection::const_iterator s = iface->sockets_.begin(); - s != iface->sockets_.end(); ++s) { + const SocketCollection& socket_collection = iface->getSockets(); + for (SocketCollection::const_iterator s = socket_collection.begin(); + s != socket_collection.end(); ++s) { if (s->addr_.getFamily() != AF_INET6) { continue; } @@ -1122,11 +1126,12 @@ uint16_t IfaceMgr::getSocket(const isc::dhcp::Pkt6& pkt) { << pkt.getIface()); } + const SocketCollection& socket_collection = iface->getSockets(); SocketCollection::const_iterator s; - for (s = iface->sockets_.begin(); s != iface->sockets_.end(); ++s) { + for (s = socket_collection.begin(); s != socket_collection.end(); ++s) { if ((s->family_ == AF_INET6) && (!s->addr_.getAddress().to_v6().is_multicast())) { - return (s->sockfd_); + return (s->sockfd_);t } /// @todo: Add more checks here later. If remote address is /// not link-local, we can't use link local bound socket @@ -1144,8 +1149,9 @@ uint16_t IfaceMgr::getSocket(isc::dhcp::Pkt4 const& pkt) { << pkt.getIface()); } + const SocketCollection& socket_collection = iface->getSockets(); SocketCollection::const_iterator s; - for (s = iface->sockets_.begin(); s != iface->sockets_.end(); ++s) { + for (s = socket_collection.begin(); s != socket_collection.end(); ++s) { if (s->family_ == AF_INET) { return (s->sockfd_); } diff --git a/src/lib/dhcp/iface_mgr.h b/src/lib/dhcp/iface_mgr.h index a40f95c69d..4a2016c89b 100644 --- a/src/lib/dhcp/iface_mgr.h +++ b/src/lib/dhcp/iface_mgr.h @@ -89,6 +89,9 @@ public: /// @param ifindex interface index (unique integer identifier) Iface(const std::string& name, int ifindex); + /// @brief Closes all open sockets on interface + void closeSockets(); + /// @brief Returns full interface name as "ifname/ifindex" string. /// /// @return string with interface name @@ -192,11 +195,23 @@ public: /// @return true if there was such socket, false otherwise bool delSocket(uint16_t sockfd); - /// socket used to sending data - /// TODO: this should be protected - SocketCollection sockets_; + /// @brief Returns collection of all sockets added to interface. + /// + /// When new socket is created with @ref IfaceMgr::openSocket + /// it is added to sockets collection on particular interface. + /// If socket is opened by other means (e.g. function that does + /// not use @ref IfaceMgr::openSocket) it will not be available + /// in this collection. Note that functions like + /// @ref IfaceMgr::openSocketFromIface use + /// @ref IfaceMgr::openSocket internally. + /// + /// @return collection of sockets added to interface + const SocketCollection& getSockets() const { return sockets_; } protected: + /// socket used to sending data + SocketCollection sockets_; + /// network interface name std::string name_; From 1127bca7125f7fb94fe3340a86e086307f80a9e2 Mon Sep 17 00:00:00 2001 From: Marcin Siodelski Date: Fri, 3 Aug 2012 13:49:41 +0100 Subject: [PATCH 05/40] [1959] Implemented DHCPDISCOVER packet sending. --- tests/tools/perfdhcp/test_control.cc | 164 ++++++++++++++++-- tests/tools/perfdhcp/test_control.h | 35 +++- .../perfdhcp/tests/test_control_unittest.cc | 2 +- 3 files changed, 182 insertions(+), 19 deletions(-) diff --git a/tests/tools/perfdhcp/test_control.cc b/tests/tools/perfdhcp/test_control.cc index 8072e75461..de9aa1d727 100644 --- a/tests/tools/perfdhcp/test_control.cc +++ b/tests/tools/perfdhcp/test_control.cc @@ -19,9 +19,11 @@ #include -#include -#include #include +#include +#include +#include +#include #include "test_control.h" #include "command_options.h" @@ -30,10 +32,43 @@ using namespace boost; using namespace boost::posix_time; using namespace isc; using namespace isc::dhcp; +using namespace isc::asiolink; namespace isc { namespace perfdhcp { +TestControl::TestControlSocket::TestControlSocket(int socket) : + socket_(socket) { + initInterface(); +} + +TestControl::TestControlSocket::~TestControlSocket() { + IfaceMgr::instance().closeSockets(); +} + +void +TestControl::TestControlSocket::initInterface() { + const IfaceMgr::IfaceCollection& ifaces = + IfaceMgr::instance().getIfaces(); + for (IfaceMgr::IfaceCollection::const_iterator it = ifaces.begin(); + it != ifaces.end(); + ++it) { + const IfaceMgr::SocketCollection& socket_collection = + it->getSockets(); + for (IfaceMgr::SocketCollection::const_iterator s = + socket_collection.begin(); + s != socket_collection.end(); + ++s) { + if (s->sockfd_ == socket_) { + iface_ = it->getName(); + return; + } + } + } + isc_throw(BadValue, "interface for for specified socket " + "descriptor not found"); +} + TestControl& TestControl::instance() { static TestControl test_control; @@ -59,24 +94,37 @@ TestControl::checkExitConditions() const { } boost::shared_ptr -TestControl::createDiscoverPkt4() const { +TestControl::createDiscoverPkt4(const std::vector& mac_addr) const { const uint32_t transid = static_cast(random()); boost::shared_ptr pkt4(new Pkt4(DHCPDISCOVER, transid)); if (!pkt4) { isc_throw(isc::Unexpected, "failed to create DISCOVER packet"); } - OptionPtr request_list_option = - Option::factory(Option::V4, DHO_DHCP_PARAMETER_REQUEST_LIST); - pkt4->addOption(request_list_option); + if (HW_ETHER_LEN != mac_addr.size()) { + isc_throw(BadValue, "invalid MAC address size"); + } + pkt4->setHWAddr(HTYPE_ETHER, HW_ETHER_LEN, mac_addr); + + OptionBuffer buf_msg_type; + buf_msg_type.push_back(DHCPDISCOVER); + pkt4->addOption(Option::factory(Option::V4, DHO_DHCP_MESSAGE_TYPE, buf_msg_type)); + pkt4->addOption(Option::factory(Option::V4, DHO_DHCP_PARAMETER_REQUEST_LIST)); return pkt4; } +OptionPtr +TestControl::factoryGeneric4(Option::Universe u, + uint16_t type, + const OptionBuffer& buf) { + OptionPtr opt(new Option(u, type, buf)); + return opt; +} + OptionPtr TestControl::factoryRequestList4(Option::Universe u, uint16_t type, - const OptionBuffer& buf) -{ + const OptionBuffer& buf) { const uint8_t buf_array[] = { DHO_SUBNET_MASK, DHO_BROADCAST_ADDRESS, @@ -88,9 +136,26 @@ TestControl::factoryRequestList4(Option::Universe u, }; OptionBuffer buf_with_options(buf_array, buf_array + sizeof(buf_array)); - Option* opt = new Option(u, type, buf); + OptionPtr opt(new Option(u, type, buf)); opt->setData(buf_with_options.begin(), buf_with_options.end()); - return OptionPtr(opt); + return opt; +} + +const std::vector& +TestControl::generateMacAddress() { + CommandOptions& options = CommandOptions::instance(); + uint32_t clients_num = options.getClientsNum(); + if ((clients_num == 0) || (clients_num == 1)) { + return last_mac_address_; + } + for (std::vector::iterator it = last_mac_address_.end() - 1; + it >= last_mac_address_.begin(); + --it) { + if (++(*it) > 0) { + break; + } + } + return last_mac_address_; } uint64_t @@ -141,10 +206,57 @@ TestControl::getNextExchangesNum() const { return (0); } +int +TestControl::openSocket() const { + CommandOptions& options = CommandOptions::instance(); + std::string localname = options.getLocalName(); + std::string servername = options.getServerName(); + uint8_t family = AF_INET; + uint16_t port = 67; + int sock = 0; + if (options.getIpVersion() == 6) { + family = AF_INET6; + port = 547; + } + if (!localname.empty()) { + bool is_interface = false;; + try { + sock = IfaceMgr::instance().openSocketFromIface(localname, + port, + family); + is_interface = true; + } catch (...) { + // This is not fatal error. It may be the case that + // parameter given from command line is not interface + // name but local IP address. + } + if (!is_interface) { + IOAddress localaddr(localname); + // We don't catch exception here because parameter given + // must be either interface name or local address. If + // both attempts failed, we want exception to be emited. + sock = IfaceMgr::instance().openSocketFromAddress(localaddr, + port); + } + } else if (!servername.empty()) { + IOAddress remoteaddr(servername); + sock = IfaceMgr::instance().openSocketFromRemoteAddress(remoteaddr, + port); + } + if (sock <= 0) { + isc_throw(BadValue, "unable to open socket to communicate with " + "DHCP server"); + } + return sock; +} + void TestControl::registerOptionFactories4() const { static bool factories_registered = false; if (!factories_registered) { + LibDHCP::OptionFactoryRegister(Option::V4, + DHO_DHCP_MESSAGE_TYPE, + &TestControl::factoryGeneric4); LibDHCP::OptionFactoryRegister(Option::V4, DHO_DHCP_PARAMETER_REQUEST_LIST, &TestControl::factoryRequestList4); @@ -176,20 +288,32 @@ TestControl::registerOptionFactories() const { } } +void +TestControl::resetMacAddress() { + CommandOptions& options = CommandOptions::instance(); + std::vector mac_prefix(options.getMacPrefix()); + if (mac_prefix.size() != HW_ETHER_LEN) { + isc_throw(Unexpected, "MAC address prefix is invalid"); + } + std::swap(mac_prefix, last_mac_address_); +} + void TestControl::run() { sent_packets_0_ = 0; sent_packets_1_ = 0; CommandOptions& options = CommandOptions::instance(); // Ip version is not set ONLY in case the command options - // where not parsed. This surely means that parse() function + // were not parsed. This surely means that parse() function // was not called prior to starting the test. This is fatal // error. if (options.getIpVersion() == 0) { - isc_throw(InvalidOperation, "command options must be parsed before running " - "a test"); + isc_throw(InvalidOperation, + "command options must be parsed before running a test"); } registerOptionFactories(); + TestControlSocket socket(openSocket()); + resetMacAddress(); uint64_t packets_sent = 0; for (;;) { updateSendDue(); @@ -198,18 +322,26 @@ TestControl::run() { } uint64_t packets_due = getNextExchangesNum(); for (uint64_t i = packets_due; i > 0; --i) { - startExchange(); + startExchange(socket); ++packets_sent; cout << "Packets sent " << packets_sent << endl; } } - } void -TestControl::startExchange() { +TestControl::startExchange(const TestControlSocket& socket) { ++sent_packets_0_; last_sent_ = microsec_clock::universal_time(); + std::vector mac_address = generateMacAddress(); + boost::shared_ptr pkt4 = createDiscoverPkt4(mac_address); + pkt4->setIface(socket.getIface()); + try { + pkt4->pack(); + IfaceMgr::instance().send(pkt4); + } catch (const Exception& e) { + std::cout << e.what() << std::endl; + } } void diff --git a/tests/tools/perfdhcp/test_control.h b/tests/tools/perfdhcp/test_control.h index ce5aa4c4b6..676be013c7 100644 --- a/tests/tools/perfdhcp/test_control.h +++ b/tests/tools/perfdhcp/test_control.h @@ -33,6 +33,24 @@ namespace perfdhcp { /// class TestControl : public boost::noncopyable { public: + + class TestControlSocket { + public: + + TestControlSocket(const int socket); + ~TestControlSocket(); + + const std::string& getIface() const { return(iface_); } + + private: + void initInterface(); + + int socket_; + std::string iface_; + }; + + static const uint8_t HW_ETHER_LEN = 6; + /// TestControl is a singleton class. This method returns reference /// to its sole instance. /// @@ -69,12 +87,19 @@ private: /// \return true if any of the exit conditions is fulfiled. bool checkExitConditions() const; - boost::shared_ptr createDiscoverPkt4() const; + boost::shared_ptr + createDiscoverPkt4(const std::vector& mac_addr) const; + + static dhcp::OptionPtr factoryGeneric4(dhcp::Option::Universe u, + uint16_t type, + const dhcp::OptionBuffer& buf); static dhcp::OptionPtr factoryRequestList4(dhcp::Option::Universe u, uint16_t type, const dhcp::OptionBuffer& buf); + const std::vector& generateMacAddress(); + /// \brief Returns number of exchanges to be started. /// /// Method returns number of new exchanges to be started as soon @@ -85,15 +110,19 @@ private: /// \return number of exchanges to be started immediatelly. uint64_t getNextExchangesNum() const; + int openSocket() const; + void registerOptionFactories4() const; void registerOptionFactories6() const; void registerOptionFactories() const; + void resetMacAddress(); + /// \brief Start new exchange of DHCP messages. /// - void startExchange(); + void startExchange(const TestControlSocket& socket); /// \brief Update due time to initiate next chunk of exchanges. /// @@ -107,6 +136,8 @@ private: boost::posix_time::ptime last_sent_; ///< Indicates when the last exchange /// was initiated. + std::vector last_mac_address_;/// Least generated MAC address. + uint64_t sent_packets_0_; uint64_t sent_packets_1_; }; diff --git a/tests/tools/perfdhcp/tests/test_control_unittest.cc b/tests/tools/perfdhcp/tests/test_control_unittest.cc index 7a66b03fb3..2cc6b93f53 100644 --- a/tests/tools/perfdhcp/tests/test_control_unittest.cc +++ b/tests/tools/perfdhcp/tests/test_control_unittest.cc @@ -48,7 +48,7 @@ TEST_F(TestControlTest, Run) { // The command line is to run single test iteration and exit. // We have to declare argv as const walk around the problem // of deprecated conversion from string to char*. - const char* argv[] = { "perfdhcp", "-l", "eth0", "-r", "10", "-n", "1" }; + const char* argv[] = { "perfdhcp", "-l", "127.0.0.1", "-r", "10", "-n", "1" }; const int argc = sizeof(argv) / sizeof(argv[0]); CommandOptions& options = CommandOptions::instance(); From ca2a79864dbc56af29eb95e01141fa86b2be8b44 Mon Sep 17 00:00:00 2001 From: Marcin Siodelski Date: Fri, 3 Aug 2012 16:56:48 +0100 Subject: [PATCH 06/40] [1959] Documentation cleanup. --- tests/tools/perfdhcp/test_control.cc | 40 +++---- tests/tools/perfdhcp/test_control.h | 157 +++++++++++++++++++++++++-- 2 files changed, 162 insertions(+), 35 deletions(-) diff --git a/tests/tools/perfdhcp/test_control.cc b/tests/tools/perfdhcp/test_control.cc index de9aa1d727..ef47be6e5a 100644 --- a/tests/tools/perfdhcp/test_control.cc +++ b/tests/tools/perfdhcp/test_control.cc @@ -141,21 +141,26 @@ TestControl::factoryRequestList4(Option::Universe u, return opt; } -const std::vector& -TestControl::generateMacAddress() { +std::vector +TestControl::generateMacAddress() const { CommandOptions& options = CommandOptions::instance(); uint32_t clients_num = options.getClientsNum(); if ((clients_num == 0) || (clients_num == 1)) { - return last_mac_address_; + return options.getMacPrefix(); } - for (std::vector::iterator it = last_mac_address_.end() - 1; - it >= last_mac_address_.begin(); + std::vector mac_addr(options.getMacPrefix()); + uint32_t r = random(); + r %= clients_num + 1; + for (std::vector::iterator it = mac_addr.end() - 1; + it >= mac_addr.begin(); --it) { - if (++(*it) > 0) { + (*it) += r; + if (r < 256) { break; } + r >>= 8; } - return last_mac_address_; + return mac_addr; } uint64_t @@ -165,7 +170,7 @@ TestControl::getNextExchangesNum() const { uint64_t due_exchanges = 0; // Get current time. ptime now(microsec_clock::universal_time()); - // The due time indicates when we should start sening next chunk + // The due time indicates when we should start sending next chunk // of packets. If it is already due time, we should calculate // how many packets to send. if (now >= send_due_) { @@ -288,16 +293,6 @@ TestControl::registerOptionFactories() const { } } -void -TestControl::resetMacAddress() { - CommandOptions& options = CommandOptions::instance(); - std::vector mac_prefix(options.getMacPrefix()); - if (mac_prefix.size() != HW_ETHER_LEN) { - isc_throw(Unexpected, "MAC address prefix is invalid"); - } - std::swap(mac_prefix, last_mac_address_); -} - void TestControl::run() { sent_packets_0_ = 0; @@ -313,7 +308,6 @@ TestControl::run() { } registerOptionFactories(); TestControlSocket socket(openSocket()); - resetMacAddress(); uint64_t packets_sent = 0; for (;;) { updateSendDue(); @@ -336,12 +330,8 @@ TestControl::startExchange(const TestControlSocket& socket) { std::vector mac_address = generateMacAddress(); boost::shared_ptr pkt4 = createDiscoverPkt4(mac_address); pkt4->setIface(socket.getIface()); - try { - pkt4->pack(); - IfaceMgr::instance().send(pkt4); - } catch (const Exception& e) { - std::cout << e.what() << std::endl; - } + pkt4->pack(); + IfaceMgr::instance().send(pkt4); } void diff --git a/tests/tools/perfdhcp/test_control.h b/tests/tools/perfdhcp/test_control.h index 676be013c7..36280f2462 100644 --- a/tests/tools/perfdhcp/test_control.h +++ b/tests/tools/perfdhcp/test_control.h @@ -29,26 +29,66 @@ namespace perfdhcp { /// \brief Test Control class. /// -/// This class is responsible for running whole perfdhcp test. +/// This class is responsible for executing DHCP performance +/// test end to end. /// class TestControl : public boost::noncopyable { public: + /// \brief Socket wrapper class. + /// + /// This is wrapper class that holds descriptor of the socket + /// used to run DHCP test. All sockets created with \ref IfaceMgr + /// are closed in the destructor. This ensures that socket is + /// closed when the function that created the socket ends + /// (normally or when exception occurs). class TestControlSocket { public: + /// \brief Constructor of socket wrapper class. + /// + /// This constructor uses provided socket descriptor to + /// find the name of the interface where socket has been + /// bound to. + /// + /// \param socket socket descriptor. + /// \throw isc::BadValue if interface for specified + /// socket descriptor does not exist. TestControlSocket(const int socket); + + /// \brief Destriuctor of the socket wrapper class. + /// + /// Destructor closes all open sockets on all interfaces. + /// TODO: close only the socket being wrapped by this class. ~TestControlSocket(); + /// \brief Return name of the interface where socket is bound to. + /// + /// \return name of the interface where socket is bound to. const std::string& getIface() const { return(iface_); } private: + /// \brief Private default constructor. + /// + /// Default constructor is private to make sure that valid + /// socket descriptor is passed to create object. + TestControlSocket(); + + /// \brief Initialize name of the interface. + /// + /// This method intializes the name of the interface where + /// socket is bound to. This name can be later retreived by + /// client class to set interface name in DHCP packet objects. + /// + /// \throw isc::BadValue if interface for specified socket + /// descriptor does not exist. void initInterface(); - int socket_; - std::string iface_; + int socket_; ///< Socket descirptor. + std::string iface_; ///< Name of the interface. }; + /// \brief Length of the Ethernet HW address (MAC) in bytes. static const uint8_t HW_ETHER_LEN = 6; /// TestControl is a singleton class. This method returns reference @@ -87,18 +127,86 @@ private: /// \return true if any of the exit conditions is fulfiled. bool checkExitConditions() const; + /// \brief Create DHCPv4 DISCOVER packet. + /// + /// Create instance of DHCPv4 DISCOVER packet with ethernet + /// HW type and MAC address specified as parameter. The following + /// DHCP options are added to the packet: + /// - DHO_DHCP_MESSAGE_TYPE with DHCPDISCOVER message type value + /// - DHO_DHCP_PARAMETER_REQUEST_LIST with the following options + /// being requested from the server: + /// - DHO_SUBNET_MASK, + /// - DHO_BROADCAST_ADDRESS, + /// - DHO_TIME_OFFSET, + /// - DHO_ROUTERS, + /// - DHO_DOMAIN_NAME, + /// - DHO_DOMAIN_NAME_SERVERS, + /// - DHO_HOST_NAME. + /// + /// \param mac_addr MAC address to be set for the packet. MAC address + /// has to be exactly 6 octets long. + /// \throw isc::Unexpected if failed to create new packet instance. + /// \throw isc::BadValue if MAC address has invalid length. + /// \return insance of the DHCPv4 DISCOVER packet. boost::shared_ptr createDiscoverPkt4(const std::vector& mac_addr) const; + /// \brief Factory function to create generic option. + /// + /// Factory function is registered using \ref LibDHCP::OptionFactoryRegister. + /// Registered factory functions provide a way to create options of the + /// same type in the same way. When new option instance is needed the + /// corresponding factory function is called to create it. This is done + /// by calling \ref Option::factory with DHCP message type specified as + /// one of parameters. Some of the parameters passed to factory function + /// may be ignored (e.g. option buffer). For generic option however, factory + /// function creates option using contents of the buffer. + /// + /// \param u universe (V6 or V4). + /// \param type option-type. + /// \param buf option-buffer. + /// \return instance o the generic option. static dhcp::OptionPtr factoryGeneric4(dhcp::Option::Universe u, uint16_t type, const dhcp::OptionBuffer& buf); - static dhcp::OptionPtr factoryRequestList4(dhcp::Option::Universe u, + /// \brief Factory function to create DHCPv4 Request List option. + /// + /// Factory function is registered using \ref LibDHCP::OptionFactoryRegister. + /// Registered factory functions provide a way to create options of the + /// same type in the same way. When new option instance is needed the + /// corresponding factory function is called to create it. This is done + /// by calling \ref Option::factory with DHCP message type specified as + /// one of parameters. This factory function ignores contents of the + /// buffer provided and creates option buffer internally with the following + /// list of requested options: + /// - DHO_SUBNET_MASK, + /// - DHO_BROADCAST_ADDRESS, + /// - DHO_TIME_OFFSET, + /// - DHO_ROUTERS, + /// - DHO_DOMAIN_NAME, + /// - DHO_DOMAIN_NAME_SERVERS, + /// - DHO_HOST_NAME. + /// + /// \param u universe (V6 or V4). + /// \param type option-type. + /// \param buf option-buffer. + /// \return instance o the generic option. + static dhcp::OptionPtr factoryRequestList4(dhcp::Option::Universe u, uint16_t type, const dhcp::OptionBuffer& buf); - const std::vector& generateMacAddress(); + /// \brief Generate MAC address. + /// + /// This method generates MAC address. The number of unique + /// MAC addresses it can generate is determined by the number + /// simulated DHCP clients specified from command line. It uses + /// \ref CommandOptions object to retrieve number of clients. + /// Based on this the random value is generated and added to + /// the MAC address prefix (default MAC address). + /// + /// \return generated MAC address. + std::vector generateMacAddress() const; /// \brief Returns number of exchanges to be started. /// @@ -110,18 +218,49 @@ private: /// \return number of exchanges to be started immediatelly. uint64_t getNextExchangesNum() const; + /// \brief Open socket to communicate with DHCP server. + /// + /// Method opens socket and binds it to local address. Function will + /// can use either interface name, local address or server address + /// to create a socket, depending on what is available (specified + /// from the command line). If socket can't be created for any + /// reason, exception is thrown. + /// + /// \throw isc::BadValue if socket can't be created. + /// \return socket descriptor. int openSocket() const; + /// \brief Register option factory functions for DHCPv4 + /// + /// Method registers option factory functions for DHCPv4. + /// These functions are called to create instances of DHCPv4 + /// options. Call \ref Option::factory to invoke factory + /// function for particular option. Don't use this function directly. + /// Use \ref registerOptionFactories instead. void registerOptionFactories4() const; + /// \brief Register option factory functions for DHCPv6 + /// + /// Method registers option factory functions for DHCPv6. + /// These functions are called to create instances of DHCPv6 + /// options. Call \ref Option::factory to invoke factory + /// function for particular option. Don't use this function directly. + /// Use \ref registerOptionFactories instead. void registerOptionFactories6() const; - void registerOptionFactories() const; - - void resetMacAddress(); + /// \brief Register option factory functions for DHCPv4 or DHCPv6. + /// + /// Method registers option factory functions for DHCPv4 or DHCPv6, + /// depending in whch mode test is currently running. + void registerOptionFactories() const; /// \brief Start new exchange of DHCP messages. /// + /// Method starts new DHCP exchange by sending new DHCPv4 DISCOVER + /// or DHCPv6 SOLICIT packet to the server. + /// + /// \param socket socket used to send DHCP message. + /// \throw isc::Unexpected if failed to create or send packet void startExchange(const TestControlSocket& socket); /// \brief Update due time to initiate next chunk of exchanges. @@ -136,8 +275,6 @@ private: boost::posix_time::ptime last_sent_; ///< Indicates when the last exchange /// was initiated. - std::vector last_mac_address_;/// Least generated MAC address. - uint64_t sent_packets_0_; uint64_t sent_packets_1_; }; From cc5e14f7a89e3d5fc6211d156d631aa37b865597 Mon Sep 17 00:00:00 2001 From: Marcin Siodelski Date: Sat, 4 Aug 2012 10:41:46 +0100 Subject: [PATCH 07/40] [1959] Receive packets from the server. --- tests/tools/perfdhcp/test_control.cc | 19 ++++++++++++++++++- tests/tools/perfdhcp/test_control.h | 2 ++ 2 files changed, 20 insertions(+), 1 deletion(-) diff --git a/tests/tools/perfdhcp/test_control.cc b/tests/tools/perfdhcp/test_control.cc index ef47be6e5a..888a992700 100644 --- a/tests/tools/perfdhcp/test_control.cc +++ b/tests/tools/perfdhcp/test_control.cc @@ -252,7 +252,21 @@ TestControl::openSocket() const { isc_throw(BadValue, "unable to open socket to communicate with " "DHCP server"); } - return sock; + return(sock); +} + +void +TestControl::receivePackets() { + int timeout = 0; + bool receiving = true; + while (receiving) { + Pkt4Ptr pkt4 = IfaceMgr::instance().receive4(timeout); + if (!pkt4) { + receiving = false; + } else { + std::cout << "Received packet" << std::endl; + } + } } void @@ -315,6 +329,9 @@ TestControl::run() { break; } uint64_t packets_due = getNextExchangesNum(); + + receivePackets(); + for (uint64_t i = packets_due; i > 0; --i) { startExchange(socket); ++packets_sent; diff --git a/tests/tools/perfdhcp/test_control.h b/tests/tools/perfdhcp/test_control.h index 36280f2462..cb1e641bbd 100644 --- a/tests/tools/perfdhcp/test_control.h +++ b/tests/tools/perfdhcp/test_control.h @@ -230,6 +230,8 @@ private: /// \return socket descriptor. int openSocket() const; + void receivePackets(); + /// \brief Register option factory functions for DHCPv4 /// /// Method registers option factory functions for DHCPv4. From 2e6923cefcd6582ccbf9ef859ff92a91665b90ef Mon Sep 17 00:00:00 2001 From: Marcin Siodelski Date: Mon, 6 Aug 2012 15:07:07 +0200 Subject: [PATCH 08/40] [1959] Set packet v4 defaults correctly --- tests/tools/perfdhcp/test_control.cc | 66 +++++++++++++---------- tests/tools/perfdhcp/test_control.h | 81 +++++++++++++++------------- 2 files changed, 80 insertions(+), 67 deletions(-) diff --git a/tests/tools/perfdhcp/test_control.cc b/tests/tools/perfdhcp/test_control.cc index 888a992700..6e793cd578 100644 --- a/tests/tools/perfdhcp/test_control.cc +++ b/tests/tools/perfdhcp/test_control.cc @@ -38,8 +38,9 @@ namespace isc { namespace perfdhcp { TestControl::TestControlSocket::TestControlSocket(int socket) : - socket_(socket) { - initInterface(); + socket_(socket), + addr_("127.0.0.1") { + initSocketData(); } TestControl::TestControlSocket::~TestControlSocket() { @@ -47,7 +48,7 @@ TestControl::TestControlSocket::~TestControlSocket() { } void -TestControl::TestControlSocket::initInterface() { +TestControl::TestControlSocket::initSocketData() { const IfaceMgr::IfaceCollection& ifaces = IfaceMgr::instance().getIfaces(); for (IfaceMgr::IfaceCollection::const_iterator it = ifaces.begin(); @@ -61,6 +62,7 @@ TestControl::TestControlSocket::initInterface() { ++s) { if (s->sockfd_ == socket_) { iface_ = it->getName(); + addr_ = s->addr_; return; } } @@ -93,26 +95,6 @@ TestControl::checkExitConditions() const { return(false); } -boost::shared_ptr -TestControl::createDiscoverPkt4(const std::vector& mac_addr) const { - const uint32_t transid = static_cast(random()); - boost::shared_ptr pkt4(new Pkt4(DHCPDISCOVER, transid)); - if (!pkt4) { - isc_throw(isc::Unexpected, "failed to create DISCOVER packet"); - } - - if (HW_ETHER_LEN != mac_addr.size()) { - isc_throw(BadValue, "invalid MAC address size"); - } - pkt4->setHWAddr(HTYPE_ETHER, HW_ETHER_LEN, mac_addr); - - OptionBuffer buf_msg_type; - buf_msg_type.push_back(DHCPDISCOVER); - pkt4->addOption(Option::factory(Option::V4, DHO_DHCP_MESSAGE_TYPE, buf_msg_type)); - pkt4->addOption(Option::factory(Option::V4, DHO_DHCP_PARAMETER_REQUEST_LIST)); - return pkt4; -} - OptionPtr TestControl::factoryGeneric4(Option::Universe u, uint16_t type, @@ -149,8 +131,11 @@ TestControl::generateMacAddress() const { return options.getMacPrefix(); } std::vector mac_addr(options.getMacPrefix()); + if (mac_addr.size() != HW_ETHER_LEN) { + isc_throw(BadValue, "invalid MAC address prefix specified"); + } uint32_t r = random(); - r %= clients_num + 1; + r %= clients_num; for (std::vector::iterator it = mac_addr.end() - 1; it >= mac_addr.begin(); --it) { @@ -333,7 +318,7 @@ TestControl::run() { receivePackets(); for (uint64_t i = packets_due; i > 0; --i) { - startExchange(socket); + sendDiscover4(socket); ++packets_sent; cout << "Packets sent " << packets_sent << endl; } @@ -341,20 +326,43 @@ TestControl::run() { } void -TestControl::startExchange(const TestControlSocket& socket) { +TestControl::sendDiscover4(const TestControlSocket& socket) { ++sent_packets_0_; last_sent_ = microsec_clock::universal_time(); std::vector mac_address = generateMacAddress(); - boost::shared_ptr pkt4 = createDiscoverPkt4(mac_address); - pkt4->setIface(socket.getIface()); + const uint32_t transid = static_cast(random()); + boost::shared_ptr pkt4(new Pkt4(DHCPDISCOVER, transid)); + if (!pkt4) { + isc_throw(Unexpected, "failed to create DISCOVER packet"); + } + OptionBuffer buf_msg_type; + buf_msg_type.push_back(DHCPDISCOVER); + pkt4->addOption(Option::factory(Option::V4, DHO_DHCP_MESSAGE_TYPE, + buf_msg_type)); + pkt4->addOption(Option::factory(Option::V4, + DHO_DHCP_PARAMETER_REQUEST_LIST)); + + setDefaults4(socket, pkt4); pkt4->pack(); IfaceMgr::instance().send(pkt4); } +void +TestControl::setDefaults4(const TestControlSocket &socket, + const boost::shared_ptr& pkt) { + CommandOptions& options = CommandOptions::instance(); + pkt->setIface(socket.getIface()); + pkt->setLocalPort(DHCP4_CLIENT_PORT); + pkt->setRemotePort(DHCP4_SERVER_PORT); + pkt->setRemoteAddr(IOAddress(options.getServerName())); + pkt->setGiaddr(IOAddress(socket.getAddress())); + pkt->setHops(1); +} + void TestControl::updateSendDue() { // If default constructor was called, this should not happen but - // if somebody has changed default constructor it is better to + // if somebody has cw/e August 3, 2012hanged default constructor it is better to // keep this check. if (last_sent_.is_not_a_date_time()) { isc_throw(Unexpected, "time of last sent packet not initialized"); diff --git a/tests/tools/perfdhcp/test_control.h b/tests/tools/perfdhcp/test_control.h index cb1e641bbd..8d6c4f3c81 100644 --- a/tests/tools/perfdhcp/test_control.h +++ b/tests/tools/perfdhcp/test_control.h @@ -67,6 +67,11 @@ public: /// \return name of the interface where socket is bound to. const std::string& getIface() const { return(iface_); } + /// \brief Return address where socket is bound to. + /// + /// \return address where socket is bound to. + const asiolink::IOAddress& getAddress() const { return(addr_); } + private: /// \brief Private default constructor. /// @@ -74,18 +79,18 @@ public: /// socket descriptor is passed to create object. TestControlSocket(); - /// \brief Initialize name of the interface. + /// \brief Initialize socket data. /// - /// This method intializes the name of the interface where - /// socket is bound to. This name can be later retreived by - /// client class to set interface name in DHCP packet objects. + /// This method initializes members of the class that Interface + /// Manager holds: interface name, local address. /// /// \throw isc::BadValue if interface for specified socket /// descriptor does not exist. - void initInterface(); + void initSocketData(); - int socket_; ///< Socket descirptor. - std::string iface_; ///< Name of the interface. + int socket_; ///< Socket descirptor. + std::string iface_; ///< Name of the interface. + asiolink::IOAddress addr_; ///< Address bound. }; /// \brief Length of the Ethernet HW address (MAC) in bytes. @@ -127,30 +132,6 @@ private: /// \return true if any of the exit conditions is fulfiled. bool checkExitConditions() const; - /// \brief Create DHCPv4 DISCOVER packet. - /// - /// Create instance of DHCPv4 DISCOVER packet with ethernet - /// HW type and MAC address specified as parameter. The following - /// DHCP options are added to the packet: - /// - DHO_DHCP_MESSAGE_TYPE with DHCPDISCOVER message type value - /// - DHO_DHCP_PARAMETER_REQUEST_LIST with the following options - /// being requested from the server: - /// - DHO_SUBNET_MASK, - /// - DHO_BROADCAST_ADDRESS, - /// - DHO_TIME_OFFSET, - /// - DHO_ROUTERS, - /// - DHO_DOMAIN_NAME, - /// - DHO_DOMAIN_NAME_SERVERS, - /// - DHO_HOST_NAME. - /// - /// \param mac_addr MAC address to be set for the packet. MAC address - /// has to be exactly 6 octets long. - /// \throw isc::Unexpected if failed to create new packet instance. - /// \throw isc::BadValue if MAC address has invalid length. - /// \return insance of the DHCPv4 DISCOVER packet. - boost::shared_ptr - createDiscoverPkt4(const std::vector& mac_addr) const; - /// \brief Factory function to create generic option. /// /// Factory function is registered using \ref LibDHCP::OptionFactoryRegister. @@ -205,6 +186,8 @@ private: /// Based on this the random value is generated and added to /// the MAC address prefix (default MAC address). /// + /// \throw isc::BadValue if MAC address prefix (default or specified + /// from the command line) has invalid size (expected 6 octets). /// \return generated MAC address. std::vector generateMacAddress() const; @@ -254,16 +237,38 @@ private: /// /// Method registers option factory functions for DHCPv4 or DHCPv6, /// depending in whch mode test is currently running. - void registerOptionFactories() const; + void registerOptionFactories() const; - /// \brief Start new exchange of DHCP messages. + /// \brief Send DHCPv4 DISCOVER message. /// - /// Method starts new DHCP exchange by sending new DHCPv4 DISCOVER - /// or DHCPv6 SOLICIT packet to the server. + /// Method creates and sends DHCPv4 DISCOVER message to the server + /// with the following options: + /// - MESSAGE_TYPE set to DHCPDISCOVER + /// - PARAMETER_REQUEST_LIST with the same list of requested options + /// as described in \ref factoryRequestList. + /// The transaction id and MAC address are randomly generated for + /// the message. Range of unique MAC addresses generated depends + /// on the number of clients specified from the command line. /// - /// \param socket socket used to send DHCP message. - /// \throw isc::Unexpected if failed to create or send packet - void startExchange(const TestControlSocket& socket); + /// \param socket socket to be used to send the message. + /// \throw isc::Unexpected if failed to create new packet instance. + /// \throw isc::BadValue if MAC address has invalid length. + void sendDiscover4(const TestControlSocket &socket); + + /// \brief Set default DHCPv4 packet data. + /// + /// This method sets default data on the DHCPv4 packet: + /// - interface name, + /// - local port = 68 (DHCP client port), + /// - remote port = 67 (DHCP server port), + /// - server's address, + /// - GIADDR = local address where socket is bound to, + /// - hops = 1 (pretending that we are a relay) + /// + /// \param socket socket used to send the packet. + /// \param pkt packet to be configured. + void setDefaults4(const TestControlSocket& socket, + const boost::shared_ptr& pkt); /// \brief Update due time to initiate next chunk of exchanges. /// From e5fc094ff9c928e8f7761de883bfa0ad34cf86c2 Mon Sep 17 00:00:00 2001 From: Marcin Siodelski Date: Tue, 7 Aug 2012 14:43:09 +0200 Subject: [PATCH 09/40] [1959] Enable use of broadcast remote address. --- src/lib/dhcp/iface_mgr.cc | 23 ++++++++++++++++++++--- 1 file changed, 20 insertions(+), 3 deletions(-) diff --git a/src/lib/dhcp/iface_mgr.cc b/src/lib/dhcp/iface_mgr.cc index 99a3108e9a..335d848cd1 100644 --- a/src/lib/dhcp/iface_mgr.cc +++ b/src/lib/dhcp/iface_mgr.cc @@ -496,11 +496,28 @@ IfaceMgr::getLocalAddress(const IOAddress& remote_addr, const uint16_t port) { asio::io_service io_service; asio::ip::udp::socket sock(io_service); - // Try to connect to remote endpoint and check if attempt is successful. asio::error_code err_code; + // If remote address is broadcast address we have to + // allow this on the socket. + if (remote_addr.getAddress().is_v4() && + (remote_addr == IOAddress("255.255.255.255"))) { + // Socket has to be open prior to setting the broadcast + // option. Otherwise set_option will complain about + // bad file descriptor. + sock.open(asio::ip::udp::v4(), err_code); + if (err_code) { + isc_throw(Unexpected, "failed to open UDPv4 socket"); + } + sock.set_option(asio::socket_base::broadcast(true), err_code); + if (err_code) { + isc_throw(Unexpected, "failed to enable broadcast on the socket"); + } + } + + // Try to connect to remote endpoint and check if attempt is successful. sock.connect(remote_endpoint->getASIOEndpoint(), err_code); if (err_code) { - isc_throw(Unexpected,"Failed to connect to remote endpoint."); + isc_throw(Unexpected,"failed to connect to remote endpoint."); } // Once we are connected socket object holds local endpoint. @@ -1131,7 +1148,7 @@ uint16_t IfaceMgr::getSocket(const isc::dhcp::Pkt6& pkt) { for (s = socket_collection.begin(); s != socket_collection.end(); ++s) { if ((s->family_ == AF_INET6) && (!s->addr_.getAddress().to_v6().is_multicast())) { - return (s->sockfd_);t + return (s->sockfd_); } /// @todo: Add more checks here later. If remote address is /// not link-local, we can't use link local bound socket From 9dc528fb9ce46478f415722abfb9068d264154ce Mon Sep 17 00:00:00 2001 From: Marcin Siodelski Date: Tue, 7 Aug 2012 14:45:06 +0200 Subject: [PATCH 10/40] [1959] Enable broadcast addresses for perfdhcp's DHCPv4 mode. --- tests/tools/perfdhcp/command_options.cc | 32 +- tests/tools/perfdhcp/command_options.h | 7 + tests/tools/perfdhcp/test_control.cc | 37 ++- tests/tools/perfdhcp/test_control.h | 5 +- .../tests/command_options_unittest.cc | 284 ++++++++++++------ 5 files changed, 261 insertions(+), 104 deletions(-) diff --git a/tests/tools/perfdhcp/command_options.cc b/tests/tools/perfdhcp/command_options.cc index 09393bbc3d..55380d086b 100644 --- a/tests/tools/perfdhcp/command_options.cc +++ b/tests/tools/perfdhcp/command_options.cc @@ -21,8 +21,8 @@ #include #include -#include "exceptions/exceptions.h" - +#include +#include #include "command_options.h" using namespace std; @@ -219,6 +219,7 @@ CommandOptions::initialize(int argc, char** argv) { case 'l': localname_ = std::string(optarg); + initIsInterface(); break; case 'L': @@ -351,7 +352,21 @@ CommandOptions::initialize(int argc, char** argv) { } } - // TODO handle -l option with IfaceManager when it is created + // Handle the local '-l' address/interface + if (!localname_.empty()) { + if (server_name_.empty()) { + if (is_interface_ && (ipversion_ == 4)) { + broadcast_ = 1; + server_name_ = "255.255.255.255"; + } else if (is_interface_ && (ipversion_ == 6)) { + server_name_ = "FF02::1:2"; + } + } + } + if (server_name_.empty()) { + isc_throw(InvalidParameter, + "without an inteface server is required"); + } } void @@ -376,6 +391,17 @@ CommandOptions::initClientsNum() { } } +void +CommandOptions::initIsInterface() { + is_interface_ = false; + if (!localname_.empty()) { + dhcp::IfaceMgr& iface_mgr = dhcp::IfaceMgr::instance(); + if (iface_mgr.getIface(localname_) != NULL) { + is_interface_ = true; + } + } +} + void CommandOptions::decodeBase(const std::string& base) { std::string b(base); diff --git a/tests/tools/perfdhcp/command_options.h b/tests/tools/perfdhcp/command_options.h index 9196857d7c..459efa598b 100644 --- a/tests/tools/perfdhcp/command_options.h +++ b/tests/tools/perfdhcp/command_options.h @@ -301,6 +301,13 @@ private: /// \throw InvalidParameter if -R is wrong void initClientsNum(); + /// \brief Sets value indicating if interface name was given. + /// + /// Method checks if the command line argument given with + /// '-l' option is the interface name. The is_interface_ member + /// is set accordingly. + void initIsInterface(); + /// \brief Decodes base provided with -b /// /// Function decodes argument of -b switch, which diff --git a/tests/tools/perfdhcp/test_control.cc b/tests/tools/perfdhcp/test_control.cc index 6e793cd578..c050c9f0a6 100644 --- a/tests/tools/perfdhcp/test_control.cc +++ b/tests/tools/perfdhcp/test_control.cc @@ -208,27 +208,24 @@ TestControl::openSocket() const { family = AF_INET6; port = 547; } + // Local name is specified along with '-l' option. + // It may point to interface name or local address. if (!localname.empty()) { - bool is_interface = false;; - try { + // CommandOptions should be already aware wether local name + // is interface name or address because it uses IfaceMgr to + // scan interfaces and get's their names. + if (options.isInterface()) { sock = IfaceMgr::instance().openSocketFromIface(localname, port, family); - is_interface = true; - } catch (...) { - // This is not fatal error. It may be the case that - // parameter given from command line is not interface - // name but local IP address. - } - if (!is_interface) { + } else { IOAddress localaddr(localname); - // We don't catch exception here because parameter given - // must be either interface name or local address. If - // both attempts failed, we want exception to be emited. sock = IfaceMgr::instance().openSocketFromAddress(localaddr, port); } } else if (!servername.empty()) { + // If only server name is given we will need to try to resolve + // the local address to bind socket to based on remote address. IOAddress remoteaddr(servername); sock = IfaceMgr::instance().openSocketFromRemoteAddress(remoteaddr, port); @@ -237,6 +234,20 @@ TestControl::openSocket() const { isc_throw(BadValue, "unable to open socket to communicate with " "DHCP server"); } + + // IfaceMgr does not set broadcast option on the socket. We rely + // on CommandOptions object to find out if socket has to have + // broadcast enabled. + if ((options.getIpVersion() == 4) && options.isBroadcast()) { + int broadcast_enable = 1; + int ret = setsockopt(sock, SOL_SOCKET, SO_BROADCAST, + &broadcast_enable, sizeof(broadcast_enable)); + if (ret < 0) { + isc_throw(InvalidOperation, + "unable to set broadcast option on the socket"); + } + } + return(sock); } @@ -362,7 +373,7 @@ TestControl::setDefaults4(const TestControlSocket &socket, void TestControl::updateSendDue() { // If default constructor was called, this should not happen but - // if somebody has cw/e August 3, 2012hanged default constructor it is better to + // if somebody has changed default constructor it is better to // keep this check. if (last_sent_.is_not_a_date_time()) { isc_throw(Unexpected, "time of last sent packet not initialized"); diff --git a/tests/tools/perfdhcp/test_control.h b/tests/tools/perfdhcp/test_control.h index 8d6c4f3c81..bb1f283712 100644 --- a/tests/tools/perfdhcp/test_control.h +++ b/tests/tools/perfdhcp/test_control.h @@ -209,7 +209,10 @@ private: /// from the command line). If socket can't be created for any /// reason, exception is thrown. /// - /// \throw isc::BadValue if socket can't be created. + /// \throw isc::BadValue if socket can't be created for given + /// interface, local address or remote address. + /// \throw isc::InvalidOperation if broadcast option can't be + /// set for the socket. /// \return socket descriptor. int openSocket() const; diff --git a/tests/tools/perfdhcp/tests/command_options_unittest.cc b/tests/tools/perfdhcp/tests/command_options_unittest.cc index 8e1053dc30..669d792bb5 100644 --- a/tests/tools/perfdhcp/tests/command_options_unittest.cc +++ b/tests/tools/perfdhcp/tests/command_options_unittest.cc @@ -17,9 +17,10 @@ #include #include -#include "../command_options.h" +#include +#include -#include "exceptions/exceptions.h" +#include "../command_options.h" using namespace std; using namespace isc; @@ -62,7 +63,7 @@ protected: /// Check if initialized values are correct void checkDefaults() { CommandOptions& opt = CommandOptions::instance(); - process("perfdhcp"); + process("perfdhcp 192.168.0.1"); EXPECT_EQ(4, opt.getIpVersion()); EXPECT_EQ(CommandOptions::DORA_SARR, opt.getExchangeMode()); EXPECT_EQ(0, opt.getRate()); @@ -104,7 +105,7 @@ protected: EXPECT_GT(0, opt.getRequestedIpOffset()); EXPECT_EQ("", opt.getDiags()); EXPECT_EQ("", opt.getWrapped()); - EXPECT_EQ("", opt.getServerName()); + EXPECT_EQ("192.168.0.1", opt.getServerName()); } /// \brief Split string to array of C-strings @@ -145,79 +146,90 @@ protected: }; TEST_F(CommandOptionsTest, Defaults) { - process("perfdhcp"); + process("perfdhcp all"); checkDefaults(); } TEST_F(CommandOptionsTest, UseFirst) { CommandOptions& opt = CommandOptions::instance(); - process("perfdhcp -1 -B -l ethx"); + process("perfdhcp -1 -B -l ethx all"); EXPECT_TRUE(opt.isUseFirst()); } TEST_F(CommandOptionsTest, IpVersion) { CommandOptions& opt = CommandOptions::instance(); - process("perfdhcp -6 -l ethx -c -i"); + process("perfdhcp -6 -l ethx -c -i all"); EXPECT_EQ(6, opt.getIpVersion()); EXPECT_EQ("ethx", opt.getLocalName()); EXPECT_TRUE(opt.isRapidCommit()); EXPECT_FALSE(opt.isBroadcast()); - process("perfdhcp -4 -B -l ethx"); + process("perfdhcp -4 -B -l ethx all"); EXPECT_EQ(4, opt.getIpVersion()); EXPECT_TRUE(opt.isBroadcast()); EXPECT_FALSE(opt.isRapidCommit()); // Negative test cases // -4 and -6 must not coexist - EXPECT_THROW(process("perfdhcp -4 -6 -l ethx"), isc::InvalidParameter); + EXPECT_THROW(process("perfdhcp -4 -6 -l ethx all"), isc::InvalidParameter); // -6 and -B must not coexist - EXPECT_THROW(process("perfdhcp -6 -B -l ethx"), isc::InvalidParameter); + EXPECT_THROW(process("perfdhcp -6 -B -l ethx all"), isc::InvalidParameter); // -c and -4 (default) must not coexist - EXPECT_THROW(process("perfdhcp -c -l ethx"), isc::InvalidParameter); + EXPECT_THROW(process("perfdhcp -c -l ethx all"), isc::InvalidParameter); } TEST_F(CommandOptionsTest, Rate) { CommandOptions& opt = CommandOptions::instance(); - process("perfdhcp -4 -r 10 -l ethx"); + process("perfdhcp -4 -r 10 -l ethx all"); EXPECT_EQ(10, opt.getRate()); // Negative test cases // Rate must not be 0 - EXPECT_THROW(process("perfdhcp -4 -r 0 -l ethx"), isc::InvalidParameter); + EXPECT_THROW(process("perfdhcp -4 -r 0 -l ethx all"), + isc::InvalidParameter); // -r must be specified to use -n, -p and -D - EXPECT_THROW(process("perfdhcp -6 -t 5 -l ethx"), isc::InvalidParameter); - EXPECT_THROW(process("perfdhcp -4 -n 150 -l ethx"), isc::InvalidParameter); - EXPECT_THROW(process("perfdhcp -6 -p 120 -l ethx"), isc::InvalidParameter); - EXPECT_THROW(process("perfdhcp -4 -D 1400 -l ethx"), isc::InvalidParameter); + EXPECT_THROW(process("perfdhcp -6 -t 5 -l ethx all"), + isc::InvalidParameter); + EXPECT_THROW(process("perfdhcp -4 -n 150 -l ethx all"), + isc::InvalidParameter); + EXPECT_THROW(process("perfdhcp -6 -p 120 -l ethx all"), + isc::InvalidParameter); + EXPECT_THROW(process("perfdhcp -4 -D 1400 -l ethx all"), + isc::InvalidParameter); } TEST_F(CommandOptionsTest, ReportDelay) { CommandOptions& opt = CommandOptions::instance(); - process("perfdhcp -r 100 -t 17 -l ethx"); + process("perfdhcp -r 100 -t 17 -l ethx all"); EXPECT_EQ(17, opt.getReportDelay()); // Negative test cases // -t must be positive integer - EXPECT_THROW(process("perfdhcp -r 10 -t -8 -l ethx"), isc::InvalidParameter); - EXPECT_THROW(process("perfdhcp -r 10 -t 0 -l ethx"), isc::InvalidParameter); - EXPECT_THROW(process("perfdhcp -r 10 -t s -l ethx"), isc::InvalidParameter); + EXPECT_THROW(process("perfdhcp -r 10 -t -8 -l ethx all"), + isc::InvalidParameter); + EXPECT_THROW(process("perfdhcp -r 10 -t 0 -l ethx all"), + isc::InvalidParameter); + EXPECT_THROW(process("perfdhcp -r 10 -t s -l ethx all"), + isc::InvalidParameter); } TEST_F(CommandOptionsTest, ClientsNum) { CommandOptions& opt = CommandOptions::instance(); - process("perfdhcp -R 200 -l ethx"); + process("perfdhcp -R 200 -l ethx all"); EXPECT_EQ(200, opt.getClientsNum()); - process("perfdhcp -R 0 -l ethx"); + process("perfdhcp -R 0 -l ethx all"); EXPECT_EQ(0, opt.getClientsNum()); // Negative test cases // Number of clients must be non-negative integer - EXPECT_THROW(process("perfdhcp -R -5 -l ethx"), isc::InvalidParameter); - EXPECT_THROW(process("perfdhcp -R gs -l ethx"), isc::InvalidParameter); + EXPECT_THROW(process("perfdhcp -R -5 -l ethx all"), + isc::InvalidParameter); + EXPECT_THROW(process("perfdhcp -R gs -l ethx all"), + isc::InvalidParameter); } TEST_F(CommandOptionsTest, Base) { CommandOptions& opt = CommandOptions::instance(); - process("perfdhcp -6 -b MAC=10::20::30::40::50::60 -l ethx -b duiD=1AB7F5670901FF"); + process("perfdhcp -6 -b MAC=10::20::30::40::50::60 " + "-l ethx -b duiD=1AB7F5670901FF all"); uint8_t mac[6] = {0x10, 0x20, 0x30, 0x40, 0x50, 0x60 }; uint8_t duid[7] = { 0x1A, 0xB7, 0xF5, 0x67, 0x09, 0x01, 0xFF }; @@ -226,72 +238,87 @@ TEST_F(CommandOptionsTest, Base) { ASSERT_EQ(6, v1.size()); EXPECT_TRUE(std::equal(v1.begin(), v1.end(), mac)); // "3x" is invalid value in MAC address - EXPECT_THROW(process("perfdhcp -b mac=10::2::3x::4::5::6 -l ethx"), isc::InvalidParameter); + EXPECT_THROW(process("perfdhcp -b mac=10::2::3x::4::5::6 -l ethx all"), + isc::InvalidParameter); // Test DUID std::vector v2 = opt.getDuidPrefix(); ASSERT_EQ(sizeof(duid) / sizeof(uint8_t), v2.size()); EXPECT_TRUE(std::equal(v2.begin(), v2.end(), duid)); // "t" is invalid digit in DUID - EXPECT_THROW(process("perfdhcp -6 -l ethx -b duiD=1AB7Ft670901FF"), isc::InvalidParameter); + EXPECT_THROW(process("perfdhcp -6 -l ethx -b duiD=1AB7Ft670901FF all"), + isc::InvalidParameter); // Some more negative test cases // Base is not specified - EXPECT_THROW(process("perfdhcp -b -l ethx"), isc::InvalidParameter); + EXPECT_THROW(process("perfdhcp -b -l ethx all"), + isc::InvalidParameter); // Typo: should be mac= instead of mc= - EXPECT_THROW(process("perfdhcp -l ethx -b mc=00:01:02:03::04:05"), isc::InvalidParameter); + EXPECT_THROW(process("perfdhcp -l ethx -b mc=00:01:02:03::04:05 all"), + isc::InvalidParameter); } TEST_F(CommandOptionsTest, DropTime) { CommandOptions& opt = CommandOptions::instance(); - process("perfdhcp -l ethx -d 12"); + process("perfdhcp -l ethx -d 12 all"); ASSERT_EQ(2, opt.getDropTime().size()); EXPECT_DOUBLE_EQ(12, opt.getDropTime()[0]); EXPECT_DOUBLE_EQ(1, opt.getDropTime()[1]); - process("perfdhcp -l ethx -d 2 -d 4.7"); + process("perfdhcp -l ethx -d 2 -d 4.7 all"); ASSERT_EQ(2, opt.getDropTime().size()); EXPECT_DOUBLE_EQ(2, opt.getDropTime()[0]); EXPECT_DOUBLE_EQ(4.7, opt.getDropTime()[1]); // Negative test cases // Drop time must not be negative - EXPECT_THROW(process("perfdhcp -l ethx -d -2 -d 4.7"), isc::InvalidParameter); - EXPECT_THROW(process("perfdhcp -l ethx -d -9.1 -d 0"), isc::InvalidParameter); + EXPECT_THROW(process("perfdhcp -l ethx -d -2 -d 4.7 all"), + isc::InvalidParameter); + EXPECT_THROW(process("perfdhcp -l ethx -d -9.1 -d 0 all"), + isc::InvalidParameter); } TEST_F(CommandOptionsTest, TimeOffset) { CommandOptions& opt = CommandOptions::instance(); - process("perfdhcp -l ethx -T file1.x -T file2.x -E 4"); + process("perfdhcp -l ethx -T file1.x -T file2.x -E 4 all"); EXPECT_EQ(4, opt.getElapsedTimeOffset()); // Negative test cases // Argument -E must be used with -T - EXPECT_THROW(process("perfdhcp -l ethx -E 3 -i"), isc::InvalidParameter); + EXPECT_THROW(process("perfdhcp -l ethx -E 3 -i all"), + isc::InvalidParameter); // Value in -E not specified - EXPECT_THROW(process("perfdhcp -l ethx -T file.x -E -i"), isc::InvalidParameter); + EXPECT_THROW(process("perfdhcp -l ethx -T file.x -E -i all"), + isc::InvalidParameter); // Value for -E must not be negative - EXPECT_THROW(process("perfdhcp -l ethx -E -3 -T file.x"), isc::InvalidParameter); + EXPECT_THROW(process("perfdhcp -l ethx -E -3 -T file.x all"), + isc::InvalidParameter); } TEST_F(CommandOptionsTest, ExchangeMode) { CommandOptions& opt = CommandOptions::instance(); - process("perfdhcp -l ethx -i"); + process("perfdhcp -l ethx -i all"); EXPECT_EQ(CommandOptions::DO_SA, opt.getExchangeMode()); // Negative test cases // No template file specified - EXPECT_THROW(process("perfdhcp -i -l ethx -X 3"), isc::InvalidParameter); + EXPECT_THROW(process("perfdhcp -i -l ethx -X 3 all"), + isc::InvalidParameter); // Offsets can't be used in simple exchanges (-i) - EXPECT_THROW(process("perfdhcp -i -l ethx -O 2 -T file.x"), isc::InvalidParameter); - EXPECT_THROW(process("perfdhcp -i -l ethx -E 3 -T file.x"), isc::InvalidParameter); - EXPECT_THROW(process("perfdhcp -i -l ethx -S 1 -T file.x"), isc::InvalidParameter); - EXPECT_THROW(process("perfdhcp -i -l ethx -I 2 -T file.x"), isc::InvalidParameter); + EXPECT_THROW(process("perfdhcp -i -l ethx -O 2 -T file.x all"), + isc::InvalidParameter); + EXPECT_THROW(process("perfdhcp -i -l ethx -E 3 -T file.x all"), + isc::InvalidParameter); + EXPECT_THROW(process("perfdhcp -i -l ethx -S 1 -T file.x all"), + isc::InvalidParameter); + EXPECT_THROW(process("perfdhcp -i -l ethx -I 2 -T file.x all"), + isc::InvalidParameter); } TEST_F(CommandOptionsTest, Offsets) { CommandOptions& opt = CommandOptions::instance(); - process("perfdhcp -E5 -4 -I 2 -S3 -O 30 -X7 -l ethx -X3 -T file1.x -T file2.x"); + process("perfdhcp -E5 -4 -I 2 -S3 -O 30 -X7 -l ethx " + "-X3 -T file1.x -T file2.x all"); EXPECT_EQ(2, opt.getRequestedIpOffset()); EXPECT_EQ(5, opt.getElapsedTimeOffset()); EXPECT_EQ(3, opt.getServerIdOffset()); @@ -304,151 +331,234 @@ TEST_F(CommandOptionsTest, Offsets) { // Negative test cases // IP offset/IA_NA offset must be positive - EXPECT_THROW(process("perfdhcp -6 -I 0 -l ethx"), isc::InvalidParameter); - EXPECT_THROW(process("perfdhcp -6 -I -4 -l ethx"), isc::InvalidParameter); + EXPECT_THROW(process("perfdhcp -6 -I 0 -l ethx all"), + isc::InvalidParameter); + EXPECT_THROW(process("perfdhcp -6 -I -4 -l ethx all"), + isc::InvalidParameter); // TODO - other negative cases } TEST_F(CommandOptionsTest, LocalPort) { CommandOptions& opt = CommandOptions::instance(); - process("perfdhcp -l ethx -L 2000"); + process("perfdhcp -l ethx -L 2000 all"); EXPECT_EQ(2000, opt.getLocalPort()); // Negative test cases // Local port must be between 0..65535 - EXPECT_THROW(process("perfdhcp -l ethx -L -2"), isc::InvalidParameter); - EXPECT_THROW(process("perfdhcp -l ethx -L"), isc::InvalidParameter); - EXPECT_THROW(process("perfdhcp -l ethx -L 65540"), isc::InvalidParameter); + EXPECT_THROW(process("perfdhcp -l ethx -L -2 all"), + isc::InvalidParameter); + EXPECT_THROW(process("perfdhcp -l ethx -L all"), + isc::InvalidParameter); + EXPECT_THROW(process("perfdhcp -l ethx -L 65540 all"), + isc::InvalidParameter); } TEST_F(CommandOptionsTest, Preload) { CommandOptions& opt = CommandOptions::instance(); - process("perfdhcp -1 -P 3 -l ethx"); + process("perfdhcp -1 -P 3 -l ethx all"); EXPECT_EQ(3, opt.getPreload()); // Negative test cases // Number of preload packages must not be negative integer - EXPECT_THROW(process("perfdhcp -P -1 -l ethx"), isc::InvalidParameter); - EXPECT_THROW(process("perfdhcp -P -3 -l ethx"), isc::InvalidParameter); + EXPECT_THROW(process("perfdhcp -P -1 -l ethx all"), + isc::InvalidParameter); + EXPECT_THROW(process("perfdhcp -P -3 -l ethx all"), + isc::InvalidParameter); } TEST_F(CommandOptionsTest, Seed) { CommandOptions& opt = CommandOptions::instance(); - process("perfdhcp -6 -P 2 -s 23 -l ethx"); + process("perfdhcp -6 -P 2 -s 23 -l ethx all"); EXPECT_EQ(23, opt.getSeed()); EXPECT_TRUE(opt.isSeeded()); - process("perfdhcp -6 -P 2 -s 0 -l ethx"); + process("perfdhcp -6 -P 2 -s 0 -l ethx all"); EXPECT_EQ(0, opt.getSeed()); EXPECT_FALSE(opt.isSeeded()); // Negtaive test cases // Seed must be non-negative integer - EXPECT_THROW(process("perfdhcp -6 -P 2 -s -5 -l ethx"), isc::InvalidParameter); - EXPECT_THROW(process("perfdhcp -6 -P 2 -s -l ethx"), isc::InvalidParameter); + EXPECT_THROW(process("perfdhcp -6 -P 2 -s -5 -l ethx all"), + isc::InvalidParameter); + EXPECT_THROW(process("perfdhcp -6 -P 2 -s -l ethx all"), + isc::InvalidParameter); } TEST_F(CommandOptionsTest, TemplateFiles) { CommandOptions& opt = CommandOptions::instance(); - process("perfdhcp -T file1.x -l ethx"); + process("perfdhcp -T file1.x -l ethx all"); ASSERT_EQ(1, opt.getTemplateFiles().size()); EXPECT_EQ("file1.x", opt.getTemplateFiles()[0]); - process("perfdhcp -T file1.x -s 12 -w start -T file2.x -4 -l ethx"); + process("perfdhcp -T file1.x -s 12 -w start -T file2.x -4 -l ethx all"); ASSERT_EQ(2, opt.getTemplateFiles().size()); EXPECT_EQ("file1.x", opt.getTemplateFiles()[0]); EXPECT_EQ("file2.x", opt.getTemplateFiles()[1]); // Negative test cases // No template file specified - EXPECT_THROW(process("perfdhcp -s 12 -l ethx -T"), isc::InvalidParameter); + EXPECT_THROW(process("perfdhcp -s 12 -T -l ethx all"), + isc::InvalidParameter); // Too many template files specified - EXPECT_THROW(process("perfdhcp -s 12 -l ethx -T file.x -T file.x -T file.x"), isc::InvalidParameter); + EXPECT_THROW(process("perfdhcp -s 12 -l ethx -T file.x " + "-T file.x -T file.x all"), + isc::InvalidParameter); } TEST_F(CommandOptionsTest, Wrapped) { CommandOptions& opt = CommandOptions::instance(); - process("perfdhcp -B -w start -i -l ethx"); + process("perfdhcp -B -w start -i -l ethx all"); EXPECT_EQ("start", opt.getWrapped()); // Negative test cases // Missing command after -w, expected start/stop - EXPECT_THROW(process("perfdhcp -B -i -l ethx -w"), isc::InvalidParameter); + EXPECT_THROW(process("perfdhcp -B -i -l ethx -w all"), + isc::InvalidParameter); } TEST_F(CommandOptionsTest, Diagnostics) { CommandOptions& opt = CommandOptions::instance(); - process("perfdhcp -l ethx -i -x asTe"); + process("perfdhcp -l ethx -i -x asTe all"); EXPECT_EQ("asTe", opt.getDiags()); // Negative test cases // No diagnostics string specified - EXPECT_THROW(process("perfdhcp -l ethx -i -x"), isc::InvalidParameter); + EXPECT_THROW(process("perfdhcp -l ethx -i -x all"), + isc::InvalidParameter); } TEST_F(CommandOptionsTest, Aggressivity) { CommandOptions& opt = CommandOptions::instance(); - process("perfdhcp -a 10 -l 192.168.0.1"); + process("perfdhcp -a 10 -l 192.168.0.1 all"); EXPECT_EQ(10, opt.getAggressivity()); // Negative test cases // Aggressivity must be non negative integer - EXPECT_THROW(process("perfdhcp -l ethx -a 0"), isc::InvalidParameter); - EXPECT_THROW(process("perfdhcp -l ethx -a"), isc::InvalidParameter); - EXPECT_THROW(process("perfdhcp -a -2 -l ethx -a 3"), isc::InvalidParameter); + EXPECT_THROW(process("perfdhcp -l ethx -a 0 all"), + isc::InvalidParameter); + EXPECT_THROW(process("perfdhcp -l ethx -a all"), + isc::InvalidParameter); + EXPECT_THROW(process("perfdhcp -a -2 -l ethx -a 3 all"), + isc::InvalidParameter); } TEST_F(CommandOptionsTest, MaxDrop) { CommandOptions& opt = CommandOptions::instance(); - process("perfdhcp -D 25 -l ethx -r 10"); + process("perfdhcp -D 25 -l ethx -r 10 all"); EXPECT_EQ(25, opt.getMaxDrop()[0]); - process("perfdhcp -D 25 -l ethx -D 15 -r 10"); + process("perfdhcp -D 25 -l ethx -D 15 -r 10 all"); EXPECT_EQ(25, opt.getMaxDrop()[0]); EXPECT_EQ(15, opt.getMaxDrop()[1]); - process("perfdhcp -D 15% -l ethx -r 10"); + process("perfdhcp -D 15% -l ethx -r 10 all"); EXPECT_EQ(15, opt.getMaxDropPercentage()[0]); - process("perfdhcp -D 15% -D25% -l ethx -r 10"); + process("perfdhcp -D 15% -D25% -l ethx -r 10 all"); EXPECT_EQ(15, opt.getMaxDropPercentage()[0]); EXPECT_EQ(25, opt.getMaxDropPercentage()[1]); - process("perfdhcp -D 1% -D 99% -l ethx -r 10"); + process("perfdhcp -D 1% -D 99% -l ethx -r 10 all"); EXPECT_EQ(1, opt.getMaxDropPercentage()[0]); EXPECT_EQ(99, opt.getMaxDropPercentage()[1]); // Negative test cases // Too many -D options - EXPECT_THROW(process("perfdhcp -D 0% -D 1 -l ethx -r20 -D 3"), isc::InvalidParameter); + EXPECT_THROW(process("perfdhcp -D 0% -D 1 -l ethx -r20 -D 3 all"), + isc::InvalidParameter); // Too many -D options - EXPECT_THROW(process("perfdhcp -D 99% -D 13% -l ethx -r20 -D 10%"), isc::InvalidParameter); + EXPECT_THROW(process("perfdhcp -D 99% -D 13% -l ethx -r20 -D 10% all"), + isc::InvalidParameter); // Percentage is out of bounds - EXPECT_THROW(process("perfdhcp -D101% -D 13% -l ethx -r20"), isc::InvalidParameter); - EXPECT_THROW(process("perfdhcp -D0% -D 13% -l ethx -r20"), isc::InvalidParameter); + EXPECT_THROW(process("perfdhcp -D101% -D 13% -l ethx -r20 all"), + isc::InvalidParameter); + EXPECT_THROW(process("perfdhcp -D0% -D 13% -l ethx -r20 all"), + isc::InvalidParameter); } TEST_F(CommandOptionsTest, NumRequest) { CommandOptions& opt = CommandOptions::instance(); - process("perfdhcp -n 1000 -r 10 -l ethx"); + process("perfdhcp -n 1000 -r 10 -l ethx all"); EXPECT_EQ(1000, opt.getNumRequests()[0]); - process("perfdhcp -n 5 -r 10 -n 500 -l ethx"); + process("perfdhcp -n 5 -r 10 -n 500 -l ethx all"); EXPECT_EQ(5, opt.getNumRequests()[0]); EXPECT_EQ(500, opt.getNumRequests()[1]); // Negative test cases // Too many -n parameters, expected maximum 2 - EXPECT_THROW(process("perfdhcp -n 1 -n 2 -l ethx -n3 -r 20"), isc::InvalidParameter); + EXPECT_THROW(process("perfdhcp -n 1 -n 2 -l ethx -n3 -r 20 all"), + isc::InvalidParameter); // Num request must be positive integer - EXPECT_THROW(process("perfdhcp -n 1 -n -22 -l ethx -r 10"), isc::InvalidParameter); - EXPECT_THROW(process("perfdhcp -n 0 -l ethx -r 10"), isc::InvalidParameter); + EXPECT_THROW(process("perfdhcp -n 1 -n -22 -l ethx -r 10 all"), + isc::InvalidParameter); + EXPECT_THROW(process("perfdhcp -n 0 -l ethx -r 10 all"), + isc::InvalidParameter); } TEST_F(CommandOptionsTest, Period) { CommandOptions& opt = CommandOptions::instance(); - process("perfdhcp -p 120 -l ethx -r 100"); + process("perfdhcp -p 120 -l ethx -r 100 all"); EXPECT_EQ(120, opt.getPeriod()); // Negative test cases // Test period must be positive integer - EXPECT_THROW(process("perfdhcp -p 0 -l ethx -r 50"), isc::InvalidParameter); - EXPECT_THROW(process("perfdhcp -p -3 -l ethx -r 50"), isc::InvalidParameter); + EXPECT_THROW(process("perfdhcp -p 0 -l ethx -r 50 all"), + isc::InvalidParameter); + EXPECT_THROW(process("perfdhcp -p -3 -l ethx -r 50 all"), + isc::InvalidParameter); +} + +TEST_F(CommandOptionsTest, Interface) { + // In order to make this test portable we need to know + // at least one interface name on OS where test is run. + // Interface Manager has ability to detect interfaces. + dhcp::IfaceMgr& iface_mgr = dhcp::IfaceMgr::instance(); + const dhcp::IfaceMgr::IfaceCollection& ifaces = iface_mgr.getIfaces(); + std::string iface_name; + CommandOptions& opt = CommandOptions::instance(); + // The local loopback interface should be available. + // If no interface have been found for any reason we should + // not fail this test. + if (ifaces.size() > 0) { + // Get the name of the interface we detected. + iface_name = ifaces.begin()->getName(); + // Use the name in the command parser. + ASSERT_NO_THROW(process("perfdhcp -4 -l " + iface_name + " abc")); + // We expect that command parser will detect that argument + // specified along with '-l' is the interface name. + EXPECT_TRUE(opt.isInterface()); + + // If neither interface nor server is specified then + // exception is expected to be thrown. + EXPECT_THROW(process("perfdhcp -4"), isc::InvalidParameter); + } +} + +TEST_F(CommandOptionsTest, Server) { + CommandOptions& opt = CommandOptions::instance(); + // There is at least server parameter needed. If server is not + // specified the local interface must be specified. + // The server value equal to 'all' means use broadcast. + ASSERT_NO_THROW(process("perfdhcp all")); + // Once command line is parsed we expect that server name is + // set to broadcast address because 'all' was specified. + EXPECT_TRUE(opt.isBroadcast()); + // The broadcast address is 255.255.255.255. + EXPECT_EQ("255.255.255.255", opt.getServerName()); + + // When all is specified for DHCPv6 mode we expect + // FF02::1:2 as a server name which means All DHCP + // servers and relay agents in local network segment + ASSERT_NO_THROW(process("perfdhcp -6 all")); + EXPECT_EQ("FF02::1:2", opt.getServerName()); + + // When server='servers' in DHCPv6 mode we expect + // FF05::1:3 as server name which means All DHCP + // servers in local network. + ASSERT_NO_THROW(process("perfdhcp -6 servers")); + EXPECT_EQ("FF05::1:3", opt.getServerName()); + + // If server name is neither 'all' nor 'servers' + // the given argument value is expected to be + // returned. + ASSERT_NO_THROW(process("perfdhcp -6 abc")); + EXPECT_EQ("abc", opt.getServerName()); } From 8bbc241df84bd1612644de8a8ff7d727a86ae485 Mon Sep 17 00:00:00 2001 From: Marcin Siodelski Date: Tue, 7 Aug 2012 15:00:14 +0200 Subject: [PATCH 11/40] [1959] Add some more comments to the test_control.cc --- tests/tools/perfdhcp/test_control.cc | 29 ++++++++++++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git a/tests/tools/perfdhcp/test_control.cc b/tests/tools/perfdhcp/test_control.cc index c050c9f0a6..ffa4d3e5c7 100644 --- a/tests/tools/perfdhcp/test_control.cc +++ b/tests/tools/perfdhcp/test_control.cc @@ -130,19 +130,31 @@ TestControl::generateMacAddress() const { if ((clients_num == 0) || (clients_num == 1)) { return options.getMacPrefix(); } + // Get the base MAC address. We are going to randomize part of it. std::vector mac_addr(options.getMacPrefix()); if (mac_addr.size() != HW_ETHER_LEN) { isc_throw(BadValue, "invalid MAC address prefix specified"); } uint32_t r = random(); + // The random number must be in the range 0..clients_num. This + // will guarantee that every client has exactly one random MAC + // address assigned. r %= clients_num; + // Randomize MAC address octets. for (std::vector::iterator it = mac_addr.end() - 1; it >= mac_addr.begin(); --it) { + // Add the random value to the current octet. (*it) += r; if (r < 256) { + // If we are here it means that there is no sense + // to randomize the remaining octets of MAC address + // because the following bytes of random value + // are zero and it will have no effect. break; } + // Randomize the next octet with the following + // byte of random value. r >>= 8; } return mac_addr; @@ -260,6 +272,9 @@ TestControl::receivePackets() { if (!pkt4) { receiving = false; } else { + // TODO: replace this with use of StatsMgr to increase + // number of received packets. This can be done once + // the 1958 ticket is reviewed and checked-in. std::cout << "Received packet" << std::endl; } } @@ -269,9 +284,11 @@ void TestControl::registerOptionFactories4() const { static bool factories_registered = false; if (!factories_registered) { + // DHCP_MESSAGE_TYPE option factory. LibDHCP::OptionFactoryRegister(Option::V4, DHO_DHCP_MESSAGE_TYPE, &TestControl::factoryGeneric4); + // DHCP_PARAMETER_REQUEST_LIST option factory. LibDHCP::OptionFactoryRegister(Option::V4, DHO_DHCP_PARAMETER_REQUEST_LIST, &TestControl::factoryRequestList4); @@ -283,6 +300,7 @@ void TestControl::registerOptionFactories6() const { static bool factories_registered = false; if (!factories_registered) { + // This is a placeholder for v6 factories. } factories_registered = true; } @@ -340,12 +358,15 @@ void TestControl::sendDiscover4(const TestControlSocket& socket) { ++sent_packets_0_; last_sent_ = microsec_clock::universal_time(); + // Generate the MAC address to be passed in the packet. std::vector mac_address = generateMacAddress(); + // Generate trasnaction id to be set for the new exchange. const uint32_t transid = static_cast(random()); boost::shared_ptr pkt4(new Pkt4(DHCPDISCOVER, transid)); if (!pkt4) { isc_throw(Unexpected, "failed to create DISCOVER packet"); } + // Set options: DHCP_MESSAGE_TYPE and DHCP_PARAMETER_REQUEST_LIST OptionBuffer buf_msg_type; buf_msg_type.push_back(DHCPDISCOVER); pkt4->addOption(Option::factory(Option::V4, DHO_DHCP_MESSAGE_TYPE, @@ -353,6 +374,8 @@ TestControl::sendDiscover4(const TestControlSocket& socket) { pkt4->addOption(Option::factory(Option::V4, DHO_DHCP_PARAMETER_REQUEST_LIST)); + // Set client's and server's ports as well as server's address, + // and local (relay) address. setDefaults4(socket, pkt4); pkt4->pack(); IfaceMgr::instance().send(pkt4); @@ -362,11 +385,17 @@ void TestControl::setDefaults4(const TestControlSocket &socket, const boost::shared_ptr& pkt) { CommandOptions& options = CommandOptions::instance(); + // Interface name. pkt->setIface(socket.getIface()); + // Local client's port (68) pkt->setLocalPort(DHCP4_CLIENT_PORT); + // Server's port (67) pkt->setRemotePort(DHCP4_SERVER_PORT); + // The remote server's name or IP. pkt->setRemoteAddr(IOAddress(options.getServerName())); + // Set relay (GIADDR) address to local address. pkt->setGiaddr(IOAddress(socket.getAddress())); + // Pretend that we have one relay (which is us). pkt->setHops(1); } From 25ae3625bc6da890c449bdca3d6b6f27a1fff0a2 Mon Sep 17 00:00:00 2001 From: Marcin Siodelski Date: Tue, 7 Aug 2012 20:10:47 +0200 Subject: [PATCH 12/40] [1959] Enabled Solicit packets creation and send. --- tests/tools/perfdhcp/test_control.cc | 125 +++++++++++++++++++++++++-- tests/tools/perfdhcp/test_control.h | 36 +++++++- 2 files changed, 150 insertions(+), 11 deletions(-) diff --git a/tests/tools/perfdhcp/test_control.cc b/tests/tools/perfdhcp/test_control.cc index ffa4d3e5c7..c956da425e 100644 --- a/tests/tools/perfdhcp/test_control.cc +++ b/tests/tools/perfdhcp/test_control.cc @@ -96,13 +96,50 @@ TestControl::checkExitConditions() const { } OptionPtr -TestControl::factoryGeneric4(Option::Universe u, - uint16_t type, - const OptionBuffer& buf) { +TestControl::factoryElapsedTimeSolicit6(Option::Universe, uint16_t, + const OptionBuffer&) { + return OptionPtr(new Option(Option::V6, D6O_ELAPSED_TIME, + OptionBuffer(2, 0))); +} + +OptionPtr +TestControl::factoryGeneric(Option::Universe u, uint16_t type, + const OptionBuffer& buf) { OptionPtr opt(new Option(u, type, buf)); return opt; } +OptionPtr +TestControl::factoryIana6(Option::Universe, uint16_t, + const OptionBuffer&) { + const uint8_t buf_array[] = { + 0, 0, 0, 1, // IAID = 1 + 0, 0, 3600 >> 8, 3600 && 0xff, // T1 = 3600 + 0, 0, 5400 >> 8, 5400 & 0xff, // T2 = 5400 + }; + OptionBuffer buf(buf_array, buf_array + sizeof(buf_array)); + return OptionPtr(new Option(Option::V6, D6O_IA_NA, buf)); +} + +OptionPtr +TestControl::factoryRapidCommit6(Option::Universe, uint16_t, + const OptionBuffer&) { + return OptionPtr(new Option(Option::V6, D6O_RAPID_COMMIT, OptionBuffer())); +} + +OptionPtr +TestControl::factoryOptionRequestOption6(Option::Universe, + uint16_t, + const OptionBuffer&) { + const uint8_t buf_array[] = { + D6O_NAME_SERVERS, + D6O_DOMAIN_SEARCH + }; + OptionBuffer buf_with_options(buf_array, buf_array + sizeof(buf_array)); + return OptionPtr(new Option(Option::V6, D6O_ORO, buf_with_options)); +} + + OptionPtr TestControl::factoryRequestList4(Option::Universe u, uint16_t type, @@ -123,6 +160,8 @@ TestControl::factoryRequestList4(Option::Universe u, return opt; } + + std::vector TestControl::generateMacAddress() const { CommandOptions& options = CommandOptions::instance(); @@ -160,6 +199,18 @@ TestControl::generateMacAddress() const { return mac_addr; } +std::vector +TestControl::generateDuid() const { + CommandOptions& options = CommandOptions::instance(); + uint32_t clients_num = options.getClientsNum(); + if ((clients_num == 0) || (clients_num == 1)) { + return options.getDuidPrefix(); + } + // Get the base MAC address. We are going to randomize part of it. + std::vector duid(options.getDuidPrefix()); + return duid; +} + uint64_t TestControl::getNextExchangesNum() const { CommandOptions& options = CommandOptions::instance(); @@ -287,7 +338,7 @@ TestControl::registerOptionFactories4() const { // DHCP_MESSAGE_TYPE option factory. LibDHCP::OptionFactoryRegister(Option::V4, DHO_DHCP_MESSAGE_TYPE, - &TestControl::factoryGeneric4); + &TestControl::factoryGeneric); // DHCP_PARAMETER_REQUEST_LIST option factory. LibDHCP::OptionFactoryRegister(Option::V4, DHO_DHCP_PARAMETER_REQUEST_LIST, @@ -300,7 +351,24 @@ void TestControl::registerOptionFactories6() const { static bool factories_registered = false; if (!factories_registered) { - // This is a placeholder for v6 factories. + LibDHCP::OptionFactoryRegister(Option::V6, + D6O_ELAPSED_TIME, + &TestControl::factoryElapsedTimeSolicit6); + LibDHCP::OptionFactoryRegister(Option::V6, + D6O_RAPID_COMMIT, + &TestControl::factoryRapidCommit6); + LibDHCP::OptionFactoryRegister(Option::V6, + D6O_ORO, + &TestControl::factoryOptionRequestOption6); + LibDHCP::OptionFactoryRegister(Option::V6, + D6O_CLIENTID, + &TestControl::factoryGeneric); + + LibDHCP::OptionFactoryRegister(Option::V6, + D6O_IA_NA, + &TestControl::factoryIana6); + + } factories_registered = true; } @@ -347,7 +415,11 @@ TestControl::run() { receivePackets(); for (uint64_t i = packets_due; i > 0; --i) { - sendDiscover4(socket); + if (options.getIpVersion() == 4) { + sendDiscover4(socket); + } else { + sendSolicit6(socket); + } ++packets_sent; cout << "Packets sent " << packets_sent << endl; } @@ -382,7 +454,32 @@ TestControl::sendDiscover4(const TestControlSocket& socket) { } void -TestControl::setDefaults4(const TestControlSocket &socket, +TestControl::sendSolicit6(const TestControlSocket& socket) { + ++sent_packets_0_; + last_sent_ = microsec_clock::universal_time(); + // Generate the MAC address to be passed in the packet. + std::vector mac_address = generateMacAddress(); + // Generate DUID to be passed to the packet + std::vector duid = generateDuid(); + // Generate trasnaction id to be set for the new exchange. + const uint32_t transid = static_cast(random()); + boost::shared_ptr pkt6(new Pkt6(DHCPV6_SOLICIT, transid)); + if (!pkt6) { + isc_throw(Unexpected, "failed to create SOLICIT packet"); + } + pkt6->addOption(Option::factory(Option::V6, D6O_ELAPSED_TIME)); + pkt6->addOption(Option::factory(Option::V6, D6O_RAPID_COMMIT)); + pkt6->addOption(Option::factory(Option::V6, D6O_CLIENTID, duid)); + pkt6->addOption(Option::factory(Option::V6, D6O_ORO)); + pkt6->addOption(Option::factory(Option::V6, D6O_IA_NA)); + + setDefaults6(socket, pkt6); + pkt6->pack(); + IfaceMgr::instance().send(pkt6); +} + +void +TestControl::setDefaults4(const TestControlSocket& socket, const boost::shared_ptr& pkt) { CommandOptions& options = CommandOptions::instance(); // Interface name. @@ -399,6 +496,20 @@ TestControl::setDefaults4(const TestControlSocket &socket, pkt->setHops(1); } +void +TestControl::setDefaults6(const TestControlSocket& socket, + const boost::shared_ptr& pkt) { + CommandOptions& options = CommandOptions::instance(); + // Interface name. + pkt->setIface(socket.getIface()); + // Local client's port (547) + pkt->setLocalPort(DHCP6_CLIENT_PORT); + // Server's port (548) + pkt->setRemotePort(DHCP6_SERVER_PORT); + // The remote server's name or IP. + pkt->setRemoteAddr(IOAddress(options.getServerName())); +} + void TestControl::updateSendDue() { // If default constructor was called, this should not happen but diff --git a/tests/tools/perfdhcp/test_control.h b/tests/tools/perfdhcp/test_control.h index bb1f283712..07a6496c9c 100644 --- a/tests/tools/perfdhcp/test_control.h +++ b/tests/tools/perfdhcp/test_control.h @@ -21,6 +21,7 @@ #include #include +#include #include #include @@ -132,6 +133,12 @@ private: /// \return true if any of the exit conditions is fulfiled. bool checkExitConditions() const; + static dhcp::OptionPtr + factoryElapsedTimeSolicit6(dhcp::Option::Universe u, + uint16_t type, + const dhcp::OptionBuffer& buf); + + /// \brief Factory function to create generic option. /// /// Factory function is registered using \ref LibDHCP::OptionFactoryRegister. @@ -147,9 +154,23 @@ private: /// \param type option-type. /// \param buf option-buffer. /// \return instance o the generic option. - static dhcp::OptionPtr factoryGeneric4(dhcp::Option::Universe u, - uint16_t type, - const dhcp::OptionBuffer& buf); + static dhcp::OptionPtr factoryGeneric(dhcp::Option::Universe u, + uint16_t type, + const dhcp::OptionBuffer& buf); + + static dhcp::OptionPtr factoryIana6(dhcp::Option::Universe u, + uint16_t type, + const dhcp::OptionBuffer& buf); + + static dhcp::OptionPtr + factoryOptionRequestOption6(dhcp::Option::Universe u, + uint16_t type, + const dhcp::OptionBuffer& buf); + + static dhcp::OptionPtr factoryRapidCommit6(dhcp::Option::Universe u, + uint16_t type, + const dhcp::OptionBuffer& buf); + /// \brief Factory function to create DHCPv4 Request List option. /// @@ -177,6 +198,8 @@ private: uint16_t type, const dhcp::OptionBuffer& buf); + std::vector generateDuid() const; + /// \brief Generate MAC address. /// /// This method generates MAC address. The number of unique @@ -256,7 +279,9 @@ private: /// \param socket socket to be used to send the message. /// \throw isc::Unexpected if failed to create new packet instance. /// \throw isc::BadValue if MAC address has invalid length. - void sendDiscover4(const TestControlSocket &socket); + void sendDiscover4(const TestControlSocket& socket); + + void sendSolicit6(const TestControlSocket& socket); /// \brief Set default DHCPv4 packet data. /// @@ -273,6 +298,9 @@ private: void setDefaults4(const TestControlSocket& socket, const boost::shared_ptr& pkt); + void setDefaults6(const TestControlSocket& socket, + const boost::shared_ptr& pkt); + /// \brief Update due time to initiate next chunk of exchanges. /// /// Method updates due time to initiate next chunk of exchanges. From 967e3ec6188b8a41e7f830bd13deda30afe5fa44 Mon Sep 17 00:00:00 2001 From: Marcin Siodelski Date: Wed, 8 Aug 2012 20:03:13 +0200 Subject: [PATCH 13/40] [1959] Added generation of DUID prefix if it is not given by user. --- tests/tools/perfdhcp/command_options.cc | 38 +++++++++++++++++++ tests/tools/perfdhcp/command_options.h | 6 +++ .../tests/command_options_unittest.cc | 16 +++++++- 3 files changed, 59 insertions(+), 1 deletion(-) diff --git a/tests/tools/perfdhcp/command_options.cc b/tests/tools/perfdhcp/command_options.cc index 55380d086b..210ed04920 100644 --- a/tests/tools/perfdhcp/command_options.cc +++ b/tests/tools/perfdhcp/command_options.cc @@ -20,8 +20,10 @@ #include #include #include +#include #include +#include #include #include "command_options.h" @@ -367,6 +369,12 @@ CommandOptions::initialize(int argc, char** argv) { isc_throw(InvalidParameter, "without an inteface server is required"); } + + // If DUID is not specified from command line we need to + // generate one. + if (duid_prefix_.size() == 0) { + generateDuidPrefix(); + } } void @@ -476,6 +484,36 @@ CommandOptions::decodeDuid(const std::string& base) { } } +void +CommandOptions::generateDuidPrefix() { + using namespace boost::posix_time; + // Duid prefix will be most likely generated only once but + // it is ok if it is called more then once so we simply + // regenerate it and discard previous value. + duid_prefix_.clear(); + const uint8_t duid_prefix_len = 14; + duid_prefix_.resize(duid_prefix_len); + // The first four octets consist of DUID LLT and hardware type. + duid_prefix_[0] = DUID_LLT >> 8; + duid_prefix_[1] = DUID_LLT & 0xff; + duid_prefix_[2] = HWTYPE_ETHERNET >> 8; + duid_prefix_[3] = HWTYPE_ETHERNET & 0xff; + + // As described in RFC3315: 'the time value is the time + // that the DUID is generated represented in seconds + // since midnight (UTC), January 1, 2000, modulo 2^32.' + ptime now = microsec_clock::universal_time(); + ptime duid_epoch(from_iso_string("20000101T000000")); + time_period period(duid_epoch, now); + uint32_t duration_sec = htonl(period.length().total_seconds()); + memcpy(&duid_prefix_[4], &duration_sec, 4); + + // Set link layer address (6 octets). This value may be + // randomized before sending a packet to simulate different + // clients. + memcpy(&duid_prefix_[8], &mac_prefix_[0], 6); +} + uint8_t CommandOptions::convertHexString(const std::string& text) const { unsigned int ui = 0; diff --git a/tests/tools/perfdhcp/command_options.h b/tests/tools/perfdhcp/command_options.h index 459efa598b..b75c43864c 100644 --- a/tests/tools/perfdhcp/command_options.h +++ b/tests/tools/perfdhcp/command_options.h @@ -346,6 +346,12 @@ private: /// \param base Base string given as -b duid=0F1234 /// \throws isc::InvalidParameter if DUID is invalid void decodeDuid(const std::string& base); + + /// \brief Generates DUID-LLT (based on link layer address). + /// + /// Function generates DUID based on link layer address and + /// initiates duid_prefix_ value with it. + void generateDuidPrefix(); /// \brief Converts two-digit hexadecimal string to a byte /// diff --git a/tests/tools/perfdhcp/tests/command_options_unittest.cc b/tests/tools/perfdhcp/tests/command_options_unittest.cc index 669d792bb5..e3e3384b22 100644 --- a/tests/tools/perfdhcp/tests/command_options_unittest.cc +++ b/tests/tools/perfdhcp/tests/command_options_unittest.cc @@ -71,11 +71,25 @@ protected: EXPECT_EQ(0, opt.getClientsNum()); // default mac - uint8_t mac[6] = { 0x00, 0x0C, 0x01, 0x02, 0x03, 0x04 }; + const uint8_t mac[6] = { 0x00, 0x0C, 0x01, 0x02, 0x03, 0x04 }; std::vector v1 = opt.getMacPrefix(); ASSERT_EQ(6, v1.size()); EXPECT_TRUE(std::equal(v1.begin(), v1.end(), mac)); + // Check if DUID is initialized. The DUID-LLT is expected + // to start with DUID_LLT value of 1 and hardware ethernet + // type equal to 1 (HWETHER_TYPE). + const uint8_t duid_llt_and_hw[4] = { 0x0, 0x1, 0x0, 0x1 }; + // We assume DUID-LLT length 14. This includes 4 octets of + // DUID_LLT value, two octets of hardware type, 4 octets + // of time value and 6 octets of variable link layer (MAC) + // address. + const int duid_llt_size = 14; + std::vector v2 = opt.getDuidPrefix(); + ASSERT_EQ(duid_llt_size, opt.getDuidPrefix().size()); + EXPECT_TRUE(std::equal(v2.begin(), v2.begin() + 4, + duid_llt_and_hw)); + EXPECT_EQ(0, opt.getBase().size()); EXPECT_EQ(0, opt.getNumRequests().size()); EXPECT_EQ(0, opt.getPeriod()); From ce6188b38f1b684459bc645a13b951453c526108 Mon Sep 17 00:00:00 2001 From: Marcin Siodelski Date: Wed, 8 Aug 2012 20:04:20 +0200 Subject: [PATCH 14/40] [1959] Added multicast option on the socket if remote addr is multicast. --- tests/tools/perfdhcp/test_control.cc | 60 +++++++++++++++++++++++----- tests/tools/perfdhcp/test_control.h | 13 +++++- 2 files changed, 62 insertions(+), 11 deletions(-) diff --git a/tests/tools/perfdhcp/test_control.cc b/tests/tools/perfdhcp/test_control.cc index c956da425e..ed9018e794 100644 --- a/tests/tools/perfdhcp/test_control.cc +++ b/tests/tools/perfdhcp/test_control.cc @@ -62,6 +62,7 @@ TestControl::TestControlSocket::initSocketData() { ++s) { if (s->sockfd_ == socket_) { iface_ = it->getName(); + ifindex_ = it->getIndex(); addr_ = s->addr_; return; } @@ -267,6 +268,7 @@ TestControl::openSocket() const { uint8_t family = AF_INET; uint16_t port = 67; int sock = 0; + IOAddress remoteaddr(servername); if (options.getIpVersion() == 6) { family = AF_INET6; port = 547; @@ -289,7 +291,6 @@ TestControl::openSocket() const { } else if (!servername.empty()) { // If only server name is given we will need to try to resolve // the local address to bind socket to based on remote address. - IOAddress remoteaddr(servername); sock = IfaceMgr::instance().openSocketFromRemoteAddress(remoteaddr, port); } @@ -309,8 +310,34 @@ TestControl::openSocket() const { isc_throw(InvalidOperation, "unable to set broadcast option on the socket"); } + } else if (options.getIpVersion() == 6) { + // If remote address is multicast we need to enable it on + // the socket that has been created. + asio::ip::address_v6 remote_v6 = remoteaddr.getAddress().to_v6(); + if (remote_v6.is_multicast()) { + int hops = 1; + int ret = setsockopt(sock, IPPROTO_IPV6, IPV6_MULTICAST_HOPS, + &hops, sizeof(hops)); + // If user specified interface name with '-l' the + // IPV6_MULTICAST_IF has to be set. + if ((ret >= 0) && options.isInterface()) { + IfaceMgr::Iface* iface = + IfaceMgr::instance().getIface(options.getLocalName()); + if (iface == NULL) { + isc_throw(Unexpected, "unknown interface " + << options.getLocalName()); + } + int idx = iface->getIndex(); + ret = setsockopt(sock, IPPROTO_IPV6, IPV6_MULTICAST_IF, + &idx, sizeof(idx)); + } + if (ret < 0) { + isc_throw(InvalidOperation, + "unable to enable multicast on socket " << sock + << ". errno = " << errno); + } + } } - return(sock); } @@ -319,14 +346,23 @@ TestControl::receivePackets() { int timeout = 0; bool receiving = true; while (receiving) { - Pkt4Ptr pkt4 = IfaceMgr::instance().receive4(timeout); - if (!pkt4) { - receiving = false; - } else { - // TODO: replace this with use of StatsMgr to increase - // number of received packets. This can be done once - // the 1958 ticket is reviewed and checked-in. - std::cout << "Received packet" << std::endl; + if (CommandOptions::instance().getIpVersion() == 4) { + Pkt4Ptr pkt4 = IfaceMgr::instance().receive4(timeout); + if (!pkt4) { + receiving = false; + } else { + // TODO: replace this with use of StatsMgr to increase + // number of received packets. This can be done once + // the 1958 ticket is reviewed and checked-in. + std::cout << "Received packet" << std::endl; + } + } else if (CommandOptions::instance().getIpVersion() == 6) { + Pkt6Ptr pkt6 = IfaceMgr::instance().receive6(); + if (!pkt6) { + receiving = false; + } else { + std::cout << "Received DHCPv6 packet" << std::endl; + } } } } @@ -484,6 +520,8 @@ TestControl::setDefaults4(const TestControlSocket& socket, CommandOptions& options = CommandOptions::instance(); // Interface name. pkt->setIface(socket.getIface()); + // Interface index. + pkt->setIndex(socket.getIfIndex()); // Local client's port (68) pkt->setLocalPort(DHCP4_CLIENT_PORT); // Server's port (67) @@ -502,6 +540,8 @@ TestControl::setDefaults6(const TestControlSocket& socket, CommandOptions& options = CommandOptions::instance(); // Interface name. pkt->setIface(socket.getIface()); + // Interface index. + pkt->setIndex(socket.getIfIndex()); // Local client's port (547) pkt->setLocalPort(DHCP6_CLIENT_PORT); // Server's port (548) diff --git a/tests/tools/perfdhcp/test_control.h b/tests/tools/perfdhcp/test_control.h index 07a6496c9c..9670cb72a1 100644 --- a/tests/tools/perfdhcp/test_control.h +++ b/tests/tools/perfdhcp/test_control.h @@ -68,6 +68,11 @@ public: /// \return name of the interface where socket is bound to. const std::string& getIface() const { return(iface_); } + /// \brief Return interface index where socket is bound to. + /// + /// \return index fo the interface where sockert is bound to. + int getIfIndex() const { return(ifindex_); } + /// \brief Return address where socket is bound to. /// /// \return address where socket is bound to. @@ -91,6 +96,7 @@ public: int socket_; ///< Socket descirptor. std::string iface_; ///< Name of the interface. + int ifindex_; ///< Index of the interface. asiolink::IOAddress addr_; ///< Address bound. }; @@ -231,11 +237,16 @@ private: /// to create a socket, depending on what is available (specified /// from the command line). If socket can't be created for any /// reason, exception is thrown. + /// If destination address is broadcast (for DHCPv4) or multicast + /// (for DHCPv6) than broadcast or multicast option is set on + /// the socket. /// /// \throw isc::BadValue if socket can't be created for given /// interface, local address or remote address. /// \throw isc::InvalidOperation if broadcast option can't be - /// set for the socket. + /// set for the v4 socket or if multicast option cat't be set + /// for the v6 socket. + /// \throw isc::Unexpected if interal unexpected error occured. /// \return socket descriptor. int openSocket() const; From 8e7ce81608718bc14740240158ae60de162734de Mon Sep 17 00:00:00 2001 From: Marcin Siodelski Date: Tue, 14 Aug 2012 13:36:57 +0200 Subject: [PATCH 15/40] [1959] Extended unit testing of TestControl class. --- tests/tools/perfdhcp/command_options.cc | 12 +- tests/tools/perfdhcp/test_control.cc | 55 +- tests/tools/perfdhcp/test_control.h | 146 ++++- .../perfdhcp/tests/command_options_helper.h | 106 ++++ .../tests/command_options_unittest.cc | 11 +- .../perfdhcp/tests/test_control_unittest.cc | 576 +++++++++++++++++- 6 files changed, 825 insertions(+), 81 deletions(-) create mode 100644 tests/tools/perfdhcp/tests/command_options_helper.h diff --git a/tests/tools/perfdhcp/command_options.cc b/tests/tools/perfdhcp/command_options.cc index 210ed04920..14a2e65b9a 100644 --- a/tests/tools/perfdhcp/command_options.cc +++ b/tests/tools/perfdhcp/command_options.cc @@ -57,8 +57,9 @@ CommandOptions::reset() { report_delay_ = 0; clients_num_ = 0; mac_prefix_.assign(mac, mac + 6); - base_.resize(0); - num_request_.resize(0); + duid_prefix_.clear(); + base_.clear(); + num_request_.clear(); period_ = 0; drop_time_set_ = 0; drop_time_.assign(dt, dt + 2); @@ -83,6 +84,8 @@ CommandOptions::reset() { diags_.clear(); wrapped_.clear(); server_name_.clear(); + + generateDuidPrefix(); } void @@ -461,6 +464,7 @@ CommandOptions::decodeMac(const std::string& base) { void CommandOptions::decodeDuid(const std::string& base) { // Strip argument from duid= + std::vector duid_prefix; size_t found = base.find('='); check(found == std::string::npos, "expected -b format for duid is -b duid="); std::string b = base.substr(found + 1); @@ -480,8 +484,10 @@ CommandOptions::decodeDuid(const std::string& base) { isc_throw(isc::InvalidParameter, "invalid characters in DUID provided, exepected hex digits"); } - duid_prefix_.push_back(static_cast(ui)); + duid_prefix.push_back(static_cast(ui)); } + // Assign the new duid only if successfully generated. + std::swap(duid_prefix, duid_prefix_); } void diff --git a/tests/tools/perfdhcp/test_control.cc b/tests/tools/perfdhcp/test_control.cc index ed9018e794..ebc3f40f9b 100644 --- a/tests/tools/perfdhcp/test_control.cc +++ b/tests/tools/perfdhcp/test_control.cc @@ -97,10 +97,16 @@ TestControl::checkExitConditions() const { } OptionPtr -TestControl::factoryElapsedTimeSolicit6(Option::Universe, uint16_t, - const OptionBuffer&) { - return OptionPtr(new Option(Option::V6, D6O_ELAPSED_TIME, - OptionBuffer(2, 0))); +TestControl::factoryElapsedTime6(Option::Universe, uint16_t, + const OptionBuffer& buf) { + if (buf.size() == 2) { + return OptionPtr(new Option(Option::V6, D6O_ELAPSED_TIME, buf)); + } else if (buf.size() == 0) { + return OptionPtr(new Option(Option::V6, D6O_ELAPSED_TIME, + OptionBuffer(2, 0))); + } + isc_throw(isc::BadValue, + "elapsed time option buffer size has to be 0 or 2"); } OptionPtr @@ -112,14 +118,17 @@ TestControl::factoryGeneric(Option::Universe u, uint16_t type, OptionPtr TestControl::factoryIana6(Option::Universe, uint16_t, - const OptionBuffer&) { + const OptionBuffer& buf) { const uint8_t buf_array[] = { 0, 0, 0, 1, // IAID = 1 0, 0, 3600 >> 8, 3600 && 0xff, // T1 = 3600 0, 0, 5400 >> 8, 5400 & 0xff, // T2 = 5400 }; - OptionBuffer buf(buf_array, buf_array + sizeof(buf_array)); - return OptionPtr(new Option(Option::V6, D6O_IA_NA, buf)); + OptionBuffer buf_ia_na(buf_array, buf_array + sizeof(buf_array)); + for (int i = 0; i < buf.size(); ++i) { + buf_ia_na.push_back(buf[i]); + } + return OptionPtr(new Option(Option::V6, D6O_IA_NA, buf_ia_na)); } OptionPtr @@ -207,8 +216,13 @@ TestControl::generateDuid() const { if ((clients_num == 0) || (clients_num == 1)) { return options.getDuidPrefix(); } - // Get the base MAC address. We are going to randomize part of it. + // Get the base DUID. We are going to randomize part of it. std::vector duid(options.getDuidPrefix()); + std::vector mac_addr(generateMacAddress()); + duid.resize(duid.size() - mac_addr.size()); + for (int i = 0; i < mac_addr.size(); ++i) { + duid.push_back(mac_addr[i]); + } return duid; } @@ -227,12 +241,6 @@ TestControl::getNextExchangesNum() const { // synchornize with it. if (options.getRate() != 0) { time_period period(send_due_, now); - // Null condition should not occur because we - // have checked it in the first if statement but - // let's keep this check just in case. - if (period.is_null()) { - return (0); - } time_duration duration = period.length(); // due_factor indicates the number of seconds that // sending next chunk of packets will take. @@ -261,17 +269,22 @@ TestControl::getNextExchangesNum() const { } int -TestControl::openSocket() const { +TestControl::openSocket(uint16_t port) const { CommandOptions& options = CommandOptions::instance(); std::string localname = options.getLocalName(); std::string servername = options.getServerName(); uint8_t family = AF_INET; - uint16_t port = 67; int sock = 0; IOAddress remoteaddr(servername); + if (port == 0) { + if (options.getIpVersion() == 6) { + port = 547; + } else if (port == 0) { + port = 67; + } + } if (options.getIpVersion() == 6) { family = AF_INET6; - port = 547; } // Local name is specified along with '-l' option. // It may point to interface name or local address. @@ -332,7 +345,7 @@ TestControl::openSocket() const { &idx, sizeof(idx)); } if (ret < 0) { - isc_throw(InvalidOperation, + isc_throw(InvalidOperation, "unable to enable multicast on socket " << sock << ". errno = " << errno); } @@ -389,7 +402,7 @@ TestControl::registerOptionFactories6() const { if (!factories_registered) { LibDHCP::OptionFactoryRegister(Option::V6, D6O_ELAPSED_TIME, - &TestControl::factoryElapsedTimeSolicit6); + &TestControl::factoryElapsedTime6); LibDHCP::OptionFactoryRegister(Option::V6, D6O_RAPID_COMMIT, &TestControl::factoryRapidCommit6); @@ -528,6 +541,8 @@ TestControl::setDefaults4(const TestControlSocket& socket, pkt->setRemotePort(DHCP4_SERVER_PORT); // The remote server's name or IP. pkt->setRemoteAddr(IOAddress(options.getServerName())); + // Set local addresss. + pkt->setLocalAddr(IOAddress(socket.getAddress())); // Set relay (GIADDR) address to local address. pkt->setGiaddr(IOAddress(socket.getAddress())); // Pretend that we have one relay (which is us). @@ -546,6 +561,8 @@ TestControl::setDefaults6(const TestControlSocket& socket, pkt->setLocalPort(DHCP6_CLIENT_PORT); // Server's port (548) pkt->setRemotePort(DHCP6_SERVER_PORT); + // Set local address. + pkt->setLocalAddr(socket.getAddress()); // The remote server's name or IP. pkt->setRemoteAddr(IOAddress(options.getServerName())); } diff --git a/tests/tools/perfdhcp/test_control.h b/tests/tools/perfdhcp/test_control.h index 9670cb72a1..de3b942ade 100644 --- a/tests/tools/perfdhcp/test_control.h +++ b/tests/tools/perfdhcp/test_control.h @@ -33,6 +33,14 @@ namespace perfdhcp { /// This class is responsible for executing DHCP performance /// test end to end. /// +/// Option factory functions are registered using +/// \ref LibDHCP::OptionFactoryRegister. Registered factory functions +/// provide a way to create options of the same type in the same way. +/// When new option instance is needed the corresponding factory +/// function is called to create it. This is done by calling +/// \ref Option::factory with DHCP message type specified as one of +/// parameters. Some of the parameters passed to factory function +/// may be ignored (e.g. option buffer). class TestControl : public boost::noncopyable { public: @@ -119,11 +127,16 @@ public: /// \throw isc::Unexpected if internal Test Controler error occured. void run(); -private: +protected: - /// \brief Private default constructor. + // We would really like these methods and members to be private but + // they have to be accessible for unit-testing. Another, possibly better, + // solution is to make this class friend of test class but this is not + // what's followed in other classes. + + /// \brief Default constructor. /// - /// Default constructor is private as the object can be created + /// Default constructor is protected as the object can be created /// only via \ref instance method. TestControl(); @@ -139,40 +152,76 @@ private: /// \return true if any of the exit conditions is fulfiled. bool checkExitConditions() const; - static dhcp::OptionPtr - factoryElapsedTimeSolicit6(dhcp::Option::Universe u, - uint16_t type, - const dhcp::OptionBuffer& buf); - - /// \brief Factory function to create generic option. + /// \brief Factory function to create DHCPv6 ELAPSED_TIME option. /// - /// Factory function is registered using \ref LibDHCP::OptionFactoryRegister. - /// Registered factory functions provide a way to create options of the - /// same type in the same way. When new option instance is needed the - /// corresponding factory function is called to create it. This is done - /// by calling \ref Option::factory with DHCP message type specified as - /// one of parameters. Some of the parameters passed to factory function - /// may be ignored (e.g. option buffer). For generic option however, factory - /// function creates option using contents of the buffer. + /// This factory function creates DHCPv6 ELAPSED_TIME option instance. + /// If empty buffer is passed the option buffer will be initialized + /// to length 2 and values will be initialized to zeros. Otherwise + /// function will initialize option buffer with values in passed buffer. /// /// \param u universe (V6 or V4). /// \param type option-type. /// \param buf option-buffer. - /// \return instance o the generic option. + /// \throw if elapsed time buffer size is neither 2 nor 0. + /// \return instance o the option. + static dhcp::OptionPtr + factoryElapsedTime6(dhcp::Option::Universe u, + uint16_t type, + const dhcp::OptionBuffer& buf); + + /// \brief Factory function to create generic option. + /// + /// This factory function creates option with specified universe, + /// type and buf. It does not have any additional logic validating + /// the buffer contents, size etc. + /// + /// \param u universe (V6 or V4). + /// \param type option-type. + /// \param buf option-buffer. + /// \return instance o the option. static dhcp::OptionPtr factoryGeneric(dhcp::Option::Universe u, uint16_t type, const dhcp::OptionBuffer& buf); + /// \brief Factory function to create IA_NA option. + /// + /// This factory function creates DHCPv6 IA_NA option instance. + /// \TODO: add support for IA Address options. + /// \param u universe (V6 or V4). + /// \param type option-type. + /// \param buf option-buffer. + /// \return instance of IA_NA option. static dhcp::OptionPtr factoryIana6(dhcp::Option::Universe u, uint16_t type, const dhcp::OptionBuffer& buf); + /// \brief Factory function to create DHCPv6 ORO option. + /// + /// This factory function creates DHCPv6 Option Request Option instance. + /// The created option will contain the following set of requested options: + /// - D6O_NAME_SERVERS + /// - D6O_DOMAIN_SEARCH + /// + /// \param u universe (V6 or V4). + /// \param type option-type. + /// \param buf option-buffer (ignored and should be empty). + /// \return instance of ORO option. static dhcp::OptionPtr factoryOptionRequestOption6(dhcp::Option::Universe u, uint16_t type, const dhcp::OptionBuffer& buf); + /// \brief Factory function to create DHCPv6 RAPID_COMMIT option instance. + /// + /// This factory function creates DHCPv6 RAPID_COMMIT option instance. + /// The buffer passed to this option must be empty because option does + /// not have any payload. + /// + /// \param u universe (V6 or V4). + /// \param type option-type. + /// \param buf option-buffer (ignored and should be empty). + /// \return instance of RAPID_COMMIT option.. static dhcp::OptionPtr factoryRapidCommit6(dhcp::Option::Universe u, uint16_t type, const dhcp::OptionBuffer& buf); @@ -180,14 +229,8 @@ private: /// \brief Factory function to create DHCPv4 Request List option. /// - /// Factory function is registered using \ref LibDHCP::OptionFactoryRegister. - /// Registered factory functions provide a way to create options of the - /// same type in the same way. When new option instance is needed the - /// corresponding factory function is called to create it. This is done - /// by calling \ref Option::factory with DHCP message type specified as - /// one of parameters. This factory function ignores contents of the - /// buffer provided and creates option buffer internally with the following - /// list of requested options: + /// This factory function creayes DHCPv4 PARAMETER_REQUEST_LIST option + /// instance with the following set of requested options: /// - DHO_SUBNET_MASK, /// - DHO_BROADCAST_ADDRESS, /// - DHO_TIME_OFFSET, @@ -198,12 +241,23 @@ private: /// /// \param u universe (V6 or V4). /// \param type option-type. - /// \param buf option-buffer. + /// \param buf option-buffer (ignored and should be empty). /// \return instance o the generic option. static dhcp::OptionPtr factoryRequestList4(dhcp::Option::Universe u, uint16_t type, const dhcp::OptionBuffer& buf); + /// \brief Generate DUID. + /// + /// Method generates unique DUID. The number of DUIDs it can generate + /// depends on the number of simulated clinets, which is specified + /// from the command line. It uses \ref CommandOptions object to retrieve + /// number of clinets. Since the last six octets of DUID are constructed + /// from the MAC address, this function uses \ref generateMacAddress + /// internally to randomize the DUID. + /// + /// \throw isc::BadValue if \ref generateMacAddress throws. + /// \return vector representing DUID. std::vector generateDuid() const; /// \brief Generate MAC address. @@ -241,6 +295,7 @@ private: /// (for DHCPv6) than broadcast or multicast option is set on /// the socket. /// + /// \param port port to bound socket to. /// \throw isc::BadValue if socket can't be created for given /// interface, local address or remote address. /// \throw isc::InvalidOperation if broadcast option can't be @@ -248,8 +303,11 @@ private: /// for the v6 socket. /// \throw isc::Unexpected if interal unexpected error occured. /// \return socket descriptor. - int openSocket() const; + int openSocket(uint16_t port = 0) const; + /// \brief Receive DHCPv4 or DHCPv6 packets from the server. + /// + /// Method receives DHCPv4 or DHCPv6 packets from the server. void receivePackets(); /// \brief Register option factory functions for DHCPv4 @@ -292,11 +350,23 @@ private: /// \throw isc::BadValue if MAC address has invalid length. void sendDiscover4(const TestControlSocket& socket); + /// \brief Send DHCPv6 SOLICIT message. + /// + /// Method creates and sends DHCPv6 SOLICIT message to the server + /// with the following options: + /// - D6O_ELAPSED_TIME, + /// - D6O_RAPID_COMMIT if rapid commit is requested in command line, + /// - D6O_CLIENTID, + /// - D6O_ORO (Option Request Option), + /// - D6O_IA_NA. + /// + /// \param socket socket to be used to send the message. + /// \throw isc::Unexpected if failed to create new packet instance. void sendSolicit6(const TestControlSocket& socket); - /// \brief Set default DHCPv4 packet data. + /// \brief Set default DHCPv4 packet parameters. /// - /// This method sets default data on the DHCPv4 packet: + /// This method sets default parameters on the DHCPv4 packet: /// - interface name, /// - local port = 68 (DHCP client port), /// - remote port = 67 (DHCP server port), @@ -305,10 +375,22 @@ private: /// - hops = 1 (pretending that we are a relay) /// /// \param socket socket used to send the packet. - /// \param pkt packet to be configured. + /// \param pkt reference to packet to be configured. void setDefaults4(const TestControlSocket& socket, const boost::shared_ptr& pkt); + /// \brief Set default DHCPv6 packet parameters. + /// + /// This method sets default parameters on the DHCPv6 packet: + /// - interface name, + /// - interface index, + /// - local port, + /// - remote port, + /// - local address, + /// - remote address (server). + /// + /// \param socket socket used to send the packet. + /// \param pkt reference to packet to be configured. void setDefaults6(const TestControlSocket& socket, const boost::shared_ptr& pkt); @@ -319,6 +401,8 @@ private: /// expected rate in its calculations. void updateSendDue(); +private: + boost::posix_time::ptime send_due_; ///< Due time to initiate next chunk ///< of exchanges. boost::posix_time::ptime last_sent_; ///< Indicates when the last exchange diff --git a/tests/tools/perfdhcp/tests/command_options_helper.h b/tests/tools/perfdhcp/tests/command_options_helper.h new file mode 100644 index 0000000000..5886c1fad7 --- /dev/null +++ b/tests/tools/perfdhcp/tests/command_options_helper.h @@ -0,0 +1,106 @@ +// Copyright (C) 2012 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 __COMMAND_OPTIONS_HELPER_H +#define __COMMAND_OPTIONS_HELPER_H + +#include +#include + +#include +#include "../command_options.h" + +namespace isc { +namespace perfdhcp { + +/// \brief Command Options Helper class. +/// +/// This helper class can be shared between unit tests that +/// need to initialize CommandOptions objects and feed it with +/// specific command line. The command line can be given as a +/// string represinting program name, options and arguments. +/// The static method exposed by this class can be used to +/// tokenize this string into array of C-strings that are later +/// consumed by \ref CommandOptions::parse. The state of the +/// CommandOptions object is reset every time the process +/// function is invoked. Also, when command line parsing is +/// ended the array of C-string is freed from the memory. +class CommandOptionsHelper { +public: + + class ArgvPtr { + public: + ArgvPtr(char** argv, int argc) : argv_(argv), argc_(argc) { } + char** getArgv() const { return(argv_); } + int getArgc() const { return(argc_); } + + ~ArgvPtr() { + if (argv_ != NULL) { + for(int i = 0; i < argc_; ++i) { + free(argv_[i]); + argv_[i] = NULL; + } + free(argv_); + } + } + + public: + char** argv_; + int argc_; + }; + + static void process(const std::string& cmdline) { + CommandOptions& opt = CommandOptions::instance(); + int argc = 0; + char** argv = tokenizeString(cmdline, argc); + ArgvPtr args(argv, argc); + opt.reset(); + opt.parse(args.getArgc(), args.getArgv()); + } + +private: + + static char** tokenizeString(const std::string& text_to_split, int& num) { + char** results = NULL; + // Tokenization with std streams + std::stringstream text_stream(text_to_split); + // Iterators to be used for tokenization + std::istream_iterator text_iterator(text_stream); + std::istream_iterator text_end; + // Tokenize string (space is a separator) using begin and end iteratos + std::vector tokens(text_iterator, text_end); + + if (tokens.size() > 0) { + // Allocate array of C-strings where we will store tokens + results = static_cast(malloc(tokens.size() * sizeof(char*))); + if (results == NULL) { + isc_throw(Unexpected, "unable to allocate array of c-strings"); + } + // Store tokens in C-strings array + for (int i = 0; i < tokens.size(); ++i) { + char* cs = static_cast(malloc(tokens[i].length() + 1)); + strcpy(cs, tokens[i].c_str()); + results[i] = cs; + } + // Return number of tokens to calling function + num = tokens.size(); + } + return results; + } +}; + +} // namespace perfdhcp +} // namespace isc + +#endif // __COMMAND_OPTIONS_HELPER_H diff --git a/tests/tools/perfdhcp/tests/command_options_unittest.cc b/tests/tools/perfdhcp/tests/command_options_unittest.cc index e3e3384b22..faed8a1e6f 100644 --- a/tests/tools/perfdhcp/tests/command_options_unittest.cc +++ b/tests/tools/perfdhcp/tests/command_options_unittest.cc @@ -245,7 +245,8 @@ TEST_F(CommandOptionsTest, Base) { process("perfdhcp -6 -b MAC=10::20::30::40::50::60 " "-l ethx -b duiD=1AB7F5670901FF all"); uint8_t mac[6] = {0x10, 0x20, 0x30, 0x40, 0x50, 0x60 }; - uint8_t duid[7] = { 0x1A, 0xB7, 0xF5, 0x67, 0x09, 0x01, 0xFF }; + uint8_t duid[14] = { 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x10, 0x11, 0x1F, 0x14 }; // Test Mac std::vector v1 = opt.getMacPrefix(); @@ -256,11 +257,15 @@ TEST_F(CommandOptionsTest, Base) { isc::InvalidParameter); // Test DUID + EXPECT_NO_THROW( + process("perfdhcp -b duid=0101010101010101010110111F14 -l 127.0.0.1 all") + ); std::vector v2 = opt.getDuidPrefix(); ASSERT_EQ(sizeof(duid) / sizeof(uint8_t), v2.size()); EXPECT_TRUE(std::equal(v2.begin(), v2.end(), duid)); - // "t" is invalid digit in DUID - EXPECT_THROW(process("perfdhcp -6 -l ethx -b duiD=1AB7Ft670901FF all"), + // "t" is invalid character in DUID + EXPECT_THROW(process("perfdhcp -6 -l ethx -b " + "duiD=010101010101010101t110111F14 all"), isc::InvalidParameter); // Some more negative test cases diff --git a/tests/tools/perfdhcp/tests/test_control_unittest.cc b/tests/tools/perfdhcp/tests/test_control_unittest.cc index 2cc6b93f53..aa8983051e 100644 --- a/tests/tools/perfdhcp/tests/test_control_unittest.cc +++ b/tests/tools/perfdhcp/tests/test_control_unittest.cc @@ -17,14 +17,50 @@ #include #include +#include + +#include +#include +#include +#include +#include "command_options_helper.h" #include "../test_control.h" -#include "../command_options.h" -#include "exceptions/exceptions.h" using namespace std; +using namespace boost::posix_time; using namespace isc; +using namespace isc::dhcp; using namespace isc::perfdhcp; +/// \brief Test Control class with protected members made public. +/// +/// This class makes protected TestControl class'es member public +/// to allow unit testing. +class NakedTestControl: public TestControl { +public: + using TestControl::checkExitConditions; + using TestControl::factoryElapsedTime6; + using TestControl::factoryGeneric; + using TestControl::factoryIana6; + using TestControl::factoryOptionRequestOption6; + using TestControl::factoryRapidCommit6; + using TestControl::factoryRequestList4; + using TestControl::generateDuid; + using TestControl::generateMacAddress; + using TestControl::getNextExchangesNum; + using TestControl::openSocket; + using TestControl::receivePackets; + using TestControl::registerOptionFactories; + using TestControl::sendDiscover4; + using TestControl::sendSolicit6; + using TestControl::setDefaults4; + using TestControl::setDefaults6; + using TestControl::updateSendDue; + + NakedTestControl() : TestControl() { }; + +}; + /// \brief Test Fixture Class /// /// This test fixture class is used to perform @@ -32,36 +68,526 @@ using namespace isc::perfdhcp; class TestControlTest : public virtual ::testing::Test { public: + + typedef std::vector MacAddress; + typedef MacAddress::iterator MacAddressIterator; + + typedef std::vector Duid; + typedef Duid::iterator DuidIterator; + /// \brief Default Constructor TestControlTest() { } -protected: + /// \brief Get local loopback interface name. + /// + /// Scan available network interfaces for local loopback + /// interface and get its name. On Linux this interface is + /// usually called 'lo' but on other systems, e.g. BSD + /// it will have slightly different name. Local loopback + /// interface is required for unit tests that require + /// socket creation. + /// + /// \return local loopback interface name. + std::string getLocalLoopback() const { + const IfaceMgr::IfaceCollection& ifaces = + IfaceMgr::instance().getIfaces(); + for (IfaceMgr::IfaceCollection::const_iterator iface = ifaces.begin(); + iface != ifaces.end(); + ++iface) { + for (IfaceMgr::AddressCollection::const_iterator addr_it = + iface->getAddresses().begin(); + addr_it != iface->getAddresses().end(); + ++addr_it) { + if (asiolink::IOAddress("127.0.0.1").getAddress() == + addr_it->getAddress()) { + return iface->getName(); + } + } + } + return(""); + } + + /// \brief Match requested options in the buffer with given list. + /// + /// This method iterates through options provided in the buffer + /// and matches them with the options specified with first parameter. + /// Options in both vectors may be layed in different order. + /// + /// \param requested_options reference buffer with options. + /// \param buf test buffer with options that will be matched. + /// \return number of options from the buffer matched with options + /// in the reference buffer. + int matchRequestedOptions(const dhcp::OptionBuffer& requested_options, + const dhcp::OptionBuffer& buf) const { + size_t matched_num = 0; + for (size_t i = 0; i < buf.size(); ++i) { + for (int j = 0; j < requested_options.size(); ++j) { + if (requested_options[j] == buf[i]) { + // Requested option has been found. + ++matched_num; + break; + } + } + } + return matched_num; + } + + /// \brief Calculate the maximum vectors' mismatch position. + /// + /// This helper function calculates the maximum mismatch position + /// between two vectors (two different DUIDs or MAC addresses). + /// Calculated position is counted from the end of vectors. + /// Calculation is based on number of simulated clients. When number + /// of clients is less than 256 different DUIDs or MAC addresses can + /// can be coded in such a way that they differ on last vector element. + /// If number of clients is between 257 and 65536 they can differ + /// on two last positions so the returned value will be 2 and so on. + /// + /// \param clients_num number of simulated clinets + /// \param randomization range - for either MAC or DUID it is 6 + /// \return maximum mismatch position + int unequalOctetPosition(const int clients_num, + const size_t random_size) const { + int unequal_pos = 0; + int n = clients_num - 1; + if (n > 0) { + for (int i = 0; i < random_size; ++i) { + if (n < 256) { + unequal_pos = i + 1; + break; + } + n %= 256; + } + } + return unequal_pos; + } + + /// brief Test generation of mulitple DUIDs + /// + /// Thie method checks the generation of multiple DUIDs. Number + /// of iterations depends on the number of simulated clients. + /// It is expected that DUID's size is 14 (consists of DUID-LLT + /// HW type field, 4 octets of time value and MAC address). The + /// MAC address can be randomized depending on the number of + /// simulated clients. The DUID-LLT and HW type are expected to + /// be constant. The time value has to be properly calculated + /// as the number of seconds since DUID time epoch. The parts + /// of MAC address has to change if multiple clients are simulated + /// and do not change if single client is simulated. + void testDuid() const { + int clients_num = CommandOptions::instance().getClientsNum(); + // The old duid will be holding the previously generated DUID. + // It will be used to compare against the new one. If we have + // multiple clients we want to make sure that duids differ. + Duid old_duid(CommandOptions::instance().getDuidPrefix()); + Duid new_duid(0); + // total_dist shows the total difference between generated duid. + // It has to be greater than zero if multiple clients are simulated. + size_t total_dist = 0; + // Holds the position if the octet on which two DUIDS can be different. + // If number of clients is 256 or less it is last DUID octet (except for + // single client when subsequent DUIDs have to be equal). If number of + // clients is between 257 and 65536 the last two octets can differ etc. + const size_t mac_addr_size = 6; + int unequal_pos = unequalOctetPosition(clients_num, mac_addr_size); + + // Initialize Test Control class. + NakedTestControl tc; + // Perform number of iterations to generate number of DUIDs. + // If single clinet is involved, try multiple times (10) and + // see if the same DUID is always generated. + for (int i = 0; i < clients_num * 10; ++i) { + if (new_duid.empty()) { + new_duid = old_duid; + } else { + std::swap(old_duid, new_duid); + new_duid = tc.generateDuid(); + } + // The DUID-LLT is expected to start with DUID_LLT value + // of 1 and hardware ethernet type equal to 1 (HWETHER_TYPE). + const uint8_t duid_llt_and_hw[4] = { 0x0, 0x1, 0x0, 0x1 }; + // We assume DUID-LLT length 14. This includes 4 octets of + // DUID_LLT value, two octets of hardware type, 4 octets + // of time value and 6 octets of variable link layer (MAC) + // address. + const int duid_llt_size = 14; + ASSERT_EQ(duid_llt_size, new_duid.size()); + // The first four octets do not change. + EXPECT_TRUE(std::equal(new_duid.begin(), new_duid.begin() + 4, + duid_llt_and_hw)); + + // As described in RFC3315: 'the time value is the time + // that the DUID is generated represented in seconds + // since midnight (UTC), January 1, 2000, modulo 2^32.' + uint32_t duid_time = 0; + // Pick 4 bytes of the time from generated DUID and put them + // in reverse order (in DUID they are stored in network order). + for (int i = 4; i < 8; ++i) { + duid_time |= new_duid[i] << (i - 4); + } + // Calculate the duration since epoch time. + ptime now = microsec_clock::universal_time(); + ptime duid_epoch(from_iso_string("20000101T000000")); + time_period period(duid_epoch, now); + + // Current time is the same or later than time from the DUID because + // DUID had been generated before reference duration was calculated. + EXPECT_GE(period.length().total_seconds(), duid_time); + + // Get the mismatch position (counting from the end) of + // mismatched octet between previously generated DUID + // and current. + std::pair mismatch_pos = + std::mismatch(old_duid.begin(), old_duid.end(), + new_duid.begin()); + size_t mismatch_dist = + std::distance(mismatch_pos.first, old_duid.end()); + // For single client total_dist is expected to be 0 because + // old_duid and new_duid should always match. If we have + // more clients then duids have to differ except the case + // if randomization algorithm generates the same values but + // this would be an error in randomization algorithm. + total_dist += mismatch_dist; + // Mismatch may have occured on the DUID octet position + // up to calculated earlier unequal_pos. + ASSERT_LE(mismatch_dist, unequal_pos); + } + // If we have more than one client at least one mismatch occured. + if (clients_num > 1) { + EXPECT_GT(total_dist, 0); + } else { + EXPECT_EQ(0, total_dist); + } + } + + /// \brief Test generation of multiple MAC addresses. + /// + /// This method validates generation of multiple MAC addresses. + /// The MAC address can be randomized depending on the number + /// of simulated clients. This test checks if different MAC + /// addresses are generated if number of simulated clients is + /// greater than 1. It also checks if the same MAC addresses is + /// generated if only 1 client is simulated. + void testMacAddress() const { + int clients_num = CommandOptions::instance().getClientsNum(); + // The old_mac will be holding the value of previously generated + // MAC address. We will be comparing the newly generated one with it + // to see if it changes when mulitple clients are simulated or if it + // does not change when single client is simulated. + MacAddress old_mac(CommandOptions::instance().getMacPrefix()); + // Holds the position if the octet on which two MAC addresses can + // be different. If number of clients is 256 or less it is last MAC + // octet (except for single client when subsequent MAC addresses + // have to be equal). If number of clients is between 257 and 65536 + // the last two octets can differ etc. + int unequal_pos = unequalOctetPosition(clients_num, old_mac.size());; + + NakedTestControl tc; + size_t total_dist = 0; + // Do many iterations to generate and test MAC address values. + for (int i = 0; i < clients_num * 10; ++i) { + // Generate new MAC address. + MacAddress new_mac(tc.generateMacAddress()); + // Get the mismatch position (counting from the end) of + // mismatched octet between previously generated MAC address + // and current. + std::pair mismatch_pos = + std::mismatch(old_mac.begin(), old_mac.end(), new_mac.begin()); + size_t mismatch_dist = + std::distance(mismatch_pos.first, old_mac.end()); + // For single client total_dist is expected to be 0 because + // old_mac and new_mac should always match. If we have + // more clients then MAC addresses have to differ except + // the case if randomization algorithm generates the same + // values but this would be an error in randomization algorithm. + total_dist += mismatch_dist; + // Mismatch may have occured on the MAC address'es octet position + // up to calculated earlier unequal_pos. + ASSERT_LE(mismatch_dist, unequal_pos); + } + // If we have more than one client at least one mismatch occured. + if (clients_num > 1) { + EXPECT_GT(total_dist, 0); + } else { + EXPECT_EQ(total_dist, 0); + } + } + + /// \brief Parse command line string with CommandOptions. + /// + /// \param cmdline command line string to be parsed. + /// \throw isc::Unexpected if unexpected error occured. + /// \throw isc::InvalidParameter if command line is invalid. + void processCmdLine(const std::string& cmdline) const { + CommandOptionsHelper::process(cmdline); + } + }; -TEST_F(TestControlTest, Run) { - // Get the instance of TestControl object. - TestControl& test_control = TestControl::instance(); - // Running test without parsing command line arguments is - // expected to cause exception. - EXPECT_THROW(test_control.run(), isc::InvalidOperation); +TEST_F(TestControlTest, GenerateDuid) { + // Simple command line that simulates one client only. Always the + // same DUID will be generated. + ASSERT_NO_THROW(processCmdLine("perfdhcp -l 127.0.0.1 all")); + testDuid(); - // The command line is to run single test iteration and exit. - // We have to declare argv as const walk around the problem - // of deprecated conversion from string to char*. - const char* argv[] = { "perfdhcp", "-l", "127.0.0.1", "-r", "10", "-n", "1" }; - const int argc = sizeof(argv) / sizeof(argv[0]); + // Simulate 50 clients. Different DUID will be generated. + ASSERT_NO_THROW(processCmdLine("perfdhcp -l 127.0.0.1 -R 50 all")); + testDuid(); +} + +TEST_F(TestControlTest, GenerateMacAddress) { + // Simulate one client only. Always the same MAC address will be + // generated. + ASSERT_NO_THROW(processCmdLine("perfdhcp -l 127.0.0.1 all")); + testMacAddress(); + + // Simulate 50 clients. Different MAC addresses will be generated. + ASSERT_NO_THROW(processCmdLine("perfdhcp -l 127.0.0.1 -R 50 all")); + testMacAddress(); +} + +TEST_F(TestControlTest, Options4) { + using namespace isc::dhcp; + NakedTestControl tc; + // By default the IP version mode is V4 so there is no need to + // parse command line to override the IP version. Note that + // registerOptionFactories is used for both V4 and V6. + tc.registerOptionFactories(); + // Create option with buffer size equal to 1 and holding DHCPDISCOVER + // message type. + OptionPtr opt_msg_type(Option::factory(Option::V4, DHO_DHCP_MESSAGE_TYPE, + OptionBuffer(1, DHCPDISCOVER))); + // Validate the option type and universe. + EXPECT_EQ(Option::V4, opt_msg_type->getUniverse()); + EXPECT_EQ(DHO_DHCP_MESSAGE_TYPE, opt_msg_type->getType()); + // Validate the message type from the option we have now created. + uint8_t msg_type = 0; + ASSERT_NO_THROW(msg_type = opt_msg_type->getUint8()); + EXPECT_EQ(DHCPDISCOVER, msg_type); + + // Create another option: DHCP_PARAMETER_REQUEST_LIST + OptionPtr + opt_requested_options(Option::factory(Option::V4, + DHO_DHCP_PARAMETER_REQUEST_LIST)); + // Here is a list of options that we are requesting in the + // server's response. + const uint8_t requested_options[] = { + DHO_SUBNET_MASK, + DHO_BROADCAST_ADDRESS, + DHO_TIME_OFFSET, + DHO_ROUTERS, + DHO_DOMAIN_NAME, + DHO_DOMAIN_NAME_SERVERS, + DHO_HOST_NAME + }; + + OptionBuffer + requested_options_ref(requested_options, + requested_options + sizeof(requested_options)); + + // Get the option buffer. It should hold the combination of values + // listed in requested_options array. However their order can be + // different in general so we need to search each value separatelly. + const OptionBuffer& requested_options_buf = + opt_requested_options->getData(); + EXPECT_EQ(requested_options_ref.size(), requested_options_buf.size()); + size_t matched_num = matchRequestedOptions(requested_options_ref, + requested_options_buf); + // We want exactly the same requested options as listed in + // requested_options array - nothing more or less. + EXPECT_EQ(requested_options_ref.size(), matched_num); +} + +TEST_F(TestControlTest, Options6) { + using namespace isc::dhcp; + + // Lets override the IP version to test V6 options (-6 parameter) + ASSERT_NO_THROW(processCmdLine("perfdhcp -l 127.0.0.1 -6 all")); + + NakedTestControl tc; + tc.registerOptionFactories(); + + // Validate the D6O_ELAPSED_TIME option. + OptionPtr opt_elapsed_time(Option::factory(Option::V6, D6O_ELAPSED_TIME)); + // Validate the option type and universe. + EXPECT_EQ(Option::V6, opt_elapsed_time->getUniverse()); + EXPECT_EQ(D6O_ELAPSED_TIME, opt_elapsed_time->getType()); + // The default value of elapsed time is zero. + uint16_t elapsed_time; + ASSERT_NO_THROW(elapsed_time = opt_elapsed_time->getUint16()); + EXPECT_EQ(0, elapsed_time); + + // With the factory function we may also specify the actual + // value of elapsed time. Let's make use of std::vector + // constructor to create the option buffer, 2 octets long + // with each octet initialized to 0x1. + size_t elapsed_time_buf_size = 2; + uint8_t elapsed_time_pattern = 0x1; + OptionPtr + opt_elapsed_time2(Option::factory(Option::V6, D6O_ELAPSED_TIME, + OptionBuffer(elapsed_time_buf_size, + elapsed_time_pattern))); + + // Any buffer that has size neither equal to 0 nor 2 is considered invalid. + elapsed_time_buf_size = 1; + EXPECT_THROW( + Option::factory(Option::V6, D6O_ELAPSED_TIME, + OptionBuffer(elapsed_time_buf_size, elapsed_time_pattern)), + isc::BadValue + ); + + // Validate the option type and universe. + EXPECT_EQ(Option::V6, opt_elapsed_time2->getUniverse()); + EXPECT_EQ(D6O_ELAPSED_TIME, opt_elapsed_time2->getType()); + // Make sure the getUint16 does not throw exception. It wile throw + // buffer is shorter than 2 octets. + ASSERT_NO_THROW(elapsed_time = opt_elapsed_time2->getUint16()); + // Check the expected value of elapsed time. + EXPECT_EQ(0x0101, elapsed_time); + + // Validate the D6O_RAPID_COMMIT option. + OptionPtr opt_rapid_commit(Option::factory(Option::V6, D6O_RAPID_COMMIT)); + // Validate the option type and universe. + EXPECT_EQ(Option::V6, opt_rapid_commit->getUniverse()); + EXPECT_EQ(D6O_RAPID_COMMIT, opt_rapid_commit->getType()); + // Rapid commit has no data payload. + EXPECT_THROW(opt_rapid_commit->getUint8(), isc::OutOfRange); + + // Validate the D6O_CLIENTID option. + OptionBuffer duid(CommandOptions::instance().getDuidPrefix()); + OptionPtr opt_clientid(Option::factory(Option::V6, D6O_CLIENTID, duid)); + EXPECT_EQ(Option::V6, opt_clientid->getUniverse()); + EXPECT_EQ(D6O_CLIENTID, opt_clientid->getType()); + const OptionBuffer& duid2 = opt_clientid->getData(); + ASSERT_EQ(duid.size(), duid2.size()); + // The Duid we set for option is the same we get. + EXPECT_TRUE(std::equal(duid.begin(), duid.end(), duid2.begin())); + + // Validate the D6O_ORO (Option Request Option). + OptionPtr opt_oro(Option::factory(Option::V6, D6O_ORO)); + // Prepare the reference buffer with requested options. + const uint8_t requested_options[] = { + D6O_NAME_SERVERS, + D6O_DOMAIN_SEARCH + }; + OptionBuffer + requested_options_ref(requested_options, + requested_options + sizeof(requested_options)); + // Get the buffer from option. + const OptionBuffer& requested_options_buf = opt_oro->getData(); + // Size of reference buffer and option buffer have to be + // the same for comparison. + EXPECT_EQ(requested_options_ref.size(), requested_options_buf.size()); + // Check if all options in the buffer are matched with reference buffer. + size_t matched_num = matchRequestedOptions(requested_options_ref, + requested_options_buf); + EXPECT_EQ(sizeof(requested_options), matched_num); + + // Validate the D6O_IA_NA option. + OptionPtr opt_ia_na(Option::factory(Option::V6, D6O_IA_NA)); + EXPECT_EQ(Option::V6, opt_ia_na->getUniverse()); + EXPECT_EQ(D6O_IA_NA, opt_ia_na->getType()); + // Every IA_NA option is expected to start with this sequence. + const uint8_t opt_ia_na_array[] = { + 0, 0, 0, 1, // IAID = 1 + 0, 0, 3600 >> 8, 3600 && 0xff, // T1 = 3600 + 0, 0, 5400 >> 8, 5400 & 0xff, // T2 = 5400 + }; + OptionBuffer opt_ia_na_ref(opt_ia_na_array, + opt_ia_na_array + sizeof(opt_ia_na_array)); + const OptionBuffer& opt_ia_na_buf = opt_ia_na->getData(); + ASSERT_EQ(opt_ia_na_buf.size(), opt_ia_na_ref.size()); + EXPECT_TRUE(std::equal(opt_ia_na_ref.begin(), opt_ia_na_ref.end(), + opt_ia_na_buf.begin())); + + // TODO: Add more tests for IA address options. +} + +TEST_F(TestControlTest, Packet4) { + // Use Interface Manager to get the local loopback interface. + // If interface can't be found we don't want to fail test. + std::string loopback_iface(getLocalLoopback()); + if (!loopback_iface.empty()) { + ASSERT_NO_THROW(processCmdLine("perfdhcp -l " + loopback_iface + + " all")); + uint16_t port = 10547; + NakedTestControl tc; + int sock_handle = 0; + // We have to create the socket to setup some parameters of + // outgoing packet. + ASSERT_NO_THROW(sock_handle = tc.openSocket(port)); + TestControl::TestControlSocket sock(sock_handle); + uint32_t transid = 123; + boost::shared_ptr pkt4(new Pkt4(DHCPDISCOVER, transid)); + // Set parameters on outgoing packet. + ASSERT_NO_THROW(tc.setDefaults4(sock, pkt4)); + // Validate that packet has been setup correctly. + EXPECT_EQ(loopback_iface, pkt4->getIface()); + EXPECT_EQ(sock.getIfIndex(), pkt4->getIndex()); + EXPECT_EQ(DHCP4_CLIENT_PORT, pkt4->getLocalPort()); + EXPECT_EQ(DHCP4_SERVER_PORT, pkt4->getRemotePort()); + EXPECT_EQ(1, pkt4->getHops()); + EXPECT_EQ(asiolink::IOAddress("255.255.255.255"), + pkt4->getRemoteAddr()); + EXPECT_EQ(asiolink::IOAddress(sock.getAddress()), pkt4->getLocalAddr()); + EXPECT_EQ(asiolink::IOAddress(sock.getAddress()), pkt4->getGiaddr()); + } else { + std::cout << "Unable to find the loopback interface. Skip test. " + << std::endl; + } +} + +TEST_F(TestControlTest, Packet6) { + // Use Interface Manager to get the local loopback interface. + // If the interface can't be found we don't want to fail test. + std::string loopback_iface(getLocalLoopback()); + if (!loopback_iface.empty()) { + ASSERT_NO_THROW(processCmdLine("perfdhcp -6 -l " + loopback_iface + + " servers")); + uint16_t port = 10547; + NakedTestControl tc; + int sock_handle = 0; + // Create the socket. It will be needed to set packet's + // parameters. + ASSERT_NO_THROW(sock_handle = tc.openSocket(port)); + TestControl::TestControlSocket sock(sock_handle); + uint32_t transid = 123; + boost::shared_ptr pkt6(new Pkt6(DHCPV6_SOLICIT, transid)); + // Set packet's parameters. + ASSERT_NO_THROW(tc.setDefaults6(sock, pkt6)); + // Validate if parameters have been set correctly. + EXPECT_EQ(loopback_iface, pkt6->getIface()); + EXPECT_EQ(sock.getIfIndex(), pkt6->getIndex()); + EXPECT_EQ(DHCP6_CLIENT_PORT, pkt6->getLocalPort()); + EXPECT_EQ(DHCP6_SERVER_PORT, pkt6->getRemotePort()); + EXPECT_EQ(sock.getAddress(), pkt6->getLocalAddr()); + EXPECT_EQ(asiolink::IOAddress("FF05::1:3"), pkt6->getRemoteAddr()); + } else { + std::cout << "Unable to find the loopback interface. Skip test. " + << std::endl; + } +} + +TEST_F(TestControlTest, RateControl) { + // We don't specify the exchange rate here so the aggressivity + // value will determine how many packets are to be send each + // time we query the getNextExchangesNum. + ASSERT_NO_THROW(processCmdLine("perfdhcp -l 127.0.0.1 all")); CommandOptions& options = CommandOptions::instance(); - // const_cast is odd but it seems to be the most straight forward way - // to achive the goal. In the future we might think about creating - // a tokenizing function that would dynamically produce non-const - // argv value. - ASSERT_NO_THROW(options.parse(argc, const_cast(argv))); - EXPECT_NO_THROW(test_control.run()); + NakedTestControl tc1; + uint64_t xchgs_num = tc1.getNextExchangesNum(); + EXPECT_EQ(options.getAggressivity(), xchgs_num); - // This is ok to run the test again with the same parameters. - // It may trigger exception if TestControl singleton is in - // invalid state after test run. We want to make sure it is - // safe to rerun the test. - EXPECT_NO_THROW(test_control.run()); + // The exchange rate is now 1 per second. We don't know how many + // exchanges have to initiated exactly but for sure it has to be + // non-zero value. Also, since aggressivity is very high we expect + // that it will not be restricted by aggressivity. + ASSERT_NO_THROW( + processCmdLine("perfdhcp -l 127.0.0.1 -a 1000000 -r 1 all") + ); + NakedTestControl tc2; + xchgs_num = tc2.getNextExchangesNum(); + EXPECT_GT(xchgs_num, 0); + EXPECT_LT(xchgs_num, options.getAggressivity()); } From 00e8a3ebd6c42824b0bfdd0ca96a6acd179a1194 Mon Sep 17 00:00:00 2001 From: Marcin Siodelski Date: Thu, 16 Aug 2012 17:04:39 +0200 Subject: [PATCH 16/40] [1959] Use StatsMgr to track packet exchanges. --- tests/tools/perfdhcp/stats_mgr.h | 28 ++++--- tests/tools/perfdhcp/test_control.cc | 105 ++++++++++++++++++++++++--- tests/tools/perfdhcp/test_control.h | 33 ++++++++- 3 files changed, 142 insertions(+), 24 deletions(-) diff --git a/tests/tools/perfdhcp/stats_mgr.h b/tests/tools/perfdhcp/stats_mgr.h index 245c69e3cf..7ade910192 100644 --- a/tests/tools/perfdhcp/stats_mgr.h +++ b/tests/tools/perfdhcp/stats_mgr.h @@ -243,20 +243,21 @@ public: /// In this mode all packets are stored throughout the test execution. ExchangeStats(const ExchangeType xchg_type, const bool archive_enabled) : xchg_type_(xchg_type), - min_delay_(std::numeric_limits::max()), - max_delay_(0.), - sum_delay_(0.), - orphans_(0), - sum_delay_squared_(0.), - ordered_lookups_(0), - unordered_lookup_size_sum_(0), - unordered_lookups_(0), - sent_packets_num_(0), - rcvd_packets_num_(0), sent_packets_(), rcvd_packets_(), archived_packets_(), - archive_enabled_(archive_enabled) { + archive_enabled_(archive_enabled), + min_delay_(std::numeric_limits::max()), + max_delay_(0.), + sum_delay_(0.), + sum_delay_squared_(0.), + orphans_(0), + unordered_lookup_size_sum_(0), + unordered_lookups_(0), + ordered_lookups_(0), + sent_packets_num_(0), + rcvd_packets_num_(0) + { next_sent_ = sent_packets_.begin(); } @@ -555,7 +556,10 @@ public: /// number of dropped packets and number of orphans. void printMainStats() const { using namespace std; - uint64_t drops = getRcvdPacketsNum() - getSentPacketsNum(); + uint64_t drops = 0; + if (getRcvdPacketsNum() >= getSentPacketsNum()) { + drops = getRcvdPacketsNum() - getSentPacketsNum(); + } cout << "sent packets: " << getSentPacketsNum() << endl << "received packets: " << getRcvdPacketsNum() << endl << "drops: " << drops << endl diff --git a/tests/tools/perfdhcp/test_control.cc b/tests/tools/perfdhcp/test_control.cc index 9f4bd5c493..286fc823e7 100644 --- a/tests/tools/perfdhcp/test_control.cc +++ b/tests/tools/perfdhcp/test_control.cc @@ -268,6 +268,27 @@ TestControl::getNextExchangesNum() const { return (0); } +void +TestControl::initializeStatsMgr() { + CommandOptions& options = CommandOptions::instance(); + if (options.getIpVersion() == 4) { + stats_mgr4_.reset(); + stats_mgr4_ = StatsMgr4Ptr(new StatsMgr4()); + stats_mgr4_->addExchangeStats(StatsMgr4::XCHG_DO); + if (options.getExchangeMode() == CommandOptions::DO_SA) { + stats_mgr4_->addExchangeStats(StatsMgr4::XCHG_RA); + } + + } else if (options.getIpVersion() == 6) { + stats_mgr6_.reset(); + stats_mgr6_ = StatsMgr6Ptr(new StatsMgr6()); + stats_mgr6_->addExchangeStats(StatsMgr6::XCHG_SA); + if (options.getExchangeMode() == CommandOptions::DO_SA) { + stats_mgr6_->addExchangeStats(StatsMgr6::XCHG_RR); + } + } +} + int TestControl::openSocket(uint16_t port) const { CommandOptions& options = CommandOptions::instance(); @@ -278,9 +299,9 @@ TestControl::openSocket(uint16_t port) const { IOAddress remoteaddr(servername); if (port == 0) { if (options.getIpVersion() == 6) { - port = 547; - } else if (port == 0) { - port = 67; + port = DHCP6_CLIENT_PORT; + } else if (options.getIpVersion() == 4) { + port = 67; // TODO: find out why port 68 is wrong here. } } if (options.getIpVersion() == 6) { @@ -351,9 +372,59 @@ TestControl::openSocket(uint16_t port) const { } } } + return(sock); } +void +TestControl::printStats() const { + CommandOptions& options = CommandOptions::instance(); + if (options.getIpVersion() == 4) { + if (!stats_mgr4_) { + isc_throw(InvalidOperation, "Statistics Manager for DHCPv4 " + "hasn't been initialized"); + } + stats_mgr4_->printStats(); + } else if (options.getIpVersion() == 6) { + if (!stats_mgr6_) { + isc_throw(InvalidOperation, "Statistics Manager for DHCPv6 " + "hasn't been initialized"); + } + stats_mgr6_->printStats(); + } +} + +void +TestControl::receivePacket4(Pkt4Ptr& pkt4) { + switch(pkt4->getType()) { + case DHCPOFFER : + stats_mgr4_->passRcvdPacket(StatsMgr4::XCHG_DO, pkt4); + break; + case DHCPACK : + stats_mgr4_->passRcvdPacket(StatsMgr4::XCHG_RA, pkt4); + break; + default: + isc_throw(BadValue, "unknown type " << pkt4->getType() + << " of received DHCPv4 packet"); + } +} + +void +TestControl::receivePacket6(Pkt6Ptr& pkt6) { + switch(pkt6->getType()) { + case DHCPV6_ADVERTISE : + stats_mgr6_->passRcvdPacket(StatsMgr6::XCHG_SA, pkt6); + break; + case DHCPV6_REPLY : + stats_mgr6_->passRcvdPacket(StatsMgr6::XCHG_RR, pkt6); + break; + default: + isc_throw(BadValue, "unknown type " << pkt6->getType() + << " of received DHCPv6 packet"); + } + +} + void TestControl::receivePackets() { int timeout = 0; @@ -364,17 +435,17 @@ TestControl::receivePackets() { if (!pkt4) { receiving = false; } else { - // TODO: replace this with use of StatsMgr to increase - // number of received packets. This can be done once - // the 1958 ticket is reviewed and checked-in. - std::cout << "Received packet" << std::endl; + pkt4->unpack(); + receivePacket4(pkt4); } } else if (CommandOptions::instance().getIpVersion() == 6) { Pkt6Ptr pkt6 = IfaceMgr::instance().receive6(timeout); if (!pkt6) { receiving = false; } else { - std::cout << "Received DHCPv6 packet" << std::endl; + if (pkt6->unpack()) { + receivePacket6(pkt6); + } } } } @@ -453,6 +524,8 @@ TestControl::run() { } registerOptionFactories(); TestControlSocket socket(openSocket()); + + initializeStatsMgr(); uint64_t packets_sent = 0; for (;;) { updateSendDue(); @@ -470,9 +543,9 @@ TestControl::run() { sendSolicit6(socket); } ++packets_sent; - cout << "Packets sent " << packets_sent << endl; } } + printStats(); } void @@ -482,7 +555,7 @@ TestControl::sendDiscover4(const TestControlSocket& socket) { // Generate the MAC address to be passed in the packet. std::vector mac_address = generateMacAddress(); // Generate trasnaction id to be set for the new exchange. - const uint32_t transid = static_cast(random()); + const uint32_t transid = static_cast(random() % 0x00FFFFFF); boost::shared_ptr pkt4(new Pkt4(DHCPDISCOVER, transid)); if (!pkt4) { isc_throw(Unexpected, "failed to create DISCOVER packet"); @@ -500,6 +573,11 @@ TestControl::sendDiscover4(const TestControlSocket& socket) { setDefaults4(socket, pkt4); pkt4->pack(); IfaceMgr::instance().send(pkt4); + if (!stats_mgr4_) { + isc_throw(InvalidOperation, "Statistics Manager for DHCPv4 " + "hasn't been initialized"); + } + stats_mgr4_->passSentPacket(StatsMgr4::XCHG_DO, pkt4); } void @@ -511,7 +589,7 @@ TestControl::sendSolicit6(const TestControlSocket& socket) { // Generate DUID to be passed to the packet std::vector duid = generateDuid(); // Generate trasnaction id to be set for the new exchange. - const uint32_t transid = static_cast(random()); + const uint32_t transid = static_cast(random() % 0x00FFFFFF); boost::shared_ptr pkt6(new Pkt6(DHCPV6_SOLICIT, transid)); if (!pkt6) { isc_throw(Unexpected, "failed to create SOLICIT packet"); @@ -525,6 +603,11 @@ TestControl::sendSolicit6(const TestControlSocket& socket) { setDefaults6(socket, pkt6); pkt6->pack(); IfaceMgr::instance().send(pkt6); + if (!stats_mgr6_) { + isc_throw(InvalidOperation, "Statistics Manager for DHCPv6 " + "hasn't been initialized"); + } + stats_mgr6_->passSentPacket(StatsMgr6::XCHG_SA, pkt6); } void diff --git a/tests/tools/perfdhcp/test_control.h b/tests/tools/perfdhcp/test_control.h index de3b942ade..c153fac3b6 100644 --- a/tests/tools/perfdhcp/test_control.h +++ b/tests/tools/perfdhcp/test_control.h @@ -19,12 +19,15 @@ #include #include +#include #include #include #include #include +#include "stats_mgr.h" + namespace isc { namespace perfdhcp { @@ -44,6 +47,15 @@ namespace perfdhcp { class TestControl : public boost::noncopyable { public: + // Statistics Manager for DHCPv4. + typedef StatsMgr StatsMgr4; + // Pointer to Statistics Manager for DHCPv4; + typedef boost::shared_ptr StatsMgr4Ptr; + // Statictics Manager for DHCPv6. + typedef StatsMgr StatsMgr6; + // Pointer to Statistics Manager for DHCPv6. + typedef boost::shared_ptr StatsMgr6Ptr; + /// \brief Socket wrapper class. /// /// This is wrapper class that holds descriptor of the socket @@ -152,7 +164,6 @@ protected: /// \return true if any of the exit conditions is fulfiled. bool checkExitConditions() const; - /// \brief Factory function to create DHCPv6 ELAPSED_TIME option. /// /// This factory function creates DHCPv6 ELAPSED_TIME option instance. @@ -284,6 +295,12 @@ protected: /// \return number of exchanges to be started immediatelly. uint64_t getNextExchangesNum() const; + /// \brief Initializes Statistics Manager. + /// + /// This function initializes Statistics Manager. If there is + /// the one initialized already it is released. + void initializeStatsMgr(); + /// \brief Open socket to communicate with DHCP server. /// /// Method opens socket and binds it to local address. Function will @@ -305,6 +322,17 @@ protected: /// \return socket descriptor. int openSocket(uint16_t port = 0) const; + /// \brief Print performance statistics. + /// + /// Method prints performance statistics. + /// \throws isc::InvalidOperation if Statistics Manager was + /// not initialized. + void printStats() const; + + void receivePacket4(dhcp::Pkt4Ptr& pkt4); + + void receivePacket6(dhcp::Pkt6Ptr& pkt4); + /// \brief Receive DHCPv4 or DHCPv6 packets from the server. /// /// Method receives DHCPv4 or DHCPv6 packets from the server. @@ -408,6 +436,9 @@ private: boost::posix_time::ptime last_sent_; ///< Indicates when the last exchange /// was initiated. + StatsMgr4Ptr stats_mgr4_; /// Statistics Manager 4. + StatsMgr6Ptr stats_mgr6_; /// Statistics Manager 6. + uint64_t sent_packets_0_; uint64_t sent_packets_1_; }; From fbbae39aea715ccca384083611b6faa8250e068d Mon Sep 17 00:00:00 2001 From: Marcin Siodelski Date: Tue, 21 Aug 2012 15:17:14 +0200 Subject: [PATCH 17/40] [1959] Send request messages to server and corresponding unit tests. --- tests/tools/perfdhcp/stats_mgr.h | 75 ++++-- tests/tools/perfdhcp/test_control.cc | 228 ++++++++++++++--- tests/tools/perfdhcp/test_control.h | 132 +++++++++- .../perfdhcp/tests/test_control_unittest.cc | 237 +++++++++++++++++- 4 files changed, 603 insertions(+), 69 deletions(-) diff --git a/tests/tools/perfdhcp/stats_mgr.h b/tests/tools/perfdhcp/stats_mgr.h index 7ade910192..5a3f11445d 100644 --- a/tests/tools/perfdhcp/stats_mgr.h +++ b/tests/tools/perfdhcp/stats_mgr.h @@ -47,8 +47,8 @@ namespace perfdhcp { /// stored on the list of sent packets. When packets are matched the /// round trip time can be calculated. /// -/// \tparam T class representing DHCPv4 or DHCPv6 packet. -template +/// \param T class representing DHCPv4 or DHCPv6 packet. +template class StatsMgr : public boost::noncopyable { public: @@ -138,7 +138,7 @@ public: /// \param packet packet which transaction id is to be hashed. /// \throw isc::BadValue if packet is null. /// \return transaction id hash. - static uint32_t hashTransid(const boost::shared_ptr& packet) { + static uint32_t hashTransid(const boost::shared_ptr& packet) { if (!packet) { isc_throw(BadValue, "Packet is null"); } @@ -214,12 +214,12 @@ public: /// } /// \endcode typedef boost::multi_index_container< - boost::shared_ptr, + boost::shared_ptr, boost::multi_index::indexed_by< boost::multi_index::sequenced<>, boost::multi_index::hashed_non_unique< boost::multi_index::global_fun< - const boost::shared_ptr&, + const boost::shared_ptr&, uint32_t, &ExchangeStats::hashTransid > @@ -228,7 +228,7 @@ public: > PktList; /// Packet list iterator for sequencial access to elements. - typedef typename PktList::const_iterator PktListIterator; + typedef typename PktList::iterator PktListIterator; /// Packet list index to search packets using transaction id hash. typedef typename PktList::template nth_index<1>::type PktListTransidHashIndex; @@ -267,7 +267,7 @@ public: /// /// \param packet packet object to be added. /// \throw isc::BadValue if packet is null. - void appendSent(const boost::shared_ptr& packet) { + void appendSent(const boost::shared_ptr& packet) { if (!packet) { isc_throw(BadValue, "Packet is null"); } @@ -281,7 +281,7 @@ public: /// /// \param packet packet object to be added. /// \throw isc::BadValue if packet is null. - void appendRcvd(const boost::shared_ptr& packet) { + void appendRcvd(const boost::shared_ptr& packet) { if (!packet) { isc_throw(BadValue, "Packet is null"); } @@ -297,8 +297,8 @@ public: /// \param rcvd_packet received packet /// \throw isc::BadValue if sent or received packet is null. /// \throw isc::Unexpected if failed to calculate timestamps - void updateDelays(const boost::shared_ptr& sent_packet, - const boost::shared_ptr& rcvd_packet) { + void updateDelays(const boost::shared_ptr& sent_packet, + const boost::shared_ptr& rcvd_packet) { if (!sent_packet) { isc_throw(BadValue, "Sent packet is null"); } @@ -356,7 +356,8 @@ public: /// \throw isc::BadValue if received packet is null. /// \return packet having specified transaction or NULL if packet /// not found - boost::shared_ptr matchPackets(const boost::shared_ptr& rcvd_packet) { + boost::shared_ptr + matchPackets(const boost::shared_ptr& rcvd_packet) { if (!rcvd_packet) { isc_throw(BadValue, "Received packet is null"); } @@ -367,7 +368,7 @@ public: // that the received packet we got has no corresponding // sent packet so orphans counter has to be updated. ++orphans_; - return(boost::shared_ptr()); + return(boost::shared_ptr()); } else if (next_sent_ == sent_packets_.end()) { // Even if there are still many unmatched packets on the // list we might hit the end of it because of unordered @@ -426,13 +427,13 @@ public: // If we are here, it means that both ordered lookup and // unordered lookup failed. Searched packet is not on the list. ++orphans_; - return(boost::shared_ptr()); + return(boost::shared_ptr()); } // Packet is matched so we count it. We don't count unmatched packets // as they are counted as orphans with a separate counter. ++rcvd_packets_num_; - boost::shared_ptr sent_packet(*next_sent_); + boost::shared_ptr sent_packet(*next_sent_); // If packet was found, we assume it will be never searched // again. We want to delete this packet from the list to // improve performance of future searches. @@ -549,6 +550,19 @@ public: /// \return number of received packets. uint64_t getRcvdPacketsNum() const { return(rcvd_packets_num_); } + /// \brief Return number of dropped packets. + /// + /// Method returns number of dropped packets. + /// + /// \return number of dropped packets. + uint64_t getDroppedPacketsNum() const { + uint64_t drops = 0; + if (getSentPacketsNum() > getRcvdPacketsNum()) { + drops = getSentPacketsNum() - getRcvdPacketsNum(); + } + return(drops); + } + /// \brief Print main statistics for packet exchange. /// /// Method prints main statistics for particular exchange. @@ -556,13 +570,9 @@ public: /// number of dropped packets and number of orphans. void printMainStats() const { using namespace std; - uint64_t drops = 0; - if (getRcvdPacketsNum() >= getSentPacketsNum()) { - drops = getRcvdPacketsNum() - getSentPacketsNum(); - } cout << "sent packets: " << getSentPacketsNum() << endl << "received packets: " << getRcvdPacketsNum() << endl - << "drops: " << drops << endl + << "drops: " << getDroppedPacketsNum() << endl << "orphans: " << getOrphans() << endl; } @@ -614,7 +624,7 @@ public: for (PktListIterator it = rcvd_packets_.begin(); it != rcvd_packets_.end(); ++it) { - boost::shared_ptr rcvd_packet = *it; + boost::shared_ptr rcvd_packet = *it; PktListTransidHashIndex& idx = archived_packets_.template get<1>(); std::pairgetTransid() == rcvd_packet->getTransid()) { - boost::shared_ptr sent_packet = *it_archived; + boost::shared_ptr sent_packet = *it_archived; // Get sent and received packet times. ptime sent_time = sent_packet->getTimestamp(); ptime rcvd_time = rcvd_packet->getTimestamp(); @@ -839,7 +849,7 @@ public: /// \throw isc::BadValue if invalid exchange type specified or /// packet is null. void passSentPacket(const ExchangeType xchg_type, - const boost::shared_ptr& packet) { + const boost::shared_ptr& packet) { ExchangeStatsPtr xchg_stats = getExchangeStats(xchg_type); xchg_stats->appendSent(packet); } @@ -857,10 +867,11 @@ public: /// or packet is null. /// \throw isc::Unexpected if corresponding packet was not /// found on the list of sent packets. - void passRcvdPacket(const ExchangeType xchg_type, - const boost::shared_ptr& packet) { + boost::shared_ptr + passRcvdPacket(const ExchangeType xchg_type, + const boost::shared_ptr& packet) { ExchangeStatsPtr xchg_stats = getExchangeStats(xchg_type); - boost::shared_ptr sent_packet + boost::shared_ptr sent_packet = xchg_stats->matchPackets(packet); if (sent_packet) { @@ -869,6 +880,7 @@ public: xchg_stats->appendRcvd(packet); } } + return(sent_packet); } /// \brief Return minumum delay between sent and received packet. @@ -1003,6 +1015,19 @@ public: return(xchg_stats->getRcvdPacketsNum()); } + /// \brief Return total number of dropped packets. + /// + /// Method returns total number of dropped packets for specified + /// exchange type. + /// + /// \param xchg_type exchange type. + /// \throw isc::BadValue if invalid exchange type specified. + /// \return number of dropped packets. + uint64_t getDroppedPacketsNum(const ExchangeType xchg_type) const { + ExchangeStatsPtr xchg_stats = getExchangeStats(xchg_type); + return(xchg_stats->getDroppedPacketsNum()); + } + /// \brief Return name of the exchange. /// /// Method returns name of the specified exchange type. diff --git a/tests/tools/perfdhcp/test_control.cc b/tests/tools/perfdhcp/test_control.cc index 286fc823e7..87b2814c84 100644 --- a/tests/tools/perfdhcp/test_control.cc +++ b/tests/tools/perfdhcp/test_control.cc @@ -80,18 +80,104 @@ TestControl::instance() { TestControl::TestControl() : send_due_(microsec_clock::universal_time()), - last_sent_(send_due_) { + last_sent_(send_due_), + transid_gen_(new TransidGenerator()) { } bool TestControl::checkExitConditions() const { CommandOptions& options = CommandOptions::instance(); - if ((options.getNumRequests().size() > 0) && - (sent_packets_0_ >= options.getNumRequests()[0])) { - return(true); - } else if ((options.getNumRequests().size() == 2) && - (sent_packets_1_ >= options.getNumRequests()[1])) { - return(true); + // Check if we reached maximum number of DISCOVER/SOLICIT sent. + if (options.getNumRequests().size() > 0) { + if (options.getIpVersion() == 4) { + if (getSentPacketsNum(StatsMgr4::XCHG_DO) >= + options.getNumRequests()[0]) { + return(true); + } + } else if (options.getIpVersion() == 6) { + if (stats_mgr6_->getSentPacketsNum(StatsMgr6::XCHG_SA) >= + options.getNumRequests()[0]) { + return(true); + } + } + } + // Check if we reached maximum number REQUEST packets. + if (options.getNumRequests().size() == 2) { + if (options.getIpVersion() == 4) { + if (stats_mgr4_->getSentPacketsNum(StatsMgr4::XCHG_RA) >= + options.getNumRequests()[1]) { + return(true); + } + } else if (options.getIpVersion() == 6) { + if (stats_mgr6_->getSentPacketsNum(StatsMgr6::XCHG_RR) >= + options.getNumRequests()[1]) { + return(true); + } + } + } + // Check if we reached maximum number of drops of OFFER/ADVERTISE packets. + if (options.getMaxDrop().size() > 0) { + if (options.getIpVersion() == 4) { + if (stats_mgr4_->getDroppedPacketsNum(StatsMgr4::XCHG_DO) >= + options.getMaxDrop()[0]) { + return(true); + } + } else if (options.getIpVersion() == 6) { + if (stats_mgr6_->getDroppedPacketsNum(StatsMgr6::XCHG_SA) >= + options.getMaxDrop()[0]) { + return(true); + } + } + } + // Check if we reached maximum number of drops of ACK/REPLY packets. + if (options.getMaxDrop().size() == 2) { + if (options.getIpVersion() == 4) { + if (stats_mgr4_->getDroppedPacketsNum(StatsMgr4::XCHG_RA) >= + options.getMaxDrop()[1]) { + return(true); + } + } else if (options.getIpVersion() == 6) { + if (stats_mgr6_->getDroppedPacketsNum(StatsMgr6::XCHG_RR) >= + options.getMaxDrop()[1]) { + return(true); + } + } + } + // Check if we reached maximum drops percentage of OFFER/ADVERTISE packets. + if (options.getMaxDropPercentage().size() > 0) { + if (options.getIpVersion() == 4) { + if ((stats_mgr4_->getSentPacketsNum(StatsMgr4::XCHG_DO) > 10) && + ((100. * stats_mgr4_->getDroppedPacketsNum(StatsMgr4::XCHG_DO) / + stats_mgr4_->getSentPacketsNum(StatsMgr4::XCHG_DO)) >= + options.getMaxDropPercentage()[0])) { + return(true); + } + } else if (options.getIpVersion() == 6) { + if ((stats_mgr6_->getSentPacketsNum(StatsMgr6::XCHG_SA) > 10) && + ((100. * stats_mgr6_->getDroppedPacketsNum(StatsMgr6::XCHG_SA) / + stats_mgr6_->getSentPacketsNum(StatsMgr6::XCHG_SA)) >= + options.getMaxDropPercentage()[0])) { + return(true); + } + } + } + // Check if we reached maximum drops percentage of ACK/REPLY packets. + if (options.getMaxDropPercentage().size() == 2) { + if (options.getIpVersion() == 4) { + if ((stats_mgr4_->getSentPacketsNum(StatsMgr4::XCHG_RA) > 10) && + ((100. * stats_mgr4_->getDroppedPacketsNum(StatsMgr4::XCHG_RA) / + stats_mgr4_->getSentPacketsNum(StatsMgr4::XCHG_RA)) >= + options.getMaxDropPercentage()[0])) { + return(true); + } + } else if (options.getIpVersion() == 6) { + if ((stats_mgr6_->getSentPacketsNum(StatsMgr6::XCHG_RR) > 10) && + ((100. * stats_mgr6_->getDroppedPacketsNum(StatsMgr6::XCHG_RR) / + stats_mgr6_->getSentPacketsNum(StatsMgr6::XCHG_RR)) >= + options.getMaxDropPercentage()[0])) { + return(true); + } + } } return(false); } @@ -170,8 +256,6 @@ TestControl::factoryRequestList4(Option::Universe u, return opt; } - - std::vector TestControl::generateMacAddress() const { CommandOptions& options = CommandOptions::instance(); @@ -268,6 +352,26 @@ TestControl::getNextExchangesNum() const { return (0); } +uint64_t +TestControl::getRcvdPacketsNum(const ExchangeType xchg_type) const { + uint8_t ip_version = CommandOptions::instance().getIpVersion(); + if (ip_version == 4) { + return(stats_mgr4_->getRcvdPacketsNum(xchg_type)); + } + return(stats_mgr6_-> + getRcvdPacketsNum(static_cast(xchg_type))); +} + +uint64_t +TestControl::getSentPacketsNum(const ExchangeType xchg_type) const { + uint8_t ip_version = CommandOptions::instance().getIpVersion(); + if (ip_version == 4) { + return(stats_mgr4_->getSentPacketsNum(xchg_type)); + } + return(stats_mgr6_-> + getSentPacketsNum(static_cast(xchg_type))); +} + void TestControl::initializeStatsMgr() { CommandOptions& options = CommandOptions::instance(); @@ -275,7 +379,7 @@ TestControl::initializeStatsMgr() { stats_mgr4_.reset(); stats_mgr4_ = StatsMgr4Ptr(new StatsMgr4()); stats_mgr4_->addExchangeStats(StatsMgr4::XCHG_DO); - if (options.getExchangeMode() == CommandOptions::DO_SA) { + if (options.getExchangeMode() == CommandOptions::DORA_SARR) { stats_mgr4_->addExchangeStats(StatsMgr4::XCHG_RA); } @@ -283,9 +387,9 @@ TestControl::initializeStatsMgr() { stats_mgr6_.reset(); stats_mgr6_ = StatsMgr6Ptr(new StatsMgr6()); stats_mgr6_->addExchangeStats(StatsMgr6::XCHG_SA); - if (options.getExchangeMode() == CommandOptions::DO_SA) { + if (options.getExchangeMode() == CommandOptions::DORA_SARR) { stats_mgr6_->addExchangeStats(StatsMgr6::XCHG_RR); - } + } } } @@ -395,7 +499,8 @@ TestControl::printStats() const { } void -TestControl::receivePacket4(Pkt4Ptr& pkt4) { +TestControl::receivePacket4(const TestControlSocket&, + const Pkt4Ptr& pkt4) { switch(pkt4->getType()) { case DHCPOFFER : stats_mgr4_->passRcvdPacket(StatsMgr4::XCHG_DO, pkt4); @@ -410,23 +515,25 @@ TestControl::receivePacket4(Pkt4Ptr& pkt4) { } void -TestControl::receivePacket6(Pkt6Ptr& pkt6) { - switch(pkt6->getType()) { - case DHCPV6_ADVERTISE : - stats_mgr6_->passRcvdPacket(StatsMgr6::XCHG_SA, pkt6); - break; - case DHCPV6_REPLY : +TestControl::receivePacket6(const TestControlSocket& socket, + const Pkt6Ptr& pkt6) { + uint8_t packet_type = pkt6->getType(); + if (packet_type == DHCPV6_ADVERTISE) { + Pkt6Ptr solicit_pkt6(stats_mgr6_->passRcvdPacket(StatsMgr6::XCHG_SA, + pkt6)); + if (solicit_pkt6) { + sendRequest6(socket, solicit_pkt6, pkt6); + } + } else if (packet_type == DHCPV6_REPLY) { stats_mgr6_->passRcvdPacket(StatsMgr6::XCHG_RR, pkt6); - break; - default: + } else { isc_throw(BadValue, "unknown type " << pkt6->getType() << " of received DHCPv6 packet"); } - } void -TestControl::receivePackets() { +TestControl::receivePackets(const TestControlSocket& socket) { int timeout = 0; bool receiving = true; while (receiving) { @@ -436,7 +543,7 @@ TestControl::receivePackets() { receiving = false; } else { pkt4->unpack(); - receivePacket4(pkt4); + receivePacket4(socket, pkt4); } } else if (CommandOptions::instance().getIpVersion() == 6) { Pkt6Ptr pkt6 = IfaceMgr::instance().receive6(timeout); @@ -444,7 +551,7 @@ TestControl::receivePackets() { receiving = false; } else { if (pkt6->unpack()) { - receivePacket6(pkt6); + receivePacket6(socket, pkt6); } } } @@ -524,7 +631,7 @@ TestControl::run() { } registerOptionFactories(); TestControlSocket socket(openSocket()); - + initializeStatsMgr(); uint64_t packets_sent = 0; for (;;) { @@ -534,7 +641,7 @@ TestControl::run() { } uint64_t packets_due = getNextExchangesNum(); - receivePackets(); + receivePackets(socket); for (uint64_t i = packets_due; i > 0; --i) { if (options.getIpVersion() == 4) { @@ -555,8 +662,8 @@ TestControl::sendDiscover4(const TestControlSocket& socket) { // Generate the MAC address to be passed in the packet. std::vector mac_address = generateMacAddress(); // Generate trasnaction id to be set for the new exchange. - const uint32_t transid = static_cast(random() % 0x00FFFFFF); - boost::shared_ptr pkt4(new Pkt4(DHCPDISCOVER, transid)); + const uint32_t transid = generateTransid(); + Pkt4Ptr pkt4(new Pkt4(DHCPDISCOVER, transid)); if (!pkt4) { isc_throw(Unexpected, "failed to create DISCOVER packet"); } @@ -580,6 +687,59 @@ TestControl::sendDiscover4(const TestControlSocket& socket) { stats_mgr4_->passSentPacket(StatsMgr4::XCHG_DO, pkt4); } +void +TestControl::sendRequest6(const TestControlSocket& socket, + const Pkt6Ptr& solicit_pkt6, + const Pkt6Ptr& advertise_pkt6) { + const uint32_t transid = static_cast(random() % 0x00FFFFFF); + Pkt6Ptr pkt6(new Pkt6(DHCPV6_REQUEST, transid)); + // Calculate elapsed time + ptime solicit_time = solicit_pkt6->getTimestamp(); + ptime advertise_time = advertise_pkt6->getTimestamp(); + if (solicit_time.is_not_a_date_time()) { + isc_throw(Unexpected, "timestamp was not set for SOLICIT packet"); + } + if (advertise_time.is_not_a_date_time()) { + isc_throw(Unexpected, "timestamp was not set for ADVERTISE packet"); + } + time_period period(solicit_time, advertise_time); + if (period.is_null()) { + pkt6->addOption(Option::factory(Option::V6, D6O_ELAPSED_TIME)); + } else { + OptionBuffer buf(); + const uint32_t elapsed_time = period.length().total_seconds(); + OptionPtr opt_elapsed_time = + Option::factory(Option::V6, D6O_ELAPSED_TIME); + opt_elapsed_time->setUint16(static_cast(elapsed_time)); + pkt6->addOption(opt_elapsed_time); + } + OptionPtr opt_clientid = advertise_pkt6->getOption(D6O_CLIENTID); + if (!opt_clientid) { + isc_throw(Unexpected, "client id not found in received packet"); + } + pkt6->addOption(opt_clientid); + OptionPtr opt_serverid = advertise_pkt6->getOption(D6O_SERVERID); + if (!opt_serverid) { + isc_throw(Unexpected, "server id not found in received packet"); + } + pkt6->addOption(opt_serverid); + OptionPtr opt_ia_na = advertise_pkt6->getOption(D6O_IA_NA); + if (!opt_ia_na) { + isc_throw(Unexpected, "DHCPv6 IA_NA option not found in received " + "packet"); + } + pkt6->addOption(opt_ia_na); + setDefaults6(socket, pkt6); + + pkt6->pack(); + IfaceMgr::instance().send(pkt6); + if (!stats_mgr6_) { + isc_throw(InvalidOperation, "Statistics Manager for DHCPv6 " + "hasn't been initialized"); + } + stats_mgr6_->passSentPacket(StatsMgr6::XCHG_RR, pkt6); +} + void TestControl::sendSolicit6(const TestControlSocket& socket) { ++sent_packets_0_; @@ -589,13 +749,13 @@ TestControl::sendSolicit6(const TestControlSocket& socket) { // Generate DUID to be passed to the packet std::vector duid = generateDuid(); // Generate trasnaction id to be set for the new exchange. - const uint32_t transid = static_cast(random() % 0x00FFFFFF); - boost::shared_ptr pkt6(new Pkt6(DHCPV6_SOLICIT, transid)); + const uint32_t transid = generateTransid(); + Pkt6Ptr pkt6(new Pkt6(DHCPV6_SOLICIT, transid)); if (!pkt6) { isc_throw(Unexpected, "failed to create SOLICIT packet"); } pkt6->addOption(Option::factory(Option::V6, D6O_ELAPSED_TIME)); - pkt6->addOption(Option::factory(Option::V6, D6O_RAPID_COMMIT)); + // pkt6->addOption(Option::factory(Option::V6, D6O_RAPID_COMMIT)); pkt6->addOption(Option::factory(Option::V6, D6O_CLIENTID, duid)); pkt6->addOption(Option::factory(Option::V6, D6O_ORO)); pkt6->addOption(Option::factory(Option::V6, D6O_IA_NA)); @@ -612,7 +772,7 @@ TestControl::sendSolicit6(const TestControlSocket& socket) { void TestControl::setDefaults4(const TestControlSocket& socket, - const boost::shared_ptr& pkt) { + const Pkt4Ptr& pkt) { CommandOptions& options = CommandOptions::instance(); // Interface name. pkt->setIface(socket.getIface()); @@ -634,7 +794,7 @@ TestControl::setDefaults4(const TestControlSocket& socket, void TestControl::setDefaults6(const TestControlSocket& socket, - const boost::shared_ptr& pkt) { + const Pkt6Ptr& pkt) { CommandOptions& options = CommandOptions::instance(); // Interface name. pkt->setIface(socket.getIface()); diff --git a/tests/tools/perfdhcp/test_control.h b/tests/tools/perfdhcp/test_control.h index c153fac3b6..1a3157b3e3 100644 --- a/tests/tools/perfdhcp/test_control.h +++ b/tests/tools/perfdhcp/test_control.h @@ -20,6 +20,7 @@ #include #include +#include #include #include @@ -55,6 +56,8 @@ public: typedef StatsMgr StatsMgr6; // Pointer to Statistics Manager for DHCPv6. typedef boost::shared_ptr StatsMgr6Ptr; + // Packet exchange type. + typedef StatsMgr<>::ExchangeType ExchangeType; /// \brief Socket wrapper class. /// @@ -120,6 +123,26 @@ public: asiolink::IOAddress addr_; ///< Address bound. }; + /// \brief Default transaction id generator class. + /// + /// This is default transaction id generator class. The member + /// function is used to generate unique transaction id value. + /// Other generator classes should derive from this one to + /// override the standard generation algorithm (e.g. unit tests + /// override this class wih algorithm that produces more predictable + /// transaction id values). + class TransidGenerator { + public: + /// \brief generate transaction id. + /// + /// \return generated transazction id value. + virtual uint32_t generate() { + return static_cast(random() % 0x00FFFFFF); + } + }; + + typedef boost::shared_ptr TransidGeneratorPtr; + /// \brief Length of the Ethernet HW address (MAC) in bytes. static const uint8_t HW_ETHER_LEN = 6; @@ -129,7 +152,7 @@ public: /// \return the only existing instance of test control static TestControl& instance(); - /// Run performance test. + /// brief\ Run performance test. /// /// Method runs whole performance test. Command line options must /// be parsed prior to running this function. Othewise function will @@ -139,6 +162,14 @@ public: /// \throw isc::Unexpected if internal Test Controler error occured. void run(); + /// \brief Set new transaction id generator. + /// + /// \param generator generator object to be used. + void setTransidGenerator(TransidGeneratorPtr& generator) { + transid_gen_.reset(); + transid_gen_ = generator; + } + protected: // We would really like these methods and members to be private but @@ -285,6 +316,13 @@ protected: /// \return generated MAC address. std::vector generateMacAddress() const; + /// \brief generate transaction id. + /// + /// \return generated transaction id. + uint32_t generateTransid() { + return(transid_gen_->generate()); + } + /// \brief Returns number of exchanges to be started. /// /// Method returns number of new exchanges to be started as soon @@ -329,14 +367,45 @@ protected: /// not initialized. void printStats() const; - void receivePacket4(dhcp::Pkt4Ptr& pkt4); + /// \brief Receive DHCPv4 packet. + /// + /// Method performs reception of the DHCPv4 packet, updates + /// statistics and responsds to the server if required, e.g. + /// when OFFER packet arrives, this function will initiate + /// REQUEST message to the server. + /// + /// \param socket socket to be used. + /// \param pkt4 object representing DHCPv4 packet received. + /// \throw isc::BadValue if unknown message type received. + /// \throw isc::Unexpected if unexpected error occured. + void receivePacket4(const TestControlSocket& socket, + const dhcp::Pkt4Ptr& pkt4); - void receivePacket6(dhcp::Pkt6Ptr& pkt4); + /// \brief Receive DHCPv6 packet. + /// + /// Method performs reception of the DHCPv6 packet, updates + /// statistics and responsds to the server if required, e.g. + /// when ADVERTISE packet arrives, this function will initiate + /// REQUEST message to the server. + /// + /// \param socket socket to be used. + /// \param pkt6 object representing DHCPv6 packet received. + /// \throw isc::BadValue if unknown message type received. + /// \throw isc::Unexpected if unexpected error occured. + void receivePacket6(const TestControlSocket& socket, + const dhcp::Pkt6Ptr& pkt6); /// \brief Receive DHCPv4 or DHCPv6 packets from the server. /// /// Method receives DHCPv4 or DHCPv6 packets from the server. - void receivePackets(); + /// This function will call \ref receivePacket4 or + /// \ref receivePacket6 depending if DHCPv4 or DHCPv6 packet + /// has arrived. + /// + /// \param socket socket to be used. + /// \throw::BadValue if unknown message type received. + /// \throw::Unexpected if unexpected error occured. + void receivePackets(const TestControlSocket& socket); /// \brief Register option factory functions for DHCPv4 /// @@ -378,6 +447,28 @@ protected: /// \throw isc::BadValue if MAC address has invalid length. void sendDiscover4(const TestControlSocket& socket); + /// \brief Sent DHCPv6 REQUEST message. + /// + /// Method creates and sends DHCPv6 REQUEST message to the server + /// with the following options: + /// - D6O_ELAPSED_TIME + /// - D6O_CLIENTID + /// - D6O_SERVERID + /// The elapsed time is calculated based on the duration between + /// sending a SOLICIT and receiving the ADVERTISE packet prior. + /// For this reason both solicit and advertise packet objects have + /// to be passed when calling this function. + /// + /// \param socket socket to be used to send message. + /// \param solicit_pkt6 SOLICIT packet object. + /// \param advertise_pkt6 ADVERTISE packet object. + /// \throw isc::Unexpected if unexpected error occured. + /// \throw isc::InvalidOperation if Statistics Manager has not been + /// initialized. + void sendRequest6(const TestControlSocket& socket, + const dhcp::Pkt6Ptr& solicit_pkt6, + const dhcp::Pkt6Ptr& advertise_pkt6); + /// \brief Send DHCPv6 SOLICIT message. /// /// Method creates and sends DHCPv6 SOLICIT message to the server @@ -405,7 +496,7 @@ protected: /// \param socket socket used to send the packet. /// \param pkt reference to packet to be configured. void setDefaults4(const TestControlSocket& socket, - const boost::shared_ptr& pkt); + const dhcp::Pkt4Ptr& pkt); /// \brief Set default DHCPv6 packet parameters. /// @@ -420,7 +511,7 @@ protected: /// \param socket socket used to send the packet. /// \param pkt reference to packet to be configured. void setDefaults6(const TestControlSocket& socket, - const boost::shared_ptr& pkt); + const dhcp::Pkt6Ptr& pkt); /// \brief Update due time to initiate next chunk of exchanges. /// @@ -431,13 +522,38 @@ protected: private: + /// \brief Generate transaction id using random function. + /// + /// \return generated transaction id value. + static uint32_t generateTransidRandom(); + + /// \brief Get number of received packets. + /// + /// Get the number of received packets from the Statistics Manager. + /// Function may throw if Statistics Manager object is not + /// initialized. + /// \param xchg_type packet exchange type. + /// \return number of received packets. + uint64_t getRcvdPacketsNum(const ExchangeType xchg_type) const; + + /// \brief Get number of sent packets. + /// + /// Get the number of sent packets from the Statistics Manager. + /// Function may throw if Statistics Manager object is not + /// initialized. + /// \param xchg_type packet exchange type. + /// \return number of sent packets. + uint64_t getSentPacketsNum(const ExchangeType xchg_type) const; + boost::posix_time::ptime send_due_; ///< Due time to initiate next chunk ///< of exchanges. boost::posix_time::ptime last_sent_; ///< Indicates when the last exchange /// was initiated. + StatsMgr4Ptr stats_mgr4_; ///< Statistics Manager 4. + StatsMgr6Ptr stats_mgr6_; ///< Statistics Manager 6. - StatsMgr4Ptr stats_mgr4_; /// Statistics Manager 4. - StatsMgr6Ptr stats_mgr6_; /// Statistics Manager 6. + // Pointers to functions. + TransidGeneratorPtr transid_gen_; ///< Transaction id generator. uint64_t sent_packets_0_; uint64_t sent_packets_1_; diff --git a/tests/tools/perfdhcp/tests/test_control_unittest.cc b/tests/tools/perfdhcp/tests/test_control_unittest.cc index aa8983051e..52c55b2f10 100644 --- a/tests/tools/perfdhcp/tests/test_control_unittest.cc +++ b/tests/tools/perfdhcp/tests/test_control_unittest.cc @@ -38,6 +38,34 @@ using namespace isc::perfdhcp; /// to allow unit testing. class NakedTestControl: public TestControl { public: + + /// \brief Incremental transaction id generaator. + /// + /// This is incremental transaction id generator. It overrides + /// the default transaction id generator that generates transaction + /// ids using random function. This generator will generate values + /// like: 1,2,3 etc. + class IncrementalGenerator : public TestControl::TransidGenerator { + public: + /// \brief Default constructor. + IncrementalGenerator() : + TransidGenerator(), + transid_(0) { + } + + /// \brief Generate unique transaction id. + /// + /// Generate unique transaction ids incrementally: + /// 1,2,3,4 etc. + /// + /// \return generated transaction id. + virtual uint32_t generate() { + return ++transid_; + } + private: + uint32_t transid_; ///< Last generated transaction id. + }; + using TestControl::checkExitConditions; using TestControl::factoryElapsedTime6; using TestControl::factoryGeneric; @@ -48,14 +76,15 @@ public: using TestControl::generateDuid; using TestControl::generateMacAddress; using TestControl::getNextExchangesNum; + using TestControl::initializeStatsMgr; using TestControl::openSocket; - using TestControl::receivePackets; + using TestControl::receivePacket4; + using TestControl::receivePacket6; using TestControl::registerOptionFactories; using TestControl::sendDiscover4; using TestControl::sendSolicit6; using TestControl::setDefaults4; using TestControl::setDefaults6; - using TestControl::updateSendDue; NakedTestControl() : TestControl() { }; @@ -78,6 +107,11 @@ public: /// \brief Default Constructor TestControlTest() { } + static uint32_t generateTransidIncremental() { + static uint32_t transid(1); + return ++transid; + } + /// \brief Get local loopback interface name. /// /// Scan available network interfaces for local loopback @@ -260,6 +294,107 @@ public: } } + /// \brief Test DHCPv4 exchanges. + /// + /// Function simulates DHCPv4 exchanges. Function caller specifies + /// number of exchanges to be simulated and number of simulated + /// responses. When number of responses is lower than number of + /// iterations than the difference between them is the number + /// of simulated packet drops. This is useful to test if program + /// exit conditions are handled properly (maximum number of packet + /// drops specified as -D is taken into account). + /// + /// \param iterations_num number of exchanges to simulate. + /// \param receive_num number of received OFFER packets. + /// \param iterations_performed actual number of iterations. + void testPkt4Exchange(int iterations_num, + int receive_num, + int& iterations_performed) const { + uint16_t port = 10547; + int sock_handle = 0; + NakedTestControl tc; + tc.initializeStatsMgr(); + // Incremental transaction id generator will generate + // predictable values of transaction id for each iteration. + // This is important because we need to simulate reponses + // from the server and use the same transaction ids as in + // packets sent by client. + TestControl::TransidGeneratorPtr + generator(new NakedTestControl::IncrementalGenerator()); + tc.setTransidGenerator(generator); + // Socket is needed to send packets through the interface. + ASSERT_NO_THROW(sock_handle = tc.openSocket(port)); + TestControl::TestControlSocket sock(sock_handle); + int i = 0; + for (; i < iterations_num; ++i) { + if (tc.checkExitConditions()) { + break; + } + ASSERT_NO_THROW(tc.sendDiscover4(sock)); + // Do not simulate responses for packets later + // that specified as receive_num. This simulates + // packet drops. + if (i - 1 < receive_num) { + boost::shared_ptr offer_pkt4(createOfferPkt4(i)); + // Receive OFFER and send REQUEST. + ASSERT_NO_THROW(tc.receivePacket4(sock, offer_pkt4)); + } + } + // Return the number of iterations performed. + iterations_performed = i; + } + + /// \brief Test DHCPv6 exchanges. + /// + /// Function simulates DHCPv6 exchanges. Function caller specifies + /// number of exchanges to be simulated and number of simulated + /// responses. When number of responses is lower than number of + /// iterations than the difference between them is the number + /// of simulated packet drops. This is useful to test if program + /// exit conditions are handled properly (maximum number of packet + /// drops specified as -D is taken into account). + /// + /// \param iterations_num number of exchanges to simulate. + /// \param receive_num number of received OFFER packets. + /// \param iterations_performed actual number of iterations. + void testPkt6Exchange(int iterations_num, + int receive_num, + int& iterations_performed) const { + uint16_t port = 10547; + int sock_handle = 0; + NakedTestControl tc; + tc.initializeStatsMgr(); + // Incremental transaction id generator will generate + // predictable values of transaction id for each iteration. + // This is important because we need to simulate reponses + // from the server and use the same transaction ids as in + // packets sent by client. + TestControl::TransidGeneratorPtr + generator(new NakedTestControl::IncrementalGenerator()); + tc.setTransidGenerator(generator); + // Socket is needed to send packets through the interface. + ASSERT_NO_THROW(sock_handle = tc.openSocket(port)); + TestControl::TestControlSocket sock(sock_handle); + int i = 0; + for (; i < iterations_num; ++i) { + if (tc.checkExitConditions()) { + break; + } + // Do not simulate responses for packets later + // that specified as receive_num. This simulates + // packet drops. + ASSERT_NO_THROW(tc.sendSolicit6(sock)); + if (i - 1 < receive_num) { + boost::shared_ptr advertise_pkt6(createAdvertisePkt6(i)); + // Receive ADVERTISE and send REQUEST. + ASSERT_NO_THROW(tc.receivePacket6(sock, advertise_pkt6)); + } + + } + // Return the number of iterations performed. + iterations_performed = i; + } + /// \brief Test generation of multiple MAC addresses. /// /// This method validates generation of multiple MAC addresses. @@ -322,6 +457,33 @@ public: CommandOptionsHelper::process(cmdline); } +private: + boost::shared_ptr + createOfferPkt4(uint32_t transid) const { + boost::shared_ptr offer(new Pkt4(DHCPOFFER, transid)); + OptionPtr opt_msg_type = Option::factory(Option::V4, DHO_DHCP_MESSAGE_TYPE, + OptionBuffer(DHCPOFFER)); + offer->setYiaddr(asiolink::IOAddress("127.0.0.1")); + offer->addOption(opt_msg_type); + offer->updateTimestamp(); + return(offer); + } + + boost::shared_ptr + createAdvertisePkt6(uint32_t transid) const { + OptionPtr opt_ia_na = Option::factory(Option::V6, D6O_IA_NA); + OptionPtr opt_serverid(new Option(Option::V6, D6O_SERVERID)); + NakedTestControl tc; + std::vector duid(tc.generateDuid()); + OptionPtr opt_clientid(Option::factory(Option::V6, D6O_CLIENTID, duid)); + boost::shared_ptr advertise(new Pkt6(DHCPV6_ADVERTISE, transid)); + advertise->addOption(opt_ia_na); + advertise->addOption(opt_serverid); + advertise->addOption(opt_clientid); + advertise->updateTimestamp(); + return(advertise); + } + }; TEST_F(TestControlTest, GenerateDuid) { @@ -568,6 +730,77 @@ TEST_F(TestControlTest, Packet6) { } } +TEST_F(TestControlTest, Packet4Exchange) { + // Get the local loopback interface to open socket on + // it and test packets exchanges. We don't want to fail + // the test if interface is not available. + std::string loopback_iface(getLocalLoopback()); + if (loopback_iface.empty()) { + std::cout << "Unable to find the loopback interface. Skip test." + << std::endl; + return; + } + + // Set number of iterations to some high value. + const int iterations_num = 100; + processCmdLine("perfdhcp -l " + loopback_iface + + " -r 100 -n 10 -R 20 127.0.0.1"); + // The actual number of iterations will be stored in the + // following variable. + int iterations_performed = 0; + testPkt4Exchange(iterations_num, iterations_num, iterations_performed); + // The command line restricts the number of iterations to 10 + // with -n 10 parameter. + EXPECT_EQ(10, iterations_performed); + + // With the following command line we restrict the maximum + // number of dropped packets to 20% of all. + processCmdLine("perfdhcp -l " + loopback_iface + + " -r 100 -R 20 -n 20 -D 10% 127.0.0.1"); + // The number iterations is restricted by the percentage of + // dropped packets (-D 10%). We also have to bump up the number + // of iterations because the percentage limitation checks starts + // at packet #10. We expect that at packet #12 the 10% threshold + // will be reached. + const int received_num = 10; + testPkt4Exchange(iterations_num, received_num, iterations_performed); + EXPECT_EQ(12, iterations_performed); +} + +TEST_F(TestControlTest, Packet6Exchange) { + // Get the local loopback interface to open socket on + // it and test packets exchanges. We don't want to fail + // the test if interface is not available. + std::string loopback_iface(getLocalLoopback()); + if (loopback_iface.empty()) { + std::cout << "Unable to find the loopback interface. Skip test." + << std::endl; + return; + } + + const int iterations_num = 100; + // Set number of iterations to 10. + processCmdLine("perfdhcp -l " + loopback_iface + + " -6 -r 100 -n 10 -R 20 ::1"); + int iterations_performed = 0; + // Set number of received packets equal to number of iterations. + // This simulates no packet drops. + testPkt6Exchange(iterations_num, iterations_num, iterations_performed); + // Actual number of iterations should be 10. + EXPECT_EQ(10, iterations_performed); + + // The maximum number of dropped packets is 3 (because of -D 3). + processCmdLine("perfdhcp -l " + loopback_iface + + " -6 -r 100 -n 10 -R 20 -D 3 ::1"); + // For the first 3 packets we are simulating responses from server. + // For other packets we don't so packet as 4,5,6 will be dropped and + // then test should be interrupted and actual number of iterations will + // be 6. + const int received_num = 3; + testPkt6Exchange(iterations_num, received_num, iterations_performed); + EXPECT_EQ(6, iterations_performed); +} + TEST_F(TestControlTest, RateControl) { // We don't specify the exchange rate here so the aggressivity // value will determine how many packets are to be send each From 3e6ed8d54322d7ebeb2721664dd7d2dd8326094a Mon Sep 17 00:00:00 2001 From: Marcin Siodelski Date: Tue, 21 Aug 2012 17:18:30 +0200 Subject: [PATCH 18/40] [1959] Print overall rate statistics. --- tests/tools/perfdhcp/stats_mgr.h | 19 ++++++++++++++++++- tests/tools/perfdhcp/test_control.cc | 23 +++++++++++++++++++++++ tests/tools/perfdhcp/test_control.h | 5 +++++ 3 files changed, 46 insertions(+), 1 deletion(-) diff --git a/tests/tools/perfdhcp/stats_mgr.h b/tests/tools/perfdhcp/stats_mgr.h index 5a3f11445d..1e57dda15b 100644 --- a/tests/tools/perfdhcp/stats_mgr.h +++ b/tests/tools/perfdhcp/stats_mgr.h @@ -775,7 +775,8 @@ public: StatsMgr(const bool archive_enabled = false) : exchanges_(), custom_counters_(), - archive_enabled_(archive_enabled) { + archive_enabled_(archive_enabled), + boot_time_(boost::posix_time::microsec_clock::universal_time()) { } /// \brief Specify new exchange type. @@ -1028,6 +1029,20 @@ public: return(xchg_stats->getDroppedPacketsNum()); } + /// \brief Get time period since the start of test. + /// + /// Calculate dna return period since the test start. This + /// can be specifically helpful when calculating packet + /// exchange rates. + /// + /// \return test period so far. + boost::posix_time::time_period getTestPeriod() const { + using namespace boost::posix_time; + time_period test_period(boot_time_, + microsec_clock::universal_time()); + return test_period; + } + /// \brief Return name of the exchange. /// /// Method returns name of the specified exchange type. @@ -1158,6 +1173,8 @@ private: /// for extended period of time and many packets have to be /// archived. bool archive_enabled_; + + boost::posix_time::ptime boot_time_; ///< Time when test is started. }; } // namespace perfdhcp diff --git a/tests/tools/perfdhcp/test_control.cc b/tests/tools/perfdhcp/test_control.cc index 87b2814c84..9ba869ac33 100644 --- a/tests/tools/perfdhcp/test_control.cc +++ b/tests/tools/perfdhcp/test_control.cc @@ -480,8 +480,31 @@ TestControl::openSocket(uint16_t port) const { return(sock); } +void +TestControl::printRate() const { + double rate = 0; + CommandOptions& options = CommandOptions::instance(); + if (options.getIpVersion() == 4) { + double duration = + stats_mgr4_->getTestPeriod().length().total_nanoseconds() / 1e9; + rate = stats_mgr4_->getRcvdPacketsNum(StatsMgr4::XCHG_DO) / duration; + } else if (options.getIpVersion() == 6) { + double duration = + stats_mgr6_->getTestPeriod().length().total_nanoseconds() / 1e9; + rate = stats_mgr6_->getRcvdPacketsNum(StatsMgr6::XCHG_SA) / duration; + } + std::cout << "***Rate statistics***" << std::endl; + if (options.getRate() > 0) { + std::cout << "Rate: " << rate << ", expected rate: " + << options.getRate() << std::endl << std::endl; + } else { + std::cout << "Rate: " << rate << std::endl << std::endl; + } +} + void TestControl::printStats() const { + printRate(); CommandOptions& options = CommandOptions::instance(); if (options.getIpVersion() == 4) { if (!stats_mgr4_) { diff --git a/tests/tools/perfdhcp/test_control.h b/tests/tools/perfdhcp/test_control.h index 1a3157b3e3..a93d05dfc2 100644 --- a/tests/tools/perfdhcp/test_control.h +++ b/tests/tools/perfdhcp/test_control.h @@ -360,6 +360,11 @@ protected: /// \return socket descriptor. int openSocket(uint16_t port = 0) const; + /// \brief Print rate statistics. + /// + /// Method print packet exchange rate statistics. + void printRate() const; + /// \brief Print performance statistics. /// /// Method prints performance statistics. From 61f144876b5c42de12b51fc79ea2f6b0544001ce Mon Sep 17 00:00:00 2001 From: Marcin Siodelski Date: Fri, 24 Aug 2012 13:18:41 +0200 Subject: [PATCH 19/40] [1959] Implemented most of the command line options. --- tests/tools/perfdhcp/test_control.cc | 205 +++++++++++++++--- tests/tools/perfdhcp/test_control.h | 45 +++- .../perfdhcp/tests/test_control_unittest.cc | 71 +++--- 3 files changed, 246 insertions(+), 75 deletions(-) diff --git a/tests/tools/perfdhcp/test_control.cc b/tests/tools/perfdhcp/test_control.cc index 9ba869ac33..42058a9adb 100644 --- a/tests/tools/perfdhcp/test_control.cc +++ b/tests/tools/perfdhcp/test_control.cc @@ -78,15 +78,27 @@ TestControl::instance() { return (test_control); } -TestControl::TestControl() : - send_due_(microsec_clock::universal_time()), - last_sent_(send_due_), - transid_gen_(new TransidGenerator()) { +TestControl::TestControl() { + reset(); } bool TestControl::checkExitConditions() const { CommandOptions& options = CommandOptions::instance(); + // Check if test period passed.. + if (options.getPeriod() != 0) { + if (options.getIpVersion() == 4) { + time_period period(stats_mgr4_->getTestPeriod()); + if (period.length().total_seconds() >= options.getPeriod()) { + return true; + } + } else if (options.getIpVersion() == 6) { + time_period period = stats_mgr6_->getTestPeriod(); + if (period.length().total_seconds() >= options.getPeriod()) { + return true; + } + } + } // Check if we reached maximum number of DISCOVER/SOLICIT sent. if (options.getNumRequests().size() > 0) { if (options.getIpVersion() == 4) { @@ -167,14 +179,14 @@ TestControl::checkExitConditions() const { if ((stats_mgr4_->getSentPacketsNum(StatsMgr4::XCHG_RA) > 10) && ((100. * stats_mgr4_->getDroppedPacketsNum(StatsMgr4::XCHG_RA) / stats_mgr4_->getSentPacketsNum(StatsMgr4::XCHG_RA)) >= - options.getMaxDropPercentage()[0])) { + options.getMaxDropPercentage()[1])) { return(true); } } else if (options.getIpVersion() == 6) { if ((stats_mgr6_->getSentPacketsNum(StatsMgr6::XCHG_RR) > 10) && ((100. * stats_mgr6_->getDroppedPacketsNum(StatsMgr6::XCHG_RR) / stats_mgr6_->getSentPacketsNum(StatsMgr6::XCHG_RR)) >= - options.getMaxDropPercentage()[0])) { + options.getMaxDropPercentage()[1])) { return(true); } } @@ -372,6 +384,18 @@ TestControl::getSentPacketsNum(const ExchangeType xchg_type) const { getSentPacketsNum(static_cast(xchg_type))); } +void +TestControl::initPacketTemplates() { + CommandOptions& options = CommandOptions::instance(); + std::vector template_files = options.getTemplateFiles(); + for (std::vector::const_iterator it = template_files.begin(); + it != template_files.end(); + ++it) { + + std::cout << "Open file " << *it << std::endl; + } +} + void TestControl::initializeStatsMgr() { CommandOptions& options = CommandOptions::instance(); @@ -394,10 +418,11 @@ TestControl::initializeStatsMgr() { } int -TestControl::openSocket(uint16_t port) const { +TestControl::openSocket() const { CommandOptions& options = CommandOptions::instance(); std::string localname = options.getLocalName(); std::string servername = options.getServerName(); + uint16_t port = options.getLocalPort(); uint8_t family = AF_INET; int sock = 0; IOAddress remoteaddr(servername); @@ -522,18 +547,18 @@ TestControl::printStats() const { } void -TestControl::receivePacket4(const TestControlSocket&, +TestControl::receivePacket4(const TestControlSocket& socket, const Pkt4Ptr& pkt4) { - switch(pkt4->getType()) { - case DHCPOFFER : - stats_mgr4_->passRcvdPacket(StatsMgr4::XCHG_DO, pkt4); - break; - case DHCPACK : + if (pkt4->getType() == DHCPOFFER) { + Pkt4Ptr discover_pkt4(stats_mgr4_->passRcvdPacket(StatsMgr4::XCHG_DO, + pkt4)); + CommandOptions::ExchangeMode xchg_mode = + CommandOptions::instance().getExchangeMode(); + if ((xchg_mode == CommandOptions::DORA_SARR) && discover_pkt4) { + sendRequest4(socket, pkt4); + } + } else if (pkt4->getType() == DHCPACK) { stats_mgr4_->passRcvdPacket(StatsMgr4::XCHG_RA, pkt4); - break; - default: - isc_throw(BadValue, "unknown type " << pkt4->getType() - << " of received DHCPv4 packet"); } } @@ -544,14 +569,13 @@ TestControl::receivePacket6(const TestControlSocket& socket, if (packet_type == DHCPV6_ADVERTISE) { Pkt6Ptr solicit_pkt6(stats_mgr6_->passRcvdPacket(StatsMgr6::XCHG_SA, pkt6)); - if (solicit_pkt6) { + CommandOptions::ExchangeMode xchg_mode = + CommandOptions::instance().getExchangeMode(); + if ((xchg_mode == CommandOptions::DORA_SARR) && solicit_pkt6) { sendRequest6(socket, solicit_pkt6, pkt6); } } else if (packet_type == DHCPV6_REPLY) { stats_mgr6_->passRcvdPacket(StatsMgr6::XCHG_RR, pkt6); - } else { - isc_throw(BadValue, "unknown type " << pkt6->getType() - << " of received DHCPv6 packet"); } } @@ -589,6 +613,9 @@ TestControl::registerOptionFactories4() const { LibDHCP::OptionFactoryRegister(Option::V4, DHO_DHCP_MESSAGE_TYPE, &TestControl::factoryGeneric); + LibDHCP::OptionFactoryRegister(Option::V4, + DHO_DHCP_SERVER_IDENTIFIER, + &TestControl::factoryGeneric); // DHCP_PARAMETER_REQUEST_LIST option factory. LibDHCP::OptionFactoryRegister(Option::V4, DHO_DHCP_PARAMETER_REQUEST_LIST, @@ -613,7 +640,9 @@ TestControl::registerOptionFactories6() const { LibDHCP::OptionFactoryRegister(Option::V6, D6O_CLIENTID, &TestControl::factoryGeneric); - + LibDHCP::OptionFactoryRegister(Option::V6, + D6O_SERVERID, + &TestControl::factoryGeneric); LibDHCP::OptionFactoryRegister(Option::V6, D6O_IA_NA, &TestControl::factoryIana6); @@ -639,10 +668,23 @@ TestControl::registerOptionFactories() const { } } +void +TestControl::reset() { + send_due_ = microsec_clock::universal_time(); + last_sent_ = send_due_; + transid_gen_.reset(); + transid_gen_ = TransidGeneratorPtr(new TransidGenerator()); + first_packet_serverid_.clear(); +} + void TestControl::run() { sent_packets_0_ = 0; sent_packets_1_ = 0; + + // Reset singleton state before test starts. + reset(); + CommandOptions& options = CommandOptions::instance(); // Ip version is not set ONLY in case the command options // were not parsed. This surely means that parse() function @@ -655,7 +697,26 @@ TestControl::run() { registerOptionFactories(); TestControlSocket socket(openSocket()); + // Initialize packet templates. + initPacketTemplates(); + + // Initialize randomization seed. + if (options.isSeeded()) { + srandom(options.getSeed()); + } + + // Preload server with number of packets. + const bool do_preload = true; + for (int i = 0; i < options.getPreload(); ++i) { + if (options.getIpVersion() == 4) { + sendDiscover4(socket, do_preload); + } else if (options.getIpVersion() == 6) { + sendSolicit6(socket, do_preload); + } + } + initializeStatsMgr(); + uint64_t packets_sent = 0; for (;;) { updateSendDue(); @@ -679,7 +740,8 @@ TestControl::run() { } void -TestControl::sendDiscover4(const TestControlSocket& socket) { +TestControl::sendDiscover4(const TestControlSocket& socket, + const bool preload /*= false*/) { ++sent_packets_0_; last_sent_ = microsec_clock::universal_time(); // Generate the MAC address to be passed in the packet. @@ -698,6 +760,61 @@ TestControl::sendDiscover4(const TestControlSocket& socket) { pkt4->addOption(Option::factory(Option::V4, DHO_DHCP_PARAMETER_REQUEST_LIST)); + // Set client's and server's ports as well as server's address, + // and local (relay) address. + setDefaults4(socket, pkt4); + pkt4->pack(); + IfaceMgr::instance().send(pkt4); + if (!preload) { + if (!stats_mgr4_) { + isc_throw(InvalidOperation, "Statistics Manager for DHCPv4 " + "hasn't been initialized"); + } + stats_mgr4_->passSentPacket(StatsMgr4::XCHG_DO, pkt4); + } +} + +void +TestControl::sendRequest4(const TestControlSocket& socket, + const dhcp::Pkt4Ptr& offer_pkt4) { + const uint32_t transid = generateTransid(); + Pkt4Ptr pkt4(new Pkt4(DHCPREQUEST, transid)); + OptionBuffer buf_msg_type; + buf_msg_type.push_back(DHCPREQUEST); + OptionPtr opt_msg_type = Option::factory(Option::V4, DHO_DHCP_MESSAGE_TYPE, + buf_msg_type); + pkt4->addOption(opt_msg_type); + if (first_packet_serverid_.size() > 0) { + pkt4->addOption(Option::factory(Option::V4, DHO_DHCP_SERVER_IDENTIFIER, + first_packet_serverid_)); + } else { + OptionPtr opt_serverid = + offer_pkt4->getOption(DHO_DHCP_SERVER_IDENTIFIER); + if (!opt_serverid) { + isc_throw(BadValue, "there is no SERVER_IDENTIFIER option " + << "in OFFER message"); + } + if (CommandOptions::instance().isUseFirst() && + stats_mgr4_->getRcvdPacketsNum(StatsMgr4::XCHG_DO) == 1) { + first_packet_serverid_ = opt_serverid->getData(); + } + pkt4->addOption(opt_serverid); + } + + /// Set client address. + asiolink::IOAddress yiaddr = offer_pkt4->getYiaddr(); + if (!yiaddr.getAddress().is_v4()) { + isc_throw(BadValue, "the YIADDR returned in OFFER packet is not " + " IPv4 address"); + } + OptionPtr opt_requested_address = + OptionPtr(new Option(Option::V4, DHO_DHCP_REQUESTED_ADDRESS, + OptionBuffer())); + opt_requested_address->setUint32(yiaddr); + pkt4->addOption(opt_requested_address); + OptionPtr opt_parameter_list = + Option::factory(Option::V4, DHO_DHCP_PARAMETER_REQUEST_LIST); + pkt4->addOption(opt_parameter_list); // Set client's and server's ports as well as server's address, // and local (relay) address. setDefaults4(socket, pkt4); @@ -707,14 +824,15 @@ TestControl::sendDiscover4(const TestControlSocket& socket) { isc_throw(InvalidOperation, "Statistics Manager for DHCPv4 " "hasn't been initialized"); } - stats_mgr4_->passSentPacket(StatsMgr4::XCHG_DO, pkt4); + stats_mgr4_->passSentPacket(StatsMgr4::XCHG_RA, pkt4); } + void TestControl::sendRequest6(const TestControlSocket& socket, const Pkt6Ptr& solicit_pkt6, const Pkt6Ptr& advertise_pkt6) { - const uint32_t transid = static_cast(random() % 0x00FFFFFF); + const uint32_t transid = generateTransid(); Pkt6Ptr pkt6(new Pkt6(DHCPV6_REQUEST, transid)); // Calculate elapsed time ptime solicit_time = solicit_pkt6->getTimestamp(); @@ -741,11 +859,20 @@ TestControl::sendRequest6(const TestControlSocket& socket, isc_throw(Unexpected, "client id not found in received packet"); } pkt6->addOption(opt_clientid); - OptionPtr opt_serverid = advertise_pkt6->getOption(D6O_SERVERID); - if (!opt_serverid) { - isc_throw(Unexpected, "server id not found in received packet"); + if (first_packet_serverid_.size() > 0) { + pkt6->addOption(Option::factory(Option::V6, D6O_SERVERID, + first_packet_serverid_)); + } else { + OptionPtr opt_serverid = advertise_pkt6->getOption(D6O_SERVERID); + if (!opt_serverid) { + isc_throw(Unexpected, "server id not found in received packet"); + } + if (CommandOptions::instance().isUseFirst() && + stats_mgr6_->getRcvdPacketsNum(StatsMgr6::XCHG_SA) == 1) { + first_packet_serverid_ = opt_serverid->getData(); + } + pkt6->addOption(opt_serverid); } - pkt6->addOption(opt_serverid); OptionPtr opt_ia_na = advertise_pkt6->getOption(D6O_IA_NA); if (!opt_ia_na) { isc_throw(Unexpected, "DHCPv6 IA_NA option not found in received " @@ -764,7 +891,8 @@ TestControl::sendRequest6(const TestControlSocket& socket, } void -TestControl::sendSolicit6(const TestControlSocket& socket) { +TestControl::sendSolicit6(const TestControlSocket& socket, + const bool preload /*= false*/) { ++sent_packets_0_; last_sent_ = microsec_clock::universal_time(); // Generate the MAC address to be passed in the packet. @@ -778,7 +906,9 @@ TestControl::sendSolicit6(const TestControlSocket& socket) { isc_throw(Unexpected, "failed to create SOLICIT packet"); } pkt6->addOption(Option::factory(Option::V6, D6O_ELAPSED_TIME)); - // pkt6->addOption(Option::factory(Option::V6, D6O_RAPID_COMMIT)); + if (CommandOptions::instance().isRapidCommit()) { + pkt6->addOption(Option::factory(Option::V6, D6O_RAPID_COMMIT)); + } pkt6->addOption(Option::factory(Option::V6, D6O_CLIENTID, duid)); pkt6->addOption(Option::factory(Option::V6, D6O_ORO)); pkt6->addOption(Option::factory(Option::V6, D6O_IA_NA)); @@ -786,11 +916,13 @@ TestControl::sendSolicit6(const TestControlSocket& socket) { setDefaults6(socket, pkt6); pkt6->pack(); IfaceMgr::instance().send(pkt6); - if (!stats_mgr6_) { - isc_throw(InvalidOperation, "Statistics Manager for DHCPv6 " - "hasn't been initialized"); + if (!preload) { + if (!stats_mgr6_) { + isc_throw(InvalidOperation, "Statistics Manager for DHCPv6 " + "hasn't been initialized"); + } + stats_mgr6_->passSentPacket(StatsMgr6::XCHG_SA, pkt6); } - stats_mgr6_->passSentPacket(StatsMgr6::XCHG_SA, pkt6); } void @@ -811,6 +943,9 @@ TestControl::setDefaults4(const TestControlSocket& socket, pkt->setLocalAddr(IOAddress(socket.getAddress())); // Set relay (GIADDR) address to local address. pkt->setGiaddr(IOAddress(socket.getAddress())); + std::vector mac = generateMacAddress(); + // Set hardware address + pkt->setHWAddr(HTYPE_ETHER, mac.size(), mac); // Pretend that we have one relay (which is us). pkt->setHops(1); } diff --git a/tests/tools/perfdhcp/test_control.h b/tests/tools/perfdhcp/test_control.h index a93d05dfc2..6c38e9263b 100644 --- a/tests/tools/perfdhcp/test_control.h +++ b/tests/tools/perfdhcp/test_control.h @@ -58,6 +58,10 @@ public: typedef boost::shared_ptr StatsMgr6Ptr; // Packet exchange type. typedef StatsMgr<>::ExchangeType ExchangeType; + // Packet template buffer. + typedef std::vector TemplateBuffer; + //Packet template buffers list. + typedef std::list TemplateBufferList; /// \brief Socket wrapper class. /// @@ -333,6 +337,8 @@ protected: /// \return number of exchanges to be started immediatelly. uint64_t getNextExchangesNum() const; + void initPacketTemplates(); + /// \brief Initializes Statistics Manager. /// /// This function initializes Statistics Manager. If there is @@ -350,7 +356,6 @@ protected: /// (for DHCPv6) than broadcast or multicast option is set on /// the socket. /// - /// \param port port to bound socket to. /// \throw isc::BadValue if socket can't be created for given /// interface, local address or remote address. /// \throw isc::InvalidOperation if broadcast option can't be @@ -358,7 +363,7 @@ protected: /// for the v6 socket. /// \throw isc::Unexpected if interal unexpected error occured. /// \return socket descriptor. - int openSocket(uint16_t port = 0) const; + int openSocket() const; /// \brief Print rate statistics. /// @@ -436,6 +441,13 @@ protected: /// depending in whch mode test is currently running. void registerOptionFactories() const; + + /// \brief Resets internal state of the object. + /// + /// Method resets internal state of the object. It has to be + /// called before new test is started. + void reset(); + /// \brief Send DHCPv4 DISCOVER message. /// /// Method creates and sends DHCPv4 DISCOVER message to the server @@ -448,11 +460,25 @@ protected: /// on the number of clients specified from the command line. /// /// \param socket socket to be used to send the message. + /// \param preload preload mode, packets not included in statistics. /// \throw isc::Unexpected if failed to create new packet instance. /// \throw isc::BadValue if MAC address has invalid length. - void sendDiscover4(const TestControlSocket& socket); + void sendDiscover4(const TestControlSocket& socket, + const bool preload = false); - /// \brief Sent DHCPv6 REQUEST message. + /// \brief Send DHCPv4 REQUEST message. + /// + /// Method creates and sends DHCPv4 REQUEST message to the server. + /// + /// \param socket socket to be used to send message. + /// \param offer_pkt4 OFFER packet object. + /// \throw isc::Unexpected if unexpected error occured. + /// \throw isc::InvalidOperation if Statistics Manager has not been + /// initialized. + void sendRequest4(const TestControlSocket& socket, + const dhcp::Pkt4Ptr& offer_pkt4); + + /// \brief Send DHCPv6 REQUEST message. /// /// Method creates and sends DHCPv6 REQUEST message to the server /// with the following options: @@ -485,8 +511,10 @@ protected: /// - D6O_IA_NA. /// /// \param socket socket to be used to send the message. + /// \param preload mode, packets not included in statistics. /// \throw isc::Unexpected if failed to create new packet instance. - void sendSolicit6(const TestControlSocket& socket); + void sendSolicit6(const TestControlSocket& socket, + const bool preload = false); /// \brief Set default DHCPv4 packet parameters. /// @@ -557,9 +585,14 @@ private: StatsMgr4Ptr stats_mgr4_; ///< Statistics Manager 4. StatsMgr6Ptr stats_mgr6_; ///< Statistics Manager 6. - // Pointers to functions. TransidGeneratorPtr transid_gen_; ///< Transaction id generator. + /// Buffer holiding server id received in first packet + dhcp::OptionBuffer first_packet_serverid_; + + /// Packet template buffers. + TemplateBufferList template_buffers_; + uint64_t sent_packets_0_; uint64_t sent_packets_1_; }; diff --git a/tests/tools/perfdhcp/tests/test_control_unittest.cc b/tests/tools/perfdhcp/tests/test_control_unittest.cc index 52c55b2f10..2284042d58 100644 --- a/tests/tools/perfdhcp/tests/test_control_unittest.cc +++ b/tests/tools/perfdhcp/tests/test_control_unittest.cc @@ -310,7 +310,6 @@ public: void testPkt4Exchange(int iterations_num, int receive_num, int& iterations_performed) const { - uint16_t port = 10547; int sock_handle = 0; NakedTestControl tc; tc.initializeStatsMgr(); @@ -323,25 +322,26 @@ public: generator(new NakedTestControl::IncrementalGenerator()); tc.setTransidGenerator(generator); // Socket is needed to send packets through the interface. - ASSERT_NO_THROW(sock_handle = tc.openSocket(port)); + ASSERT_NO_THROW(sock_handle = tc.openSocket()); TestControl::TestControlSocket sock(sock_handle); - int i = 0; - for (; i < iterations_num; ++i) { - if (tc.checkExitConditions()) { - break; - } + uint32_t transid = 0; + for (int i = 0; i < iterations_num; ++i) { ASSERT_NO_THROW(tc.sendDiscover4(sock)); + ++transid; // Do not simulate responses for packets later // that specified as receive_num. This simulates // packet drops. - if (i - 1 < receive_num) { - boost::shared_ptr offer_pkt4(createOfferPkt4(i)); - // Receive OFFER and send REQUEST. + if (i < receive_num) { + boost::shared_ptr offer_pkt4(createOfferPkt4(transid)); ASSERT_NO_THROW(tc.receivePacket4(sock, offer_pkt4)); + ++transid; } + if (tc.checkExitConditions()) { + iterations_performed = i + 1; + break; + } + iterations_performed = i + 1; } - // Return the number of iterations performed. - iterations_performed = i; } /// \brief Test DHCPv6 exchanges. @@ -360,7 +360,6 @@ public: void testPkt6Exchange(int iterations_num, int receive_num, int& iterations_performed) const { - uint16_t port = 10547; int sock_handle = 0; NakedTestControl tc; tc.initializeStatsMgr(); @@ -373,26 +372,28 @@ public: generator(new NakedTestControl::IncrementalGenerator()); tc.setTransidGenerator(generator); // Socket is needed to send packets through the interface. - ASSERT_NO_THROW(sock_handle = tc.openSocket(port)); + ASSERT_NO_THROW(sock_handle = tc.openSocket()); TestControl::TestControlSocket sock(sock_handle); - int i = 0; - for (; i < iterations_num; ++i) { - if (tc.checkExitConditions()) { - break; - } + uint32_t transid = 0; + for (int i = 0; i < iterations_num; ++i) { // Do not simulate responses for packets later // that specified as receive_num. This simulates // packet drops. ASSERT_NO_THROW(tc.sendSolicit6(sock)); - if (i - 1 < receive_num) { - boost::shared_ptr advertise_pkt6(createAdvertisePkt6(i)); + ++transid; + if (i < receive_num) { + boost::shared_ptr + advertise_pkt6(createAdvertisePkt6(transid)); // Receive ADVERTISE and send REQUEST. ASSERT_NO_THROW(tc.receivePacket6(sock, advertise_pkt6)); + ++transid; } - + if (tc.checkExitConditions()) { + iterations_performed = i + 1; + break; + } + iterations_performed = i + 1; } - // Return the number of iterations performed. - iterations_performed = i; } /// \brief Test generation of multiple MAC addresses. @@ -463,8 +464,12 @@ private: boost::shared_ptr offer(new Pkt4(DHCPOFFER, transid)); OptionPtr opt_msg_type = Option::factory(Option::V4, DHO_DHCP_MESSAGE_TYPE, OptionBuffer(DHCPOFFER)); + OptionPtr opt_serverid = Option::factory(Option::V4, + DHO_DHCP_SERVER_IDENTIFIER, + OptionBuffer(4, 1)); offer->setYiaddr(asiolink::IOAddress("127.0.0.1")); offer->addOption(opt_msg_type); + offer->addOption(opt_serverid); offer->updateTimestamp(); return(offer); } @@ -671,13 +676,12 @@ TEST_F(TestControlTest, Packet4) { std::string loopback_iface(getLocalLoopback()); if (!loopback_iface.empty()) { ASSERT_NO_THROW(processCmdLine("perfdhcp -l " + loopback_iface + - " all")); - uint16_t port = 10547; + " -L 10547 all")); NakedTestControl tc; int sock_handle = 0; // We have to create the socket to setup some parameters of // outgoing packet. - ASSERT_NO_THROW(sock_handle = tc.openSocket(port)); + ASSERT_NO_THROW(sock_handle = tc.openSocket()); TestControl::TestControlSocket sock(sock_handle); uint32_t transid = 123; boost::shared_ptr pkt4(new Pkt4(DHCPDISCOVER, transid)); @@ -705,13 +709,12 @@ TEST_F(TestControlTest, Packet6) { std::string loopback_iface(getLocalLoopback()); if (!loopback_iface.empty()) { ASSERT_NO_THROW(processCmdLine("perfdhcp -6 -l " + loopback_iface + - " servers")); - uint16_t port = 10547; + " -L 10547 servers")); NakedTestControl tc; int sock_handle = 0; // Create the socket. It will be needed to set packet's // parameters. - ASSERT_NO_THROW(sock_handle = tc.openSocket(port)); + ASSERT_NO_THROW(sock_handle = tc.openSocket()); TestControl::TestControlSocket sock(sock_handle); uint32_t transid = 123; boost::shared_ptr pkt6(new Pkt6(DHCPV6_SOLICIT, transid)); @@ -744,7 +747,7 @@ TEST_F(TestControlTest, Packet4Exchange) { // Set number of iterations to some high value. const int iterations_num = 100; processCmdLine("perfdhcp -l " + loopback_iface - + " -r 100 -n 10 -R 20 127.0.0.1"); + + " -r 100 -n 10 -R 20 -L 10547 127.0.0.1"); // The actual number of iterations will be stored in the // following variable. int iterations_performed = 0; @@ -756,7 +759,7 @@ TEST_F(TestControlTest, Packet4Exchange) { // With the following command line we restrict the maximum // number of dropped packets to 20% of all. processCmdLine("perfdhcp -l " + loopback_iface - + " -r 100 -R 20 -n 20 -D 10% 127.0.0.1"); + + " -r 100 -R 20 -n 20 -D 10% -L 10547 127.0.0.1"); // The number iterations is restricted by the percentage of // dropped packets (-D 10%). We also have to bump up the number // of iterations because the percentage limitation checks starts @@ -781,7 +784,7 @@ TEST_F(TestControlTest, Packet6Exchange) { const int iterations_num = 100; // Set number of iterations to 10. processCmdLine("perfdhcp -l " + loopback_iface - + " -6 -r 100 -n 10 -R 20 ::1"); + + " -6 -r 100 -n 10 -R 20 -L 10547 ::1"); int iterations_performed = 0; // Set number of received packets equal to number of iterations. // This simulates no packet drops. @@ -791,7 +794,7 @@ TEST_F(TestControlTest, Packet6Exchange) { // The maximum number of dropped packets is 3 (because of -D 3). processCmdLine("perfdhcp -l " + loopback_iface - + " -6 -r 100 -n 10 -R 20 -D 3 ::1"); + + " -6 -r 100 -n 10 -R 20 -D 3 -L 10547 ::1"); // For the first 3 packets we are simulating responses from server. // For other packets we don't so packet as 4,5,6 will be dropped and // then test should be interrupted and actual number of iterations will From 23c33b29ab5841a3fe5469d2056708e707938998 Mon Sep 17 00:00:00 2001 From: Marcin Siodelski Date: Fri, 24 Aug 2012 15:01:04 +0200 Subject: [PATCH 20/40] [1959] Print intermediate statistics (support for -t parameter). --- tests/tools/perfdhcp/stats_mgr.h | 27 +++++++++++++++++++++++++++ tests/tools/perfdhcp/test_control.cc | 20 ++++++++++++++++++++ tests/tools/perfdhcp/test_control.h | 9 +++++++++ 3 files changed, 56 insertions(+) diff --git a/tests/tools/perfdhcp/stats_mgr.h b/tests/tools/perfdhcp/stats_mgr.h index 1e57dda15b..7ea522301c 100644 --- a/tests/tools/perfdhcp/stats_mgr.h +++ b/tests/tools/perfdhcp/stats_mgr.h @@ -1096,6 +1096,33 @@ public: } } + /// \brief Print intermediate statistics. + /// + /// Method prints intermediate statistics for all exchanges. + /// Statistics includes sent, received and dropped packets + /// counters. + void printIntermediateStats() const { + std::ostringstream stream_sent(""); + std::ostringstream stream_rcvd(""); + std::ostringstream stream_drops(""); + std::string sep(""); + for (ExchangesMapIterator it = exchanges_.begin(); + it != exchanges_.end(); + ++it) { + + if (it != exchanges_.begin()) { + sep = "/"; + } + stream_sent << sep << it->second->getSentPacketsNum(); + stream_rcvd << sep << it->second->getRcvdPacketsNum(); + stream_drops << sep << it->second->getDroppedPacketsNum(); + } + std::cout << "sent: " << stream_sent.str() + << "; received: " << stream_rcvd.str() + << "; drops: " << stream_drops.str() + << std::endl; + } + /// \brief Print timestamps of all packets. /// /// Method prints timestamps of all sent and received diff --git a/tests/tools/perfdhcp/test_control.cc b/tests/tools/perfdhcp/test_control.cc index 42058a9adb..af59105dc6 100644 --- a/tests/tools/perfdhcp/test_control.cc +++ b/tests/tools/perfdhcp/test_control.cc @@ -527,6 +527,22 @@ TestControl::printRate() const { } } +void +TestControl::printIntermediateStats() { + CommandOptions& options = CommandOptions::instance(); + int delay = options.getReportDelay(); + ptime now = microsec_clock::universal_time(); + time_period time_since_report(last_report_, now); + if (time_since_report.length().total_seconds() >= delay) { + if (options.getIpVersion() == 4) { + stats_mgr4_->printIntermediateStats(); + } else if (options.getIpVersion() == 6) { + stats_mgr6_->printIntermediateStats(); + } + last_report_ = now; + } +} + void TestControl::printStats() const { printRate(); @@ -672,6 +688,7 @@ void TestControl::reset() { send_due_ = microsec_clock::universal_time(); last_sent_ = send_due_; + last_report_ = send_due_; transid_gen_.reset(); transid_gen_ = TransidGeneratorPtr(new TransidGenerator()); first_packet_serverid_.clear(); @@ -735,6 +752,9 @@ TestControl::run() { } ++packets_sent; } + if (options.getReportDelay() > 0) { + printIntermediateStats(); + } } printStats(); } diff --git a/tests/tools/perfdhcp/test_control.h b/tests/tools/perfdhcp/test_control.h index 6c38e9263b..c94d7b685e 100644 --- a/tests/tools/perfdhcp/test_control.h +++ b/tests/tools/perfdhcp/test_control.h @@ -365,6 +365,12 @@ protected: /// \return socket descriptor. int openSocket() const; + /// \brief Print intermediate statistics. + /// + /// Print brief statistics regarding number of sent packets, + /// received packets and dropped packets so far. + void printIntermediateStats(); + /// \brief Print rate statistics. /// /// Method print packet exchange rate statistics. @@ -582,6 +588,9 @@ private: ///< of exchanges. boost::posix_time::ptime last_sent_; ///< Indicates when the last exchange /// was initiated. + + boost::posix_time::ptime last_report_; ///< Last intermediate report time. + StatsMgr4Ptr stats_mgr4_; ///< Statistics Manager 4. StatsMgr6Ptr stats_mgr6_; ///< Statistics Manager 6. From b753e8ebd1f6b3e16628e22640efab38fff2cac8 Mon Sep 17 00:00:00 2001 From: Marcin Siodelski Date: Fri, 24 Aug 2012 16:09:36 +0200 Subject: [PATCH 21/40] [1959] End the program gracefully if interrupted. --- tests/tools/perfdhcp/test_control.cc | 17 ++++++++++++++++- tests/tools/perfdhcp/test_control.h | 10 ++++++++++ 2 files changed, 26 insertions(+), 1 deletion(-) diff --git a/tests/tools/perfdhcp/test_control.cc b/tests/tools/perfdhcp/test_control.cc index af59105dc6..775ed6fd31 100644 --- a/tests/tools/perfdhcp/test_control.cc +++ b/tests/tools/perfdhcp/test_control.cc @@ -16,6 +16,7 @@ #include #include #include +#include #include @@ -37,6 +38,8 @@ using namespace isc::asiolink; namespace isc { namespace perfdhcp { +bool TestControl::interrupted_ = false; + TestControl::TestControlSocket::TestControlSocket(int socket) : socket_(socket), addr_("127.0.0.1") { @@ -84,8 +87,11 @@ TestControl::TestControl() { bool TestControl::checkExitConditions() const { + if (interrupted_) { + return(true); + } CommandOptions& options = CommandOptions::instance(); - // Check if test period passed.. + // Check if test period passed. if (options.getPeriod() != 0) { if (options.getIpVersion() == 4) { time_period period(stats_mgr4_->getTestPeriod()); @@ -384,6 +390,11 @@ TestControl::getSentPacketsNum(const ExchangeType xchg_type) const { getSentPacketsNum(static_cast(xchg_type))); } +void +TestControl::handleInterrupt(int) { + interrupted_ = true; +} + void TestControl::initPacketTemplates() { CommandOptions& options = CommandOptions::instance(); @@ -692,6 +703,7 @@ TestControl::reset() { transid_gen_.reset(); transid_gen_ = TransidGeneratorPtr(new TransidGenerator()); first_packet_serverid_.clear(); + interrupted_ = false; } void @@ -722,6 +734,9 @@ TestControl::run() { srandom(options.getSeed()); } + // If user interrupts the program we will exit gracefully. + signal(SIGINT, TestControl::handleInterrupt); + // Preload server with number of packets. const bool do_preload = true; for (int i = 0; i < options.getPreload(); ++i) { diff --git a/tests/tools/perfdhcp/test_control.h b/tests/tools/perfdhcp/test_control.h index c94d7b685e..e3a03afd39 100644 --- a/tests/tools/perfdhcp/test_control.h +++ b/tests/tools/perfdhcp/test_control.h @@ -584,6 +584,14 @@ private: /// \return number of sent packets. uint64_t getSentPacketsNum(const ExchangeType xchg_type) const; + /// \brief Handle interrupt signal. + /// + /// Function sets flag indicating that program has been + /// interupted. + /// + /// \param sig signal (ignored) + static void handleInterrupt(int sig); + boost::posix_time::ptime send_due_; ///< Due time to initiate next chunk ///< of exchanges. boost::posix_time::ptime last_sent_; ///< Indicates when the last exchange @@ -602,6 +610,8 @@ private: /// Packet template buffers. TemplateBufferList template_buffers_; + static bool interrupted_; + uint64_t sent_packets_0_; uint64_t sent_packets_1_; }; From c47f7da793ecc7bb0486d6f78b9405803c2d2701 Mon Sep 17 00:00:00 2001 From: Marcin Siodelski Date: Fri, 24 Aug 2012 22:29:17 +0200 Subject: [PATCH 22/40] [1959] Implemented diagnostics selectors. --- tests/tools/perfdhcp/command_options.cc | 92 +++++++++ tests/tools/perfdhcp/command_options.h | 3 + tests/tools/perfdhcp/main.cc | 6 +- tests/tools/perfdhcp/stats_mgr.h | 2 +- tests/tools/perfdhcp/test_control.cc | 192 +++++++++++++++--- tests/tools/perfdhcp/test_control.h | 29 ++- .../perfdhcp/tests/stats_mgr_unittest.cc | 4 +- 7 files changed, 290 insertions(+), 38 deletions(-) diff --git a/tests/tools/perfdhcp/command_options.cc b/tests/tools/perfdhcp/command_options.cc index 14a2e65b9a..fcdb7bd484 100644 --- a/tests/tools/perfdhcp/command_options.cc +++ b/tests/tools/perfdhcp/command_options.cc @@ -634,6 +634,98 @@ CommandOptions::nonEmptyString(const std::string& errmsg) const { return sarg; } +void +CommandOptions::printCommandLine() const { + std::cout << "IPv" << static_cast(ipversion_) << std::endl; + if (exchange_mode_ == DO_SA) { + if (ipversion_ == 4) { + std::cout << "DISCOVER-OFFER only" << std::endl; + } else { + std::cout << "SOLICIT-ADVERETISE only" << std::endl; + } + } + if (rate_ != 0) { + std::cout << "rate[1/s]=" << rate_ << std::endl; + } + if (report_delay_ != 0) { + std::cout << "report[s]=" << report_delay_ << std::endl; + } + if (clients_num_ != 0) { + std::cout << "clients=" << clients_num_ << std::endl; + } + for (int i = 0; i < base_.size(); ++i) { + std::cout << "base[" << i << "]=" << base_[i] << std::endl; + } + for (int i = 0; i < num_request_.size(); ++i) { + std::cout << "num-request[" << i << "]=" << num_request_[i] << std::endl; + } + if (period_ != 0) { + std::cout << "test-period=" << period_ << std::endl; + } + for (int i = 0; i < drop_time_.size(); ++i) { + std::cout << "drop-time[" << i << "]=" << drop_time_[i] << std::endl; + } + for (int i = 0; i < max_drop_.size(); ++i) { + std::cout << "max-drop{" << i << "]=" << max_drop_[i] << std::endl; + } + for (int i = 0; i < max_pdrop_.size(); ++i) { + std::cout << "max-pdrop{" << i << "]=" << max_pdrop_[i] << std::endl; + } + if (preload_ != 0) { + std::cout << "preload=" << preload_ << std::endl; + } + std::cout << "aggressivity=" << aggressivity_ << std::endl; + if (getLocalPort() != 0) { + std::cout << "local-port=" << local_port_ << std::endl; + } + if (seeded_) { + std::cout << "seed=" << seed_ << std::endl; + } + if (broadcast_) { + std::cout << "broadcast" << std::endl; + } + if (rapid_commit_) { + std::cout << "rapid-commit" << std::endl; + } + if (use_first_) { + std::cout << "use-first" << std::endl; + } + for (int i = 0; i < template_file_.size(); ++i) { + std::cout << "template-file[" << i << "]=" << template_file_[i] << std::endl; + } + for (int i = 0; i < xid_offset_.size(); ++i) { + std::cout << "xid-offset[" << i << "]=" << xid_offset_[i] << std::endl; + } + if (elp_offset_ != 0) { + std::cout << "elp-offset=" << elp_offset_ << std::endl; + } + for (int i = 0; i < rnd_offset_.size(); ++i) { + std::cout << "rnd-offset[" << i << "]=" << rnd_offset_[i] << std::endl; + } + if (sid_offset_ != 0) { + std::cout << "sid-offset=" << sid_offset_ << std::endl; + } + if (rip_offset_ != 0) { + std::cout << "rip-offset=" << rip_offset_ << std::endl; + } + if (!diags_.empty()) { + std::cout << "diagnostic-selectors=" << diags_ << std::endl; + } + if (!wrapped_.empty()) { + std::cout << "wrapped=" << wrapped_ << std::endl; + } + if (!localname_.empty()) { + if (is_interface_) { + std::cout << "interface=" << localname_ << std::endl; + } else { + std::cout << "local-addr=" << localname_ << std::endl; + } + } + if (!server_name_.empty()) { + std::cout << "server=" << server_name_ << std::endl; + } +} + void CommandOptions::usage() const { fprintf(stdout, "%s", diff --git a/tests/tools/perfdhcp/command_options.h b/tests/tools/perfdhcp/command_options.h index b75c43864c..b9f2c3a198 100644 --- a/tests/tools/perfdhcp/command_options.h +++ b/tests/tools/perfdhcp/command_options.h @@ -230,6 +230,9 @@ public: /// /// \return server name std::string getServerName() const { return server_name_; } + + /// \brief Prints command line arguments. + void printCommandLine() const; /// \brief Print usage /// diff --git a/tests/tools/perfdhcp/main.cc b/tests/tools/perfdhcp/main.cc index d70b8fae8d..0c706a2b8c 100644 --- a/tests/tools/perfdhcp/main.cc +++ b/tests/tools/perfdhcp/main.cc @@ -38,7 +38,11 @@ main(int argc, char* argv[]) { TestControl& test_control = TestControl::instance(); test_control.run(); } catch (isc::Exception& e) { - std::cout << "Error starting perfdhcp: " << e.what() << std::endl; + std::cout << "Error running perfdhcp: " << e.what() << std::endl; + std::string diags(command_options.getDiags()); + if (diags.find('e') != std::string::npos) { + std::cout << "Fatal error" << std::endl; + } return(1); } return(0); diff --git a/tests/tools/perfdhcp/stats_mgr.h b/tests/tools/perfdhcp/stats_mgr.h index 7ea522301c..806fb8dee6 100644 --- a/tests/tools/perfdhcp/stats_mgr.h +++ b/tests/tools/perfdhcp/stats_mgr.h @@ -834,7 +834,7 @@ public: /// /// \param counter_key key poitinh to the counter in the counters map. /// \return pointer to specified counter after incrementation. - const CustomCounter& IncrementCounter(const std::string& counter_key) { + const CustomCounter& incrementCounter(const std::string& counter_key) { CustomCounterPtr counter = getCounter(counter_key); return(++(*counter)); } diff --git a/tests/tools/perfdhcp/test_control.cc b/tests/tools/perfdhcp/test_control.cc index 775ed6fd31..c58631f841 100644 --- a/tests/tools/perfdhcp/test_control.cc +++ b/tests/tools/perfdhcp/test_control.cc @@ -85,37 +85,55 @@ TestControl::TestControl() { reset(); } +std::string +TestControl::byte2Hex(const uint8_t b) const { + const int b1 = b / 16; + const int b0 = b % 16; + ostringstream stream; + stream << std::hex << b1 << b0 << std::dec; + return stream.str(); +} + bool TestControl::checkExitConditions() const { if (interrupted_) { return(true); } CommandOptions& options = CommandOptions::instance(); + bool test_period_reached = false; // Check if test period passed. if (options.getPeriod() != 0) { if (options.getIpVersion() == 4) { time_period period(stats_mgr4_->getTestPeriod()); if (period.length().total_seconds() >= options.getPeriod()) { - return true; + test_period_reached = true; } } else if (options.getIpVersion() == 6) { time_period period = stats_mgr6_->getTestPeriod(); if (period.length().total_seconds() >= options.getPeriod()) { - return true; + test_period_reached = true; } } } + if (test_period_reached) { + if (testDiags('e')) { + std::cout << "Reached test period." << std::endl; + } + return(true); + } + + bool max_requests = false; // Check if we reached maximum number of DISCOVER/SOLICIT sent. if (options.getNumRequests().size() > 0) { if (options.getIpVersion() == 4) { if (getSentPacketsNum(StatsMgr4::XCHG_DO) >= options.getNumRequests()[0]) { - return(true); + max_requests = true; } } else if (options.getIpVersion() == 6) { if (stats_mgr6_->getSentPacketsNum(StatsMgr6::XCHG_SA) >= options.getNumRequests()[0]) { - return(true); + max_requests = true; } } } @@ -124,26 +142,34 @@ TestControl::checkExitConditions() const { if (options.getIpVersion() == 4) { if (stats_mgr4_->getSentPacketsNum(StatsMgr4::XCHG_RA) >= options.getNumRequests()[1]) { - return(true); + max_requests = true; } } else if (options.getIpVersion() == 6) { if (stats_mgr6_->getSentPacketsNum(StatsMgr6::XCHG_RR) >= options.getNumRequests()[1]) { - return(true); + max_requests = true; } } } + if (max_requests) { + if (testDiags('e')) { + std::cout << "Reached test period." << std::endl; + } + return(true); + } + // Check if we reached maximum number of drops of OFFER/ADVERTISE packets. + bool max_drops = false; if (options.getMaxDrop().size() > 0) { if (options.getIpVersion() == 4) { if (stats_mgr4_->getDroppedPacketsNum(StatsMgr4::XCHG_DO) >= options.getMaxDrop()[0]) { - return(true); + max_drops = true; } } else if (options.getIpVersion() == 6) { if (stats_mgr6_->getDroppedPacketsNum(StatsMgr6::XCHG_SA) >= options.getMaxDrop()[0]) { - return(true); + max_drops = true; } } } @@ -152,30 +178,39 @@ TestControl::checkExitConditions() const { if (options.getIpVersion() == 4) { if (stats_mgr4_->getDroppedPacketsNum(StatsMgr4::XCHG_RA) >= options.getMaxDrop()[1]) { - return(true); + max_drops = true; } } else if (options.getIpVersion() == 6) { if (stats_mgr6_->getDroppedPacketsNum(StatsMgr6::XCHG_RR) >= options.getMaxDrop()[1]) { - return(true); + max_drops = true; } } } + if (max_drops) { + if (testDiags('e')) { + std::cout << "Reached maximum drops number." << std::endl; + } + return(true); + } + // Check if we reached maximum drops percentage of OFFER/ADVERTISE packets. + bool max_pdrops = false; if (options.getMaxDropPercentage().size() > 0) { if (options.getIpVersion() == 4) { if ((stats_mgr4_->getSentPacketsNum(StatsMgr4::XCHG_DO) > 10) && ((100. * stats_mgr4_->getDroppedPacketsNum(StatsMgr4::XCHG_DO) / stats_mgr4_->getSentPacketsNum(StatsMgr4::XCHG_DO)) >= options.getMaxDropPercentage()[0])) { - return(true); + max_pdrops = true; + } } else if (options.getIpVersion() == 6) { if ((stats_mgr6_->getSentPacketsNum(StatsMgr6::XCHG_SA) > 10) && ((100. * stats_mgr6_->getDroppedPacketsNum(StatsMgr6::XCHG_SA) / stats_mgr6_->getSentPacketsNum(StatsMgr6::XCHG_SA)) >= options.getMaxDropPercentage()[0])) { - return(true); + max_pdrops = true; } } } @@ -186,17 +221,24 @@ TestControl::checkExitConditions() const { ((100. * stats_mgr4_->getDroppedPacketsNum(StatsMgr4::XCHG_RA) / stats_mgr4_->getSentPacketsNum(StatsMgr4::XCHG_RA)) >= options.getMaxDropPercentage()[1])) { - return(true); + max_pdrops = true; } } else if (options.getIpVersion() == 6) { if ((stats_mgr6_->getSentPacketsNum(StatsMgr6::XCHG_RR) > 10) && ((100. * stats_mgr6_->getDroppedPacketsNum(StatsMgr6::XCHG_RR) / stats_mgr6_->getSentPacketsNum(StatsMgr6::XCHG_RR)) >= options.getMaxDropPercentage()[1])) { - return(true); + max_pdrops = true; } } } + if (max_pdrops) { + if (testDiags('e')) { + std::cout << "Reached maximum percentage of drops." << std::endl; + } + return(true); + } + return(false); } @@ -335,9 +377,6 @@ TestControl::getNextExchangesNum() const { uint64_t due_exchanges = 0; // Get current time. ptime now(microsec_clock::universal_time()); - // The due time indicates when we should start sending next chunk - // of packets. If it is already due time, we should calculate - // how many packets to send. if (now >= send_due_) { // If rate is specified from the command line we have to // synchornize with it. @@ -426,6 +465,19 @@ TestControl::initializeStatsMgr() { stats_mgr6_->addExchangeStats(StatsMgr6::XCHG_RR); } } + if (testDiags('i')) { + if (options.getIpVersion() == 4) { + stats_mgr4_->addCustomCounter("latesend", "Late sent packets"); + stats_mgr4_->addCustomCounter("shortwait", "Short waits for packets"); + stats_mgr4_->addCustomCounter("multircvd", "Multiple packets receives"); + // stats_mgr4_->addCustomCounter("latercvd", "Late received packets"); + } else if (options.getIpVersion() == 6) { + stats_mgr6_->addCustomCounter("latesend", "Late sent packets"); + stats_mgr6_->addCustomCounter("shortwait", "Short waits for packets"); + stats_mgr6_->addCustomCounter("multircvd", "Multiple packets receives"); + // stats_mgr6_->addCustomCounter("latercvd", "Late received packets"); + } + } } int @@ -516,6 +568,23 @@ TestControl::openSocket() const { return(sock); } +void +TestControl::printDiagnostics() const { + CommandOptions& options = CommandOptions::instance(); + if (testDiags('a')) { + // Print all command line parameters. + options.printCommandLine(); + + // Print MAC and DUID. + std::cout << "Set MAC to " << vector2Hex(options.getMacPrefix(), "::") + << std::endl; + if (options.getDuidPrefix().size() > 0) { + std::cout << "Set DUID to " << vector2Hex(options.getDuidPrefix()) << std::endl; + } + + } +} + void TestControl::printRate() const { double rate = 0; @@ -564,15 +633,37 @@ TestControl::printStats() const { "hasn't been initialized"); } stats_mgr4_->printStats(); + if (testDiags('i')) { + stats_mgr4_->printCustomCounters(); + } } else if (options.getIpVersion() == 6) { if (!stats_mgr6_) { isc_throw(InvalidOperation, "Statistics Manager for DHCPv6 " "hasn't been initialized"); } stats_mgr6_->printStats(); + if (testDiags('i')) { + stats_mgr6_->printCustomCounters(); + } } } +std::string +TestControl::vector2Hex(const std::vector& vec, + const std::string& separator /* ="" */) const { + std::ostringstream stream; + for (std::vector::const_iterator it = vec.begin(); + it != vec.end(); + ++it) { + if (it == vec.begin()) { + stream << byte2Hex(*it); + } else { + stream << separator << byte2Hex(*it); + } + } + return(stream.str()); +} + void TestControl::receivePacket4(const TestControlSocket& socket, const Pkt4Ptr& pkt4) { @@ -610,12 +701,17 @@ void TestControl::receivePackets(const TestControlSocket& socket) { int timeout = 0; bool receiving = true; + uint64_t received = 0; while (receiving) { if (CommandOptions::instance().getIpVersion() == 4) { Pkt4Ptr pkt4 = IfaceMgr::instance().receive4(timeout); if (!pkt4) { receiving = false; } else { + ++received; + if ((received > 1) && testDiags('i')) { + stats_mgr4_->incrementCounter("multircvd"); + } pkt4->unpack(); receivePacket4(socket, pkt4); } @@ -624,6 +720,10 @@ TestControl::receivePackets(const TestControlSocket& socket) { if (!pkt6) { receiving = false; } else { + ++received; + if ((received > 1) && testDiags('i')) { + stats_mgr6_->incrementCounter("multircvd"); + } if (pkt6->unpack()) { receivePacket6(socket, pkt6); } @@ -708,9 +808,6 @@ TestControl::reset() { void TestControl::run() { - sent_packets_0_ = 0; - sent_packets_1_ = 0; - // Reset singleton state before test starts. reset(); @@ -723,6 +820,9 @@ TestControl::run() { isc_throw(InvalidOperation, "command options must be parsed before running a test"); } + + printDiagnostics(); + registerOptionFactories(); TestControlSocket socket(openSocket()); @@ -749,14 +849,22 @@ TestControl::run() { initializeStatsMgr(); - uint64_t packets_sent = 0; for (;;) { updateSendDue(); if (checkExitConditions()) { break; } uint64_t packets_due = getNextExchangesNum(); + if ((packets_due == 0) && testDiags('i')) { + if (options.getIpVersion() == 4) { + stats_mgr4_->incrementCounter("shortwait"); + } else if (options.getIpVersion() == 6) { + stats_mgr6_->incrementCounter("shortwait"); + } + } + // @todo: set non-zero timeout for packets once we implement + // microseconds timeout in IfaceMgr. receivePackets(socket); for (uint64_t i = packets_due; i > 0; --i) { @@ -765,19 +873,24 @@ TestControl::run() { } else { sendSolicit6(socket); } - ++packets_sent; } if (options.getReportDelay() > 0) { printIntermediateStats(); } } printStats(); + if (testDiags('s') && (first_packet_serverid_.size() > 0)) { + std::cout << "Server id: " << vector2Hex(first_packet_serverid_) << std::endl; + } + // Diagnostics flag 'e' means show exit reason. + if (testDiags('e')) { + std::cout << "Interrupted" << std::endl; + } } void TestControl::sendDiscover4(const TestControlSocket& socket, const bool preload /*= false*/) { - ++sent_packets_0_; last_sent_ = microsec_clock::universal_time(); // Generate the MAC address to be passed in the packet. std::vector mac_address = generateMacAddress(); @@ -819,7 +932,8 @@ TestControl::sendRequest4(const TestControlSocket& socket, OptionPtr opt_msg_type = Option::factory(Option::V4, DHO_DHCP_MESSAGE_TYPE, buf_msg_type); pkt4->addOption(opt_msg_type); - if (first_packet_serverid_.size() > 0) { + if (CommandOptions::instance().isUseFirst() && + (first_packet_serverid_.size() > 0)) { pkt4->addOption(Option::factory(Option::V4, DHO_DHCP_SERVER_IDENTIFIER, first_packet_serverid_)); } else { @@ -829,8 +943,7 @@ TestControl::sendRequest4(const TestControlSocket& socket, isc_throw(BadValue, "there is no SERVER_IDENTIFIER option " << "in OFFER message"); } - if (CommandOptions::instance().isUseFirst() && - stats_mgr4_->getRcvdPacketsNum(StatsMgr4::XCHG_DO) == 1) { + if (stats_mgr4_->getRcvdPacketsNum(StatsMgr4::XCHG_DO) == 1) { first_packet_serverid_ = opt_serverid->getData(); } pkt4->addOption(opt_serverid); @@ -894,7 +1007,8 @@ TestControl::sendRequest6(const TestControlSocket& socket, isc_throw(Unexpected, "client id not found in received packet"); } pkt6->addOption(opt_clientid); - if (first_packet_serverid_.size() > 0) { + if (CommandOptions::instance().isUseFirst() && + (first_packet_serverid_.size() > 0)) { pkt6->addOption(Option::factory(Option::V6, D6O_SERVERID, first_packet_serverid_)); } else { @@ -902,8 +1016,7 @@ TestControl::sendRequest6(const TestControlSocket& socket, if (!opt_serverid) { isc_throw(Unexpected, "server id not found in received packet"); } - if (CommandOptions::instance().isUseFirst() && - stats_mgr6_->getRcvdPacketsNum(StatsMgr6::XCHG_SA) == 1) { + if (stats_mgr6_->getRcvdPacketsNum(StatsMgr6::XCHG_SA) == 1) { first_packet_serverid_ = opt_serverid->getData(); } pkt6->addOption(opt_serverid); @@ -928,7 +1041,6 @@ TestControl::sendRequest6(const TestControlSocket& socket, void TestControl::sendSolicit6(const TestControlSocket& socket, const bool preload /*= false*/) { - ++sent_packets_0_; last_sent_ = microsec_clock::universal_time(); // Generate the MAC address to be passed in the packet. std::vector mac_address = generateMacAddress(); @@ -1003,6 +1115,15 @@ TestControl::setDefaults6(const TestControlSocket& socket, pkt->setRemoteAddr(IOAddress(options.getServerName())); } +bool +TestControl::testDiags(const char diag) const { + std::string diags(CommandOptions::instance().getDiags()); + if (diags.find(diag) != std::string::npos) { + return true; + } + return false; +} + void TestControl::updateSendDue() { // If default constructor was called, this should not happen but @@ -1026,6 +1147,17 @@ TestControl::updateSendDue() { } // Calculate due time to initate next chunk of exchanges. send_due_ = last_sent_ + time_duration(0, 0, 0, duration); + // Check if it is already due. + ptime now(microsec_clock::universal_time()); + if (now > send_due_) { + if (testDiags('i')) { + if (options.getIpVersion() == 4) { + stats_mgr4_->incrementCounter("latesend"); + } else if (options.getIpVersion() == 6) { + stats_mgr6_->incrementCounter("latesend"); + } + } + } } diff --git a/tests/tools/perfdhcp/test_control.h b/tests/tools/perfdhcp/test_control.h index e3a03afd39..9b634ce5d7 100644 --- a/tests/tools/perfdhcp/test_control.h +++ b/tests/tools/perfdhcp/test_control.h @@ -552,6 +552,12 @@ protected: void setDefaults6(const TestControlSocket& socket, const dhcp::Pkt6Ptr& pkt); + /// \brief Find of diagnostic flag has been set. + /// + /// \param diag diagnostic flag (a,e,i,s,r,t,T). + /// \return true if diagnostics flag has been set. + bool testDiags(const char diag) const; + /// \brief Update due time to initiate next chunk of exchanges. /// /// Method updates due time to initiate next chunk of exchanges. @@ -561,6 +567,12 @@ protected: private: + /// \brief Convert binary value to hex string. + /// + /// \param b byte to convert. + /// \return hex string. + std::string byte2Hex(const uint8_t b) const; + /// \brief Generate transaction id using random function. /// /// \return generated transaction id value. @@ -592,6 +604,18 @@ private: /// \param sig signal (ignored) static void handleInterrupt(int sig); + /// \brief Convert vector in hexadecimal string. + /// + /// \param vec vector to be converted. + /// \param separator separator. + std::string vector2Hex(const std::vector& vec, + const std::string& separator = "") const; + + /// \brief Print main diagnostics data. + /// + /// Method prints main diagnostics data. + void printDiagnostics() const; + boost::posix_time::ptime send_due_; ///< Due time to initiate next chunk ///< of exchanges. boost::posix_time::ptime last_sent_; ///< Indicates when the last exchange @@ -610,10 +634,7 @@ private: /// Packet template buffers. TemplateBufferList template_buffers_; - static bool interrupted_; - - uint64_t sent_packets_0_; - uint64_t sent_packets_1_; + static bool interrupted_; ///< Is program interrupted. }; } // namespace perfdhcp diff --git a/tests/tools/perfdhcp/tests/stats_mgr_unittest.cc b/tests/tools/perfdhcp/tests/stats_mgr_unittest.cc index 2233847bb1..d6b3aeff24 100644 --- a/tests/tools/perfdhcp/tests/stats_mgr_unittest.cc +++ b/tests/tools/perfdhcp/tests/stats_mgr_unittest.cc @@ -387,13 +387,13 @@ TEST_F(StatsMgrTest, CustomCounters) { // Increment one of the counters 10 times. const uint64_t tooshort_num = 10; for (uint64_t i = 0; i < tooshort_num; ++i) { - stats_mgr->IncrementCounter(too_short_key); + stats_mgr->incrementCounter(too_short_key); } // Increment another counter by 5 times. const uint64_t toolate_num = 5; for (uint64_t i = 0; i < toolate_num; ++i) { - stats_mgr->IncrementCounter(too_late_key); + stats_mgr->incrementCounter(too_late_key); } // Check counter's current value and name. From 6efd035f6178974adb48ace599f6d2335c0afd7e Mon Sep 17 00:00:00 2001 From: Marcin Siodelski Date: Wed, 29 Aug 2012 18:01:01 +0200 Subject: [PATCH 23/40] [1959] Added support for packet templates. --- tests/tools/perfdhcp/localized_option.h | 60 +- tests/tools/perfdhcp/perf_pkt4.cc | 10 +- tests/tools/perfdhcp/perf_pkt4.h | 27 + tests/tools/perfdhcp/perf_pkt6.cc | 8 + tests/tools/perfdhcp/perf_pkt6.h | 26 + tests/tools/perfdhcp/pkt_transform.cc | 13 +- tests/tools/perfdhcp/pkt_transform.h | 28 + .../perfdhcp/templates/discover-example.hex | 1 + .../perfdhcp/templates/request4-example.hex | 1 + .../perfdhcp/templates/request6-example.hex | 1 + .../perfdhcp/templates/solicit-example.hex | 1 + tests/tools/perfdhcp/test_control.cc | 623 +++++++++++++++--- tests/tools/perfdhcp/test_control.h | 160 ++++- .../perfdhcp/tests/test_control_unittest.cc | 95 ++- 14 files changed, 922 insertions(+), 132 deletions(-) create mode 100644 tests/tools/perfdhcp/templates/discover-example.hex create mode 100644 tests/tools/perfdhcp/templates/request4-example.hex create mode 100644 tests/tools/perfdhcp/templates/request6-example.hex create mode 100644 tests/tools/perfdhcp/templates/solicit-example.hex diff --git a/tests/tools/perfdhcp/localized_option.h b/tests/tools/perfdhcp/localized_option.h index 5374684f89..f93089fbad 100644 --- a/tests/tools/perfdhcp/localized_option.h +++ b/tests/tools/perfdhcp/localized_option.h @@ -16,6 +16,8 @@ #define __LOCALIZED_OPTION_H #include +#include +#include namespace isc { namespace perfdhcp { @@ -51,7 +53,7 @@ public: uint16_t type, const dhcp::OptionBuffer& data) : dhcp::Option(u, type, data), - offset_(0) { + offset_(0), option_valid_(true) { } @@ -66,7 +68,49 @@ public: const dhcp::OptionBuffer& data, const size_t offset) : dhcp::Option(u, type, data), - offset_(offset) { + offset_(offset), option_valid_(true) { + } + + /// \brief Copy constructor, creates LocalizedOption from Option6IA. + /// + /// This copy constructor creates regular option from Option6IA. + /// The data from Option6IA data members are copied to + /// option buffer in appropriate sequence. + /// + /// \param opt_ia option to be copied. + /// \param offset location of the option in a packet. + LocalizedOption(const boost::shared_ptr& opt_ia, + const size_t offset) : + dhcp::Option(Option::V6, 0, dhcp::OptionBuffer()), + offset_(offset), option_valid_(false) { + // If given option is NULL we will mark this new option + // as invalid. User may query if option is valid when + // object is created. + if (opt_ia) { + // Set universe and type. + universe_ = opt_ia->getUniverse(); + type_ = opt_ia->getType(); + util::OutputBuffer buf(opt_ia->len() - opt_ia->getHeaderLen()); + try { + // Try to pack option data into the temporary buffer. + opt_ia->pack(buf); + if (buf.getLength() > 0) { + const char* buf_data = static_cast(buf.getData()); + // Option has been packed along with option type flag + // and transaction id so we have to skip first 4 bytes + // when copying temporary buffer option buffer. + data_.assign(buf_data + 4, buf_data + buf.getLength()); + } + option_valid_ = true; + } catch (const Exception&) { + // If there was an exception somewhere when packing + // the data into the buffer we assume that option is + // not valid and should not be used. + option_valid_ = false; + } + } else { + option_valid_ = false; + } } /// \brief Constructor, sets default (0) option offset @@ -84,7 +128,7 @@ public: dhcp::OptionBufferConstIter first, dhcp::OptionBufferConstIter last) : dhcp::Option(u, type, first, last), - offset_(0) { + offset_(0), option_valid_(true) { } @@ -104,7 +148,7 @@ public: dhcp::OptionBufferConstIter first, dhcp::OptionBufferConstIter last, const size_t offset) : dhcp::Option(u, type, first, last), - offset_(offset) { + offset_(offset), option_valid_(true) { } /// \brief Returns offset of an option in a DHCP packet. @@ -112,8 +156,16 @@ public: /// \return option offset in a packet size_t getOffset() const { return offset_; }; + /// \brief Checks if option is valid. + /// + /// \return true, if option is valid. + virtual bool valid() { + return (Option::valid() && option_valid_); + } + private: size_t offset_; ///< Offset of DHCP option in a packet + bool option_valid_; ///< Is option valid. }; diff --git a/tests/tools/perfdhcp/perf_pkt4.cc b/tests/tools/perfdhcp/perf_pkt4.cc index 3f733afacc..3ccef94e98 100644 --- a/tests/tools/perfdhcp/perf_pkt4.cc +++ b/tests/tools/perfdhcp/perf_pkt4.cc @@ -16,7 +16,6 @@ #include #include "perf_pkt4.h" -#include "pkt_transform.h" using namespace std; using namespace isc; @@ -58,5 +57,14 @@ PerfPkt4::rawUnpack() { return (res); } +void +PerfPkt4::writeAt(size_t dest_pos, + std::vector::iterator first, + std::vector::iterator last) { + return (PktTransform::writeAt(data_, dest_pos, first, last)); +} + + + } // namespace perfdhcp } // namespace isc diff --git a/tests/tools/perfdhcp/perf_pkt4.h b/tests/tools/perfdhcp/perf_pkt4.h index f4cc440773..1a19a08749 100644 --- a/tests/tools/perfdhcp/perf_pkt4.h +++ b/tests/tools/perfdhcp/perf_pkt4.h @@ -20,6 +20,7 @@ #include #include "localized_option.h" +#include "pkt_transform.h" namespace isc { namespace perfdhcp { @@ -102,11 +103,37 @@ public: /// \return false If unpack operation failed. bool rawUnpack(); + /// \brief Replace contents of buffer with data. + /// + /// Function replaces part of the buffer with data from vector. + /// + /// \param dest_pos position in buffer where data is replaced. + /// \param first beginning of data range in source vector. + /// \param last end of data range in source vector. + void writeAt(size_t dest_pos, + std::vector::iterator first, + std::vector::iterator last); + + + /// \brief Replace contents of buffer with value. + /// + /// Function replaces part of buffer with value. + /// + /// \param dest_pos position in buffer where value is + /// to be written. + /// \param val value to be written. + template + void writeValueAt(size_t dest_pos, T val) { + PktTransform::writeValueAt(data_, dest_pos, val); + } + private: size_t transid_offset_; ///< transaction id offset }; +typedef boost::shared_ptr PerfPkt4Ptr; + } // namespace perfdhcp } // namespace isc diff --git a/tests/tools/perfdhcp/perf_pkt6.cc b/tests/tools/perfdhcp/perf_pkt6.cc index 24cfb931a9..56fe9dfd77 100644 --- a/tests/tools/perfdhcp/perf_pkt6.cc +++ b/tests/tools/perfdhcp/perf_pkt6.cc @@ -60,5 +60,13 @@ PerfPkt6::rawUnpack() { return (res); } +void +PerfPkt6::writeAt(size_t dest_pos, + std::vector::iterator first, + std::vector::iterator last) { + return (PktTransform::writeAt(data_, dest_pos, first, last)); +} + + } // namespace perfdhcp } // namespace isc diff --git a/tests/tools/perfdhcp/perf_pkt6.h b/tests/tools/perfdhcp/perf_pkt6.h index 94fe47bada..25fb4e53a4 100644 --- a/tests/tools/perfdhcp/perf_pkt6.h +++ b/tests/tools/perfdhcp/perf_pkt6.h @@ -20,6 +20,7 @@ #include #include "localized_option.h" +#include "pkt_transform.h" namespace isc { namespace perfdhcp { @@ -102,11 +103,36 @@ public: /// \return false if unpack operation failed. bool rawUnpack(); + /// \brief Replace contents of buffer with data. + /// + /// Function replaces part of the buffer with data from vector. + /// + /// \param dest_pos position in buffer where data is replaced. + /// \param first beginning of data range in source vector. + /// \param last end of data range in source vector. + void writeAt(size_t dest_pos, + std::vector::iterator first, + std::vector::iterator last); + + /// \brief Replace contents of buffer with value. + /// + /// Function replaces part of buffer with value. + /// + /// \param dest_pos position in buffer where value is + /// to be written. + /// \param val value to be written. + template + void writeValueAt(size_t dest_pos, T val) { + PktTransform::writeValueAt(data_, dest_pos, val); + } + private: size_t transid_offset_; ///< transaction id offset }; +typedef boost::shared_ptr PerfPkt6Ptr; + } // namespace perfdhcp } // namespace isc diff --git a/tests/tools/perfdhcp/pkt_transform.cc b/tests/tools/perfdhcp/pkt_transform.cc index 5ed39bfc5f..d87ca19c3b 100644 --- a/tests/tools/perfdhcp/pkt_transform.cc +++ b/tests/tools/perfdhcp/pkt_transform.cc @@ -216,7 +216,18 @@ PktTransform::unpackOptions(const OptionBuffer& in_buffer, in_buffer.begin() + offset + opt_len); } } - + +void +PktTransform::writeAt(dhcp::OptionBuffer& in_buffer, size_t dest_pos, + dhcp::OptionBuffer::iterator first, + dhcp::OptionBuffer::iterator last) { + int i = 0; + for (std::vector::iterator it = first; + it != last; + ++it, ++i) { + in_buffer[dest_pos + i] = *it; + } +} } // namespace perfdhcp } // namespace isc diff --git a/tests/tools/perfdhcp/pkt_transform.h b/tests/tools/perfdhcp/pkt_transform.h index 7fb19f48c4..e7665a2dc8 100644 --- a/tests/tools/perfdhcp/pkt_transform.h +++ b/tests/tools/perfdhcp/pkt_transform.h @@ -92,6 +92,33 @@ public: const size_t transid_offset, uint32_t& transid); + /// \brief Replace contents of buffer with vector. + /// + /// Function replaces data of the buffer with data from vector. + /// + /// \param in_buffer destination buffer. + /// \param dest_pos position in destination buffer. + /// \param first beginning of data range in source vector. + /// \param last end of data range in source vector. + static void writeAt(dhcp::OptionBuffer& in_buffer, size_t dest_pos, + std::vector::iterator first, + std::vector::iterator last); + + /// \brief Replace contents of one vector with uint16 value. + /// + /// Function replaces data inside one vector with uint16_t value. + /// + /// \param in_buffer destination buffer. + /// \param dest_pos position in destination buffer. + /// \param val value to be written. + template + static void writeValueAt(dhcp::OptionBuffer& in_buffer, size_t dest_pos, + T val) { + for (int i = 0; i < sizeof(T); ++i) { + in_buffer[dest_pos + i] = (val >> (sizeof(T) - 8 * i - 1)) & 0xFF; + } + } + private: /// \brief Replaces contents of options in a buffer. /// @@ -131,6 +158,7 @@ private: /// \throw isc::Unexpected if options unpack failed. static void unpackOptions(const dhcp::OptionBuffer& in_buffer, const dhcp::Option::OptionCollection& options); + }; } // namespace perfdhcp diff --git a/tests/tools/perfdhcp/templates/discover-example.hex b/tests/tools/perfdhcp/templates/discover-example.hex new file mode 100644 index 0000000000..9a6e5ea53d --- /dev/null +++ b/tests/tools/perfdhcp/templates/discover-example.hex @@ -0,0 +1 @@ +01010601008b45d200000000000000000000000000000000ac100102000c0102030400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000638253633501013707011c02030f060cff \ No newline at end of file diff --git a/tests/tools/perfdhcp/templates/request4-example.hex b/tests/tools/perfdhcp/templates/request4-example.hex new file mode 100644 index 0000000000..32447d6ba8 --- /dev/null +++ b/tests/tools/perfdhcp/templates/request4-example.hex @@ -0,0 +1 @@ +01010601007b23f800000000000000000000000000000000ac100102000c0102030400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000638253633204ac1001813501033604ac1001013707011c02030f060cff \ No newline at end of file diff --git a/tests/tools/perfdhcp/templates/request6-example.hex b/tests/tools/perfdhcp/templates/request6-example.hex new file mode 100644 index 0000000000..1e3e76f5e7 --- /dev/null +++ b/tests/tools/perfdhcp/templates/request6-example.hex @@ -0,0 +1 @@ +03da30c60001000e0001000117cf8e76000c010203060002000e0001000117cf8a5c080027a87b3400030028000000010000000a0000000e0005001820010db800010000000000000001b568000000be000000c8000800020000 \ No newline at end of file diff --git a/tests/tools/perfdhcp/templates/solicit-example.hex b/tests/tools/perfdhcp/templates/solicit-example.hex new file mode 100644 index 0000000000..41c5ad33a8 --- /dev/null +++ b/tests/tools/perfdhcp/templates/solicit-example.hex @@ -0,0 +1 @@ +015f4e650001000e0001000117cf8e76000c010203040003000c0000000100000e01000015180006000400170018000800020000 \ No newline at end of file diff --git a/tests/tools/perfdhcp/test_control.cc b/tests/tools/perfdhcp/test_control.cc index c58631f841..6da2549182 100644 --- a/tests/tools/perfdhcp/test_control.cc +++ b/tests/tools/perfdhcp/test_control.cc @@ -12,6 +12,8 @@ // OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR // PERFORMANCE OF THIS SOFTWARE. + +#include #include #include #include @@ -25,8 +27,11 @@ #include #include #include +#include #include "test_control.h" #include "command_options.h" +#include "perf_pkt4.h" +#include "perf_pkt6.h" using namespace std; using namespace boost; @@ -91,13 +96,13 @@ TestControl::byte2Hex(const uint8_t b) const { const int b0 = b % 16; ostringstream stream; stream << std::hex << b1 << b0 << std::dec; - return stream.str(); + return (stream.str()); } bool TestControl::checkExitConditions() const { if (interrupted_) { - return(true); + return (true); } CommandOptions& options = CommandOptions::instance(); bool test_period_reached = false; @@ -117,9 +122,9 @@ TestControl::checkExitConditions() const { } if (test_period_reached) { if (testDiags('e')) { - std::cout << "Reached test period." << std::endl; + std::cout << "reached test-period." << std::endl; } - return(true); + return (true); } bool max_requests = false; @@ -153,9 +158,9 @@ TestControl::checkExitConditions() const { } if (max_requests) { if (testDiags('e')) { - std::cout << "Reached test period." << std::endl; + std::cout << "Reached max requests limit." << std::endl; } - return(true); + return (true); } // Check if we reached maximum number of drops of OFFER/ADVERTISE packets. @@ -191,7 +196,7 @@ TestControl::checkExitConditions() const { if (testDiags('e')) { std::cout << "Reached maximum drops number." << std::endl; } - return(true); + return (true); } // Check if we reached maximum drops percentage of OFFER/ADVERTISE packets. @@ -236,20 +241,19 @@ TestControl::checkExitConditions() const { if (testDiags('e')) { std::cout << "Reached maximum percentage of drops." << std::endl; } - return(true); + return (true); } - - return(false); + return (false); } OptionPtr TestControl::factoryElapsedTime6(Option::Universe, uint16_t, const OptionBuffer& buf) { if (buf.size() == 2) { - return OptionPtr(new Option(Option::V6, D6O_ELAPSED_TIME, buf)); + return (OptionPtr(new Option(Option::V6, D6O_ELAPSED_TIME, buf))); } else if (buf.size() == 0) { - return OptionPtr(new Option(Option::V6, D6O_ELAPSED_TIME, - OptionBuffer(2, 0))); + return (OptionPtr(new Option(Option::V6, D6O_ELAPSED_TIME, + OptionBuffer(2, 0)))); } isc_throw(isc::BadValue, "elapsed time option buffer size has to be 0 or 2"); @@ -259,7 +263,7 @@ OptionPtr TestControl::factoryGeneric(Option::Universe u, uint16_t type, const OptionBuffer& buf) { OptionPtr opt(new Option(u, type, buf)); - return opt; + return (opt); } OptionPtr @@ -274,13 +278,13 @@ TestControl::factoryIana6(Option::Universe, uint16_t, for (int i = 0; i < buf.size(); ++i) { buf_ia_na.push_back(buf[i]); } - return OptionPtr(new Option(Option::V6, D6O_IA_NA, buf_ia_na)); + return (OptionPtr(new Option(Option::V6, D6O_IA_NA, buf_ia_na))); } OptionPtr TestControl::factoryRapidCommit6(Option::Universe, uint16_t, const OptionBuffer&) { - return OptionPtr(new Option(Option::V6, D6O_RAPID_COMMIT, OptionBuffer())); + return (OptionPtr(new Option(Option::V6, D6O_RAPID_COMMIT, OptionBuffer()))); } OptionPtr @@ -288,11 +292,11 @@ TestControl::factoryOptionRequestOption6(Option::Universe, uint16_t, const OptionBuffer&) { const uint8_t buf_array[] = { - D6O_NAME_SERVERS, - D6O_DOMAIN_SEARCH + 0, D6O_NAME_SERVERS, + 0, D6O_DOMAIN_SEARCH }; OptionBuffer buf_with_options(buf_array, buf_array + sizeof(buf_array)); - return OptionPtr(new Option(Option::V6, D6O_ORO, buf_with_options)); + return (OptionPtr(new Option(Option::V6, D6O_ORO, buf_with_options))); } @@ -313,15 +317,15 @@ TestControl::factoryRequestList4(Option::Universe u, OptionBuffer buf_with_options(buf_array, buf_array + sizeof(buf_array)); OptionPtr opt(new Option(u, type, buf)); opt->setData(buf_with_options.begin(), buf_with_options.end()); - return opt; + return (opt); } std::vector -TestControl::generateMacAddress() const { +TestControl::generateMacAddress(uint8_t& randomized) const { CommandOptions& options = CommandOptions::instance(); uint32_t clients_num = options.getClientsNum(); if ((clients_num == 0) || (clients_num == 1)) { - return options.getMacPrefix(); + return (options.getMacPrefix()); } // Get the base MAC address. We are going to randomize part of it. std::vector mac_addr(options.getMacPrefix()); @@ -333,12 +337,14 @@ TestControl::generateMacAddress() const { // will guarantee that every client has exactly one random MAC // address assigned. r %= clients_num; + randomized = 0; // Randomize MAC address octets. for (std::vector::iterator it = mac_addr.end() - 1; it >= mac_addr.begin(); --it) { // Add the random value to the current octet. (*it) += r; + ++randomized; if (r < 256) { // If we are here it means that there is no sense // to randomize the remaining octets of MAC address @@ -350,24 +356,24 @@ TestControl::generateMacAddress() const { // byte of random value. r >>= 8; } - return mac_addr; + return (mac_addr); } std::vector -TestControl::generateDuid() const { +TestControl::generateDuid(uint8_t& randomized) const { CommandOptions& options = CommandOptions::instance(); uint32_t clients_num = options.getClientsNum(); if ((clients_num == 0) || (clients_num == 1)) { - return options.getDuidPrefix(); + return (options.getDuidPrefix()); } // Get the base DUID. We are going to randomize part of it. std::vector duid(options.getDuidPrefix()); - std::vector mac_addr(generateMacAddress()); + std::vector mac_addr(generateMacAddress(randomized)); duid.resize(duid.size() - mac_addr.size()); for (int i = 0; i < mac_addr.size(); ++i) { duid.push_back(mac_addr[i]); } - return duid; + return (duid); } uint64_t @@ -413,20 +419,28 @@ uint64_t TestControl::getRcvdPacketsNum(const ExchangeType xchg_type) const { uint8_t ip_version = CommandOptions::instance().getIpVersion(); if (ip_version == 4) { - return(stats_mgr4_->getRcvdPacketsNum(xchg_type)); + return (stats_mgr4_->getRcvdPacketsNum(xchg_type)); } - return(stats_mgr6_-> - getRcvdPacketsNum(static_cast(xchg_type))); + return (stats_mgr6_-> + getRcvdPacketsNum(static_cast(xchg_type))); } uint64_t TestControl::getSentPacketsNum(const ExchangeType xchg_type) const { uint8_t ip_version = CommandOptions::instance().getIpVersion(); if (ip_version == 4) { - return(stats_mgr4_->getSentPacketsNum(xchg_type)); + return (stats_mgr4_->getSentPacketsNum(xchg_type)); } - return(stats_mgr6_-> - getSentPacketsNum(static_cast(xchg_type))); + return (stats_mgr6_-> + getSentPacketsNum(static_cast(xchg_type))); +} + +TestControl::TemplateBuffer +TestControl::getTemplateBuffer(const size_t idx) const { + if (template_buffers_.size() > idx) { + return (template_buffers_[idx]); + } + return (TemplateBuffer()); } void @@ -436,13 +450,13 @@ TestControl::handleInterrupt(int) { void TestControl::initPacketTemplates() { + template_buffers_.clear(); CommandOptions& options = CommandOptions::instance(); std::vector template_files = options.getTemplateFiles(); for (std::vector::const_iterator it = template_files.begin(); it != template_files.end(); ++it) { - - std::cout << "Open file " << *it << std::endl; + readPacketTemplate(*it); } } @@ -565,7 +579,7 @@ TestControl::openSocket() const { } } - return(sock); + return (sock); } void @@ -574,7 +588,6 @@ TestControl::printDiagnostics() const { if (testDiags('a')) { // Print all command line parameters. options.printCommandLine(); - // Print MAC and DUID. std::cout << "Set MAC to " << vector2Hex(options.getMacPrefix(), "::") << std::endl; @@ -661,7 +674,38 @@ TestControl::vector2Hex(const std::vector& vec, stream << separator << byte2Hex(*it); } } - return(stream.str()); + return (stream.str()); +} + +void +TestControl::readPacketTemplate(const std::string& file_name) { + std::ifstream temp_file; + temp_file.open(file_name.c_str(), ios::in | ios::binary | ios::ate); + if (!temp_file.is_open()) { + isc_throw(BadValue, "unable to open template file " << file_name); + } + std::ifstream::pos_type temp_size = temp_file.tellg(); + if (temp_size % 2 != 0) { + temp_file.close(); + isc_throw(BadValue, "odd number of digits in template file"); + } + temp_file.seekg(0, ios::beg); + std::vector hex_digits(temp_size); + std::vector binary_stream; + temp_file.read(&hex_digits[0], temp_size); + temp_file.close(); + for (int i = 0; i < hex_digits.size(); i += 2) { + if (!isxdigit(hex_digits[i]) || !isxdigit(hex_digits[i+1])) { + isc_throw(BadValue, "the '" << hex_digits[i] << hex_digits[i+1] + << "' is not hexadecimal digit"); + } + stringstream s; + s << "0x" << hex_digits[i] << hex_digits[i+1]; + int b; + s >> std::hex >> b; + binary_stream.push_back(static_cast(b)); + } + template_buffers_.push_back(binary_stream); } void @@ -673,7 +717,11 @@ TestControl::receivePacket4(const TestControlSocket& socket, CommandOptions::ExchangeMode xchg_mode = CommandOptions::instance().getExchangeMode(); if ((xchg_mode == CommandOptions::DORA_SARR) && discover_pkt4) { - sendRequest4(socket, pkt4); + if (template_buffers_.size() < 2) { + sendRequest4(socket, discover_pkt4, pkt4); + } else { + sendRequest4(socket, template_buffers_[1], discover_pkt4, pkt4); + } } } else if (pkt4->getType() == DHCPACK) { stats_mgr4_->passRcvdPacket(StatsMgr4::XCHG_RA, pkt4); @@ -690,7 +738,11 @@ TestControl::receivePacket6(const TestControlSocket& socket, CommandOptions::ExchangeMode xchg_mode = CommandOptions::instance().getExchangeMode(); if ((xchg_mode == CommandOptions::DORA_SARR) && solicit_pkt6) { - sendRequest6(socket, solicit_pkt6, pkt6); + if (template_buffers_.size() < 2) { + sendRequest6(socket, solicit_pkt6, pkt6); + } else { + sendRequest6(socket, template_buffers_[1], solicit_pkt6, pkt6); + } } } else if (packet_type == DHCPV6_REPLY) { stats_mgr6_->passRcvdPacket(StatsMgr6::XCHG_RR, pkt6); @@ -740,6 +792,7 @@ TestControl::registerOptionFactories4() const { LibDHCP::OptionFactoryRegister(Option::V4, DHO_DHCP_MESSAGE_TYPE, &TestControl::factoryGeneric); + // DHCP_SERVER_IDENTIFIER option factory. LibDHCP::OptionFactoryRegister(Option::V4, DHO_DHCP_SERVER_IDENTIFIER, &TestControl::factoryGeneric); @@ -755,21 +808,27 @@ void TestControl::registerOptionFactories6() const { static bool factories_registered = false; if (!factories_registered) { + // D60_ELAPSED_TIME LibDHCP::OptionFactoryRegister(Option::V6, D6O_ELAPSED_TIME, &TestControl::factoryElapsedTime6); + // D6O_RAPID_COMMIT LibDHCP::OptionFactoryRegister(Option::V6, D6O_RAPID_COMMIT, &TestControl::factoryRapidCommit6); + // D6O_ORO (option request option) factory. LibDHCP::OptionFactoryRegister(Option::V6, D6O_ORO, &TestControl::factoryOptionRequestOption6); + // D6O_CLIENTID option factory. LibDHCP::OptionFactoryRegister(Option::V6, D6O_CLIENTID, &TestControl::factoryGeneric); + // D6O_SERVERID option factory. LibDHCP::OptionFactoryRegister(Option::V6, D6O_SERVERID, &TestControl::factoryGeneric); + // D6O_IA_NA option factory. LibDHCP::OptionFactoryRegister(Option::V6, D6O_IA_NA, &TestControl::factoryIana6); @@ -821,39 +880,57 @@ TestControl::run() { "command options must be parsed before running a test"); } + // Diagnostics is command line options mainly. printDiagnostics(); - + // Option factories have to be registered. registerOptionFactories(); TestControlSocket socket(openSocket()); - // Initialize packet templates. initPacketTemplates(); - // Initialize randomization seed. if (options.isSeeded()) { srandom(options.getSeed()); } - // If user interrupts the program we will exit gracefully. signal(SIGINT, TestControl::handleInterrupt); - // Preload server with number of packets. const bool do_preload = true; for (int i = 0; i < options.getPreload(); ++i) { if (options.getIpVersion() == 4) { - sendDiscover4(socket, do_preload); + // No template buffer means no -T option specified. + // We will build packet ourselfs. + if (template_buffers_.size() == 0) { + sendDiscover4(socket, do_preload); + } else { + const uint8_t template_idx = 0; + sendDiscover4(socket, template_buffers_[template_idx], + do_preload); + } } else if (options.getIpVersion() == 6) { - sendSolicit6(socket, do_preload); + // No template buffer means no -T option specified. + // We will build packet ourselfs. + if (template_buffers_.size() == 0) { + sendSolicit6(socket, do_preload); + } else { + const uint8_t template_idx = 0; + sendSolicit6(socket, template_buffers_[template_idx], + do_preload); + } } } - + // Initialize Statistics Manager. Release previous if any. initializeStatsMgr(); - for (;;) { + // Calculate send due based on when last exchange was initiated. updateSendDue(); + // If test period finished, maximum number of packet drops + // has been reached or test has been interrupted we have to + // finish the test. if (checkExitConditions()) { break; } + // Calculate number of packets to be sent to stay + // catch up with rate. uint64_t packets_due = getNextExchangesNum(); if ((packets_due == 0) && testDiags('i')) { if (options.getIpVersion() == 4) { @@ -866,19 +943,36 @@ TestControl::run() { // @todo: set non-zero timeout for packets once we implement // microseconds timeout in IfaceMgr. receivePackets(socket); - + // Send packets. for (uint64_t i = packets_due; i > 0; --i) { if (options.getIpVersion() == 4) { - sendDiscover4(socket); + // No template packets means that no -T option was specified. + // We have to build packets ourselfs. + if (template_buffers_.size() == 0) { + sendDiscover4(socket); + } else { + const uint8_t template_idx = 0; + sendDiscover4(socket, template_buffers_[template_idx]); + } } else { - sendSolicit6(socket); + // No template packets means that no -T option was specified. + // We have to build packets ourselfs. + if (template_buffers_.size() == 0) { + sendSolicit6(socket); + } else { + const uint8_t template_idx = 0; + sendSolicit6(socket, template_buffers_[template_idx]); + } } } + // Report delay means that user requested printing number + // of sent/received/dropped packets repeatedly. if (options.getReportDelay() > 0) { printIntermediateStats(); } } printStats(); + // Print server id. if (testDiags('s') && (first_packet_serverid_.size() > 0)) { std::cout << "Server id: " << vector2Hex(first_packet_serverid_) << std::endl; } @@ -893,7 +987,8 @@ TestControl::sendDiscover4(const TestControlSocket& socket, const bool preload /*= false*/) { last_sent_ = microsec_clock::universal_time(); // Generate the MAC address to be passed in the packet. - std::vector mac_address = generateMacAddress(); + uint8_t randomized = 0; + std::vector mac_address = generateMacAddress(randomized); // Generate trasnaction id to be set for the new exchange. const uint32_t transid = generateTransid(); Pkt4Ptr pkt4(new Pkt4(DHCPDISCOVER, transid)); @@ -911,6 +1006,10 @@ TestControl::sendDiscover4(const TestControlSocket& socket, // Set client's and server's ports as well as server's address, // and local (relay) address. setDefaults4(socket, pkt4); + + // Set hardware address + pkt4->setHWAddr(HTYPE_ETHER, mac_address.size(), mac_address); + pkt4->pack(); IfaceMgr::instance().send(pkt4); if (!preload) { @@ -922,8 +1021,70 @@ TestControl::sendDiscover4(const TestControlSocket& socket, } } +void +TestControl::sendDiscover4(const TestControlSocket& socket, + const std::vector& template_buf, + const bool preload /* = false */) { + // last_sent_ has to be updated for each function that initiates + // new transaction. The packet exchange synchronization relies on this. + last_sent_ = microsec_clock::universal_time(); + CommandOptions& options = CommandOptions::instance(); + // Get the first argument if mulitple the same arguments specified + // in the command line. First one refers to DISCOVER packets. + const uint8_t arg_idx = 0; + // Generate the MAC address to be passed in the packet. + uint8_t randomized = 0; + std::vector mac_address = generateMacAddress(randomized); + // Generate trasnaction id to be set for the new exchange. + const uint32_t transid = generateTransid(); + // Get transaction id offset. + size_t transid_offset = DHCPV4_TRANSID_OFFSET; + if (options.getTransactionIdOffset().size() > arg_idx) { + transid_offset = options.getTransactionIdOffset()[arg_idx]; + } + // Calculate randomization offset. + size_t rand_offset = DHCPV4_RANDOMIZATION_OFFSET; + if (options.getRandomOffset().size() > arg_idx) { + rand_offset = options.getRandomOffset()[arg_idx]; + } + // We need to go back by HW_ETHER_LEN (MAC address length) + // because this offset points to last octet of MAC address. + rand_offset -= HW_ETHER_LEN + 1; + // Create temporary buffer with template contents. We will + // modify this temporary buffer but we don't want to modify + // the original template. + std::vector in_buf(template_buf.begin(), + template_buf.end()); + // Check if we are not going out of bounds. + if (rand_offset + HW_ETHER_LEN > in_buf.size()) { + isc_throw(OutOfRange, "randomization offset is out of bounds"); + } + PerfPkt4Ptr pkt4(new PerfPkt4(&in_buf[0], in_buf.size(), + transid_offset, + transid)); + + // Replace MAC address in the template with actual MAC address. + pkt4->writeAt(rand_offset, mac_address.begin(), mac_address.end()); + // Create a packet from the temporary buffer. + setDefaults4(socket, static_pointer_cast(pkt4)); + // Pack the input packet buffer to output buffer so as it can + // be sent to server. + pkt4->rawPack(); + IfaceMgr::instance().send(static_pointer_cast(pkt4)); + if (!preload) { + if (!stats_mgr4_) { + isc_throw(InvalidOperation, "Statistics Manager for DHCPv4 " + "hasn't been initialized"); + } + // Update packet stats. + stats_mgr4_->passSentPacket(StatsMgr4::XCHG_DO, + static_pointer_cast(pkt4)); + } +} + void TestControl::sendRequest4(const TestControlSocket& socket, + const dhcp::Pkt4Ptr& discover_pkt4, const dhcp::Pkt4Ptr& offer_pkt4) { const uint32_t transid = generateTransid(); Pkt4Ptr pkt4(new Pkt4(DHCPREQUEST, transid)); @@ -958,7 +1119,7 @@ TestControl::sendRequest4(const TestControlSocket& socket, OptionPtr opt_requested_address = OptionPtr(new Option(Option::V4, DHO_DHCP_REQUESTED_ADDRESS, OptionBuffer())); - opt_requested_address->setUint32(yiaddr); + opt_requested_address->setUint32(static_cast(yiaddr)); pkt4->addOption(opt_requested_address); OptionPtr opt_parameter_list = Option::factory(Option::V4, DHO_DHCP_PARAMETER_REQUEST_LIST); @@ -966,6 +1127,15 @@ TestControl::sendRequest4(const TestControlSocket& socket, // Set client's and server's ports as well as server's address, // and local (relay) address. setDefaults4(socket, pkt4); + + // Set hardware address + const uint8_t* chaddr = offer_pkt4->getChaddr(); + std::vector mac_address(chaddr, chaddr + HW_ETHER_LEN); + pkt4->setHWAddr(HTYPE_ETHER, mac_address.size(), mac_address); + // Set elapsed time. + uint32_t elapsed_time = getElapsedTime(discover_pkt4, offer_pkt4); + pkt4->setSecs(static_cast(elapsed_time / 1000)); + // Prepare on wire data to send. pkt4->pack(); IfaceMgr::instance().send(pkt4); if (!stats_mgr4_) { @@ -975,6 +1145,124 @@ TestControl::sendRequest4(const TestControlSocket& socket, stats_mgr4_->passSentPacket(StatsMgr4::XCHG_RA, pkt4); } +void +TestControl::sendRequest4(const TestControlSocket& socket, + const std::vector& template_buf, + const dhcp::Pkt4Ptr& discover_pkt4, + const dhcp::Pkt4Ptr& offer_pkt4) { + CommandOptions& options = CommandOptions::instance(); + // Get the second argument if multiple the same arguments specified + // in the command line. Second one refers to REQUEST packets. + const uint8_t arg_idx = 1; + // Generate new transaction id. + const uint32_t transid = generateTransid(); + // Get transaction id offset. + size_t transid_offset = DHCPV4_TRANSID_OFFSET; + if (options.getTransactionIdOffset().size() > arg_idx) { + transid_offset = options.getTransactionIdOffset()[arg_idx]; + } + // Get the offset of MAC's last octet. + size_t rand_offset = DHCPV4_RANDOMIZATION_OFFSET; + if (options.getRandomOffset().size() > arg_idx) { + rand_offset = options.getRandomOffset()[arg_idx]; + } + // We need to go back by HW_ETHER_LEN (MAC address length) + // because this offset points to last octet of MAC address. + rand_offset -= HW_ETHER_LEN + 1; + // Create temporaru buffer from the template. + std::vector in_buf(template_buf.begin(), + template_buf.end()); + // Check if given randomization offset is not out of bounds. + if (rand_offset + HW_ETHER_LEN > in_buf.size()) { + isc_throw(OutOfRange, "randomization offset is out of bounds"); + } + + // Create packet from the temporary buffer. + PerfPkt4Ptr pkt4(new PerfPkt4(&in_buf[0], in_buf.size(), + transid_offset, + transid)); + + // Set hardware address from OFFER packet received. + const uint8_t* chaddr = offer_pkt4->getChaddr(); + std::vector mac_address(chaddr, chaddr + HW_ETHER_LEN); + pkt4->writeAt(rand_offset, mac_address.begin(), mac_address.end()); + + // Set elapsed time. + size_t elp_offset = 0; + if (options.getElapsedTimeOffset() > 0) { + elp_offset = options.getElapsedTimeOffset(); + } + uint32_t elapsed_time = getElapsedTime(discover_pkt4, offer_pkt4); + pkt4->writeValueAt(elp_offset, + static_cast(elapsed_time / 1000)); + + // Get the actual server id offset. + size_t sid_offset = DHCPV4_SERVERID_OFFSET; + if (options.getServerIdOffset() > 0) { + sid_offset = options.getServerIdOffset(); + } + if (CommandOptions::instance().isUseFirst() && + (first_packet_serverid_.size() > 0)) { + boost::shared_ptr + opt_serverid(new LocalizedOption(Option::V4, + DHO_DHCP_SERVER_IDENTIFIER, + first_packet_serverid_, + sid_offset)); + pkt4->addOption(opt_serverid); + } else { + // Copy the contents of server identifier received in + // OFFER packet to put this into REQUEST. + OptionPtr opt_serverid_offer = + offer_pkt4->getOption(DHO_DHCP_SERVER_IDENTIFIER); + if (!opt_serverid_offer) { + isc_throw(BadValue, "there is no SERVER_IDENTIFIER option " + << "in OFFER message"); + } + boost::shared_ptr + opt_serverid(new LocalizedOption(Option::V4, + DHO_DHCP_SERVER_IDENTIFIER, + opt_serverid_offer->getData(), + sid_offset)); + pkt4->addOption(opt_serverid); + if (stats_mgr4_->getRcvdPacketsNum(StatsMgr4::XCHG_DO) == 1) { + first_packet_serverid_ = opt_serverid_offer->getData(); + } + } + + /// Set client address. + asiolink::IOAddress yiaddr = offer_pkt4->getYiaddr(); + if (!yiaddr.getAddress().is_v4()) { + isc_throw(BadValue, "the YIADDR returned in OFFER packet is not " + " IPv4 address"); + } + + // Get the actual offset of requested ip. + size_t rip_offset = DHCPV4_REQUESTED_IP_OFFSET; + if (options.getRequestedIpOffset() > 0) { + rip_offset = options.getRequestedIpOffset(); + } + // Place requested IP option at specified position (rip_offset). + boost::shared_ptr + opt_requested_ip(new LocalizedOption(Option::V4, + DHO_DHCP_REQUESTED_ADDRESS, + OptionBuffer(), + rip_offset)); + // The IOAddress is castable to uint32_t and returns exactly what we need. + opt_requested_ip->setUint32(static_cast(yiaddr)); + pkt4->addOption(opt_requested_ip); + + setDefaults4(socket, static_pointer_cast(pkt4)); + // Prepare on-wire data. + pkt4->rawPack(); + IfaceMgr::instance().send(static_pointer_cast(pkt4)); + if (!stats_mgr4_) { + isc_throw(InvalidOperation, "Statistics Manager for DHCPv4 " + "hasn't been initialized"); + } + // Update packet stats. + stats_mgr4_->passSentPacket(StatsMgr4::XCHG_RA, + static_pointer_cast(pkt4)); +} void TestControl::sendRequest6(const TestControlSocket& socket, @@ -982,31 +1270,22 @@ TestControl::sendRequest6(const TestControlSocket& socket, const Pkt6Ptr& advertise_pkt6) { const uint32_t transid = generateTransid(); Pkt6Ptr pkt6(new Pkt6(DHCPV6_REQUEST, transid)); - // Calculate elapsed time - ptime solicit_time = solicit_pkt6->getTimestamp(); - ptime advertise_time = advertise_pkt6->getTimestamp(); - if (solicit_time.is_not_a_date_time()) { - isc_throw(Unexpected, "timestamp was not set for SOLICIT packet"); - } - if (advertise_time.is_not_a_date_time()) { - isc_throw(Unexpected, "timestamp was not set for ADVERTISE packet"); - } - time_period period(solicit_time, advertise_time); - if (period.is_null()) { - pkt6->addOption(Option::factory(Option::V6, D6O_ELAPSED_TIME)); - } else { - OptionBuffer buf(); - const uint32_t elapsed_time = period.length().total_seconds(); - OptionPtr opt_elapsed_time = - Option::factory(Option::V6, D6O_ELAPSED_TIME); - opt_elapsed_time->setUint16(static_cast(elapsed_time)); - pkt6->addOption(opt_elapsed_time); - } + // Set elapsed time. + const uint32_t elapsed_time = + getElapsedTime(solicit_pkt6, advertise_pkt6); + OptionPtr opt_elapsed_time = + Option::factory(Option::V6, D6O_ELAPSED_TIME); + opt_elapsed_time->setUint16(static_cast(elapsed_time / 10)); + pkt6->addOption(opt_elapsed_time); + // Set client id. OptionPtr opt_clientid = advertise_pkt6->getOption(D6O_CLIENTID); if (!opt_clientid) { isc_throw(Unexpected, "client id not found in received packet"); } pkt6->addOption(opt_clientid); + + // Use first flags indicates that we want to use the server + // id captured in fisrt packet. if (CommandOptions::instance().isUseFirst() && (first_packet_serverid_.size() > 0)) { pkt6->addOption(Option::factory(Option::V6, D6O_SERVERID, @@ -1021,14 +1300,17 @@ TestControl::sendRequest6(const TestControlSocket& socket, } pkt6->addOption(opt_serverid); } + // Set IA_NA option. OptionPtr opt_ia_na = advertise_pkt6->getOption(D6O_IA_NA); if (!opt_ia_na) { isc_throw(Unexpected, "DHCPv6 IA_NA option not found in received " "packet"); } pkt6->addOption(opt_ia_na); - setDefaults6(socket, pkt6); + // Set default packet data. + setDefaults6(socket, pkt6); + // Prepare on-wire data. pkt6->pack(); IfaceMgr::instance().send(pkt6); if (!stats_mgr6_) { @@ -1038,14 +1320,140 @@ TestControl::sendRequest6(const TestControlSocket& socket, stats_mgr6_->passSentPacket(StatsMgr6::XCHG_RR, pkt6); } +void +TestControl::sendRequest6(const TestControlSocket& socket, + const std::vector& template_buf, + const Pkt6Ptr& solicit_pkt6, + const Pkt6Ptr& advertise_pkt6) { + CommandOptions& options = CommandOptions::instance(); + // Get the second argument if multiple the same arguments specified + // in the command line. Second one refers to REQUEST packets. + const uint8_t arg_idx = 1; + // Generate transaction id. + const uint32_t transid = generateTransid(); + // Get transaction id offset. + size_t transid_offset = DHCPV6_TRANSID_OFFSET; + if (options.getTransactionIdOffset().size() > arg_idx) { + transid_offset = options.getTransactionIdOffset()[arg_idx]; + } + PerfPkt6Ptr pkt6(new PerfPkt6(&template_buf[0], template_buf.size(), + transid_offset, transid)); + // Set elapsed time. + size_t elp_offset = DHCPV6_ELAPSED_TIME_OFFSET; + if (options.getElapsedTimeOffset() > 0) { + elp_offset = options.getElapsedTimeOffset(); + } + uint32_t elapsed_time = + getElapsedTime(solicit_pkt6, advertise_pkt6); + boost::shared_ptr + opt_elapsed_time(new LocalizedOption(Option::V6, D6O_ELAPSED_TIME, + OptionBuffer(), elp_offset)); + opt_elapsed_time->setUint16(static_cast(elapsed_time / 10)); + pkt6->addOption(opt_elapsed_time); + + // Get the actual server id offset. + size_t sid_offset = DHCPV6_SERVERID_OFFSET; + if (options.getServerIdOffset() > 0) { + sid_offset = options.getServerIdOffset(); + } + if (CommandOptions::instance().isUseFirst() && + (first_packet_serverid_.size() > 0)) { + boost::shared_ptr + opt_serverid(new LocalizedOption(Option::V6, + D6O_SERVERID, + first_packet_serverid_, + sid_offset)); + pkt6->addOption(opt_serverid); + + } else { + // Copy the contents of server identifier received in + // ADVERTISE packet to put this into REQUEST. + OptionPtr opt_serverid_advertise = + advertise_pkt6->getOption(D6O_SERVERID); + if (!opt_serverid_advertise) { + isc_throw(BadValue, "there is no SERVERID option " + << "in ADVERTISE message"); + } + boost::shared_ptr + opt_serverid(new LocalizedOption(Option::V6, + D6O_SERVERID, + opt_serverid_advertise->getData(), + sid_offset)); + pkt6->addOption(opt_serverid); + if (stats_mgr6_->getRcvdPacketsNum(StatsMgr6::XCHG_SA) == 1) { + first_packet_serverid_ = opt_serverid_advertise->getData(); + } + } + // Set IA_NA + boost::shared_ptr opt_ia_na_advertise = + static_pointer_cast(advertise_pkt6->getOption(D6O_IA_NA)); + if (!opt_ia_na_advertise) { + isc_throw(Unexpected, "DHCPv6 IA_NA option not found in received " + "packet"); + } + size_t addr_offset = DHCPV6_IA_NA_OFFSET; + if (options.getRequestedIpOffset() > 0) { + addr_offset = options.getRequestedIpOffset(); + } + boost::shared_ptr + opt_ia_na(new LocalizedOption(opt_ia_na_advertise, addr_offset)); + if (!opt_ia_na->valid()) { + isc_throw(BadValue, "Option IA_NA in advertise packet is invalid"); + } + pkt6->addOption(opt_ia_na); + // Set server id. + OptionPtr opt_serverid_advertise = advertise_pkt6->getOption(D6O_SERVERID); + if (!opt_serverid_advertise) { + isc_throw(Unexpected, "DHCPV6 SERVERID option not found in received " + "packet"); + } + size_t srvid_offset = DHCPV6_SERVERID_OFFSET; + if (options.getServerIdOffset() > 0) { + srvid_offset = options.getServerIdOffset(); + } + boost::shared_ptr + opt_serverid(new LocalizedOption(Option::V6, D6O_SERVERID, + opt_serverid_advertise->getData(), + srvid_offset)); + pkt6->addOption(opt_serverid); + // Get randomization offset. + size_t rand_offset = DHCPV6_RANDOMIZATION_OFFSET; + if (options.getRandomOffset().size() > arg_idx) { + rand_offset = options.getRandomOffset()[arg_idx]; + } + OptionPtr opt_clientid_advertise = advertise_pkt6->getOption(D6O_CLIENTID); + if (!opt_clientid_advertise) { + isc_throw(Unexpected, "DHCPV6 CLIENTID option not found in received packet"); + } + rand_offset -= (opt_clientid_advertise->len() - 1); + // Set client id. + boost::shared_ptr + opt_clientid(new LocalizedOption(Option::V6, D6O_CLIENTID, + opt_clientid_advertise->getData(), + rand_offset)); + pkt6->addOption(opt_clientid); + // Set default packet data. + setDefaults6(socket, pkt6); + // Prepare on wire data. + pkt6->rawPack(); + // Send packet. + IfaceMgr::instance().send(pkt6); + if (!stats_mgr6_) { + isc_throw(InvalidOperation, "Statistics Manager for DHCPv6 " + "hasn't been initialized"); + } + // Update packet stats. + stats_mgr6_->passSentPacket(StatsMgr6::XCHG_RR, pkt6); + +} + void TestControl::sendSolicit6(const TestControlSocket& socket, const bool preload /*= false*/) { last_sent_ = microsec_clock::universal_time(); - // Generate the MAC address to be passed in the packet. - std::vector mac_address = generateMacAddress(); // Generate DUID to be passed to the packet - std::vector duid = generateDuid(); + uint8_t randomized = 0; + std::vector duid = generateDuid(randomized); // Generate trasnaction id to be set for the new exchange. const uint32_t transid = generateTransid(); Pkt6Ptr pkt6(new Pkt6(DHCPV6_SOLICIT, transid)); @@ -1072,6 +1480,58 @@ TestControl::sendSolicit6(const TestControlSocket& socket, } } +void +TestControl::sendSolicit6(const TestControlSocket& socket, + const std::vector& template_buf, + const bool preload /*= false*/) { + last_sent_ = microsec_clock::universal_time(); + CommandOptions& options = CommandOptions::instance(); + const int arg_idx = 0; + // Get transaction id offset. + size_t transid_offset = DHCPV6_TRANSID_OFFSET; + if (options.getTransactionIdOffset().size() > arg_idx) { + transid_offset = options.getTransactionIdOffset()[arg_idx]; + } + // Generate trasnaction id to be set for the new exchange. + const uint32_t transid = generateTransid(); + // Create packet. + PerfPkt6Ptr pkt6(new PerfPkt6(&template_buf[0], template_buf.size(), + transid_offset, transid)); + if (!pkt6) { + isc_throw(Unexpected, "failed to create SOLICIT packet"); + } + size_t rand_offset = DHCPV6_RANDOMIZATION_OFFSET; + if (options.getRandomOffset().size() > arg_idx) { + rand_offset = options.getRandomOffset()[arg_idx]; + } + // randomized will pick number of bytes randomized so we can + // just use part of the generated duid and substitude a few bytes + /// in template. + uint8_t randomized = 0; + std::vector duid = generateDuid(randomized); + if (rand_offset > template_buf.size()) { + isc_throw(OutOfRange, "randomization offset is out of bounds"); + } + // Store random part of the DUID into the packet. + pkt6->writeAt(rand_offset - randomized + 1, + duid.end() - randomized, duid.end()); + + // Prepare on-wire data. + pkt6->rawPack(); + setDefaults6(socket, pkt6); + // Send solicit packet. + IfaceMgr::instance().send(pkt6); + if (!preload) { + if (!stats_mgr6_) { + isc_throw(InvalidOperation, "Statistics Manager for DHCPv6 " + "hasn't been initialized"); + } + // Update packet stats. + stats_mgr6_->passSentPacket(StatsMgr6::XCHG_SA, pkt6); + } +} + + void TestControl::setDefaults4(const TestControlSocket& socket, const Pkt4Ptr& pkt) { @@ -1090,9 +1550,6 @@ TestControl::setDefaults4(const TestControlSocket& socket, pkt->setLocalAddr(IOAddress(socket.getAddress())); // Set relay (GIADDR) address to local address. pkt->setGiaddr(IOAddress(socket.getAddress())); - std::vector mac = generateMacAddress(); - // Set hardware address - pkt->setHWAddr(HTYPE_ETHER, mac.size(), mac); // Pretend that we have one relay (which is us). pkt->setHops(1); } @@ -1119,9 +1576,9 @@ bool TestControl::testDiags(const char diag) const { std::string diags(CommandOptions::instance().getDiags()); if (diags.find(diag) != std::string::npos) { - return true; + return (true); } - return false; + return (false); } void diff --git a/tests/tools/perfdhcp/test_control.h b/tests/tools/perfdhcp/test_control.h index 9b634ce5d7..2bace3a22e 100644 --- a/tests/tools/perfdhcp/test_control.h +++ b/tests/tools/perfdhcp/test_control.h @@ -48,20 +48,41 @@ namespace perfdhcp { class TestControl : public boost::noncopyable { public: - // Statistics Manager for DHCPv4. + /// Default transaction id offset. + static const size_t DHCPV4_TRANSID_OFFSET = 4; + /// Default offset of MAC's last octet. + static const size_t DHCPV4_RANDOMIZATION_OFFSET = 35; + /// Default elapsed time offset. + static const size_t DHCPV4_ELAPSED_TIME_OFFSET = 8; + /// Default server id offset. + static const size_t DHCPV4_SERVERID_OFFSET = 54; + /// Default requested ip offset. + static const size_t DHCPV4_REQUESTED_IP_OFFSET = 240; + /// Default DHCPV6 transaction id offset. + static const size_t DHCPV6_TRANSID_OFFSET = 1; + /// Default DHCPV6 randomization offset (last octet of DUID) + static const size_t DHCPV6_RANDOMIZATION_OFFSET = 21; + /// Default DHCPV6 elapsed time offset. + static const size_t DHCPV6_ELAPSED_TIME_OFFSET = 84; + /// Default DHCPV6 server id offset. + static const size_t DHCPV6_SERVERID_OFFSET = 22; + /// Default DHCPV6 IA_NA offset. + static const size_t DHCPV6_IA_NA_OFFSET = 40; + + /// Statistics Manager for DHCPv4. typedef StatsMgr StatsMgr4; - // Pointer to Statistics Manager for DHCPv4; + /// Pointer to Statistics Manager for DHCPv4; typedef boost::shared_ptr StatsMgr4Ptr; - // Statictics Manager for DHCPv6. + /// Statictics Manager for DHCPv6. typedef StatsMgr StatsMgr6; - // Pointer to Statistics Manager for DHCPv6. + /// Pointer to Statistics Manager for DHCPv6. typedef boost::shared_ptr StatsMgr6Ptr; - // Packet exchange type. + /// Packet exchange type. typedef StatsMgr<>::ExchangeType ExchangeType; - // Packet template buffer. + /// Packet template buffer. typedef std::vector TemplateBuffer; - //Packet template buffers list. - typedef std::list TemplateBufferList; + /// Packet template buffers list. + typedef std::vector TemplateBufferCollection; /// \brief Socket wrapper class. /// @@ -302,9 +323,10 @@ protected: /// from the MAC address, this function uses \ref generateMacAddress /// internally to randomize the DUID. /// + /// \param randomized number of bytes randomized. /// \throw isc::BadValue if \ref generateMacAddress throws. /// \return vector representing DUID. - std::vector generateDuid() const; + std::vector generateDuid(uint8_t& randomized) const; /// \brief Generate MAC address. /// @@ -315,10 +337,11 @@ protected: /// Based on this the random value is generated and added to /// the MAC address prefix (default MAC address). /// + /// \param randomized number of bytes randomized. /// \throw isc::BadValue if MAC address prefix (default or specified /// from the command line) has invalid size (expected 6 octets). /// \return generated MAC address. - std::vector generateMacAddress() const; + std::vector generateMacAddress(uint8_t& randomized) const; /// \brief generate transaction id. /// @@ -337,6 +360,21 @@ protected: /// \return number of exchanges to be started immediatelly. uint64_t getNextExchangesNum() const; + /// \brief Return template buffer. + /// + /// Method returns template buffer at specified index. + /// + /// \param idx index of template buffer. + /// \return reference to template buffer or empty buffer if index + /// is out of bounds. + TemplateBuffer getTemplateBuffer(const size_t idx) const; + + /// \brief Reads packet templates from files. + /// + /// Method iterates through all specified template files, reads + /// their content and stores it in class internal buffers + /// + /// \throw isc::BadValue if any of the template files does not exist void initPacketTemplates(); /// \brief Initializes Statistics Manager. @@ -448,9 +486,9 @@ protected: void registerOptionFactories() const; - /// \brief Resets internal state of the object. + /// \brief Resets internal state of the object. /// - /// Method resets internal state of the object. It has to be + /// Method resets internal state of the object. It has to be /// called before new test is started. void reset(); @@ -472,16 +510,46 @@ protected: void sendDiscover4(const TestControlSocket& socket, const bool preload = false); + /// \brief Send DHCPv4 DISCOVER message from template. + /// + /// Method sends DHCPv4 DISCOVER message from template. The + /// template data is exepcted to be in binary format. Provided + /// buffer is copied and parts of it are replaced with actual + /// data (e.g. MAC address, transaction id etc.). + /// + /// \param socket socket to be used to send the message. + /// \param template_buf buffer holding template packet. + /// \param preload preload mode, packets not included in statistics. + /// \throw isc::OutOfRange if randomization offset is out of bounds. + void sendDiscover4(const TestControlSocket& socket, + const std::vector& template_buf, + const bool preload = false); + /// \brief Send DHCPv4 REQUEST message. /// /// Method creates and sends DHCPv4 REQUEST message to the server. /// /// \param socket socket to be used to send message. + /// \param discover_pkt4 DISCOVER packet sent. /// \param offer_pkt4 OFFER packet object. /// \throw isc::Unexpected if unexpected error occured. /// \throw isc::InvalidOperation if Statistics Manager has not been /// initialized. void sendRequest4(const TestControlSocket& socket, + const dhcp::Pkt4Ptr& discover_pkt4, + const dhcp::Pkt4Ptr& offer_pkt4); + + /// \brief Send DHCPv4 REQUEST message from template. + /// + /// Method sends DHCPv4 REQUEST message from template. + /// + /// \param socket socket to be used to send message. + /// \param template_buf buffer holding template packet. + /// \param discover_pkt4 DISCOVER packet sent. + /// \param offer_pkt4 OFFER packet received. + void sendRequest4(const TestControlSocket& socket, + const std::vector& template_buf, + const dhcp::Pkt4Ptr& discover_pkt4, const dhcp::Pkt4Ptr& offer_pkt4); /// \brief Send DHCPv6 REQUEST message. @@ -506,6 +574,19 @@ protected: const dhcp::Pkt6Ptr& solicit_pkt6, const dhcp::Pkt6Ptr& advertise_pkt6); + /// \brief Send DHCPv6 REQUEST message from template. + /// + /// Method sends DHCPv6 REQUEST message from template. + /// + /// \param socket socket to be used to send message. + /// \param template_buf packet template buffer. + /// \param solicit_pkt6 SOLICIT packet object. + /// \param advertise_pkt6 ADVERTISE packet object. + void sendRequest6(const TestControlSocket& socket, + const std::vector& template_buf, + const dhcp::Pkt6Ptr& solicit_pkt6, + const dhcp::Pkt6Ptr& advertise_pkt6); + /// \brief Send DHCPv6 SOLICIT message. /// /// Method creates and sends DHCPv6 SOLICIT message to the server @@ -522,6 +603,17 @@ protected: void sendSolicit6(const TestControlSocket& socket, const bool preload = false); + /// \brief Send DHCPv6 SOLICIT message from template. + /// + /// Method sends DHCPv6 SOLICIT message from template. + /// + /// \param socket socket to be used to send the message. + /// \param template_buf packet template buffer. + /// \param preload mode, packets not included in statistics. + void sendSolicit6(const TestControlSocket& socket, + const std::vector& template_buf, + const bool preload = false); + /// \brief Set default DHCPv4 packet parameters. /// /// This method sets default parameters on the DHCPv4 packet: @@ -573,10 +665,27 @@ private: /// \return hex string. std::string byte2Hex(const uint8_t b) const; - /// \brief Generate transaction id using random function. + /// \brief Calculate elapsed time between two packets. /// - /// \return generated transaction id value. - static uint32_t generateTransidRandom(); + /// \param T Pkt4Ptr or Pkt6Ptr class. + /// \param pkt1 first packet. + /// \param pkt2 second packet. + /// \return elapsed time in milliseconds between pkt1 and pkt2. + template + uint32_t getElapsedTime(const T& pkt1, const T& pkt2) { + using namespace boost::posix_time; + ptime pkt1_time = pkt1->getTimestamp(); + ptime pkt2_time = pkt2->getTimestamp(); + if (pkt1_time.is_not_a_date_time() || + pkt2_time.is_not_a_date_time()) { + return (0); + } + time_period elapsed_period(pkt1_time, pkt2_time); + if (elapsed_period.is_null()) { + return (0); + } + return(elapsed_period.length().total_milliseconds()); + } /// \brief Get number of received packets. /// @@ -604,6 +713,18 @@ private: /// \param sig signal (ignored) static void handleInterrupt(int sig); + /// \brief Print main diagnostics data. + /// + /// Method prints main diagnostics data. + void printDiagnostics() const; + + /// \brief Read DHCP message template from file. + /// + /// Method reads DHCP message template from file and + /// converts it to binary format. Read data is appended + /// to template_buffers_ vector. + void readPacketTemplate(const std::string& file_name); + /// \brief Convert vector in hexadecimal string. /// /// \param vec vector to be converted. @@ -611,11 +732,6 @@ private: std::string vector2Hex(const std::vector& vec, const std::string& separator = "") const; - /// \brief Print main diagnostics data. - /// - /// Method prints main diagnostics data. - void printDiagnostics() const; - boost::posix_time::ptime send_due_; ///< Due time to initiate next chunk ///< of exchanges. boost::posix_time::ptime last_sent_; ///< Indicates when the last exchange @@ -629,10 +745,10 @@ private: TransidGeneratorPtr transid_gen_; ///< Transaction id generator. /// Buffer holiding server id received in first packet - dhcp::OptionBuffer first_packet_serverid_; + dhcp::OptionBuffer first_packet_serverid_; /// Packet template buffers. - TemplateBufferList template_buffers_; + TemplateBufferCollection template_buffers_; static bool interrupted_; ///< Is program interrupted. }; diff --git a/tests/tools/perfdhcp/tests/test_control_unittest.cc b/tests/tools/perfdhcp/tests/test_control_unittest.cc index 2284042d58..d72cd0f755 100644 --- a/tests/tools/perfdhcp/tests/test_control_unittest.cc +++ b/tests/tools/perfdhcp/tests/test_control_unittest.cc @@ -60,7 +60,7 @@ public: /// /// \return generated transaction id. virtual uint32_t generate() { - return ++transid_; + return (++transid_); } private: uint32_t transid_; ///< Last generated transaction id. @@ -76,6 +76,8 @@ public: using TestControl::generateDuid; using TestControl::generateMacAddress; using TestControl::getNextExchangesNum; + using TestControl::getTemplateBuffer; + using TestControl::initPacketTemplates; using TestControl::initializeStatsMgr; using TestControl::openSocket; using TestControl::receivePacket4; @@ -109,7 +111,7 @@ public: static uint32_t generateTransidIncremental() { static uint32_t transid(1); - return ++transid; + return (++transid); } /// \brief Get local loopback interface name. @@ -134,11 +136,11 @@ public: ++addr_it) { if (asiolink::IOAddress("127.0.0.1").getAddress() == addr_it->getAddress()) { - return iface->getName(); + return (iface->getName()); } } } - return(""); + return (""); } /// \brief Match requested options in the buffer with given list. @@ -163,7 +165,7 @@ public: } } } - return matched_num; + return (matched_num); } /// \brief Calculate the maximum vectors' mismatch position. @@ -193,7 +195,7 @@ public: n %= 256; } } - return unequal_pos; + return (unequal_pos); } /// brief Test generation of mulitple DUIDs @@ -235,7 +237,8 @@ public: new_duid = old_duid; } else { std::swap(old_duid, new_duid); - new_duid = tc.generateDuid(); + uint8_t randomized = 0; + new_duid = tc.generateDuid(randomized); } // The DUID-LLT is expected to start with DUID_LLT value // of 1 and hardware ethernet type equal to 1 (HWETHER_TYPE). @@ -309,10 +312,19 @@ public: /// \param iterations_performed actual number of iterations. void testPkt4Exchange(int iterations_num, int receive_num, + bool use_templates, int& iterations_performed) const { int sock_handle = 0; NakedTestControl tc; tc.initializeStatsMgr(); + + // Use templates files to crate packets. + if (use_templates) { + tc.initPacketTemplates(); + ASSERT_GT(tc.getTemplateBuffer(0).size(), 0); + ASSERT_GT(tc.getTemplateBuffer(1).size(), 0); + } + // Incremental transaction id generator will generate // predictable values of transaction id for each iteration. // This is important because we need to simulate reponses @@ -326,7 +338,11 @@ public: TestControl::TestControlSocket sock(sock_handle); uint32_t transid = 0; for (int i = 0; i < iterations_num; ++i) { - ASSERT_NO_THROW(tc.sendDiscover4(sock)); + if (use_templates) { + ASSERT_NO_THROW(tc.sendDiscover4(sock, tc.getTemplateBuffer(0))); + } else { + ASSERT_NO_THROW(tc.sendDiscover4(sock)); + } ++transid; // Do not simulate responses for packets later // that specified as receive_num. This simulates @@ -359,10 +375,19 @@ public: /// \param iterations_performed actual number of iterations. void testPkt6Exchange(int iterations_num, int receive_num, + bool use_templates, int& iterations_performed) const { int sock_handle = 0; NakedTestControl tc; tc.initializeStatsMgr(); + + // Use templates files to crate packets. + if (use_templates) { + tc.initPacketTemplates(); + ASSERT_GT(tc.getTemplateBuffer(0).size(), 0); + ASSERT_GT(tc.getTemplateBuffer(1).size(), 0); + } + // Incremental transaction id generator will generate // predictable values of transaction id for each iteration. // This is important because we need to simulate reponses @@ -379,7 +404,11 @@ public: // Do not simulate responses for packets later // that specified as receive_num. This simulates // packet drops. - ASSERT_NO_THROW(tc.sendSolicit6(sock)); + if (use_templates) { + ASSERT_NO_THROW(tc.sendSolicit6(sock, tc.getTemplateBuffer(0))); + } else { + ASSERT_NO_THROW(tc.sendSolicit6(sock)); + } ++transid; if (i < receive_num) { boost::shared_ptr @@ -423,7 +452,8 @@ public: // Do many iterations to generate and test MAC address values. for (int i = 0; i < clients_num * 10; ++i) { // Generate new MAC address. - MacAddress new_mac(tc.generateMacAddress()); + uint8_t randomized = 0; + MacAddress new_mac(tc.generateMacAddress(randomized)); // Get the mismatch position (counting from the end) of // mismatched octet between previously generated MAC address // and current. @@ -471,7 +501,7 @@ private: offer->addOption(opt_msg_type); offer->addOption(opt_serverid); offer->updateTimestamp(); - return(offer); + return (offer); } boost::shared_ptr @@ -479,14 +509,15 @@ private: OptionPtr opt_ia_na = Option::factory(Option::V6, D6O_IA_NA); OptionPtr opt_serverid(new Option(Option::V6, D6O_SERVERID)); NakedTestControl tc; - std::vector duid(tc.generateDuid()); + uint8_t randomized = 0; + std::vector duid(tc.generateDuid(randomized)); OptionPtr opt_clientid(Option::factory(Option::V6, D6O_CLIENTID, duid)); boost::shared_ptr advertise(new Pkt6(DHCPV6_ADVERTISE, transid)); advertise->addOption(opt_ia_na); advertise->addOption(opt_serverid); advertise->addOption(opt_clientid); advertise->updateTimestamp(); - return(advertise); + return (advertise); } }; @@ -634,8 +665,8 @@ TEST_F(TestControlTest, Options6) { OptionPtr opt_oro(Option::factory(Option::V6, D6O_ORO)); // Prepare the reference buffer with requested options. const uint8_t requested_options[] = { - D6O_NAME_SERVERS, - D6O_DOMAIN_SEARCH + 0, D6O_NAME_SERVERS, + 0, D6O_DOMAIN_SEARCH }; OptionBuffer requested_options_ref(requested_options, @@ -751,22 +782,28 @@ TEST_F(TestControlTest, Packet4Exchange) { // The actual number of iterations will be stored in the // following variable. int iterations_performed = 0; - testPkt4Exchange(iterations_num, iterations_num, iterations_performed); + bool use_templates = false; + testPkt4Exchange(iterations_num, iterations_num, use_templates, iterations_performed); // The command line restricts the number of iterations to 10 // with -n 10 parameter. EXPECT_EQ(10, iterations_performed); // With the following command line we restrict the maximum // number of dropped packets to 20% of all. + // Use templates for this test. processCmdLine("perfdhcp -l " + loopback_iface - + " -r 100 -R 20 -n 20 -D 10% -L 10547 127.0.0.1"); + + " -r 100 -R 20 -n 20 -D 10% -L 10547" + + " -T ../templates/discover-example.hex" + + " -T ../templates/request4-example.hex" + + " 127.0.0.1"); // The number iterations is restricted by the percentage of // dropped packets (-D 10%). We also have to bump up the number // of iterations because the percentage limitation checks starts // at packet #10. We expect that at packet #12 the 10% threshold // will be reached. const int received_num = 10; - testPkt4Exchange(iterations_num, received_num, iterations_performed); + use_templates = true; + testPkt4Exchange(iterations_num, received_num, use_templates, iterations_performed); EXPECT_EQ(12, iterations_performed); } @@ -788,22 +825,38 @@ TEST_F(TestControlTest, Packet6Exchange) { int iterations_performed = 0; // Set number of received packets equal to number of iterations. // This simulates no packet drops. - testPkt6Exchange(iterations_num, iterations_num, iterations_performed); + bool use_templates = false; + testPkt6Exchange(iterations_num, iterations_num, use_templates, iterations_performed); // Actual number of iterations should be 10. EXPECT_EQ(10, iterations_performed); // The maximum number of dropped packets is 3 (because of -D 3). + use_templates = true; processCmdLine("perfdhcp -l " + loopback_iface - + " -6 -r 100 -n 10 -R 20 -D 3 -L 10547 ::1"); + + " -6 -r 100 -n 10 -R 20 -D 3 -L 10547" + + " -T ../templates/solicit-example.hex" + + " -T ../templates/request6-example.hex ::1"); // For the first 3 packets we are simulating responses from server. // For other packets we don't so packet as 4,5,6 will be dropped and // then test should be interrupted and actual number of iterations will // be 6. const int received_num = 3; - testPkt6Exchange(iterations_num, received_num, iterations_performed); + testPkt6Exchange(iterations_num, received_num, use_templates, iterations_performed); EXPECT_EQ(6, iterations_performed); } +TEST_F(TestControlTest, PacketTemplates) { + CommandOptions& options = CommandOptions::instance(); + NakedTestControl tc; + + ASSERT_NO_THROW( + processCmdLine("perfdhcp -l 127.0.0.1" + " -T ../templates/discover-example.hex" + " -T ../templates/request4-example.hex all") + ); + ASSERT_NO_THROW(tc.initPacketTemplates()); +} + TEST_F(TestControlTest, RateControl) { // We don't specify the exchange rate here so the aggressivity // value will determine how many packets are to be send each From 6ca3dadca8255b8bb2aa58421f3f4289efe4e63a Mon Sep 17 00:00:00 2001 From: Marcin Siodelski Date: Mon, 3 Sep 2012 14:15:22 +0200 Subject: [PATCH 24/40] [1959] Implemented first set of changes from the code review. --- src/lib/dhcp/iface_mgr.cc | 1 - src/lib/dhcp/iface_mgr.h | 4 +- src/lib/dhcp/libdhcp++.cc | 2 +- src/lib/dhcp/libdhcp++.h | 1 - src/lib/dhcp/option.h | 1 - src/lib/dhcp/tests/iface_mgr_unittest.cc | 98 ++++++++++++++++++++++++ src/lib/dhcp/tests/libdhcp++_unittest.cc | 34 ++++++-- 7 files changed, 131 insertions(+), 10 deletions(-) diff --git a/src/lib/dhcp/iface_mgr.cc b/src/lib/dhcp/iface_mgr.cc index 5f78653614..54db434d8f 100644 --- a/src/lib/dhcp/iface_mgr.cc +++ b/src/lib/dhcp/iface_mgr.cc @@ -70,7 +70,6 @@ void IfaceMgr::Iface::closeSockets() { for (SocketCollection::iterator sock = sockets_.begin(); sock != sockets_.end(); ++sock) { - cout << "Closing socket " << sock->sockfd_ << endl; close(sock->sockfd_); } sockets_.clear(); diff --git a/src/lib/dhcp/iface_mgr.h b/src/lib/dhcp/iface_mgr.h index 03ccf2426a..178d24a9ec 100644 --- a/src/lib/dhcp/iface_mgr.h +++ b/src/lib/dhcp/iface_mgr.h @@ -89,7 +89,7 @@ public: /// @param ifindex interface index (unique integer identifier) Iface(const std::string& name, int ifindex); - /// @brief Closes all open sockets on interface + /// @brief Closes all open sockets on interface. void closeSockets(); /// @brief Returns full interface name as "ifname/ifindex" string. @@ -204,6 +204,8 @@ public: /// in this collection. Note that functions like /// @ref IfaceMgr::openSocketFromIface use /// @ref IfaceMgr::openSocket internally. + /// The returned reference is only valid during the lifetime of + /// the IfaceMgr object that returned it. /// /// @return collection of sockets added to interface const SocketCollection& getSockets() const { return sockets_; } diff --git a/src/lib/dhcp/libdhcp++.cc b/src/lib/dhcp/libdhcp++.cc index 4ff07eac6a..22cd47bb6e 100644 --- a/src/lib/dhcp/libdhcp++.cc +++ b/src/lib/dhcp/libdhcp++.cc @@ -56,7 +56,7 @@ LibDHCP::optionFactory(Option::Universe u, isc_throw(BadValue, "invalid universe specified (expected " "Option::V4 or Option::V6"); } - return(it->second(u, type, buf)); + return (it->second(u, type, buf)); } diff --git a/src/lib/dhcp/libdhcp++.h b/src/lib/dhcp/libdhcp++.h index c959609c61..ae907012cc 100644 --- a/src/lib/dhcp/libdhcp++.h +++ b/src/lib/dhcp/libdhcp++.h @@ -45,7 +45,6 @@ public: uint16_t type, const OptionBuffer& buf); - /// Builds collection of options. /// /// Builds raw (on-wire) data for provided collection of options. diff --git a/src/lib/dhcp/option.h b/src/lib/dhcp/option.h index 37069d6df5..9589c37ab9 100644 --- a/src/lib/dhcp/option.h +++ b/src/lib/dhcp/option.h @@ -99,7 +99,6 @@ public: return factory(u, type, OptionBuffer()); } - /// @brief ctor, used for options constructed, usually during transmission /// /// @param u option universe (DHCPv4 or DHCPv6) diff --git a/src/lib/dhcp/tests/iface_mgr_unittest.cc b/src/lib/dhcp/tests/iface_mgr_unittest.cc index 5562551a0e..7f8555c1fa 100644 --- a/src/lib/dhcp/tests/iface_mgr_unittest.cc +++ b/src/lib/dhcp/tests/iface_mgr_unittest.cc @@ -59,6 +59,7 @@ public: ~IfaceMgrTest() { } + }; // We need some known interface to work reliably. Loopback interface @@ -217,6 +218,94 @@ TEST_F(IfaceMgrTest, getIface) { } +TEST_F(IfaceMgrTest, multipleSockets) { + boost::scoped_ptr ifacemgr(new NakedIfaceMgr()); + + // container for initialized socket descriptors + std::list init_sockets; + + // create socket #1 + int socket1 = 0; + ASSERT_NO_THROW( + socket1 = ifacemgr->openSocketFromIface(LOOPBACK, PORT1, AF_INET); + ); + ASSERT_GT(socket1, 0); + init_sockets.push_back(socket1); + + // create socket #2 + IOAddress loAddr("127.0.0.1"); + int socket2 = 0; + ASSERT_NO_THROW( + socket2 = ifacemgr->openSocketFromRemoteAddress(loAddr, PORT2); + ); + ASSERT_GT(socket2, 0); + init_sockets.push_back(socket2); + + // Get loopback interface. If we don't find one we are unable to run + // this test but we don't want to fail. + IfaceMgr::Iface* iface_ptr = ifacemgr->getIface(LOOPBACK); + if (iface_ptr == NULL) { + cout << "Local loopback interface not found. Skipping test. " << endl; + return; + } + // Once sockets have been sucessfully opened, they are supposed to + // be on the list. Here we start to test if all expected sockets + // are on the list and no other (unexpected) socket is there. + IfaceMgr::SocketCollection sockets = iface_ptr->getSockets(); + int matched_sockets = 0; + for (std::list::iterator init_sockets_it = + init_sockets.begin(); + init_sockets_it != init_sockets.end(); ++init_sockets_it) { + // Set socket descriptors non blocking in order to be able + // to call recv() on them without hang. + int flags = fcntl(*init_sockets_it, F_GETFL, 0); + ASSERT_GE(flags, 0); + ASSERT_GE(fcntl(*init_sockets_it, F_SETFL, flags | O_NONBLOCK), 0); + // recv() is expected to result in EWOULDBLOCK error on non-blocking + // socket in case socket is valid but simply no data are coming in. + char buf; + recv(*init_sockets_it, &buf, 1, MSG_PEEK); + EXPECT_EQ(EWOULDBLOCK, errno); + // Apart from the ability to use the socket we want to make + // sure that socket on the list is the one that we created. + for (IfaceMgr::SocketCollection::const_iterator socket_it = + sockets.begin(); socket_it != sockets.end(); ++socket_it) { + if (*init_sockets_it == socket_it->sockfd_) { + // This socket is the one that we created. + ++matched_sockets; + break; + } + } + } + // all created sockets have been matched if this condition works. + EXPECT_EQ(sockets.size(), matched_sockets); + + // closeSockets() is the other function that we want to test. It + // is supposed to close all sockets so as we will not be able to use + // them anymore communication. + ifacemgr->closeSockets(); + + // closed sockets are supposed to be removed from the list + sockets = iface_ptr->getSockets(); + ASSERT_EQ(0, sockets.size()); + + // We are still in posession of socket descriptors that we created + // on the beginning of this test. We can use them to check whether + // closeSockets() only removed them from the list or they have been + // really closed. + for (std::list::const_iterator init_sockets_it = + init_sockets.begin(); + init_sockets_it != init_sockets.end(); ++init_sockets_it) { + // recv() must result in error when using invalid socket. + char buf; + recv(*init_sockets_it, &buf, 1, MSG_PEEK); + // EWOULDBLOCK would mean that socket is valid/open but + // simply no data is received so we have to check for + // other errors. + EXPECT_NE(EWOULDBLOCK, errno); + } +} + TEST_F(IfaceMgrTest, sockets6) { // testing socket operation in a portable way is tricky // without interface detection implemented @@ -317,6 +406,15 @@ TEST_F(IfaceMgrTest, socketsFromRemoteAddress) { ); EXPECT_GT(socket2, 0); close(socket2); + + // Open v4 socket to connect to broadcast address. + int socket3 = 0; + IOAddress bcastAddr("255.255.255.255"); + EXPECT_NO_THROW( + socket3 = ifacemgr->openSocketFromRemoteAddress(bcastAddr, PORT2); + ); + EXPECT_GT(socket3, 0); + close(socket3); } // TODO: disabled due to other naming on various systems diff --git a/src/lib/dhcp/tests/libdhcp++_unittest.cc b/src/lib/dhcp/tests/libdhcp++_unittest.cc index ac758bed9c..d17eb6537f 100644 --- a/src/lib/dhcp/tests/libdhcp++_unittest.cc +++ b/src/lib/dhcp/tests/libdhcp++_unittest.cc @@ -87,21 +87,45 @@ TEST(LibDhcpTest, optionFactory) { DHO_SUBNET_MASK, buf); // Check if non-NULL DHO_SUBNET_MASK option pointer has been returned. - EXPECT_TRUE(opt_subnet_mask); + ASSERT_TRUE(opt_subnet_mask); + // Validate if type and universe is correct. + EXPECT_EQ(Option::V4, opt_subnet_mask->getUniverse()); + EXPECT_EQ(DHO_SUBNET_MASK, opt_subnet_mask->getType()); + // Expect that option does not have content.. + EXPECT_EQ(0, opt_subnet_mask->len() - opt_subnet_mask->getHeaderLen()); + // Fill the time offset buffer with 4 bytes of data. Each byte set to 1. + OptionBuffer time_offset_buf(4, 1); OptionPtr opt_time_offset; opt_time_offset = LibDHCP::optionFactory(Option::V4, DHO_TIME_OFFSET, - buf); + time_offset_buf); // Check if non-NULL DHO_TIME_OFFSET option pointer has been returned. - EXPECT_TRUE(opt_time_offset); + ASSERT_TRUE(opt_time_offset); + // Validate if option length, type and universe is correct. + EXPECT_EQ(Option::V4, opt_time_offset->getUniverse()); + EXPECT_EQ(DHO_TIME_OFFSET, opt_time_offset->getType()); + EXPECT_EQ(time_offset_buf.size(), + opt_time_offset->len() - opt_time_offset->getHeaderLen()); + // Validate data in the option. + EXPECT_TRUE(std::equal(time_offset_buf.begin(), time_offset_buf.end(), + opt_time_offset->getData().begin())); + // Fill the client id buffer with 20 bytes of data. Each byte set to 2. + OptionBuffer clientid_buf(20, 2); OptionPtr opt_clientid; opt_clientid = LibDHCP::optionFactory(Option::V6, D6O_CLIENTID, - buf); + clientid_buf); // Check if non-NULL D6O_CLIENTID option pointer has been returned. - EXPECT_TRUE(opt_clientid); + ASSERT_TRUE(opt_clientid); + // Validate if option length, type and universe is correct. + EXPECT_EQ(Option::V6, opt_clientid->getUniverse()); + EXPECT_EQ(D6O_CLIENTID, opt_clientid->getType()); + EXPECT_EQ(clientid_buf.size(), opt_clientid->len() - opt_clientid->getHeaderLen()); + // Validate data in the option. + EXPECT_TRUE(std::equal(clientid_buf.begin(), clientid_buf.end(), + opt_clientid->getData().begin())); } TEST(LibDhcpTest, packOptions6) { From 6a3c2c618081b259a7eef9f747b1e6cc8aea4572 Mon Sep 17 00:00:00 2001 From: Marcin Siodelski Date: Tue, 4 Sep 2012 10:25:00 +0200 Subject: [PATCH 25/40] [1959] Implemented changes from the second part of code review. --- tests/tools/perfdhcp/command_options.cc | 55 ++-- tests/tools/perfdhcp/command_options.h | 237 +++++++++--------- tests/tools/perfdhcp/localized_option.h | 89 +++---- tests/tools/perfdhcp/perf_pkt4.h | 1 - tests/tools/perfdhcp/pkt_transform.cc | 7 +- tests/tools/perfdhcp/pkt_transform.h | 2 +- tests/tools/perfdhcp/stats_mgr.h | 9 +- tests/tools/perfdhcp/test_control.cc | 18 +- tests/tools/perfdhcp/test_control.h | 4 +- .../tests/command_options_unittest.cc | 29 ++- .../perfdhcp/tests/perf_pkt4_unittest.cc | 46 ++++ .../perfdhcp/tests/test_control_unittest.cc | 6 +- 12 files changed, 274 insertions(+), 229 deletions(-) diff --git a/tests/tools/perfdhcp/command_options.cc b/tests/tools/perfdhcp/command_options.cc index fcdb7bd484..dbb01e7d4c 100644 --- a/tests/tools/perfdhcp/command_options.cc +++ b/tests/tools/perfdhcp/command_options.cc @@ -56,8 +56,8 @@ CommandOptions::reset() { rate_ = 0; report_delay_ = 0; clients_num_ = 0; - mac_prefix_.assign(mac, mac + 6); - duid_prefix_.clear(); + mac_template_.assign(mac, mac + 6); + duid_template_.clear(); base_.clear(); num_request_.clear(); period_ = 0; @@ -84,8 +84,8 @@ CommandOptions::reset() { diags_.clear(); wrapped_.clear(); server_name_.clear(); - - generateDuidPrefix(); + generateDuidTemplate(); + commandline_.clear(); } void @@ -132,9 +132,16 @@ CommandOptions::initialize(int argc, char** argv) { int offset_arg = 0; // Temporary variable holding offset arguments std::string sarg; // Temporary variable for string args + std::ostringstream stream; + stream << "perfdhcp"; + // In this section we collect argument values from command line // they will be tuned and validated elsewhere while((opt = getopt(argc, argv, "hv46r:t:R:b:n:p:d:D:l:P:a:L:s:iBc1T:X:O:E:S:I:x:w:")) != -1) { + stream << " -" << opt; + if (optarg) { + stream << " " << optarg; + } switch (opt) { case 'v': version(); @@ -318,6 +325,8 @@ CommandOptions::initialize(int argc, char** argv) { } } + std::cout << "Running: " << stream.str() << std::endl; + // If the IP version was not specified in the // command line, assume IPv4. if (ipversion_ == 0) { @@ -375,8 +384,8 @@ CommandOptions::initialize(int argc, char** argv) { // If DUID is not specified from command line we need to // generate one. - if (duid_prefix_.size() == 0) { - generateDuidPrefix(); + if (duid_template_.size() == 0) { + generateDuidTemplate(); } } @@ -439,7 +448,7 @@ CommandOptions::decodeMac(const std::string& base) { // Decode mac address to vector of uint8_t std::istringstream s1(base.substr(found + 1)); std::string token; - mac_prefix_.clear(); + mac_template_.clear(); // Get pieces of MAC address separated with : (or even ::) while (std::getline(s1, token, ':')) { unsigned int ui = 0; @@ -454,17 +463,17 @@ CommandOptions::decodeMac(const std::string& base) { } // If conversion succeeded store byte value - mac_prefix_.push_back(ui); + mac_template_.push_back(ui); } } // MAC address must consist of 6 octets, otherwise it is invalid - check(mac_prefix_.size() != 6, errmsg); + check(mac_template_.size() != 6, errmsg); } void CommandOptions::decodeDuid(const std::string& base) { // Strip argument from duid= - std::vector duid_prefix; + std::vector duid_template; size_t found = base.find('='); check(found == std::string::npos, "expected -b format for duid is -b duid="); std::string b = base.substr(found + 1); @@ -484,26 +493,26 @@ CommandOptions::decodeDuid(const std::string& base) { isc_throw(isc::InvalidParameter, "invalid characters in DUID provided, exepected hex digits"); } - duid_prefix.push_back(static_cast(ui)); + duid_template.push_back(static_cast(ui)); } // Assign the new duid only if successfully generated. - std::swap(duid_prefix, duid_prefix_); + std::swap(duid_template, duid_template_); } void -CommandOptions::generateDuidPrefix() { +CommandOptions::generateDuidTemplate() { using namespace boost::posix_time; - // Duid prefix will be most likely generated only once but + // Duid template will be most likely generated only once but // it is ok if it is called more then once so we simply // regenerate it and discard previous value. - duid_prefix_.clear(); - const uint8_t duid_prefix_len = 14; - duid_prefix_.resize(duid_prefix_len); + duid_template_.clear(); + const uint8_t duid_template_len = 14; + duid_template_.resize(duid_template_len); // The first four octets consist of DUID LLT and hardware type. - duid_prefix_[0] = DUID_LLT >> 8; - duid_prefix_[1] = DUID_LLT & 0xff; - duid_prefix_[2] = HWTYPE_ETHERNET >> 8; - duid_prefix_[3] = HWTYPE_ETHERNET & 0xff; + duid_template_[0] = DUID_LLT >> 8; + duid_template_[1] = DUID_LLT & 0xff; + duid_template_[2] = HWTYPE_ETHERNET >> 8; + duid_template_[3] = HWTYPE_ETHERNET & 0xff; // As described in RFC3315: 'the time value is the time // that the DUID is generated represented in seconds @@ -512,12 +521,12 @@ CommandOptions::generateDuidPrefix() { ptime duid_epoch(from_iso_string("20000101T000000")); time_period period(duid_epoch, now); uint32_t duration_sec = htonl(period.length().total_seconds()); - memcpy(&duid_prefix_[4], &duration_sec, 4); + memcpy(&duid_template_[4], &duration_sec, 4); // Set link layer address (6 octets). This value may be // randomized before sending a packet to simulate different // clients. - memcpy(&duid_prefix_[8], &mac_prefix_[0], 6); + memcpy(&duid_template_[8], &mac_template_[0], 6); } uint8_t diff --git a/tests/tools/perfdhcp/command_options.h b/tests/tools/perfdhcp/command_options.h index b9f2c3a198..b9e2b9e70e 100644 --- a/tests/tools/perfdhcp/command_options.h +++ b/tests/tools/perfdhcp/command_options.h @@ -23,7 +23,7 @@ namespace isc { namespace perfdhcp { -/// \brief Command Options +/// \brief Command Options. /// /// This class is responsible for parsing the command-line and storing the /// specified options. @@ -49,64 +49,64 @@ public: /// command line options. void reset(); - /// \brief Parse command line + /// \brief Parse command line. /// /// Parses the command line and stores the selected options /// in class data members. /// /// \param argc Argument count passed to main(). /// \param argv Argument value array passed to main(). - /// \throws isc::InvalidParameter if parse fails + /// \throws isc::InvalidParameter if parse fails. void parse(int argc, char** const argv); - /// \brief Returns IP version + /// \brief Returns IP version. /// - /// \return IP version to be used + /// \return IP version to be used. uint8_t getIpVersion() const { return ipversion_; } - /// \brief Returns packet exchange mode + /// \brief Returns packet exchange mode. /// - /// \return packet exchange mode + /// \return packet exchange mode. ExchangeMode getExchangeMode() const { return exchange_mode_; } - /// \brief Returns echange rate + /// \brief Returns echange rate. /// - /// \return exchange rate per second + /// \return exchange rate per second. int getRate() const { return rate_; } - /// \brief Returns delay between two performance reports + /// \brief Returns delay between two performance reports. /// - /// \return delay between two consecutive performance reports + /// \return delay between two consecutive performance reports. int getReportDelay() const { return report_delay_; } - /// \brief Returns number of simulated clients + /// \brief Returns number of simulated clients. /// - /// \return number of simulated clients + /// \return number of simulated clients. uint32_t getClientsNum() const { return clients_num_; } - /// \brief Returns MAC address prefix + /// \brief Returns MAC address template. /// - /// \ return MAC address prefix to simulate different clients - std::vector getMacPrefix() const { return mac_prefix_; } + /// \return MAC address template to simulate different clients. + std::vector getMacTemplate() const { return mac_template_; } - /// \brief Returns DUID prefix + /// \brief Returns DUID template. /// - /// \return DUID prefix to simulate different clients - std::vector getDuidPrefix() const { return duid_prefix_; } + /// \return DUID template to simulate different clients. + std::vector getDuidTemplate() const { return duid_template_; } - /// \brief Returns base values + /// \brief Returns base values. /// - /// \return all base values specified + /// \return all base values specified. std::vector getBase() const { return base_; } - /// \brief Returns maximum number of exchanges + /// \brief Returns maximum number of exchanges. /// - /// \return number of exchange requests before test is aborted + /// \return number of exchange requests before test is aborted. std::vector getNumRequests() const { return num_request_; } - /// \brief Returns test period + /// \brief Returns test period. /// - /// \return test period before it is aborted + /// \return test period before it is aborted. int getPeriod() const { return period_; } /// \brief Returns drop time @@ -114,139 +114,139 @@ public: /// The method returns maximum time elapsed from /// sending the packet before it is assumed dropped. /// - /// \return return time before request is assumed dropped + /// \return return time before request is assumed dropped. std::vector getDropTime() const { return drop_time_; } - /// \brief Returns maximum drops number + /// \brief Returns maximum drops number. /// /// Returns maximum number of packet drops before /// aborting a test. /// - /// \return maximum number of dropped requests + /// \return maximum number of dropped requests. std::vector getMaxDrop() const { return max_drop_; } - /// \brief Returns maximal percentage of drops + /// \brief Returns maximal percentage of drops. /// /// Returns maximal percentage of packet drops /// before aborting a test. /// - /// \return maximum percentage of lost requests + /// \return maximum percentage of lost requests. std::vector getMaxDropPercentage() const { return max_pdrop_; } - /// \brief Returns local address or interface name + /// \brief Returns local address or interface name. /// - /// \return local address or interface name + /// \return local address or interface name. std::string getLocalName() const { return localname_; } - /// \brief Checks if interface name was used + /// \brief Checks if interface name was used. /// /// The method checks if interface name was used /// rather than address. /// - /// \return true if interface name was used + /// \return true if interface name was used. bool isInterface() const { return is_interface_; } - /// \brief Returns number of preload exchanges + /// \brief Returns number of preload exchanges. /// - /// \return number of preload exchanges + /// \return number of preload exchanges. int getPreload() const { return preload_; } - /// \brief Returns aggressivity value + /// \brief Returns aggressivity value. /// - /// \return aggressivity value + /// \return aggressivity value. int getAggressivity() const { return aggressivity_; } - /// \brief Returns local port number + /// \brief Returns local port number. /// - /// \return local port number + /// \return local port number. int getLocalPort() const { return local_port_; } - /// \brief Checks if seed provided + /// \brief Checks if seed provided. /// - /// \return true if seed was provided + /// \return true if seed was provided. bool isSeeded() const { return seeded_; } - /// \brief Returns radom seed + /// \brief Returns radom seed. /// - /// \return random seed + /// \return random seed. uint32_t getSeed() const { return seed_; } - /// \brief Checks if broadcast address is to be used + /// \brief Checks if broadcast address is to be used. /// - /// \return true if broadcast address is to be used + /// \return true if broadcast address is to be used. bool isBroadcast() const { return broadcast_; } - /// \brief Check if rapid commit option used + /// \brief Check if rapid commit option used. /// - /// \return true if rapid commit option is used + /// \return true if rapid commit option is used. bool isRapidCommit() const { return rapid_commit_; } - /// \brief Check if server-ID to be taken from first package + /// \brief Check if server-ID to be taken from first package. /// - /// \return true if server-iD to be taken from first package + /// \return true if server-iD to be taken from first package. bool isUseFirst() const { return use_first_; } - /// \brief Returns template file names + /// \brief Returns template file names. /// - /// \return template file names + /// \return template file names. std::vector getTemplateFiles() const { return template_file_; } - /// brief Returns template offsets for xid + /// brief Returns template offsets for xid. /// - /// \return template offsets for xid + /// \return template offsets for xid. std::vector getTransactionIdOffset() const { return xid_offset_; } - /// \brief Returns template offsets for rnd + /// \brief Returns template offsets for rnd. /// - /// \return template offsets for rnd + /// \return template offsets for rnd. std::vector getRandomOffset() const { return rnd_offset_; } - /// \brief Returns template offset for elapsed time + /// \brief Returns template offset for elapsed time. /// - /// \return template offset for elapsed time + /// \return template offset for elapsed time. int getElapsedTimeOffset() const { return elp_offset_; } - /// \brief Returns template offset for server-ID + /// \brief Returns template offset for server-ID. /// - /// \return template offset for server-ID + /// \return template offset for server-ID. int getServerIdOffset() const { return sid_offset_; } - /// \brief Returns template offset for requested IP + /// \brief Returns template offset for requested IP. /// - /// \return template offset for requested IP + /// \return template offset for requested IP. int getRequestedIpOffset() const { return rip_offset_; } - /// \brief Returns diagnostic selectors + /// \brief Returns diagnostic selectors. /// - /// \return diagnostics selector + /// \return diagnostics selector. std::string getDiags() const { return diags_; } - /// \brief Returns wrapped command + /// \brief Returns wrapped command. /// - /// \return wrapped command (start/stop) + /// \return wrapped command (start/stop). std::string getWrapped() const { return wrapped_; } - /// \brief Returns server name + /// \brief Returns server name. /// - /// \return server name + /// \return server name. std::string getServerName() const { return server_name_; } - /// \brief Prints command line arguments. + /// \brief Print command line arguments. void printCommandLine() const; - /// \brief Print usage + /// \brief Print usage. /// - /// Prints perfdhcp usage + /// Prints perfdhcp usage. void usage() const; - /// \brief Print program version + /// \brief Print program version. /// - /// Prints perfdhcp version + /// Prints perfdhcp version. void version() const; private: - /// \brief Default Constructor + /// \brief Default Constructor. /// /// Private constructor as this is a singleton class. /// Use CommandOptions::instance() to get instance of it. @@ -254,54 +254,54 @@ private: reset(); } - /// \brief Initializes class members based command line + /// \brief Initializes class members based on the command line. /// - /// Reads each command line parameter and sets class member values + /// Reads each command line parameter and sets class member values. /// /// \param argc Argument count passed to main(). /// \param argv Argument value array passed to main(). - /// \throws isc::InvalidParameter if command line options initialization fails + /// \throws isc::InvalidParameter if command line options initialization fails. void initialize(int argc, char** argv); - /// \brief Validates initialized options + /// \brief Validates initialized options. /// - /// \throws isc::InvalidParameter if command line validation fails + /// \throws isc::InvalidParameter if command line validation fails. void validate() const; - /// \brief Throws !InvalidParameter exception if condition is true + /// \brief Throws !InvalidParameter exception if condition is true. /// /// Convenience function that throws an InvalidParameter exception if - /// the condition argument is true + /// the condition argument is true. /// - /// \param condition Condition to be checked - /// \param errmsg Error message in exception - /// \throws isc::InvalidParameter if condition argument true + /// \param condition Condition to be checked. + /// \param errmsg Error message in exception. + /// \throws isc::InvalidParameter if condition argument true. inline void check(bool condition, const std::string& errmsg) const; - /// \brief Casts command line argument to positive integer + /// \brief Casts command line argument to positive integer. /// - /// \param errmsg Error message if lexical cast fails - /// \throw InvalidParameter if lexical cast fails + /// \param errmsg Error message if lexical cast fails. + /// \throw InvalidParameter if lexical cast fails. int positiveInteger(const std::string& errmsg) const; - /// \brief Casts command line argument to non-negative integer + /// \brief Casts command line argument to non-negative integer. /// - /// \param errmsg Error message if lexical cast fails - /// \throw InvalidParameter if lexical cast fails + /// \param errmsg Error message if lexical cast fails. + /// \throw InvalidParameter if lexical cast fails. int nonNegativeInteger(const std::string& errmsg) const; - /// \brief Returns command line string if it is not empty + /// \brief Returns command line string if it is not empty. /// - /// \param errmsg Error message if string is empty - /// \throw InvalidParameter if string is empty + /// \param errmsg Error message if string is empty. + /// \throw InvalidParameter if string is empty. std::string nonEmptyString(const std::string& errmsg) const; - /// \brief Set number of clients + /// \brief Set number of clients. /// /// Interprets the getopt() "opt" global variable as the number of clients /// (a non-negative number). This value is specified by the "-R" switch. /// - /// \throw InvalidParameter if -R is wrong + /// \throw InvalidParameter if -R is wrong. void initClientsNum(); /// \brief Sets value indicating if interface name was given. @@ -311,7 +311,7 @@ private: /// is set accordingly. void initIsInterface(); - /// \brief Decodes base provided with -b + /// \brief Decodes base provided with -b. /// /// Function decodes argument of -b switch, which /// specifies a base value used to generate unique @@ -321,45 +321,47 @@ private: /// - -b mac=00:01:02:03:04:05 /// - -b duid=0F1234 (duid can be up to 128 hex digits) // Function will decode 00:01:02:03:04:05 and/or - /// 0F1234 respectively and initialize mac_prefix_ - /// and/or duid_prefix_ members + /// 0F1234 respectively and initialize mac_template_ + /// and/or duid_template_ members. /// - /// \param base Base in string format - /// \throws isc::InvalidParameter if base is invalid + /// \param base Base in string format. + /// \throws isc::InvalidParameter if base is invalid. void decodeBase(const std::string& base); - /// \brief Decodes base MAC address provided with -b + /// \brief Decodes base MAC address provided with -b. /// /// Function decodes parameter given as -b mac=00:01:02:03:04:05 - /// The function will decode 00:01:02:03:04:05 initialize mac_prefix_ + /// The function will decode 00:01:02:03:04:05 initialize mac_template_ /// class member. - /// Provided MAC address is for example only + /// Provided MAC address is for example only. /// - /// \param base Base string given as -b mac=00:01:02:03:04:05 - /// \throws isc::InvalidParameter if mac address is invalid + /// \param base Base string given as -b mac=00:01:02:03:04:05. + /// \throws isc::InvalidParameter if mac address is invalid. void decodeMac(const std::string& base); - /// \brief Decodes base DUID provided with -b + /// \brief Decodes base DUID provided with -b. /// - /// Function decodes parameter given as -b duid=0F1234 - /// The function will decode 0F1234 and initialize duid_prefix_ + /// Function decodes parameter given as -b duid=0F1234. + /// The function will decode 0F1234 and initialize duid_template_ /// class member. /// Provided DUID is for example only. /// - /// \param base Base string given as -b duid=0F1234 - /// \throws isc::InvalidParameter if DUID is invalid + /// \param base Base string given as -b duid=0F1234. + /// \throws isc::InvalidParameter if DUID is invalid. void decodeDuid(const std::string& base); /// \brief Generates DUID-LLT (based on link layer address). /// /// Function generates DUID based on link layer address and - /// initiates duid_prefix_ value with it. - void generateDuidPrefix(); + /// initiates duid_template_ value with it. + /// \todo add support to generate DUIDs other than based on + /// 6-octets long MACs (e.g. DUID-UUID. + void generateDuidTemplate(); - /// \brief Converts two-digit hexadecimal string to a byte + /// \brief Converts two-digit hexadecimal string to a byte. /// - /// \param hex_text Hexadecimal string e.g. AF - /// \throw isc::InvalidParameter if string does not represent hex byte + /// \param hex_text Hexadecimal string e.g. AF. + /// \throw isc::InvalidParameter if string does not represent hex byte. uint8_t convertHexString(const std::string& hex_text) const; uint8_t ipversion_; ///< IP protocol version to be used, expected values are: @@ -369,9 +371,9 @@ private: int report_delay_; ///< Delay between generation of two consecutive ///< performance reports uint32_t clients_num_; ///< Number of simulated clients (aka randomization range). - std::vector mac_prefix_; ///< MAC address prefix used to generate unique DUIDs + std::vector mac_template_; ///< MAC address template used to generate unique DUIDs ///< for simulated clients. - std::vector duid_prefix_; ///< DUID prefix used to generate unique DUIDs for + std::vector duid_template_; ///< DUID template used to generate unique DUIDs for ///< simulated clients std::vector base_; ///< Collection of base values specified with -b ///< options. Supported "bases" are mac= and duid= @@ -420,6 +422,7 @@ private: std::string wrapped_; ///< Wrapped command specified as -w. Expected ///< values are start and stop. std::string server_name_; ///< Server name specified as last argument of command line. + std::string commandline_; ///< Entire command line as typed in by the user. }; } // namespace perfdhcp diff --git a/tests/tools/perfdhcp/localized_option.h b/tests/tools/perfdhcp/localized_option.h index f93089fbad..336e08366e 100644 --- a/tests/tools/perfdhcp/localized_option.h +++ b/tests/tools/perfdhcp/localized_option.h @@ -44,33 +44,46 @@ namespace perfdhcp { /// class LocalizedOption : public dhcp::Option { public: - /// \brief Constructor, sets default (0) option offset - /// - /// \param u specifies universe (V4 or V6) - /// \param type option type (0-255 for V4 and 0-65535 for V6) - /// \param data content of the option - LocalizedOption(dhcp::Option::Universe u, - uint16_t type, - const dhcp::OptionBuffer& data) : - dhcp::Option(u, type, data), - offset_(0), option_valid_(true) { - } - - /// \brief Constructor, used to create localized option from buffer + /// \brief Constructor, used to create localized option from buffer. /// - /// \param u specifies universe (V4 or V6) - /// \param type option type (0-255 for V4 and 0-65535 for V6) - /// \param data content of the option - /// \param offset location of option in a packet (zero is default) + /// This constructor creates localized option using whole provided + /// option buffer. + /// + /// \param u universe (V4 or V6). + /// \param type option type (0-255 for V4 and 0-65535 for V6). + /// Option values 0 and 255 (v4) and 0 (v6) are not valid option + /// codes but they are accepted here for the server testing purposes. + /// \param data content of the option. + /// \param offset location of option in a packet (zero is default). LocalizedOption(dhcp::Option::Universe u, uint16_t type, const dhcp::OptionBuffer& data, - const size_t offset) : + const size_t offset = 0) : dhcp::Option(u, type, data), offset_(offset), option_valid_(true) { } + /// \brief Constructor, used to create option from buffer iterators. + /// + /// This constructor creates localized option using part of the + /// option buffer pointed by iterators. + /// + /// \param u specifies universe (V4 or V6) + /// \param type option type (0-255 for V4 and 0-65535 for V6) + /// \param first iterator to the first element that should be copied + /// \param last iterator to the next element after the last one + /// to be copied. + /// \param offset offset of option in a packet (zero is default) + LocalizedOption(dhcp::Option::Universe u, + uint16_t type, + dhcp::OptionBufferConstIter first, + dhcp::OptionBufferConstIter last, + const size_t offset = 0) : + dhcp::Option(u, type, first, last), + offset_(offset), option_valid_(true) { + } + /// \brief Copy constructor, creates LocalizedOption from Option6IA. /// /// This copy constructor creates regular option from Option6IA. @@ -113,44 +126,6 @@ public: } } - /// \brief Constructor, sets default (0) option offset - /// - /// This contructor is similar to the previous one, but it does not take - /// the whole vector, but rather subset of it. - /// - /// \param u specifies universe (V4 or V6) - /// \param type option type (0-255 for V4 and 0-65535 for V6) - /// \param first iterator to the first element that should be copied - /// \param last iterator to the next element after the last one - /// to be copied. - LocalizedOption(dhcp::Option::Universe u, - uint16_t type, - dhcp::OptionBufferConstIter first, - dhcp::OptionBufferConstIter last) : - dhcp::Option(u, type, first, last), - offset_(0), option_valid_(true) { - } - - - /// \brief Constructor, used to create option from buffer iterators - /// - /// This contructor is similar to the previous one, but it does not take - /// the whole vector, but rather subset of it. - /// - /// \param u specifies universe (V4 or V6) - /// \param type option type (0-255 for V4 and 0-65535 for V6) - /// \param first iterator to the first element that should be copied - /// \param last iterator to the next element after the last one - /// to be copied. - /// \param offset offset of option in a packet (zero is default) - LocalizedOption(dhcp::Option::Universe u, - uint16_t type, - dhcp::OptionBufferConstIter first, - dhcp::OptionBufferConstIter last, const size_t offset) : - dhcp::Option(u, type, first, last), - offset_(offset), option_valid_(true) { - } - /// \brief Returns offset of an option in a DHCP packet. /// /// \return option offset in a packet @@ -169,7 +144,7 @@ private: }; -} // namespace perfdhcp +} // namespace isc::perfdhcp } // namespace isc #endif // __LOCALIZED_OPTION_H diff --git a/tests/tools/perfdhcp/perf_pkt4.h b/tests/tools/perfdhcp/perf_pkt4.h index 1a19a08749..87c7bb0974 100644 --- a/tests/tools/perfdhcp/perf_pkt4.h +++ b/tests/tools/perfdhcp/perf_pkt4.h @@ -113,7 +113,6 @@ public: void writeAt(size_t dest_pos, std::vector::iterator first, std::vector::iterator last); - /// \brief Replace contents of buffer with value. /// diff --git a/tests/tools/perfdhcp/pkt_transform.cc b/tests/tools/perfdhcp/pkt_transform.cc index d87ca19c3b..b1c64e2339 100644 --- a/tests/tools/perfdhcp/pkt_transform.cc +++ b/tests/tools/perfdhcp/pkt_transform.cc @@ -221,12 +221,7 @@ void PktTransform::writeAt(dhcp::OptionBuffer& in_buffer, size_t dest_pos, dhcp::OptionBuffer::iterator first, dhcp::OptionBuffer::iterator last) { - int i = 0; - for (std::vector::iterator it = first; - it != last; - ++it, ++i) { - in_buffer[dest_pos + i] = *it; - } + memcpy(&in_buffer[dest_pos], &(*first), std::distance(first, last)); } } // namespace perfdhcp diff --git a/tests/tools/perfdhcp/pkt_transform.h b/tests/tools/perfdhcp/pkt_transform.h index e7665a2dc8..08563a2720 100644 --- a/tests/tools/perfdhcp/pkt_transform.h +++ b/tests/tools/perfdhcp/pkt_transform.h @@ -115,7 +115,7 @@ public: static void writeValueAt(dhcp::OptionBuffer& in_buffer, size_t dest_pos, T val) { for (int i = 0; i < sizeof(T); ++i) { - in_buffer[dest_pos + i] = (val >> (sizeof(T) - 8 * i - 1)) & 0xFF; + in_buffer[dest_pos + i] = (val >> 8 * (sizeof(T) - i - 1)) & 0xFF; } } diff --git a/tests/tools/perfdhcp/stats_mgr.h b/tests/tools/perfdhcp/stats_mgr.h index 806fb8dee6..aae6934778 100644 --- a/tests/tools/perfdhcp/stats_mgr.h +++ b/tests/tools/perfdhcp/stats_mgr.h @@ -1102,13 +1102,12 @@ public: /// Statistics includes sent, received and dropped packets /// counters. void printIntermediateStats() const { - std::ostringstream stream_sent(""); - std::ostringstream stream_rcvd(""); - std::ostringstream stream_drops(""); + std::ostringstream stream_sent; + std::ostringstream stream_rcvd; + std::ostringstream stream_drops; std::string sep(""); for (ExchangesMapIterator it = exchanges_.begin(); - it != exchanges_.end(); - ++it) { + it != exchanges_.end(); ++it) { if (it != exchanges_.begin()) { sep = "/"; diff --git a/tests/tools/perfdhcp/test_control.cc b/tests/tools/perfdhcp/test_control.cc index 6da2549182..3c0f6d1b11 100644 --- a/tests/tools/perfdhcp/test_control.cc +++ b/tests/tools/perfdhcp/test_control.cc @@ -325,12 +325,12 @@ TestControl::generateMacAddress(uint8_t& randomized) const { CommandOptions& options = CommandOptions::instance(); uint32_t clients_num = options.getClientsNum(); if ((clients_num == 0) || (clients_num == 1)) { - return (options.getMacPrefix()); + return (options.getMacTemplate()); } // Get the base MAC address. We are going to randomize part of it. - std::vector mac_addr(options.getMacPrefix()); + std::vector mac_addr(options.getMacTemplate()); if (mac_addr.size() != HW_ETHER_LEN) { - isc_throw(BadValue, "invalid MAC address prefix specified"); + isc_throw(BadValue, "invalid MAC address template specified"); } uint32_t r = random(); // The random number must be in the range 0..clients_num. This @@ -364,10 +364,11 @@ TestControl::generateDuid(uint8_t& randomized) const { CommandOptions& options = CommandOptions::instance(); uint32_t clients_num = options.getClientsNum(); if ((clients_num == 0) || (clients_num == 1)) { - return (options.getDuidPrefix()); + return (options.getDuidTemplate()); } // Get the base DUID. We are going to randomize part of it. - std::vector duid(options.getDuidPrefix()); + std::vector duid(options.getDuidTemplate()); + // @todo: add support for DUIDs of different sizes. std::vector mac_addr(generateMacAddress(randomized)); duid.resize(duid.size() - mac_addr.size()); for (int i = 0; i < mac_addr.size(); ++i) { @@ -589,12 +590,11 @@ TestControl::printDiagnostics() const { // Print all command line parameters. options.printCommandLine(); // Print MAC and DUID. - std::cout << "Set MAC to " << vector2Hex(options.getMacPrefix(), "::") + std::cout << "Set MAC to " << vector2Hex(options.getMacTemplate(), "::") << std::endl; - if (options.getDuidPrefix().size() > 0) { - std::cout << "Set DUID to " << vector2Hex(options.getDuidPrefix()) << std::endl; + if (options.getDuidTemplate().size() > 0) { + std::cout << "Set DUID to " << vector2Hex(options.getDuidTemplate()) << std::endl; } - } } diff --git a/tests/tools/perfdhcp/test_control.h b/tests/tools/perfdhcp/test_control.h index 2bace3a22e..36ee55a32f 100644 --- a/tests/tools/perfdhcp/test_control.h +++ b/tests/tools/perfdhcp/test_control.h @@ -335,10 +335,10 @@ protected: /// simulated DHCP clients specified from command line. It uses /// \ref CommandOptions object to retrieve number of clients. /// Based on this the random value is generated and added to - /// the MAC address prefix (default MAC address). + /// the MAC address template (default MAC address). /// /// \param randomized number of bytes randomized. - /// \throw isc::BadValue if MAC address prefix (default or specified + /// \throw isc::BadValue if MAC address template (default or specified /// from the command line) has invalid size (expected 6 octets). /// \return generated MAC address. std::vector generateMacAddress(uint8_t& randomized) const; diff --git a/tests/tools/perfdhcp/tests/command_options_unittest.cc b/tests/tools/perfdhcp/tests/command_options_unittest.cc index faed8a1e6f..01f0357a70 100644 --- a/tests/tools/perfdhcp/tests/command_options_unittest.cc +++ b/tests/tools/perfdhcp/tests/command_options_unittest.cc @@ -16,6 +16,7 @@ #include #include #include +#include #include #include @@ -25,6 +26,7 @@ using namespace std; using namespace isc; using namespace isc::perfdhcp; +using namespace boost::posix_time; /// \brief Test Fixture Class /// @@ -72,7 +74,7 @@ protected: // default mac const uint8_t mac[6] = { 0x00, 0x0C, 0x01, 0x02, 0x03, 0x04 }; - std::vector v1 = opt.getMacPrefix(); + std::vector v1 = opt.getMacTemplate(); ASSERT_EQ(6, v1.size()); EXPECT_TRUE(std::equal(v1.begin(), v1.end(), mac)); @@ -85,10 +87,27 @@ protected: // of time value and 6 octets of variable link layer (MAC) // address. const int duid_llt_size = 14; - std::vector v2 = opt.getDuidPrefix(); - ASSERT_EQ(duid_llt_size, opt.getDuidPrefix().size()); + std::vector v2 = opt.getDuidTemplate(); + ASSERT_EQ(duid_llt_size, opt.getDuidTemplate().size()); EXPECT_TRUE(std::equal(v2.begin(), v2.begin() + 4, duid_llt_and_hw)); + // Check time field contents. + ptime now = microsec_clock::universal_time(); + ptime duid_epoch(from_iso_string("20000101T000000")); + time_period period(duid_epoch, now); + uint32_t duration_sec = period.length().total_seconds(); + // Read time from the template generated. + uint32_t duration_from_template = 0; + memcpy(&duration_from_template, &v2[4], 4); + duration_from_template = htonl(duration_from_template); + // In special cases, we may have overflow in time field + // so we give ourselves the margin of 10 seconds here. + // If time value has been set more then 10 seconds back + // it is safe to compare it with the time value generated + // from now. + if (duration_from_template > 10) { + EXPECT_GE(duration_sec, duration_from_template); + } EXPECT_EQ(0, opt.getBase().size()); EXPECT_EQ(0, opt.getNumRequests().size()); @@ -249,7 +268,7 @@ TEST_F(CommandOptionsTest, Base) { 0x01, 0x01, 0x01, 0x10, 0x11, 0x1F, 0x14 }; // Test Mac - std::vector v1 = opt.getMacPrefix(); + std::vector v1 = opt.getMacTemplate(); ASSERT_EQ(6, v1.size()); EXPECT_TRUE(std::equal(v1.begin(), v1.end(), mac)); // "3x" is invalid value in MAC address @@ -260,7 +279,7 @@ TEST_F(CommandOptionsTest, Base) { EXPECT_NO_THROW( process("perfdhcp -b duid=0101010101010101010110111F14 -l 127.0.0.1 all") ); - std::vector v2 = opt.getDuidPrefix(); + std::vector v2 = opt.getDuidTemplate(); ASSERT_EQ(sizeof(duid) / sizeof(uint8_t), v2.size()); EXPECT_TRUE(std::equal(v2.begin(), v2.end(), duid)); // "t" is invalid character in DUID diff --git a/tests/tools/perfdhcp/tests/perf_pkt4_unittest.cc b/tests/tools/perfdhcp/tests/perf_pkt4_unittest.cc index 3863faa111..5523c640ec 100644 --- a/tests/tools/perfdhcp/tests/perf_pkt4_unittest.cc +++ b/tests/tools/perfdhcp/tests/perf_pkt4_unittest.cc @@ -381,4 +381,50 @@ TEST_F(PerfPkt4Test, UnpackTransactionId) { EXPECT_FALSE(pkt2->rawUnpack()); } +TEST_F(PerfPkt4Test, Writes) { + // Initialize intput buffer with 260 elements set to value 1. + dhcp::OptionBuffer in_data(260, 1); + // Initialize buffer to be used for write: 1,2,3,4,...,9 + dhcp::OptionBuffer write_buf(10); + for (int i = 0; i < write_buf.size(); ++i) { + write_buf[i] = i; + } + // Create packet from the input buffer. + const size_t transid_offset = 4; + boost::scoped_ptr pkt1(new PerfPkt4(&in_data[0], + in_data.size(), + transid_offset)); + // Write numbers 4,5,6,7 to the packet's input buffer at position 10. + pkt1->writeAt(10, write_buf.begin() + 3, write_buf.begin() + 7); + // We have to pack data to output buffer here because Pkt4 provides no + // way to retrieve input buffer. If we pack data it will go to + // output buffer that has getter available. + ASSERT_TRUE(pkt1->rawPack()); + const util::OutputBuffer& out_buf = pkt1->getBuffer(); + ASSERT_EQ(in_data.size(), out_buf.getLength()); + // Verify that 4,5,6,7 has been written to the packet's buffer. + const char* out_data = static_cast(out_buf.getData()); + EXPECT_TRUE(std::equal(write_buf.begin() + 3, write_buf.begin() + 7, + out_data + 10)); + // Write 1 octet (0x51) at position 10. + pkt1->writeValueAt(10, 0x51); + ASSERT_TRUE(pkt1->rawPack()); + ASSERT_EQ(in_data.size(), pkt1->getBuffer().getLength()); + EXPECT_EQ(0x51, pkt1->getBuffer()[10]); + // Write 2 octets (0x5251) at position 20. + pkt1->writeValueAt(20, 0x5251); + ASSERT_TRUE(pkt1->rawPack()); + ASSERT_EQ(in_data.size(), pkt1->getBuffer().getLength()); + EXPECT_EQ(0x52, pkt1->getBuffer()[20]); + EXPECT_EQ(0x51, pkt1->getBuffer()[21]); + // Write 4 octets (0x54535251) at position 30. + pkt1->writeValueAt(30, 0x54535251); + ASSERT_TRUE(pkt1->rawPack()); + ASSERT_EQ(in_data.size(), pkt1->getBuffer().getLength()); + EXPECT_EQ(0x54, pkt1->getBuffer()[30]); + EXPECT_EQ(0x53, pkt1->getBuffer()[31]); + EXPECT_EQ(0x52, pkt1->getBuffer()[32]); + EXPECT_EQ(0x51, pkt1->getBuffer()[33]); +} + } diff --git a/tests/tools/perfdhcp/tests/test_control_unittest.cc b/tests/tools/perfdhcp/tests/test_control_unittest.cc index d72cd0f755..2ea3667ab6 100644 --- a/tests/tools/perfdhcp/tests/test_control_unittest.cc +++ b/tests/tools/perfdhcp/tests/test_control_unittest.cc @@ -215,7 +215,7 @@ public: // The old duid will be holding the previously generated DUID. // It will be used to compare against the new one. If we have // multiple clients we want to make sure that duids differ. - Duid old_duid(CommandOptions::instance().getDuidPrefix()); + Duid old_duid(CommandOptions::instance().getDuidTemplate()); Duid new_duid(0); // total_dist shows the total difference between generated duid. // It has to be greater than zero if multiple clients are simulated. @@ -439,7 +439,7 @@ public: // MAC address. We will be comparing the newly generated one with it // to see if it changes when mulitple clients are simulated or if it // does not change when single client is simulated. - MacAddress old_mac(CommandOptions::instance().getMacPrefix()); + MacAddress old_mac(CommandOptions::instance().getMacTemplate()); // Holds the position if the octet on which two MAC addresses can // be different. If number of clients is 256 or less it is last MAC // octet (except for single client when subsequent MAC addresses @@ -652,7 +652,7 @@ TEST_F(TestControlTest, Options6) { EXPECT_THROW(opt_rapid_commit->getUint8(), isc::OutOfRange); // Validate the D6O_CLIENTID option. - OptionBuffer duid(CommandOptions::instance().getDuidPrefix()); + OptionBuffer duid(CommandOptions::instance().getDuidTemplate()); OptionPtr opt_clientid(Option::factory(Option::V6, D6O_CLIENTID, duid)); EXPECT_EQ(Option::V6, opt_clientid->getUniverse()); EXPECT_EQ(D6O_CLIENTID, opt_clientid->getType()); From ad297b10887895eae9cb6e3c5e00e38f7d1f5fda Mon Sep 17 00:00:00 2001 From: Marcin Siodelski Date: Tue, 4 Sep 2012 11:12:14 +0200 Subject: [PATCH 26/40] [1959] A couple of minor changes to cover second part of the review. --- tests/tools/perfdhcp/stats_mgr.h | 22 ++++++++++++++----- tests/tools/perfdhcp/test_control.h | 8 +++++-- .../tests/command_options_unittest.cc | 10 +++++++-- 3 files changed, 31 insertions(+), 9 deletions(-) diff --git a/tests/tools/perfdhcp/stats_mgr.h b/tests/tools/perfdhcp/stats_mgr.h index aae6934778..a8dfa8b7bd 100644 --- a/tests/tools/perfdhcp/stats_mgr.h +++ b/tests/tools/perfdhcp/stats_mgr.h @@ -214,15 +214,27 @@ public: /// } /// \endcode typedef boost::multi_index_container< + // Container holds shared_ptr or shared_ptr objects. boost::shared_ptr, + // List container indexes. boost::multi_index::indexed_by< + // Sequenced index provides the way to use this container + // in the same way as std::list. boost::multi_index::sequenced<>, + // The other index keeps products of transaction id. boost::multi_index::hashed_non_unique< - boost::multi_index::global_fun< - const boost::shared_ptr&, - uint32_t, - &ExchangeStats::hashTransid - > + // Specify hash function to get the product of + // transaction id. This product is obtained by calling + // hashTransid() function. + boost::multi_index::global_fun< + // Hashing function takes shared_ptr or + // shared_ptr as argument. + const boost::shared_ptr&, + // ... and returns uint32 value. + uint32_t, + // ... and here is a reference to it. + &ExchangeStats::hashTransid + > > > > PktList; diff --git a/tests/tools/perfdhcp/test_control.h b/tests/tools/perfdhcp/test_control.h index 36ee55a32f..5994006cea 100644 --- a/tests/tools/perfdhcp/test_control.h +++ b/tests/tools/perfdhcp/test_control.h @@ -108,7 +108,7 @@ public: /// \brief Destriuctor of the socket wrapper class. /// /// Destructor closes all open sockets on all interfaces. - /// TODO: close only the socket being wrapped by this class. + /// \todo close only the socket being wrapped by this class. ~TestControlSocket(); /// \brief Return name of the interface where socket is bound to. @@ -254,7 +254,9 @@ protected: /// \brief Factory function to create IA_NA option. /// /// This factory function creates DHCPv6 IA_NA option instance. - /// \TODO: add support for IA Address options. + /// + /// \todo add support for IA Address options. + /// /// \param u universe (V6 or V4). /// \param type option-type. /// \param buf option-buffer. @@ -323,6 +325,8 @@ protected: /// from the MAC address, this function uses \ref generateMacAddress /// internally to randomize the DUID. /// + /// \todo add support for other types of DUID. + /// /// \param randomized number of bytes randomized. /// \throw isc::BadValue if \ref generateMacAddress throws. /// \return vector representing DUID. diff --git a/tests/tools/perfdhcp/tests/command_options_unittest.cc b/tests/tools/perfdhcp/tests/command_options_unittest.cc index 01f0357a70..70cb77d787 100644 --- a/tests/tools/perfdhcp/tests/command_options_unittest.cc +++ b/tests/tools/perfdhcp/tests/command_options_unittest.cc @@ -86,7 +86,10 @@ protected: // DUID_LLT value, two octets of hardware type, 4 octets // of time value and 6 octets of variable link layer (MAC) // address. - const int duid_llt_size = 14; + const int duid_llt_size = 14; + // DUID is not given from the command line but it is supposed + // to be initialized by the CommandOptions private method + // generateDuidTemplate(). std::vector v2 = opt.getDuidTemplate(); ASSERT_EQ(duid_llt_size, opt.getDuidTemplate().size()); EXPECT_TRUE(std::equal(v2.begin(), v2.begin() + 4, @@ -548,6 +551,9 @@ TEST_F(CommandOptionsTest, Interface) { // In order to make this test portable we need to know // at least one interface name on OS where test is run. // Interface Manager has ability to detect interfaces. + // Altough we don't call initIsInterface explicitely + // here it is called by CommandOptions object interally + // so this function is covered by the test. dhcp::IfaceMgr& iface_mgr = dhcp::IfaceMgr::instance(); const dhcp::IfaceMgr::IfaceCollection& ifaces = iface_mgr.getIfaces(); std::string iface_name; @@ -568,7 +574,7 @@ TEST_F(CommandOptionsTest, Interface) { // exception is expected to be thrown. EXPECT_THROW(process("perfdhcp -4"), isc::InvalidParameter); } -} +} TEST_F(CommandOptionsTest, Server) { CommandOptions& opt = CommandOptions::instance(); From c2fa97e23503ea00f82e0adff38d14473f475da8 Mon Sep 17 00:00:00 2001 From: Marcin Siodelski Date: Tue, 4 Sep 2012 16:39:23 +0200 Subject: [PATCH 27/40] [1959] Implemented changes from the 3rd part of code review. --- tests/tools/perfdhcp/test_control.cc | 118 +++++++---- tests/tools/perfdhcp/test_control.h | 190 +++++++++--------- .../perfdhcp/tests/test_control_unittest.cc | 18 +- 3 files changed, 182 insertions(+), 144 deletions(-) diff --git a/tests/tools/perfdhcp/test_control.cc b/tests/tools/perfdhcp/test_control.cc index 3c0f6d1b11..6e7fa05654 100644 --- a/tests/tools/perfdhcp/test_control.cc +++ b/tests/tools/perfdhcp/test_control.cc @@ -45,14 +45,21 @@ namespace perfdhcp { bool TestControl::interrupted_ = false; -TestControl::TestControlSocket::TestControlSocket(int socket) : - socket_(socket), - addr_("127.0.0.1") { - initSocketData(); +TestControl::TestControlSocket::TestControlSocket(const int socket) : + SocketInfo(socket, asiolink::IOAddress("127.0.0.1"), 0), + ifindex_(0), valid_(true) { + try { + initSocketData(); + } catch (const Exception&) { + valid_ = false; + } } TestControl::TestControlSocket::~TestControlSocket() { - IfaceMgr::instance().closeSockets(); + IfaceMgr::Iface* iface = IfaceMgr::instance().getIface(ifindex_); + if (iface) { + iface->delSocket(sockfd_); + } } void @@ -68,8 +75,7 @@ TestControl::TestControlSocket::initSocketData() { socket_collection.begin(); s != socket_collection.end(); ++s) { - if (s->sockfd_ == socket_) { - iface_ = it->getName(); + if (s->sockfd_ == sockfd_) { ifindex_ = it->getIndex(); addr_ = s->addr_; return; @@ -269,6 +275,7 @@ TestControl::factoryGeneric(Option::Universe u, uint16_t type, OptionPtr TestControl::factoryIana6(Option::Universe, uint16_t, const OptionBuffer& buf) { + // @todo allow different values of T1, T2 and IAID. const uint8_t buf_array[] = { 0, 0, 0, 1, // IAID = 1 0, 0, 3600 >> 8, 3600 && 0xff, // T1 = 3600 @@ -333,7 +340,7 @@ TestControl::generateMacAddress(uint8_t& randomized) const { isc_throw(BadValue, "invalid MAC address template specified"); } uint32_t r = random(); - // The random number must be in the range 0..clients_num. This + // The random number must be in the range 0..clients_num-1. This // will guarantee that every client has exactly one random MAC // address assigned. r %= clients_num; @@ -370,13 +377,31 @@ TestControl::generateDuid(uint8_t& randomized) const { std::vector duid(options.getDuidTemplate()); // @todo: add support for DUIDs of different sizes. std::vector mac_addr(generateMacAddress(randomized)); - duid.resize(duid.size() - mac_addr.size()); - for (int i = 0; i < mac_addr.size(); ++i) { - duid.push_back(mac_addr[i]); - } + duid.resize(duid.size()); + std::copy(mac_addr.begin(), mac_addr.end(), + duid.begin() + duid.size() - mac_addr.size()); return (duid); } +template +uint32_t +TestControl::getElapsedTime(const T& pkt1, const T& pkt2) { + using namespace boost::posix_time; + ptime pkt1_time = pkt1->getTimestamp(); + ptime pkt2_time = pkt2->getTimestamp(); + if (pkt1_time.is_not_a_date_time() || + pkt2_time.is_not_a_date_time()) { + isc_throw(InvalidOperation, "packet timestamp not set");; + } + time_period elapsed_period(pkt1_time, pkt2_time); + if (elapsed_period.is_null()) { + isc_throw(InvalidOperation, "unable to calculate time elapsed" + " between packets"); + } + return(elapsed_period.length().total_milliseconds()); +} + + uint64_t TestControl::getNextExchangesNum() const { CommandOptions& options = CommandOptions::instance(); @@ -399,7 +424,9 @@ TestControl::getNextExchangesNum() const { // of exchanges to be initiated. due_exchanges = static_cast(due_factor * options.getRate()); // We want to make sure that at least one packet goes out. - due_exchanges += 1; + if (due_exchanges == 0) { + due_exchanges = 1; + } // We should not exceed aggressivity as it could have been // restricted from command line. if (due_exchanges > options.getAggressivity()) { @@ -441,7 +468,7 @@ TestControl::getTemplateBuffer(const size_t idx) const { if (template_buffers_.size() > idx) { return (template_buffers_[idx]); } - return (TemplateBuffer()); + isc_throw(OutOfRange, "invalid buffer index"); } void @@ -455,8 +482,7 @@ TestControl::initPacketTemplates() { CommandOptions& options = CommandOptions::instance(); std::vector template_files = options.getTemplateFiles(); for (std::vector::const_iterator it = template_files.begin(); - it != template_files.end(); - ++it) { + it != template_files.end(); ++it) { readPacketTemplate(*it); } } @@ -738,10 +764,13 @@ TestControl::receivePacket6(const TestControlSocket& socket, CommandOptions::ExchangeMode xchg_mode = CommandOptions::instance().getExchangeMode(); if ((xchg_mode == CommandOptions::DORA_SARR) && solicit_pkt6) { + // \todo check whether received ADVERTISE packet is sane. + // We might want to check if STATUS_CODE option is non-zero + // and if there is IAADR option in IA_NA. if (template_buffers_.size() < 2) { - sendRequest6(socket, solicit_pkt6, pkt6); + sendRequest6(socket, pkt6); } else { - sendRequest6(socket, template_buffers_[1], solicit_pkt6, pkt6); + sendRequest6(socket, template_buffers_[1], pkt6); } } } else if (packet_type == DHCPV6_REPLY) { @@ -880,16 +909,25 @@ TestControl::run() { "command options must be parsed before running a test"); } - // Diagnostics is command line options mainly. + // Diagnostics are command line options mainly. printDiagnostics(); // Option factories have to be registered. registerOptionFactories(); TestControlSocket socket(openSocket()); + if (!socket.valid_) { + isc_throw(Unexpected, "invalid socket descriptor"); + } // Initialize packet templates. initPacketTemplates(); // Initialize randomization seed. if (options.isSeeded()) { srandom(options.getSeed()); + } else { + // Seed with current time. + time_period duration(from_iso_string("20111231T235959"), + microsec_clock::universal_time()); + srandom(duration.length().total_seconds() + + duration.length().fractional_seconds()); } // If user interrupts the program we will exit gracefully. signal(SIGINT, TestControl::handleInterrupt); @@ -898,10 +936,12 @@ TestControl::run() { for (int i = 0; i < options.getPreload(); ++i) { if (options.getIpVersion() == 4) { // No template buffer means no -T option specified. - // We will build packet ourselfs. + // We will build packet ourselves. if (template_buffers_.size() == 0) { sendDiscover4(socket, do_preload); } else { + // Pick template #0 if Discover is being sent. + // For Request it would be #1. const uint8_t template_idx = 0; sendDiscover4(socket, template_buffers_[template_idx], do_preload); @@ -912,6 +952,8 @@ TestControl::run() { if (template_buffers_.size() == 0) { sendSolicit6(socket, do_preload); } else { + // Pick template #0 if Solicit is being sent. + // For Request it would be #1. const uint8_t template_idx = 0; sendSolicit6(socket, template_buffers_[template_idx], do_preload); @@ -1187,7 +1229,7 @@ TestControl::sendRequest4(const TestControlSocket& socket, std::vector mac_address(chaddr, chaddr + HW_ETHER_LEN); pkt4->writeAt(rand_offset, mac_address.begin(), mac_address.end()); - // Set elapsed time. + // Set elapsed time. size_t elp_offset = 0; if (options.getElapsedTimeOffset() > 0) { elp_offset = options.getElapsedTimeOffset(); @@ -1266,16 +1308,12 @@ TestControl::sendRequest4(const TestControlSocket& socket, void TestControl::sendRequest6(const TestControlSocket& socket, - const Pkt6Ptr& solicit_pkt6, const Pkt6Ptr& advertise_pkt6) { const uint32_t transid = generateTransid(); Pkt6Ptr pkt6(new Pkt6(DHCPV6_REQUEST, transid)); // Set elapsed time. - const uint32_t elapsed_time = - getElapsedTime(solicit_pkt6, advertise_pkt6); OptionPtr opt_elapsed_time = Option::factory(Option::V6, D6O_ELAPSED_TIME); - opt_elapsed_time->setUint16(static_cast(elapsed_time / 10)); pkt6->addOption(opt_elapsed_time); // Set client id. OptionPtr opt_clientid = advertise_pkt6->getOption(D6O_CLIENTID); @@ -1323,7 +1361,6 @@ TestControl::sendRequest6(const TestControlSocket& socket, void TestControl::sendRequest6(const TestControlSocket& socket, const std::vector& template_buf, - const Pkt6Ptr& solicit_pkt6, const Pkt6Ptr& advertise_pkt6) { CommandOptions& options = CommandOptions::instance(); // Get the second argument if multiple the same arguments specified @@ -1343,12 +1380,9 @@ TestControl::sendRequest6(const TestControlSocket& socket, if (options.getElapsedTimeOffset() > 0) { elp_offset = options.getElapsedTimeOffset(); } - uint32_t elapsed_time = - getElapsedTime(solicit_pkt6, advertise_pkt6); boost::shared_ptr opt_elapsed_time(new LocalizedOption(Option::V6, D6O_ELAPSED_TIME, OptionBuffer(), elp_offset)); - opt_elapsed_time->setUint16(static_cast(elapsed_time / 10)); pkt6->addOption(opt_elapsed_time); // Get the actual server id offset. @@ -1537,9 +1571,13 @@ TestControl::setDefaults4(const TestControlSocket& socket, const Pkt4Ptr& pkt) { CommandOptions& options = CommandOptions::instance(); // Interface name. - pkt->setIface(socket.getIface()); + IfaceMgr::Iface* iface = IfaceMgr::instance().getIface(socket.ifindex_); + if (iface == NULL) { + isc_throw(BadValue, "unable to find interface with given index"); + } + pkt->setIface(iface->getName()); // Interface index. - pkt->setIndex(socket.getIfIndex()); + pkt->setIndex(socket.ifindex_); // Local client's port (68) pkt->setLocalPort(DHCP4_CLIENT_PORT); // Server's port (67) @@ -1547,9 +1585,9 @@ TestControl::setDefaults4(const TestControlSocket& socket, // The remote server's name or IP. pkt->setRemoteAddr(IOAddress(options.getServerName())); // Set local addresss. - pkt->setLocalAddr(IOAddress(socket.getAddress())); + pkt->setLocalAddr(IOAddress(socket.addr_)); // Set relay (GIADDR) address to local address. - pkt->setGiaddr(IOAddress(socket.getAddress())); + pkt->setGiaddr(IOAddress(socket.addr_)); // Pretend that we have one relay (which is us). pkt->setHops(1); } @@ -1559,15 +1597,19 @@ TestControl::setDefaults6(const TestControlSocket& socket, const Pkt6Ptr& pkt) { CommandOptions& options = CommandOptions::instance(); // Interface name. - pkt->setIface(socket.getIface()); + IfaceMgr::Iface* iface = IfaceMgr::instance().getIface(socket.ifindex_); + if (iface == NULL) { + isc_throw(BadValue, "unable to find interface with given index"); + } + pkt->setIface(iface->getName()); // Interface index. - pkt->setIndex(socket.getIfIndex()); + pkt->setIndex(socket.ifindex_); // Local client's port (547) pkt->setLocalPort(DHCP6_CLIENT_PORT); // Server's port (548) pkt->setRemotePort(DHCP6_SERVER_PORT); // Set local address. - pkt->setLocalAddr(socket.getAddress()); + pkt->setLocalAddr(socket.addr_); // The remote server's name or IP. pkt->setRemoteAddr(IOAddress(options.getServerName())); } @@ -1606,6 +1648,10 @@ TestControl::updateSendDue() { send_due_ = last_sent_ + time_duration(0, 0, 0, duration); // Check if it is already due. ptime now(microsec_clock::universal_time()); + // \todo verify if this condition is not too tight. In other words + // verify if this will not produce too many late sends. + // We might want to look at this once we are done implementing + // microsecond timeouts in IfaceMgr. if (now > send_due_) { if (testDiags('i')) { if (options.getIpVersion() == 4) { diff --git a/tests/tools/perfdhcp/test_control.h b/tests/tools/perfdhcp/test_control.h index 5994006cea..cf83113daf 100644 --- a/tests/tools/perfdhcp/test_control.h +++ b/tests/tools/perfdhcp/test_control.h @@ -23,6 +23,7 @@ #include #include +#include #include #include #include @@ -48,7 +49,7 @@ namespace perfdhcp { class TestControl : public boost::noncopyable { public: - /// Default transaction id offset. + /// Default transaction id offset. static const size_t DHCPV4_TRANSID_OFFSET = 4; /// Default offset of MAC's last octet. static const size_t DHCPV4_RANDOMIZATION_OFFSET = 35; @@ -84,55 +85,38 @@ public: /// Packet template buffers list. typedef std::vector TemplateBufferCollection; - /// \brief Socket wrapper class. + /// \brief Socket wrapper structure. /// - /// This is wrapper class that holds descriptor of the socket - /// used to run DHCP test. All sockets created with \ref IfaceMgr - /// are closed in the destructor. This ensures that socket is - /// closed when the function that created the socket ends - /// (normally or when exception occurs). - class TestControlSocket { - public: + /// This is the wrapper that holds descriptor of the socket + /// used to run DHCP test. The wrapped socket is closed in + /// the destructor. This prevents resource leaks when when + /// function that created the socket ends (normally or + /// when exception occurs). This structure extends parent + /// structure with new field ifindex_ that holds interface + /// index where socket is bound to. + struct TestControlSocket : public dhcp::IfaceMgr::SocketInfo { + /// Interface index. + uint16_t ifindex_; + /// Is socket valid. It will not be valid if the provided socket + /// descriptor does not point to valid socket. + bool valid_; /// \brief Constructor of socket wrapper class. /// /// This constructor uses provided socket descriptor to /// find the name of the interface where socket has been - /// bound to. + /// bound to. If provided socket descriptor is invalid then + /// valid_ field is set to false; /// /// \param socket socket descriptor. - /// \throw isc::BadValue if interface for specified - /// socket descriptor does not exist. TestControlSocket(const int socket); /// \brief Destriuctor of the socket wrapper class. /// - /// Destructor closes all open sockets on all interfaces. - /// \todo close only the socket being wrapped by this class. + /// Destructor closes wrapped socket. ~TestControlSocket(); - /// \brief Return name of the interface where socket is bound to. - /// - /// \return name of the interface where socket is bound to. - const std::string& getIface() const { return(iface_); } - - /// \brief Return interface index where socket is bound to. - /// - /// \return index fo the interface where sockert is bound to. - int getIfIndex() const { return(ifindex_); } - - /// \brief Return address where socket is bound to. - /// - /// \return address where socket is bound to. - const asiolink::IOAddress& getAddress() const { return(addr_); } - private: - /// \brief Private default constructor. - /// - /// Default constructor is private to make sure that valid - /// socket descriptor is passed to create object. - TestControlSocket(); - /// \brief Initialize socket data. /// /// This method initializes members of the class that Interface @@ -141,11 +125,6 @@ public: /// \throw isc::BadValue if interface for specified socket /// descriptor does not exist. void initSocketData(); - - int socket_; ///< Socket descirptor. - std::string iface_; ///< Name of the interface. - int ifindex_; ///< Index of the interface. - asiolink::IOAddress addr_; ///< Address bound. }; /// \brief Default transaction id generator class. @@ -169,6 +148,9 @@ public: typedef boost::shared_ptr TransidGeneratorPtr; /// \brief Length of the Ethernet HW address (MAC) in bytes. + /// + /// \todo Make this variable length as there are cases when HW + /// address is longer than this (e.g. 20 bytes). static const uint8_t HW_ETHER_LEN = 6; /// TestControl is a singleton class. This method returns reference @@ -195,22 +177,20 @@ public: transid_gen_ = generator; } -protected: - - // We would really like these methods and members to be private but + // We would really like following methods and members to be private but // they have to be accessible for unit-testing. Another, possibly better, // solution is to make this class friend of test class but this is not // what's followed in other classes. - +protected: /// \brief Default constructor. /// /// Default constructor is protected as the object can be created /// only via \ref instance method. TestControl(); - /// \brief Check if test exit condtitions fulfiled. + /// \brief Check if test exit condtitions fulfilled. /// - /// Method checks if test exit conditions are fulfiled. + /// Method checks if the test exit conditions are fulfiled. /// Exit conditions are checked periodically from the /// main loop. Program should break the main loop when /// this method returns true. It is calling function @@ -227,9 +207,10 @@ protected: /// to length 2 and values will be initialized to zeros. Otherwise /// function will initialize option buffer with values in passed buffer. /// - /// \param u universe (V6 or V4). - /// \param type option-type. - /// \param buf option-buffer. + /// \param u universe (ignored) + /// \param type option-type (ignored). + /// \param buf option-buffer containing option content (2 bytes) or + /// empty buffer if option content has to be set to default (0) value. /// \throw if elapsed time buffer size is neither 2 nor 0. /// \return instance o the option. static dhcp::OptionPtr @@ -244,7 +225,7 @@ protected: /// the buffer contents, size etc. /// /// \param u universe (V6 or V4). - /// \param type option-type. + /// \param type option-type (ignored). /// \param buf option-buffer. /// \return instance o the option. static dhcp::OptionPtr factoryGeneric(dhcp::Option::Universe u, @@ -257,9 +238,9 @@ protected: /// /// \todo add support for IA Address options. /// - /// \param u universe (V6 or V4). - /// \param type option-type. - /// \param buf option-buffer. + /// \param u universe (ignored). + /// \param type option-type (ignored). + /// \param buf option-buffer carrying IANA suboptions. /// \return instance of IA_NA option. static dhcp::OptionPtr factoryIana6(dhcp::Option::Universe u, uint16_t type, @@ -272,9 +253,9 @@ protected: /// - D6O_NAME_SERVERS /// - D6O_DOMAIN_SEARCH /// - /// \param u universe (V6 or V4). - /// \param type option-type. - /// \param buf option-buffer (ignored and should be empty). + /// \param u universe (ignored). + /// \param type option-type (ignored). + /// \param buf option-buffer (ignored). /// \return instance of ORO option. static dhcp::OptionPtr factoryOptionRequestOption6(dhcp::Option::Universe u, @@ -287,9 +268,9 @@ protected: /// The buffer passed to this option must be empty because option does /// not have any payload. /// - /// \param u universe (V6 or V4). - /// \param type option-type. - /// \param buf option-buffer (ignored and should be empty). + /// \param u universe (ignored). + /// \param type option-type (ignored). + /// \param buf option-buffer (ignored). /// \return instance of RAPID_COMMIT option.. static dhcp::OptionPtr factoryRapidCommit6(dhcp::Option::Universe u, uint16_t type, @@ -308,9 +289,9 @@ protected: /// - DHO_DOMAIN_NAME_SERVERS, /// - DHO_HOST_NAME. /// - /// \param u universe (V6 or V4). - /// \param type option-type. - /// \param buf option-buffer (ignored and should be empty). + /// \param u universe (ignored). + /// \param type option-type (ignored). + /// \param buf option-buffer (ignored). /// \return instance o the generic option. static dhcp::OptionPtr factoryRequestList4(dhcp::Option::Universe u, uint16_t type, @@ -319,15 +300,16 @@ protected: /// \brief Generate DUID. /// /// Method generates unique DUID. The number of DUIDs it can generate - /// depends on the number of simulated clinets, which is specified + /// depends on the number of simulated clients, which is specified /// from the command line. It uses \ref CommandOptions object to retrieve - /// number of clinets. Since the last six octets of DUID are constructed + /// number of clients. Since the last six octets of DUID are constructed /// from the MAC address, this function uses \ref generateMacAddress /// internally to randomize the DUID. /// /// \todo add support for other types of DUID. /// - /// \param randomized number of bytes randomized. + /// \param [out] randomized number of bytes randomized (initial value + /// is ignored). /// \throw isc::BadValue if \ref generateMacAddress throws. /// \return vector representing DUID. std::vector generateDuid(uint8_t& randomized) const; @@ -341,7 +323,8 @@ protected: /// Based on this the random value is generated and added to /// the MAC address template (default MAC address). /// - /// \param randomized number of bytes randomized. + /// \param [out] randomized number of bytes randomized (initial + /// value is ignored). /// \throw isc::BadValue if MAC address template (default or specified /// from the command line) has invalid size (expected 6 octets). /// \return generated MAC address. @@ -349,6 +332,9 @@ protected: /// \brief generate transaction id. /// + /// Generate transaction id value (32-bit for DHCPv4, + /// 24-bit for DHCPv6). + /// /// \return generated transaction id. uint32_t generateTransid() { return(transid_gen_->generate()); @@ -361,7 +347,7 @@ protected: /// is based on current time, due time calculated with /// \ref updateSendTime function and expected rate. /// - /// \return number of exchanges to be started immediatelly. + /// \return number of exchanges to be started immediately. uint64_t getNextExchangesNum() const; /// \brief Return template buffer. @@ -369,14 +355,15 @@ protected: /// Method returns template buffer at specified index. /// /// \param idx index of template buffer. - /// \return reference to template buffer or empty buffer if index - /// is out of bounds. + /// \throw isc::OutOfRange if buffer index out of bounds. + /// \return reference to template buffer. TemplateBuffer getTemplateBuffer(const size_t idx) const; /// \brief Reads packet templates from files. /// /// Method iterates through all specified template files, reads - /// their content and stores it in class internal buffers + /// their content and stores it in class internal buffers. Template + /// file names are specified from the command line with -T option. /// /// \throw isc::BadValue if any of the template files does not exist void initPacketTemplates(); @@ -390,13 +377,13 @@ protected: /// \brief Open socket to communicate with DHCP server. /// /// Method opens socket and binds it to local address. Function will - /// can use either interface name, local address or server address + /// use either interface name, local address or server address /// to create a socket, depending on what is available (specified /// from the command line). If socket can't be created for any /// reason, exception is thrown. /// If destination address is broadcast (for DHCPv4) or multicast /// (for DHCPv6) than broadcast or multicast option is set on - /// the socket. + /// the socket. Opened socket is registered and managed by IfaceMgr. /// /// \throw isc::BadValue if socket can't be created for given /// interface, local address or remote address. @@ -432,8 +419,11 @@ protected: /// when OFFER packet arrives, this function will initiate /// REQUEST message to the server. /// - /// \param socket socket to be used. - /// \param pkt4 object representing DHCPv4 packet received. + /// \warning this method does not check if provided socket is + /// valid (specifically if v4 socket for received v4 packet). + /// + /// \param [in] socket socket to be used. + /// \param [in] pkt4 object representing DHCPv4 packet received. /// \throw isc::BadValue if unknown message type received. /// \throw isc::Unexpected if unexpected error occured. void receivePacket4(const TestControlSocket& socket, @@ -446,8 +436,11 @@ protected: /// when ADVERTISE packet arrives, this function will initiate /// REQUEST message to the server. /// - /// \param socket socket to be used. - /// \param pkt6 object representing DHCPv6 packet received. + /// \warning this method does not check if provided socket is + /// valid (specifically if v4 socket for received v4 packet). + /// + /// \param [in] socket socket to be used. + /// \param [in] pkt6 object representing DHCPv6 packet received. /// \throw isc::BadValue if unknown message type received. /// \throw isc::Unexpected if unexpected error occured. void receivePacket6(const TestControlSocket& socket, @@ -460,6 +453,9 @@ protected: /// \ref receivePacket6 depending if DHCPv4 or DHCPv6 packet /// has arrived. /// + /// \warning this method does not check if provided socket is + /// valid. Ensure that it is valid prior to calling it. + /// /// \param socket socket to be used. /// \throw::BadValue if unknown message type received. /// \throw::Unexpected if unexpected error occured. @@ -506,6 +502,8 @@ protected: /// The transaction id and MAC address are randomly generated for /// the message. Range of unique MAC addresses generated depends /// on the number of clients specified from the command line. + /// Copy of sent packet is stored in the stats_mgr4_ object to + /// update statistics. /// /// \param socket socket to be used to send the message. /// \param preload preload mode, packets not included in statistics. @@ -520,6 +518,8 @@ protected: /// template data is exepcted to be in binary format. Provided /// buffer is copied and parts of it are replaced with actual /// data (e.g. MAC address, transaction id etc.). + /// Copy of sent packet is stored in the stats_mgr4_ object to + /// update statistics. /// /// \param socket socket to be used to send the message. /// \param template_buf buffer holding template packet. @@ -532,6 +532,8 @@ protected: /// \brief Send DHCPv4 REQUEST message. /// /// Method creates and sends DHCPv4 REQUEST message to the server. + /// Copy of sent packet is stored in the stats_mgr4_ object to + /// update statistics. /// /// \param socket socket to be used to send message. /// \param discover_pkt4 DISCOVER packet sent. @@ -546,6 +548,8 @@ protected: /// \brief Send DHCPv4 REQUEST message from template. /// /// Method sends DHCPv4 REQUEST message from template. + /// Copy of sent packet is stored in the stats_mgr4_ object to + /// update statistics. /// /// \param socket socket to be used to send message. /// \param template_buf buffer holding template packet. @@ -563,32 +567,28 @@ protected: /// - D6O_ELAPSED_TIME /// - D6O_CLIENTID /// - D6O_SERVERID - /// The elapsed time is calculated based on the duration between - /// sending a SOLICIT and receiving the ADVERTISE packet prior. - /// For this reason both solicit and advertise packet objects have - /// to be passed when calling this function. + /// Copy of sent packet is stored in the stats_mgr6_ object to + /// update statistics. /// /// \param socket socket to be used to send message. - /// \param solicit_pkt6 SOLICIT packet object. /// \param advertise_pkt6 ADVERTISE packet object. /// \throw isc::Unexpected if unexpected error occured. /// \throw isc::InvalidOperation if Statistics Manager has not been /// initialized. void sendRequest6(const TestControlSocket& socket, - const dhcp::Pkt6Ptr& solicit_pkt6, const dhcp::Pkt6Ptr& advertise_pkt6); /// \brief Send DHCPv6 REQUEST message from template. /// /// Method sends DHCPv6 REQUEST message from template. + /// Copy of sent packet is stored in the stats_mgr6_ object to + /// update statistics. /// /// \param socket socket to be used to send message. /// \param template_buf packet template buffer. - /// \param solicit_pkt6 SOLICIT packet object. /// \param advertise_pkt6 ADVERTISE packet object. void sendRequest6(const TestControlSocket& socket, const std::vector& template_buf, - const dhcp::Pkt6Ptr& solicit_pkt6, const dhcp::Pkt6Ptr& advertise_pkt6); /// \brief Send DHCPv6 SOLICIT message. @@ -600,6 +600,8 @@ protected: /// - D6O_CLIENTID, /// - D6O_ORO (Option Request Option), /// - D6O_IA_NA. + /// Copy of sent packet is stored in the stats_mgr6_ object to + /// update statistics. /// /// \param socket socket to be used to send the message. /// \param preload mode, packets not included in statistics. @@ -610,6 +612,8 @@ protected: /// \brief Send DHCPv6 SOLICIT message from template. /// /// Method sends DHCPv6 SOLICIT message from template. + /// Copy of sent packet is stored in the stats_mgr6_ object to + /// update statistics. /// /// \param socket socket to be used to send the message. /// \param template_buf packet template buffer. @@ -648,7 +652,7 @@ protected: void setDefaults6(const TestControlSocket& socket, const dhcp::Pkt6Ptr& pkt); - /// \brief Find of diagnostic flag has been set. + /// \brief Find if diagnostic flag has been set. /// /// \param diag diagnostic flag (a,e,i,s,r,t,T). /// \return true if diagnostics flag has been set. @@ -674,22 +678,10 @@ private: /// \param T Pkt4Ptr or Pkt6Ptr class. /// \param pkt1 first packet. /// \param pkt2 second packet. + /// \throw InvalidOperation if packet timestamps are invalid. /// \return elapsed time in milliseconds between pkt1 and pkt2. template - uint32_t getElapsedTime(const T& pkt1, const T& pkt2) { - using namespace boost::posix_time; - ptime pkt1_time = pkt1->getTimestamp(); - ptime pkt2_time = pkt2->getTimestamp(); - if (pkt1_time.is_not_a_date_time() || - pkt2_time.is_not_a_date_time()) { - return (0); - } - time_period elapsed_period(pkt1_time, pkt2_time); - if (elapsed_period.is_null()) { - return (0); - } - return(elapsed_period.length().total_milliseconds()); - } + uint32_t getElapsedTime(const T& pkt1, const T& pkt2); /// \brief Get number of received packets. /// diff --git a/tests/tools/perfdhcp/tests/test_control_unittest.cc b/tests/tools/perfdhcp/tests/test_control_unittest.cc index 2ea3667ab6..f75c3109d3 100644 --- a/tests/tools/perfdhcp/tests/test_control_unittest.cc +++ b/tests/tools/perfdhcp/tests/test_control_unittest.cc @@ -321,8 +321,8 @@ public: // Use templates files to crate packets. if (use_templates) { tc.initPacketTemplates(); - ASSERT_GT(tc.getTemplateBuffer(0).size(), 0); - ASSERT_GT(tc.getTemplateBuffer(1).size(), 0); + ASSERT_NO_THROW(tc.getTemplateBuffer(0)); + ASSERT_NO_THROW(tc.getTemplateBuffer(1)); } // Incremental transaction id generator will generate @@ -384,8 +384,8 @@ public: // Use templates files to crate packets. if (use_templates) { tc.initPacketTemplates(); - ASSERT_GT(tc.getTemplateBuffer(0).size(), 0); - ASSERT_GT(tc.getTemplateBuffer(1).size(), 0); + ASSERT_NO_THROW(tc.getTemplateBuffer(0)); + ASSERT_NO_THROW(tc.getTemplateBuffer(1)); } // Incremental transaction id generator will generate @@ -720,14 +720,14 @@ TEST_F(TestControlTest, Packet4) { ASSERT_NO_THROW(tc.setDefaults4(sock, pkt4)); // Validate that packet has been setup correctly. EXPECT_EQ(loopback_iface, pkt4->getIface()); - EXPECT_EQ(sock.getIfIndex(), pkt4->getIndex()); + EXPECT_EQ(sock.ifindex_, pkt4->getIndex()); EXPECT_EQ(DHCP4_CLIENT_PORT, pkt4->getLocalPort()); EXPECT_EQ(DHCP4_SERVER_PORT, pkt4->getRemotePort()); EXPECT_EQ(1, pkt4->getHops()); EXPECT_EQ(asiolink::IOAddress("255.255.255.255"), pkt4->getRemoteAddr()); - EXPECT_EQ(asiolink::IOAddress(sock.getAddress()), pkt4->getLocalAddr()); - EXPECT_EQ(asiolink::IOAddress(sock.getAddress()), pkt4->getGiaddr()); + EXPECT_EQ(asiolink::IOAddress(sock.addr_), pkt4->getLocalAddr()); + EXPECT_EQ(asiolink::IOAddress(sock.addr_), pkt4->getGiaddr()); } else { std::cout << "Unable to find the loopback interface. Skip test. " << std::endl; @@ -753,10 +753,10 @@ TEST_F(TestControlTest, Packet6) { ASSERT_NO_THROW(tc.setDefaults6(sock, pkt6)); // Validate if parameters have been set correctly. EXPECT_EQ(loopback_iface, pkt6->getIface()); - EXPECT_EQ(sock.getIfIndex(), pkt6->getIndex()); + EXPECT_EQ(sock.ifindex_, pkt6->getIndex()); EXPECT_EQ(DHCP6_CLIENT_PORT, pkt6->getLocalPort()); EXPECT_EQ(DHCP6_SERVER_PORT, pkt6->getRemotePort()); - EXPECT_EQ(sock.getAddress(), pkt6->getLocalAddr()); + EXPECT_EQ(sock.addr_, pkt6->getLocalAddr()); EXPECT_EQ(asiolink::IOAddress("FF05::1:3"), pkt6->getRemoteAddr()); } else { std::cout << "Unable to find the loopback interface. Skip test. " From 5585299f3e340017f57307bbf9568bb98931279f Mon Sep 17 00:00:00 2001 From: Marcin Siodelski Date: Tue, 4 Sep 2012 17:23:09 +0200 Subject: [PATCH 28/40] [1959] Minor change in checkExitConditions. --- tests/tools/perfdhcp/test_control.cc | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tests/tools/perfdhcp/test_control.cc b/tests/tools/perfdhcp/test_control.cc index 6e7fa05654..446de81c3b 100644 --- a/tests/tools/perfdhcp/test_control.cc +++ b/tests/tools/perfdhcp/test_control.cc @@ -149,7 +149,7 @@ TestControl::checkExitConditions() const { } } // Check if we reached maximum number REQUEST packets. - if (options.getNumRequests().size() == 2) { + if (options.getNumRequests().size() > 1) { if (options.getIpVersion() == 4) { if (stats_mgr4_->getSentPacketsNum(StatsMgr4::XCHG_RA) >= options.getNumRequests()[1]) { @@ -185,7 +185,7 @@ TestControl::checkExitConditions() const { } } // Check if we reached maximum number of drops of ACK/REPLY packets. - if (options.getMaxDrop().size() == 2) { + if (options.getMaxDrop().size() > 1) { if (options.getIpVersion() == 4) { if (stats_mgr4_->getDroppedPacketsNum(StatsMgr4::XCHG_RA) >= options.getMaxDrop()[1]) { @@ -226,7 +226,7 @@ TestControl::checkExitConditions() const { } } // Check if we reached maximum drops percentage of ACK/REPLY packets. - if (options.getMaxDropPercentage().size() == 2) { + if (options.getMaxDropPercentage().size() > 1) { if (options.getIpVersion() == 4) { if ((stats_mgr4_->getSentPacketsNum(StatsMgr4::XCHG_RA) > 10) && ((100. * stats_mgr4_->getDroppedPacketsNum(StatsMgr4::XCHG_RA) / From 7c7d7c9120c5a8a8b7b9d324ee6733a968412750 Mon Sep 17 00:00:00 2001 From: Marcin Siodelski Date: Wed, 5 Sep 2012 17:53:45 +0200 Subject: [PATCH 29/40] [1959] Implemented changes for 4th part of code review. --- tests/tools/perfdhcp/command_options.cc | 4 + tests/tools/perfdhcp/test_control.cc | 27 +- tests/tools/perfdhcp/test_control.h | 72 +++-- .../tests/command_options_unittest.cc | 54 +++- .../perfdhcp/tests/test_control_unittest.cc | 259 +++++++++++++----- 5 files changed, 311 insertions(+), 105 deletions(-) diff --git a/tests/tools/perfdhcp/command_options.cc b/tests/tools/perfdhcp/command_options.cc index dbb01e7d4c..8d9f02e763 100644 --- a/tests/tools/perfdhcp/command_options.cc +++ b/tests/tools/perfdhcp/command_options.cc @@ -495,6 +495,10 @@ CommandOptions::decodeDuid(const std::string& base) { } duid_template.push_back(static_cast(ui)); } + // @todo Get rid of this limitation when we manage add support + // for DUIDs other than LLT. Shorter DUIDs may be useful for + // server testing purposes. + check(duid_template.size() < 6, "DUID must be at least 6 octets long"); // Assign the new duid only if successfully generated. std::swap(duid_template, duid_template_); } diff --git a/tests/tools/perfdhcp/test_control.cc b/tests/tools/perfdhcp/test_control.cc index 446de81c3b..ba8bf252bc 100644 --- a/tests/tools/perfdhcp/test_control.cc +++ b/tests/tools/perfdhcp/test_control.cc @@ -298,9 +298,9 @@ OptionPtr TestControl::factoryOptionRequestOption6(Option::Universe, uint16_t, const OptionBuffer&) { - const uint8_t buf_array[] = { - 0, D6O_NAME_SERVERS, - 0, D6O_DOMAIN_SEARCH + const uint16_t buf_array[] = { + htons(D6O_NAME_SERVERS), + htons(D6O_DOMAIN_SEARCH) }; OptionBuffer buf_with_options(buf_array, buf_array + sizeof(buf_array)); return (OptionPtr(new Option(Option::V6, D6O_ORO, buf_with_options))); @@ -331,7 +331,7 @@ std::vector TestControl::generateMacAddress(uint8_t& randomized) const { CommandOptions& options = CommandOptions::instance(); uint32_t clients_num = options.getClientsNum(); - if ((clients_num == 0) || (clients_num == 1)) { + if (clients_num < 2) { return (options.getMacTemplate()); } // Get the base MAC address. We are going to randomize part of it. @@ -339,11 +339,7 @@ TestControl::generateMacAddress(uint8_t& randomized) const { if (mac_addr.size() != HW_ETHER_LEN) { isc_throw(BadValue, "invalid MAC address template specified"); } - uint32_t r = random(); - // The random number must be in the range 0..clients_num-1. This - // will guarantee that every client has exactly one random MAC - // address assigned. - r %= clients_num; + uint32_t r = macaddr_gen_->generate(); randomized = 0; // Randomize MAC address octets. for (std::vector::iterator it = mac_addr.end() - 1; @@ -889,7 +885,10 @@ TestControl::reset() { last_sent_ = send_due_; last_report_ = send_due_; transid_gen_.reset(); - transid_gen_ = TransidGeneratorPtr(new TransidGenerator()); + // Actual generators will have to be set later on because we need to + // get command line parameters first. + setTransidGenerator(NumberGeneratorPtr()); + setMacAddrGenerator(NumberGeneratorPtr()); first_packet_serverid_.clear(); interrupted_ = false; } @@ -907,8 +906,16 @@ TestControl::run() { if (options.getIpVersion() == 0) { isc_throw(InvalidOperation, "command options must be parsed before running a test"); + } else if (options.getIpVersion() == 4) { + setTransidGenerator(NumberGeneratorPtr(new SequencialGenerator())); + } else { + setTransidGenerator(NumberGeneratorPtr(new SequencialGenerator(0x00FFFFFF))); } + uint32_t clients_num = options.getClientsNum() == 0 ? + 1 : options.getClientsNum(); + setMacAddrGenerator(NumberGeneratorPtr(new SequencialGenerator(clients_num))); + // Diagnostics are command line options mainly. printDiagnostics(); // Option factories have to be registered. diff --git a/tests/tools/perfdhcp/test_control.h b/tests/tools/perfdhcp/test_control.h index cf83113daf..69dde67fc5 100644 --- a/tests/tools/perfdhcp/test_control.h +++ b/tests/tools/perfdhcp/test_control.h @@ -127,25 +127,51 @@ public: void initSocketData(); }; - /// \brief Default transaction id generator class. + /// \brief Number generator class. /// - /// This is default transaction id generator class. The member - /// function is used to generate unique transaction id value. - /// Other generator classes should derive from this one to - /// override the standard generation algorithm (e.g. unit tests - /// override this class wih algorithm that produces more predictable - /// transaction id values). - class TransidGenerator { + /// This is default numbers generator class. The member function is + /// used to generate uint32_t values. Other generator classes should + /// derive from this one to implement generation algorithms + /// (e.g. sequencial or based on random function). + class NumberGenerator { public: - /// \brief generate transaction id. + /// \brief Generate number. /// - /// \return generated transazction id value. - virtual uint32_t generate() { - return static_cast(random() % 0x00FFFFFF); - } + /// \return Generate number. + virtual uint32_t generate() = 0; }; - typedef boost::shared_ptr TransidGeneratorPtr; + /// The default generator pointer. + typedef boost::shared_ptr NumberGeneratorPtr; + + /// \brief Sequencial numbers generatorc class. + class SequencialGenerator : public NumberGenerator { + public: + /// \Constructor. + /// + /// \param range maximum number generated. If 0 is given then + /// range defaults to maximym uint32_t value. + SequencialGenerator(uint32_t range = 0xFFFFFFFF) : + NumberGenerator(), + num_(0), + range_(range) { + if (range_ == 0) { + range_ = 0xFFFFFFFF; + } + } + + /// \brief Generate number sequencialy. + /// + /// \return generated number. + virtual uint32_t generate() { + uint32_t num = num_; + num_ = (num_ + 1) % range_; + return (num); + } + private: + uint32_t num_; ///< Current number. + uint32_t range_; ///< Maximum number generated. + }; /// \brief Length of the Ethernet HW address (MAC) in bytes. /// @@ -172,11 +198,22 @@ public: /// \brief Set new transaction id generator. /// /// \param generator generator object to be used. - void setTransidGenerator(TransidGeneratorPtr& generator) { + void setTransidGenerator(const NumberGeneratorPtr& generator) { transid_gen_.reset(); transid_gen_ = generator; } + /// \brief Set new MAC address generator. + /// + /// Set numbers generator that will be used to generate various + /// MAC addresses to simulate number of clients. + /// + /// \param generator object to be used. + void setMacAddrGenerator(const NumberGeneratorPtr& generator) { + macaddr_gen_.reset(); + macaddr_gen_ = generator; + } + // We would really like following methods and members to be private but // they have to be accessible for unit-testing. Another, possibly better, // solution is to make this class friend of test class but this is not @@ -337,7 +374,7 @@ protected: /// /// \return generated transaction id. uint32_t generateTransid() { - return(transid_gen_->generate()); + return (transid_gen_->generate()); } /// \brief Returns number of exchanges to be started. @@ -738,7 +775,8 @@ private: StatsMgr4Ptr stats_mgr4_; ///< Statistics Manager 4. StatsMgr6Ptr stats_mgr6_; ///< Statistics Manager 6. - TransidGeneratorPtr transid_gen_; ///< Transaction id generator. + NumberGeneratorPtr transid_gen_; ///< Transaction id generator. + NumberGeneratorPtr macaddr_gen_; ///< Numbers generator for MAC address. /// Buffer holiding server id received in first packet dhcp::OptionBuffer first_packet_serverid_; diff --git a/tests/tools/perfdhcp/tests/command_options_unittest.cc b/tests/tools/perfdhcp/tests/command_options_unittest.cc index 70cb77d787..0481d28da3 100644 --- a/tests/tools/perfdhcp/tests/command_options_unittest.cc +++ b/tests/tools/perfdhcp/tests/command_options_unittest.cc @@ -264,39 +264,65 @@ TEST_F(CommandOptionsTest, ClientsNum) { TEST_F(CommandOptionsTest, Base) { CommandOptions& opt = CommandOptions::instance(); - process("perfdhcp -6 -b MAC=10::20::30::40::50::60 " - "-l ethx -b duiD=1AB7F5670901FF all"); uint8_t mac[6] = {0x10, 0x20, 0x30, 0x40, 0x50, 0x60 }; uint8_t duid[14] = { 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x10, 0x11, 0x1F, 0x14 }; - - // Test Mac + // Test DUID and MAC together. + EXPECT_NO_THROW(process("perfdhcp -b DUID=0101010101010101010110111F14" + " -b MAC=10::20::30::40::50::60" + " -l 127.0.0.1 all")); std::vector v1 = opt.getMacTemplate(); - ASSERT_EQ(6, v1.size()); + std::vector v2 = opt.getDuidTemplate(); + v2 = opt.getDuidTemplate(); EXPECT_TRUE(std::equal(v1.begin(), v1.end(), mac)); - // "3x" is invalid value in MAC address - EXPECT_THROW(process("perfdhcp -b mac=10::2::3x::4::5::6 -l ethx all"), - isc::InvalidParameter); - - // Test DUID + EXPECT_TRUE(std::equal(v2.begin(), v2.end(), duid)); + // Test valid DUID. EXPECT_NO_THROW( process("perfdhcp -b duid=0101010101010101010110111F14 -l 127.0.0.1 all") ); - std::vector v2 = opt.getDuidTemplate(); + ASSERT_EQ(sizeof(duid) / sizeof(uint8_t), v2.size()); EXPECT_TRUE(std::equal(v2.begin(), v2.end(), duid)); + // Test mix of upper/lower case letters. + EXPECT_NO_THROW(process("perfdhcp -b DuiD=0101010101010101010110111F14" + " -b Mac=10::20::30::40::50::60" + " -l 127.0.0.1 all")); + v1 = opt.getMacTemplate(); + v2 = opt.getDuidTemplate(); + EXPECT_TRUE(std::equal(v1.begin(), v1.end(), mac)); + EXPECT_TRUE(std::equal(v2.begin(), v2.end(), duid)); + // Use "ether" instead of "mac". + EXPECT_NO_THROW(process("perfdhcp -b ether=10::20::30::40::50::60" + " -l 127.0.0.1 all")); + v1 = opt.getMacTemplate(); + EXPECT_TRUE(std::equal(v1.begin(), v1.end(), mac)); + // Use "ETHER" in upper case. + EXPECT_NO_THROW(process("perfdhcp -b ETHER=10::20::30::40::50::60" + " -l 127.0.0.1 all")); + v1 = opt.getMacTemplate(); + EXPECT_TRUE(std::equal(v1.begin(), v1.end(), mac)); // "t" is invalid character in DUID EXPECT_THROW(process("perfdhcp -6 -l ethx -b " - "duiD=010101010101010101t110111F14 all"), + "duid=010101010101010101t110111F14 all"), + isc::InvalidParameter); + // "3x" is invalid value in MAC address + EXPECT_THROW(process("perfdhcp -b mac=10::2::3x::4::5::6 -l ethx all"), isc::InvalidParameter); - - // Some more negative test cases // Base is not specified EXPECT_THROW(process("perfdhcp -b -l ethx all"), isc::InvalidParameter); // Typo: should be mac= instead of mc= EXPECT_THROW(process("perfdhcp -l ethx -b mc=00:01:02:03::04:05 all"), isc::InvalidParameter); + // Too short DUID (< 6). + EXPECT_THROW(process("perfdhcp -l ethx -b duid=00010203 all"), + isc::InvalidParameter); + // Odd number of digits. + EXPECT_THROW(process("perfdhcp -l ethx -b duid=000102030405060 all"), + isc::InvalidParameter); + // Too short MAC (!= 6). + EXPECT_THROW(process("perfdhcp -l ethx -b mac=00:01:02:04 all"), + isc::InvalidParameter); } TEST_F(CommandOptionsTest, DropTime) { diff --git a/tests/tools/perfdhcp/tests/test_control_unittest.cc b/tests/tools/perfdhcp/tests/test_control_unittest.cc index f75c3109d3..f94f6f73de 100644 --- a/tests/tools/perfdhcp/tests/test_control_unittest.cc +++ b/tests/tools/perfdhcp/tests/test_control_unittest.cc @@ -15,6 +15,7 @@ #include #include #include +#include #include #include @@ -45,11 +46,11 @@ public: /// the default transaction id generator that generates transaction /// ids using random function. This generator will generate values /// like: 1,2,3 etc. - class IncrementalGenerator : public TestControl::TransidGenerator { + class IncrementalGenerator : public TestControl::NumberGenerator { public: /// \brief Default constructor. IncrementalGenerator() : - TransidGenerator(), + NumberGenerator(), transid_(0) { } @@ -88,7 +89,11 @@ public: using TestControl::setDefaults4; using TestControl::setDefaults6; - NakedTestControl() : TestControl() { }; + NakedTestControl() : TestControl() { + uint32_t clients_num = CommandOptions::instance().getClientsNum() == 0 ? + 1 : CommandOptions::instance().getClientsNum(); + setMacAddrGenerator(NumberGeneratorPtr(new TestControl::SequencialGenerator(clients_num))); + }; }; @@ -109,9 +114,27 @@ public: /// \brief Default Constructor TestControlTest() { } - static uint32_t generateTransidIncremental() { - static uint32_t transid(1); - return (++transid); + /// \brief Create packet template file from binary data. + /// + /// Function creates file containing data from the provided buffer + /// in hexadecimal format. + /// \param filename template file to be created. + /// \param buffer with binary datato be stored in file. + /// \return true if file creation successful. + bool createTemplateFile(const std::string& filename, + const std::vector& buf) const { + std::ofstream temp_file; + temp_file.open(filename.c_str(), ios::out | ios::trunc); + if (!temp_file.is_open()) { + return (false); + } + for (int i = 0; i < buf.size(); ++i) { + int first_digit = buf[i] / 16; + int second_digit = buf[i] % 16; + temp_file << std::hex << first_digit << second_digit << std::dec; + } + temp_file.close(); + return (true); } /// \brief Get local loopback interface name. @@ -130,14 +153,8 @@ public: for (IfaceMgr::IfaceCollection::const_iterator iface = ifaces.begin(); iface != ifaces.end(); ++iface) { - for (IfaceMgr::AddressCollection::const_iterator addr_it = - iface->getAddresses().begin(); - addr_it != iface->getAddresses().end(); - ++addr_it) { - if (asiolink::IOAddress("127.0.0.1").getAddress() == - addr_it->getAddress()) { - return (iface->getName()); - } + if (iface->flag_loopback_) { + return (iface->getName()); } } return (""); @@ -147,7 +164,7 @@ public: /// /// This method iterates through options provided in the buffer /// and matches them with the options specified with first parameter. - /// Options in both vectors may be layed in different order. + /// Options in both vectors may be laid in different order. /// /// \param requested_options reference buffer with options. /// \param buf test buffer with options that will be matched. @@ -168,6 +185,38 @@ public: return (matched_num); } + /// \brief Match requested DHCPv6 options in the buffer with given list. + /// + /// This method iterates through options provided in the buffer and + /// matches them with the options specified with first parameter. + /// Options in both vectors ma be laid in different order. + /// + /// \param requested_options reference buffer with options. + /// \param buf test buffer with options that will be matched. + /// \return number of options from the buffer matched with options in + /// the reference buffer or -1 if error occured. + int matchRequestedOptions6(const dhcp::OptionBuffer& requested_options, + const dhcp::OptionBuffer& buf) const { + // Sanity check. + if ((requested_options.size() % 2 != 0) || + (buf.size() % 2 != 0)) { + return -1; + } + size_t matched_num = 0; + for (size_t i = 0; i < buf.size(); i += 2) { + for (int j = 0; j < requested_options.size(); j += 2) { + uint16_t opt_i = buf[i + 1] << 8 + buf[i]; + uint16_t opt_j = buf[j + 1] << 8 + buf[j]; + if (opt_i == opt_j) { + // Requested option has been found. + ++matched_num; + break; + } + } + } + return (matched_num); + } + /// \brief Calculate the maximum vectors' mismatch position. /// /// This helper function calculates the maximum mismatch position @@ -180,22 +229,20 @@ public: /// on two last positions so the returned value will be 2 and so on. /// /// \param clients_num number of simulated clinets - /// \param randomization range - for either MAC or DUID it is 6 /// \return maximum mismatch position - int unequalOctetPosition(const int clients_num, - const size_t random_size) const { - int unequal_pos = 0; - int n = clients_num - 1; - if (n > 0) { - for (int i = 0; i < random_size; ++i) { - if (n < 256) { - unequal_pos = i + 1; - break; - } - n %= 256; - } + int unequalOctetPosition(int clients_num) const { + if (!clients_num) { + return (0); } - return (unequal_pos); + clients_num--; + + int cnt = 0; + while (clients_num) { + clients_num >>= 8; + ++cnt; + } + + return (cnt); } /// brief Test generation of mulitple DUIDs @@ -212,32 +259,32 @@ public: /// and do not change if single client is simulated. void testDuid() const { int clients_num = CommandOptions::instance().getClientsNum(); + // Initialize Test Control class. + NakedTestControl tc; // The old duid will be holding the previously generated DUID. // It will be used to compare against the new one. If we have // multiple clients we want to make sure that duids differ. - Duid old_duid(CommandOptions::instance().getDuidTemplate()); + uint8_t randomized = 0; + Duid old_duid(tc.generateDuid(randomized)); Duid new_duid(0); // total_dist shows the total difference between generated duid. // It has to be greater than zero if multiple clients are simulated. size_t total_dist = 0; + // Number of unique DUIDs. + size_t unique_duids = 0; // Holds the position if the octet on which two DUIDS can be different. // If number of clients is 256 or less it is last DUID octet (except for // single client when subsequent DUIDs have to be equal). If number of // clients is between 257 and 65536 the last two octets can differ etc. - const size_t mac_addr_size = 6; - int unequal_pos = unequalOctetPosition(clients_num, mac_addr_size); - - // Initialize Test Control class. - NakedTestControl tc; + int unequal_pos = unequalOctetPosition(clients_num); + // Keep generated DUIDs in this container. + std::list > duids; // Perform number of iterations to generate number of DUIDs. - // If single clinet is involved, try multiple times (10) and - // see if the same DUID is always generated. - for (int i = 0; i < clients_num * 10; ++i) { + for (int i = 0; i < 10 * clients_num; ++i) { if (new_duid.empty()) { new_duid = old_duid; } else { std::swap(old_duid, new_duid); - uint8_t randomized = 0; new_duid = tc.generateDuid(randomized); } // The DUID-LLT is expected to start with DUID_LLT value @@ -259,8 +306,8 @@ public: uint32_t duid_time = 0; // Pick 4 bytes of the time from generated DUID and put them // in reverse order (in DUID they are stored in network order). - for (int i = 4; i < 8; ++i) { - duid_time |= new_duid[i] << (i - 4); + for (int j = 4; j < 8; ++j) { + duid_time |= new_duid[j] << (j - 4); } // Calculate the duration since epoch time. ptime now = microsec_clock::universal_time(); @@ -288,11 +335,35 @@ public: // Mismatch may have occured on the DUID octet position // up to calculated earlier unequal_pos. ASSERT_LE(mismatch_dist, unequal_pos); + // unique will inform if tested DUID is unique. + bool unique = true; + for (std::list >::const_iterator it = + duids.begin(); + it != duids.end(); ++it) { + // DUIDs should be of the same size if we want to compare them. + ASSERT_EQ(new_duid.size(), it->size()); + // Check if DUID is unique. + if (std::equal(new_duid.begin(), new_duid.end(), it->begin())) { + unique = false; + } + } + // Expecting that DUIDs will be unique only when + // first clients-num iterations is performed. + // After that, DUIDs become non unique. + if (unique) { + ++unique_duids; + } + // For number of iterations equal to clients_num,2*clients_num + // 3*clients_num ... we have to have number of unique duids + // equal to clients_num. + if ((i != 0) && (i % clients_num == 0)) { + ASSERT_EQ(clients_num, unique_duids); + } + // Remember generated DUID. + duids.push_back(new_duid); } // If we have more than one client at least one mismatch occured. - if (clients_num > 1) { - EXPECT_GT(total_dist, 0); - } else { + if (clients_num < 2) { EXPECT_EQ(0, total_dist); } } @@ -327,10 +398,10 @@ public: // Incremental transaction id generator will generate // predictable values of transaction id for each iteration. - // This is important because we need to simulate reponses + // This is important because we need to simulate responses // from the server and use the same transaction ids as in // packets sent by client. - TestControl::TransidGeneratorPtr + TestControl::NumberGeneratorPtr generator(new NakedTestControl::IncrementalGenerator()); tc.setTransidGenerator(generator); // Socket is needed to send packets through the interface. @@ -393,7 +464,7 @@ public: // This is important because we need to simulate reponses // from the server and use the same transaction ids as in // packets sent by client. - TestControl::TransidGeneratorPtr + TestControl::NumberGeneratorPtr generator(new NakedTestControl::IncrementalGenerator()); tc.setTransidGenerator(generator); // Socket is needed to send packets through the interface. @@ -445,10 +516,14 @@ public: // octet (except for single client when subsequent MAC addresses // have to be equal). If number of clients is between 257 and 65536 // the last two octets can differ etc. - int unequal_pos = unequalOctetPosition(clients_num, old_mac.size());; - + int unequal_pos = unequalOctetPosition(clients_num); + // Number of unique MACs. + size_t unique_macs = 0; + // Initialize Test Controller. NakedTestControl tc; size_t total_dist = 0; + // Keep generated MACs in this container. + std::list > macs; // Do many iterations to generate and test MAC address values. for (int i = 0; i < clients_num * 10; ++i) { // Generate new MAC address. @@ -470,11 +545,35 @@ public: // Mismatch may have occured on the MAC address'es octet position // up to calculated earlier unequal_pos. ASSERT_LE(mismatch_dist, unequal_pos); + // unique will inform if tested DUID is unique. + bool unique = true; + for (std::list >::const_iterator it = + macs.begin(); + it != macs.end(); ++it) { + // MACs should be of the same size if we want to compare them. + ASSERT_EQ(new_mac.size(), it->size()); + // Check if MAC is unique. + if (std::equal(new_mac.begin(), new_mac.end(), it->begin())) { + unique = false; + } + } + // Expecting that MACs will be unique only when + // first clients-num iterations is performed. + // After that, MACs become non unique. + if (unique) { + ++unique_macs; + } + // For number of iterations equal to clients_num,2*clients_num + // 3*clients_num ... we have to have number of unique MACs + // equal to clients_num. + if ((i != 0) && (i % clients_num == 0)) { + ASSERT_EQ(clients_num, unique_macs); + } + // Remember generated MAC. + macs.push_back(new_mac); + } - // If we have more than one client at least one mismatch occured. - if (clients_num > 1) { - EXPECT_GT(total_dist, 0); - } else { + if (clients_num < 2) { EXPECT_EQ(total_dist, 0); } } @@ -489,6 +588,10 @@ public: } private: + /// \brief Create DHCPv4 OFFER packet. + /// + /// \param transid transaction id. + /// \return instance of the packet. boost::shared_ptr createOfferPkt4(uint32_t transid) const { boost::shared_ptr offer(new Pkt4(DHCPOFFER, transid)); @@ -504,6 +607,10 @@ private: return (offer); } + /// \brief Create DHCPv6 ADVERTISE packet. + /// + /// \param transid transaction id. + /// \return instance of the packet. boost::shared_ptr createAdvertisePkt6(uint32_t transid) const { OptionPtr opt_ia_na = Option::factory(Option::V6, D6O_IA_NA); @@ -664,10 +771,12 @@ TEST_F(TestControlTest, Options6) { // Validate the D6O_ORO (Option Request Option). OptionPtr opt_oro(Option::factory(Option::V6, D6O_ORO)); // Prepare the reference buffer with requested options. - const uint8_t requested_options[] = { - 0, D6O_NAME_SERVERS, - 0, D6O_DOMAIN_SEARCH + const uint16_t requested_options[] = { + htons(D6O_NAME_SERVERS), + htons(D6O_DOMAIN_SEARCH) }; + int requested_options_num = + sizeof(requested_options) / sizeof(requested_options[0]); OptionBuffer requested_options_ref(requested_options, requested_options + sizeof(requested_options)); @@ -677,9 +786,9 @@ TEST_F(TestControlTest, Options6) { // the same for comparison. EXPECT_EQ(requested_options_ref.size(), requested_options_buf.size()); // Check if all options in the buffer are matched with reference buffer. - size_t matched_num = matchRequestedOptions(requested_options_ref, - requested_options_buf); - EXPECT_EQ(sizeof(requested_options), matched_num); + size_t matched_num = matchRequestedOptions6(requested_options_ref, + requested_options_buf); + EXPECT_EQ(requested_options_num, matched_num); // Validate the D6O_IA_NA option. OptionPtr opt_ia_na(Option::factory(Option::V6, D6O_IA_NA)); @@ -698,7 +807,7 @@ TEST_F(TestControlTest, Options6) { EXPECT_TRUE(std::equal(opt_ia_na_ref.begin(), opt_ia_na_ref.end(), opt_ia_na_buf.begin())); - // TODO: Add more tests for IA address options. + // @todo Add more tests for IA address options. } TEST_F(TestControlTest, Packet4) { @@ -826,7 +935,8 @@ TEST_F(TestControlTest, Packet6Exchange) { // Set number of received packets equal to number of iterations. // This simulates no packet drops. bool use_templates = false; - testPkt6Exchange(iterations_num, iterations_num, use_templates, iterations_performed); + testPkt6Exchange(iterations_num, iterations_num, use_templates, + iterations_performed); // Actual number of iterations should be 10. EXPECT_EQ(10, iterations_performed); @@ -841,20 +951,40 @@ TEST_F(TestControlTest, Packet6Exchange) { // then test should be interrupted and actual number of iterations will // be 6. const int received_num = 3; - testPkt6Exchange(iterations_num, received_num, use_templates, iterations_performed); + testPkt6Exchange(iterations_num, received_num, use_templates, + iterations_performed); EXPECT_EQ(6, iterations_performed); } TEST_F(TestControlTest, PacketTemplates) { + std::vector template1(256); + std::string file1("../templates/test1.hex"); + std::vector template2(233); + std::string file2("../templates/test2.hex"); + for (int i = 0; i < template1.size(); ++i) { + template1[i] = static_cast(random() % 256); + } + for (int i = 0; i < template2.size(); ++i) { + template2[i] = static_cast(random() % 256); + } + ASSERT_TRUE(createTemplateFile(file1, template1)); + ASSERT_TRUE(createTemplateFile(file2, template2)); CommandOptions& options = CommandOptions::instance(); NakedTestControl tc; ASSERT_NO_THROW( processCmdLine("perfdhcp -l 127.0.0.1" - " -T ../templates/discover-example.hex" - " -T ../templates/request4-example.hex all") + " -T " + file1 + " -T " + file2 + " all") ); ASSERT_NO_THROW(tc.initPacketTemplates()); + TestControl::TemplateBuffer buf1; + TestControl::TemplateBuffer buf2; + ASSERT_NO_THROW(buf1 = tc.getTemplateBuffer(0)); + ASSERT_NO_THROW(buf2 = tc.getTemplateBuffer(1)); + ASSERT_EQ(template1.size(), buf1.size()); + ASSERT_EQ(template2.size(), buf2.size()); + EXPECT_TRUE(std::equal(template1.begin(), template1.end(), buf1.begin())); + EXPECT_TRUE(std::equal(template2.begin(), template2.end(), buf2.begin())); } TEST_F(TestControlTest, RateControl) { @@ -879,4 +1009,5 @@ TEST_F(TestControlTest, RateControl) { xchgs_num = tc2.getNextExchangesNum(); EXPECT_GT(xchgs_num, 0); EXPECT_LT(xchgs_num, options.getAggressivity()); + // @todo add more thorough checks for rate values. } From 9f8954c665a65233a6c697be3769f02d702a2728 Mon Sep 17 00:00:00 2001 From: Marcin Siodelski Date: Wed, 5 Sep 2012 18:00:13 +0200 Subject: [PATCH 30/40] [1959] Fixed doxygen typos in test_control.h. --- tests/tools/perfdhcp/test_control.h | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/tests/tools/perfdhcp/test_control.h b/tests/tools/perfdhcp/test_control.h index 69dde67fc5..8e492af133 100644 --- a/tests/tools/perfdhcp/test_control.h +++ b/tests/tools/perfdhcp/test_control.h @@ -39,11 +39,11 @@ namespace perfdhcp { /// test end to end. /// /// Option factory functions are registered using -/// \ref LibDHCP::OptionFactoryRegister. Registered factory functions +/// \ref dhcp::LibDHCP::OptionFactoryRegister. Registered factory functions /// provide a way to create options of the same type in the same way. /// When new option instance is needed the corresponding factory /// function is called to create it. This is done by calling -/// \ref Option::factory with DHCP message type specified as one of +/// \ref dhcp::Option::factory with DHCP message type specified as one of /// parameters. Some of the parameters passed to factory function /// may be ignored (e.g. option buffer). class TestControl : public boost::noncopyable { @@ -147,7 +147,7 @@ public: /// \brief Sequencial numbers generatorc class. class SequencialGenerator : public NumberGenerator { public: - /// \Constructor. + /// \brief Constructor. /// /// \param range maximum number generated. If 0 is given then /// range defaults to maximym uint32_t value. @@ -382,7 +382,7 @@ protected: /// Method returns number of new exchanges to be started as soon /// as possible to satisfy expected rate. Calculation used here /// is based on current time, due time calculated with - /// \ref updateSendTime function and expected rate. + /// \ref updateSendDue function and expected rate. /// /// \return number of exchanges to be started immediately. uint64_t getNextExchangesNum() const; @@ -494,15 +494,15 @@ protected: /// valid. Ensure that it is valid prior to calling it. /// /// \param socket socket to be used. - /// \throw::BadValue if unknown message type received. - /// \throw::Unexpected if unexpected error occured. + /// \throw isc::BadValue if unknown message type received. + /// \throw isc::Unexpected if unexpected error occured. void receivePackets(const TestControlSocket& socket); /// \brief Register option factory functions for DHCPv4 /// /// Method registers option factory functions for DHCPv4. /// These functions are called to create instances of DHCPv4 - /// options. Call \ref Option::factory to invoke factory + /// options. Call \ref dhcp::Option::factory to invoke factory /// function for particular option. Don't use this function directly. /// Use \ref registerOptionFactories instead. void registerOptionFactories4() const; @@ -511,7 +511,7 @@ protected: /// /// Method registers option factory functions for DHCPv6. /// These functions are called to create instances of DHCPv6 - /// options. Call \ref Option::factory to invoke factory + /// options. Call \ref dhcp::Option::factory to invoke factory /// function for particular option. Don't use this function directly. /// Use \ref registerOptionFactories instead. void registerOptionFactories6() const; @@ -535,7 +535,7 @@ protected: /// with the following options: /// - MESSAGE_TYPE set to DHCPDISCOVER /// - PARAMETER_REQUEST_LIST with the same list of requested options - /// as described in \ref factoryRequestList. + /// as described in \ref factoryRequestList4. /// The transaction id and MAC address are randomly generated for /// the message. Range of unique MAC addresses generated depends /// on the number of clients specified from the command line. From fe103d1a3845a08dbf3d0af2bb3c17f10f78a6d7 Mon Sep 17 00:00:00 2001 From: Marcin Siodelski Date: Wed, 5 Sep 2012 18:16:33 +0200 Subject: [PATCH 31/40] [1959] Fixed doxygen comments in CommandOptionsHelper class. --- .../perfdhcp/tests/command_options_helper.h | 44 ++++++++++++++++--- 1 file changed, 38 insertions(+), 6 deletions(-) diff --git a/tests/tools/perfdhcp/tests/command_options_helper.h b/tests/tools/perfdhcp/tests/command_options_helper.h index 5886c1fad7..860a040fd3 100644 --- a/tests/tools/perfdhcp/tests/command_options_helper.h +++ b/tests/tools/perfdhcp/tests/command_options_helper.h @@ -29,7 +29,7 @@ namespace perfdhcp { /// This helper class can be shared between unit tests that /// need to initialize CommandOptions objects and feed it with /// specific command line. The command line can be given as a -/// string represinting program name, options and arguments. +/// string representing program name, options and arguments. /// The static method exposed by this class can be used to /// tokenize this string into array of C-strings that are later /// consumed by \ref CommandOptions::parse. The state of the @@ -39,12 +39,21 @@ namespace perfdhcp { class CommandOptionsHelper { public: + /// \brief Wrapper class for allocated argv[] array. + /// + /// This class wraps allocated char** array and ensures that memory + /// allocated for this array is freed at the end o the scope. class ArgvPtr { public: + /// \brief Constructor. + /// + /// \param argv array of C-strings. + /// \param number of C-strings in the array. ArgvPtr(char** argv, int argc) : argv_(argv), argc_(argc) { } - char** getArgv() const { return(argv_); } - int getArgc() const { return(argc_); } + /// \brief Destructor. + /// + /// Dealocates wrapped array of C-strings. ~ArgvPtr() { if (argv_ != NULL) { for(int i = 0; i < argc_; ++i) { @@ -55,11 +64,29 @@ public: } } + /// \brief Return the array of C-strings. + /// + /// \return array of C-strings. + char** getArgv() const { return (argv_); } + + /// \brief Return C-strings counter. + /// + /// \return C-strings counter. + int getArgc() const { return(argc_); } + public: - char** argv_; - int argc_; + char** argv_; ///< array of C-strings being wrapped. + int argc_; ///< number of C-strings. }; + /// \brief Parse command line provided as string. + /// + /// Method transforms the string representing command line + /// to the array of C-strings consumed by the + /// \ref CommandOptions::parse function and performs + /// parsing. + /// + /// \param cmdline command line provided as single string. static void process(const std::string& cmdline) { CommandOptions& opt = CommandOptions::instance(); int argc = 0; @@ -71,6 +98,11 @@ public: private: + /// \brief Split string to the array of C-strings. + /// + /// \param text_to_split string to be splited. + /// \param [out] num number of substrings returned. + /// \return array of C-strings created from split. static char** tokenizeString(const std::string& text_to_split, int& num) { char** results = NULL; // Tokenization with std streams @@ -100,7 +132,7 @@ private: } }; -} // namespace perfdhcp +} // namespace isc::perfdhcp } // namespace isc #endif // __COMMAND_OPTIONS_HELPER_H From f6ef8ec398f1711245c25eeb30887fe4de18641f Mon Sep 17 00:00:00 2001 From: Marcin Siodelski Date: Wed, 5 Sep 2012 19:04:12 +0200 Subject: [PATCH 32/40] [1959] Fixed bug in TestControlTest.Options6. --- tests/tools/perfdhcp/test_control.cc | 6 +++--- tests/tools/perfdhcp/tests/test_control_unittest.cc | 12 +++++------- 2 files changed, 8 insertions(+), 10 deletions(-) diff --git a/tests/tools/perfdhcp/test_control.cc b/tests/tools/perfdhcp/test_control.cc index ba8bf252bc..de7579d773 100644 --- a/tests/tools/perfdhcp/test_control.cc +++ b/tests/tools/perfdhcp/test_control.cc @@ -298,9 +298,9 @@ OptionPtr TestControl::factoryOptionRequestOption6(Option::Universe, uint16_t, const OptionBuffer&) { - const uint16_t buf_array[] = { - htons(D6O_NAME_SERVERS), - htons(D6O_DOMAIN_SEARCH) + const uint8_t buf_array[] = { + D6O_NAME_SERVERS, 0, + D6O_DOMAIN_SEARCH, 0, }; OptionBuffer buf_with_options(buf_array, buf_array + sizeof(buf_array)); return (OptionPtr(new Option(Option::V6, D6O_ORO, buf_with_options))); diff --git a/tests/tools/perfdhcp/tests/test_control_unittest.cc b/tests/tools/perfdhcp/tests/test_control_unittest.cc index f94f6f73de..06dc3e561b 100644 --- a/tests/tools/perfdhcp/tests/test_control_unittest.cc +++ b/tests/tools/perfdhcp/tests/test_control_unittest.cc @@ -178,7 +178,6 @@ public: if (requested_options[j] == buf[i]) { // Requested option has been found. ++matched_num; - break; } } } @@ -205,12 +204,11 @@ public: size_t matched_num = 0; for (size_t i = 0; i < buf.size(); i += 2) { for (int j = 0; j < requested_options.size(); j += 2) { - uint16_t opt_i = buf[i + 1] << 8 + buf[i]; - uint16_t opt_j = buf[j + 1] << 8 + buf[j]; + uint16_t opt_i = buf[i + 1] << 8 + buf[i] & 0xFF; + uint16_t opt_j = requested_options[j + 1] << 8 + requested_options[j] & 0xFF; if (opt_i == opt_j) { // Requested option has been found. ++matched_num; - break; } } } @@ -771,9 +769,9 @@ TEST_F(TestControlTest, Options6) { // Validate the D6O_ORO (Option Request Option). OptionPtr opt_oro(Option::factory(Option::V6, D6O_ORO)); // Prepare the reference buffer with requested options. - const uint16_t requested_options[] = { - htons(D6O_NAME_SERVERS), - htons(D6O_DOMAIN_SEARCH) + const uint8_t requested_options[] = { + D6O_NAME_SERVERS, 0, + D6O_DOMAIN_SEARCH, 0, }; int requested_options_num = sizeof(requested_options) / sizeof(requested_options[0]); From 58131685191d678f16320f532d86ddc2a90211ce Mon Sep 17 00:00:00 2001 From: Marcin Siodelski Date: Thu, 6 Sep 2012 12:24:23 +0200 Subject: [PATCH 33/40] [1959] Small fix in logical operator. --- tests/tools/perfdhcp/test_control.cc | 2 +- tests/tools/perfdhcp/tests/test_control_unittest.cc | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/tools/perfdhcp/test_control.cc b/tests/tools/perfdhcp/test_control.cc index de7579d773..ef970a7948 100644 --- a/tests/tools/perfdhcp/test_control.cc +++ b/tests/tools/perfdhcp/test_control.cc @@ -278,7 +278,7 @@ TestControl::factoryIana6(Option::Universe, uint16_t, // @todo allow different values of T1, T2 and IAID. const uint8_t buf_array[] = { 0, 0, 0, 1, // IAID = 1 - 0, 0, 3600 >> 8, 3600 && 0xff, // T1 = 3600 + 0, 0, 3600 >> 8, 3600 & 0xff, // T1 = 3600 0, 0, 5400 >> 8, 5400 & 0xff, // T2 = 5400 }; OptionBuffer buf_ia_na(buf_array, buf_array + sizeof(buf_array)); diff --git a/tests/tools/perfdhcp/tests/test_control_unittest.cc b/tests/tools/perfdhcp/tests/test_control_unittest.cc index 06dc3e561b..e5dbaf12a2 100644 --- a/tests/tools/perfdhcp/tests/test_control_unittest.cc +++ b/tests/tools/perfdhcp/tests/test_control_unittest.cc @@ -795,7 +795,7 @@ TEST_F(TestControlTest, Options6) { // Every IA_NA option is expected to start with this sequence. const uint8_t opt_ia_na_array[] = { 0, 0, 0, 1, // IAID = 1 - 0, 0, 3600 >> 8, 3600 && 0xff, // T1 = 3600 + 0, 0, 3600 >> 8, 3600 & 0xff, // T1 = 3600 0, 0, 5400 >> 8, 5400 & 0xff, // T2 = 5400 }; OptionBuffer opt_ia_na_ref(opt_ia_na_array, From 10cf1a64da6a0d8851984e6ca0c6c7baa6769c20 Mon Sep 17 00:00:00 2001 From: Tomek Mrugalski Date: Mon, 10 Sep 2012 15:22:30 +0200 Subject: [PATCH 34/40] [1959] Added couple @todos for postponed things during review, part 1. --- src/lib/dhcp/iface_mgr.cc | 12 +++++++++--- src/lib/dhcp/iface_mgr.h | 2 ++ src/lib/dhcp/option.h | 3 +++ 3 files changed, 14 insertions(+), 3 deletions(-) diff --git a/src/lib/dhcp/iface_mgr.cc b/src/lib/dhcp/iface_mgr.cc index 54db434d8f..9c03794c2d 100644 --- a/src/lib/dhcp/iface_mgr.cc +++ b/src/lib/dhcp/iface_mgr.cc @@ -498,11 +498,15 @@ IfaceMgr::getLocalAddress(const IOAddress& remote_addr, const uint16_t port) { asio::error_code err_code; // If remote address is broadcast address we have to // allow this on the socket. - if (remote_addr.getAddress().is_v4() && + if (remote_addr.getAddress().is_v4() && (remote_addr == IOAddress("255.255.255.255"))) { // Socket has to be open prior to setting the broadcast // option. Otherwise set_option will complain about // bad file descriptor. + + // @todo: We don't specify interface in any way here. 255.255.255.255 + // We can very easily end up with a socket working on a different + // interface. sock.open(asio::ip::udp::v4(), err_code); if (err_code) { isc_throw(Unexpected, "failed to open UDPv4 socket"); @@ -580,8 +584,10 @@ int IfaceMgr::openSocket6(Iface& iface, const IOAddress& addr, uint16_t port) { struct sockaddr_in6 addr6; memset(&addr6, 0, sizeof(addr6)); addr6.sin6_family = AF_INET6; - addr6.sin6_port = htons(port); if (addr.toText() != "::1") - addr6.sin6_scope_id = if_nametoindex(iface.getName().c_str()); + addr6.sin6_port = htons(port); + if (addr.toText() != "::1") { + addr6.sin6_scope_id = if_nametoindex(iface.getName().c_str()); + } memcpy(&addr6.sin6_addr, addr.getAddress().to_v6().to_bytes().data(), diff --git a/src/lib/dhcp/iface_mgr.h b/src/lib/dhcp/iface_mgr.h index 178d24a9ec..cacd720318 100644 --- a/src/lib/dhcp/iface_mgr.h +++ b/src/lib/dhcp/iface_mgr.h @@ -72,8 +72,10 @@ public: }; /// type that holds a list of socket informations + /// @todo: Add SocketCollectionConstIter type typedef std::list SocketCollection; + /// @brief represents a single network interface /// /// Iface structure represents network interface with all useful diff --git a/src/lib/dhcp/option.h b/src/lib/dhcp/option.h index 9589c37ab9..080a869996 100644 --- a/src/lib/dhcp/option.h +++ b/src/lib/dhcp/option.h @@ -63,6 +63,9 @@ public: /// @param type option type /// @param buf pointer to a buffer /// + /// @todo Passing a separate buffer for each option means that a copy + /// was done. We can avoid it by passing 2 iterators. + /// /// @return a pointer to a created option object typedef OptionPtr Factory(Option::Universe u, uint16_t type, const OptionBuffer& buf); From b5aa1dadff2c0e7c9fce8fab0aa6dee28071e29b Mon Sep 17 00:00:00 2001 From: Marcin Siodelski Date: Mon, 10 Sep 2012 20:43:51 +0200 Subject: [PATCH 35/40] [1959] Implemented suggestions from code review. --- tests/tools/perfdhcp/command_options.cc | 3 +- tests/tools/perfdhcp/pkt_transform.h | 2 ++ tests/tools/perfdhcp/test_control.cc | 36 +++++++++++-------- tests/tools/perfdhcp/test_control.h | 34 +++++++++++------- .../perfdhcp/tests/test_control_unittest.cc | 12 +++---- 5 files changed, 54 insertions(+), 33 deletions(-) diff --git a/tests/tools/perfdhcp/command_options.cc b/tests/tools/perfdhcp/command_options.cc index 8d9f02e763..24e7e790dc 100644 --- a/tests/tools/perfdhcp/command_options.cc +++ b/tests/tools/perfdhcp/command_options.cc @@ -12,6 +12,7 @@ // OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR // PERFORMANCE OF THIS SOFTWARE. +#include #include #include #include @@ -866,7 +867,7 @@ CommandOptions::usage() const { void CommandOptions::version() const { - fprintf(stdout, "version 0.01\n"); + std::cout << "VERSION: " << VERSION << std::endl; } diff --git a/tests/tools/perfdhcp/pkt_transform.h b/tests/tools/perfdhcp/pkt_transform.h index 08563a2720..1f57105720 100644 --- a/tests/tools/perfdhcp/pkt_transform.h +++ b/tests/tools/perfdhcp/pkt_transform.h @@ -114,6 +114,8 @@ public: template static void writeValueAt(dhcp::OptionBuffer& in_buffer, size_t dest_pos, T val) { + // @todo consider replacing the loop with switch statement + // checking sizeof(T). for (int i = 0; i < sizeof(T); ++i) { in_buffer[dest_pos + i] = (val >> 8 * (sizeof(T) - i - 1)) & 0xFF; } diff --git a/tests/tools/perfdhcp/test_control.cc b/tests/tools/perfdhcp/test_control.cc index ef970a7948..090fb32411 100644 --- a/tests/tools/perfdhcp/test_control.cc +++ b/tests/tools/perfdhcp/test_control.cc @@ -299,8 +299,8 @@ TestControl::factoryOptionRequestOption6(Option::Universe, uint16_t, const OptionBuffer&) { const uint8_t buf_array[] = { - D6O_NAME_SERVERS, 0, - D6O_DOMAIN_SEARCH, 0, + 0, D6O_NAME_SERVERS, + 0, D6O_DOMAIN_SEARCH, }; OptionBuffer buf_with_options(buf_array, buf_array + sizeof(buf_array)); return (OptionPtr(new Option(Option::V6, D6O_ORO, buf_with_options))); @@ -731,7 +731,7 @@ TestControl::readPacketTemplate(const std::string& file_name) { } void -TestControl::receivePacket4(const TestControlSocket& socket, +TestControl::processReceivedPacket4(const TestControlSocket& socket, const Pkt4Ptr& pkt4) { if (pkt4->getType() == DHCPOFFER) { Pkt4Ptr discover_pkt4(stats_mgr4_->passRcvdPacket(StatsMgr4::XCHG_DO, @@ -742,6 +742,8 @@ TestControl::receivePacket4(const TestControlSocket& socket, if (template_buffers_.size() < 2) { sendRequest4(socket, discover_pkt4, pkt4); } else { + // @todo add defines for packet type index that can be + // used to access template_buffers_. sendRequest4(socket, template_buffers_[1], discover_pkt4, pkt4); } } @@ -751,7 +753,7 @@ TestControl::receivePacket4(const TestControlSocket& socket, } void -TestControl::receivePacket6(const TestControlSocket& socket, +TestControl::processReceivedPacket6(const TestControlSocket& socket, const Pkt6Ptr& pkt6) { uint8_t packet_type = pkt6->getType(); if (packet_type == DHCPV6_ADVERTISE) { @@ -766,6 +768,8 @@ TestControl::receivePacket6(const TestControlSocket& socket, if (template_buffers_.size() < 2) { sendRequest6(socket, pkt6); } else { + // @todo add defines for packet type index that can be + // used to access template_buffers_. sendRequest6(socket, template_buffers_[1], pkt6); } } @@ -790,7 +794,7 @@ TestControl::receivePackets(const TestControlSocket& socket) { stats_mgr4_->incrementCounter("multircvd"); } pkt4->unpack(); - receivePacket4(socket, pkt4); + processReceivedPacket4(socket, pkt4); } } else if (CommandOptions::instance().getIpVersion() == 6) { Pkt6Ptr pkt6 = IfaceMgr::instance().receive6(timeout); @@ -802,7 +806,7 @@ TestControl::receivePackets(const TestControlSocket& socket) { stats_mgr6_->incrementCounter("multircvd"); } if (pkt6->unpack()) { - receivePacket6(socket, pkt6); + processReceivedPacket6(socket, pkt6); } } } @@ -949,8 +953,9 @@ TestControl::run() { } else { // Pick template #0 if Discover is being sent. // For Request it would be #1. - const uint8_t template_idx = 0; - sendDiscover4(socket, template_buffers_[template_idx], + // @todo add defines for packet type index that can be + // used to access template_buffers_. + sendDiscover4(socket, template_buffers_[0], do_preload); } } else if (options.getIpVersion() == 6) { @@ -961,8 +966,9 @@ TestControl::run() { } else { // Pick template #0 if Solicit is being sent. // For Request it would be #1. - const uint8_t template_idx = 0; - sendSolicit6(socket, template_buffers_[template_idx], + // @todo add defines for packet type index that can be + // used to access template_buffers_. + sendSolicit6(socket, template_buffers_[0], do_preload); } } @@ -1000,8 +1006,9 @@ TestControl::run() { if (template_buffers_.size() == 0) { sendDiscover4(socket); } else { - const uint8_t template_idx = 0; - sendDiscover4(socket, template_buffers_[template_idx]); + // @todo add defines for packet type index that can be + // used to access template_buffers_. + sendDiscover4(socket, template_buffers_[0]); } } else { // No template packets means that no -T option was specified. @@ -1009,8 +1016,9 @@ TestControl::run() { if (template_buffers_.size() == 0) { sendSolicit6(socket); } else { - const uint8_t template_idx = 0; - sendSolicit6(socket, template_buffers_[template_idx]); + // @todo add defines for packet type index that can be + // used to access template_buffers_. + sendSolicit6(socket, template_buffers_[0]); } } } diff --git a/tests/tools/perfdhcp/test_control.h b/tests/tools/perfdhcp/test_control.h index 8e492af133..1b0b83c385 100644 --- a/tests/tools/perfdhcp/test_control.h +++ b/tests/tools/perfdhcp/test_control.h @@ -46,6 +46,12 @@ namespace perfdhcp { /// \ref dhcp::Option::factory with DHCP message type specified as one of /// parameters. Some of the parameters passed to factory function /// may be ignored (e.g. option buffer). +/// Please note that naming convention for factory functions within this +/// class is as follows: +/// - factoryABC4 - factory function for DHCPv4 option, +/// - factoryDEF6 - factory function for DHCPv6 option, +/// - factoryGHI - factory function that can be used to create either +/// DHCPv4 or DHCPv6 option. class TestControl : public boost::noncopyable { public: @@ -449,11 +455,11 @@ protected: /// not initialized. void printStats() const; - /// \brief Receive DHCPv4 packet. + /// \brief Process received DHCPv4 packet. /// - /// Method performs reception of the DHCPv4 packet, updates - /// statistics and responsds to the server if required, e.g. - /// when OFFER packet arrives, this function will initiate + /// Method performs processing of the received DHCPv4 packet, + /// updates statistics and responds to the server if required, + /// e.g. when OFFER packet arrives, this function will initiate /// REQUEST message to the server. /// /// \warning this method does not check if provided socket is @@ -463,14 +469,14 @@ protected: /// \param [in] pkt4 object representing DHCPv4 packet received. /// \throw isc::BadValue if unknown message type received. /// \throw isc::Unexpected if unexpected error occured. - void receivePacket4(const TestControlSocket& socket, - const dhcp::Pkt4Ptr& pkt4); + void processReceivedPacket4(const TestControlSocket& socket, + const dhcp::Pkt4Ptr& pkt4); - /// \brief Receive DHCPv6 packet. + /// \brief Process received DHCPv6 packet. /// - /// Method performs reception of the DHCPv6 packet, updates - /// statistics and responsds to the server if required, e.g. - /// when ADVERTISE packet arrives, this function will initiate + /// Method performs processing of the received DHCPv6 packet, + /// updates statistics and responsds to the server if required, + /// e.g. when ADVERTISE packet arrives, this function will initiate /// REQUEST message to the server. /// /// \warning this method does not check if provided socket is @@ -480,8 +486,8 @@ protected: /// \param [in] pkt6 object representing DHCPv6 packet received. /// \throw isc::BadValue if unknown message type received. /// \throw isc::Unexpected if unexpected error occured. - void receivePacket6(const TestControlSocket& socket, - const dhcp::Pkt6Ptr& pkt6); + void processReceivedPacket6(const TestControlSocket& socket, + const dhcp::Pkt6Ptr& pkt6); /// \brief Receive DHCPv4 or DHCPv6 packets from the server. /// @@ -706,6 +712,8 @@ private: /// \brief Convert binary value to hex string. /// + /// \todo Consider moving this function to src/lib/util. + /// /// \param b byte to convert. /// \return hex string. std::string byte2Hex(const uint8_t b) const; @@ -760,6 +768,8 @@ private: /// \brief Convert vector in hexadecimal string. /// + /// \todo Consider moving this function to src/lib/util. + /// /// \param vec vector to be converted. /// \param separator separator. std::string vector2Hex(const std::vector& vec, diff --git a/tests/tools/perfdhcp/tests/test_control_unittest.cc b/tests/tools/perfdhcp/tests/test_control_unittest.cc index e5dbaf12a2..a4cde00f19 100644 --- a/tests/tools/perfdhcp/tests/test_control_unittest.cc +++ b/tests/tools/perfdhcp/tests/test_control_unittest.cc @@ -81,8 +81,8 @@ public: using TestControl::initPacketTemplates; using TestControl::initializeStatsMgr; using TestControl::openSocket; - using TestControl::receivePacket4; - using TestControl::receivePacket6; + using TestControl::processReceivedPacket4; + using TestControl::processReceivedPacket6; using TestControl::registerOptionFactories; using TestControl::sendDiscover4; using TestControl::sendSolicit6; @@ -418,7 +418,7 @@ public: // packet drops. if (i < receive_num) { boost::shared_ptr offer_pkt4(createOfferPkt4(transid)); - ASSERT_NO_THROW(tc.receivePacket4(sock, offer_pkt4)); + ASSERT_NO_THROW(tc.processReceivedPacket4(sock, offer_pkt4)); ++transid; } if (tc.checkExitConditions()) { @@ -483,7 +483,7 @@ public: boost::shared_ptr advertise_pkt6(createAdvertisePkt6(transid)); // Receive ADVERTISE and send REQUEST. - ASSERT_NO_THROW(tc.receivePacket6(sock, advertise_pkt6)); + ASSERT_NO_THROW(tc.processReceivedPacket6(sock, advertise_pkt6)); ++transid; } if (tc.checkExitConditions()) { @@ -770,8 +770,8 @@ TEST_F(TestControlTest, Options6) { OptionPtr opt_oro(Option::factory(Option::V6, D6O_ORO)); // Prepare the reference buffer with requested options. const uint8_t requested_options[] = { - D6O_NAME_SERVERS, 0, - D6O_DOMAIN_SEARCH, 0, + 0, D6O_NAME_SERVERS, + 0, D6O_DOMAIN_SEARCH, }; int requested_options_num = sizeof(requested_options) / sizeof(requested_options[0]); From 1b84c5e9b4391869185ceb41195b462f553b4f25 Mon Sep 17 00:00:00 2001 From: Marcin Siodelski Date: Tue, 11 Sep 2012 21:19:47 +0200 Subject: [PATCH 36/40] [1959] Removed using namespece boost in TestControl. This is supposed to resolve ambiguous uintX_t types on Solaris 11. --- tests/tools/perfdhcp/test_control.cc | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/tests/tools/perfdhcp/test_control.cc b/tests/tools/perfdhcp/test_control.cc index 090fb32411..c154cf9ceb 100644 --- a/tests/tools/perfdhcp/test_control.cc +++ b/tests/tools/perfdhcp/test_control.cc @@ -34,7 +34,6 @@ #include "perf_pkt6.h" using namespace std; -using namespace boost; using namespace boost::posix_time; using namespace isc; using namespace isc::dhcp; @@ -1123,11 +1122,11 @@ TestControl::sendDiscover4(const TestControlSocket& socket, // Replace MAC address in the template with actual MAC address. pkt4->writeAt(rand_offset, mac_address.begin(), mac_address.end()); // Create a packet from the temporary buffer. - setDefaults4(socket, static_pointer_cast(pkt4)); + setDefaults4(socket, boost::static_pointer_cast(pkt4)); // Pack the input packet buffer to output buffer so as it can // be sent to server. pkt4->rawPack(); - IfaceMgr::instance().send(static_pointer_cast(pkt4)); + IfaceMgr::instance().send(boost::static_pointer_cast(pkt4)); if (!preload) { if (!stats_mgr4_) { isc_throw(InvalidOperation, "Statistics Manager for DHCPv4 " @@ -1135,7 +1134,7 @@ TestControl::sendDiscover4(const TestControlSocket& socket, } // Update packet stats. stats_mgr4_->passSentPacket(StatsMgr4::XCHG_DO, - static_pointer_cast(pkt4)); + boost::static_pointer_cast(pkt4)); } } @@ -1308,17 +1307,17 @@ TestControl::sendRequest4(const TestControlSocket& socket, opt_requested_ip->setUint32(static_cast(yiaddr)); pkt4->addOption(opt_requested_ip); - setDefaults4(socket, static_pointer_cast(pkt4)); + setDefaults4(socket, boost::static_pointer_cast(pkt4)); // Prepare on-wire data. pkt4->rawPack(); - IfaceMgr::instance().send(static_pointer_cast(pkt4)); + IfaceMgr::instance().send(boost::static_pointer_cast(pkt4)); if (!stats_mgr4_) { isc_throw(InvalidOperation, "Statistics Manager for DHCPv4 " "hasn't been initialized"); } // Update packet stats. stats_mgr4_->passSentPacket(StatsMgr4::XCHG_RA, - static_pointer_cast(pkt4)); + boost::static_pointer_cast(pkt4)); } void @@ -1435,7 +1434,7 @@ TestControl::sendRequest6(const TestControlSocket& socket, } // Set IA_NA boost::shared_ptr opt_ia_na_advertise = - static_pointer_cast(advertise_pkt6->getOption(D6O_IA_NA)); + boost::static_pointer_cast(advertise_pkt6->getOption(D6O_IA_NA)); if (!opt_ia_na_advertise) { isc_throw(Unexpected, "DHCPv6 IA_NA option not found in received " "packet"); From 1a4c5a70d4917bd1942d5f940bfaf0a1e35b6a0f Mon Sep 17 00:00:00 2001 From: Marcin Siodelski Date: Wed, 12 Sep 2012 11:09:09 +0200 Subject: [PATCH 37/40] [1959] Include command_options_helper.h in Makefile. --- tests/tools/perfdhcp/tests/Makefile.am | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/tools/perfdhcp/tests/Makefile.am b/tests/tools/perfdhcp/tests/Makefile.am index 0cbeb8a456..73ec6babc5 100644 --- a/tests/tools/perfdhcp/tests/Makefile.am +++ b/tests/tools/perfdhcp/tests/Makefile.am @@ -23,6 +23,7 @@ run_unittests_SOURCES += perf_pkt4_unittest.cc run_unittests_SOURCES += localized_option_unittest.cc run_unittests_SOURCES += stats_mgr_unittest.cc run_unittests_SOURCES += test_control_unittest.cc +run_unittests_SOURCES += command_options_helper.h run_unittests_SOURCES += $(top_builddir)/tests/tools/perfdhcp/command_options.cc run_unittests_SOURCES += $(top_builddir)/tests/tools/perfdhcp/pkt_transform.cc run_unittests_SOURCES += $(top_builddir)/tests/tools/perfdhcp/perf_pkt6.cc From 45ca30e42d6f87bd1039cbcd7f83dfa15ebdcfa0 Mon Sep 17 00:00:00 2001 From: Marcin Siodelski Date: Wed, 12 Sep 2012 11:16:17 +0200 Subject: [PATCH 38/40] [1959] Test opening socket from remote broadcast address on Linux only. In order to run this test interface detection is required. Since it is right now implemented on Linux only it had to be disabled on every other OS. --- src/lib/dhcp/iface_mgr.cc | 5 +++++ src/lib/dhcp/tests/iface_mgr_unittest.cc | 6 ++++++ 2 files changed, 11 insertions(+) diff --git a/src/lib/dhcp/iface_mgr.cc b/src/lib/dhcp/iface_mgr.cc index 9c03794c2d..be17b09b5d 100644 --- a/src/lib/dhcp/iface_mgr.cc +++ b/src/lib/dhcp/iface_mgr.cc @@ -513,6 +513,7 @@ IfaceMgr::getLocalAddress(const IOAddress& remote_addr, const uint16_t port) { } sock.set_option(asio::socket_base::broadcast(true), err_code); if (err_code) { + sock.close(); isc_throw(Unexpected, "failed to enable broadcast on the socket"); } } @@ -520,6 +521,7 @@ IfaceMgr::getLocalAddress(const IOAddress& remote_addr, const uint16_t port) { // Try to connect to remote endpoint and check if attempt is successful. sock.connect(remote_endpoint->getASIOEndpoint(), err_code); if (err_code) { + sock.close(); isc_throw(Unexpected,"failed to connect to remote endpoint."); } @@ -528,6 +530,9 @@ IfaceMgr::getLocalAddress(const IOAddress& remote_addr, const uint16_t port) { sock.local_endpoint(); asio::ip::address local_address(local_endpoint.address()); + // Close the socket. + sock.close(); + // Return address of local endpoint. return IOAddress(local_address); } diff --git a/src/lib/dhcp/tests/iface_mgr_unittest.cc b/src/lib/dhcp/tests/iface_mgr_unittest.cc index 7f8555c1fa..ad8dba905b 100644 --- a/src/lib/dhcp/tests/iface_mgr_unittest.cc +++ b/src/lib/dhcp/tests/iface_mgr_unittest.cc @@ -407,6 +407,11 @@ TEST_F(IfaceMgrTest, socketsFromRemoteAddress) { EXPECT_GT(socket2, 0); close(socket2); + // The following test is currently disabled for OSes other than + // Linux because interface detection is not implemented on them. + // @todo enable this test for all OSes once interface detection + // is implemented. +#if defined(OS_LINUX) // Open v4 socket to connect to broadcast address. int socket3 = 0; IOAddress bcastAddr("255.255.255.255"); @@ -415,6 +420,7 @@ TEST_F(IfaceMgrTest, socketsFromRemoteAddress) { ); EXPECT_GT(socket3, 0); close(socket3); +#endif } // TODO: disabled due to other naming on various systems From 9ddbc590eab2267c8c7b98f1c41f5d75d81a860c Mon Sep 17 00:00:00 2001 From: Marcin Siodelski Date: Wed, 12 Sep 2012 13:39:47 +0200 Subject: [PATCH 39/40] [1959] Included perfdhcp/templates folder in Makefiles. --- configure.ac | 1 + tests/tools/perfdhcp/Makefile.am | 2 +- tests/tools/perfdhcp/templates/Makefile.am | 4 ++++ 3 files changed, 6 insertions(+), 1 deletion(-) create mode 100644 tests/tools/perfdhcp/templates/Makefile.am diff --git a/configure.ac b/configure.ac index e75cbd8d3b..5368514353 100644 --- a/configure.ac +++ b/configure.ac @@ -1199,6 +1199,7 @@ AC_CONFIG_FILES([Makefile tests/tools/badpacket/tests/Makefile tests/tools/perfdhcp/Makefile tests/tools/perfdhcp/tests/Makefile + tests/tools/perfdhcp/templates/Makefile dns++.pc ]) AC_OUTPUT([doc/version.ent diff --git a/tests/tools/perfdhcp/Makefile.am b/tests/tools/perfdhcp/Makefile.am index 53bc539d9d..0532f27dcc 100644 --- a/tests/tools/perfdhcp/Makefile.am +++ b/tests/tools/perfdhcp/Makefile.am @@ -1,4 +1,4 @@ -SUBDIRS = . tests +SUBDIRS = . tests templates AM_CPPFLAGS = -I$(top_srcdir)/src/lib -I$(top_builddir)/src/lib AM_CPPFLAGS += -I$(top_srcdir)/src/lib/log -I$(top_builddir)/src/lib/log diff --git a/tests/tools/perfdhcp/templates/Makefile.am b/tests/tools/perfdhcp/templates/Makefile.am new file mode 100644 index 0000000000..9253c6d449 --- /dev/null +++ b/tests/tools/perfdhcp/templates/Makefile.am @@ -0,0 +1,4 @@ +SUBDIRS = . + +perfdhcp_DATA = discover-example.hex request4-example.hex \ + solicit-example.hex request6-example.hex From 9a3a5db5ee5cf4571a2c573e046faef0be07410f Mon Sep 17 00:00:00 2001 From: Marcin Siodelski Date: Wed, 12 Sep 2012 16:12:11 +0200 Subject: [PATCH 40/40] [1959] Minor corrections. --- tests/tools/perfdhcp/templates/Makefile.am | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/tests/tools/perfdhcp/templates/Makefile.am b/tests/tools/perfdhcp/templates/Makefile.am index 9253c6d449..4da2027903 100644 --- a/tests/tools/perfdhcp/templates/Makefile.am +++ b/tests/tools/perfdhcp/templates/Makefile.am @@ -1,4 +1,8 @@ SUBDIRS = . +perfdhcpdir = $(pkgdatadir) perfdhcp_DATA = discover-example.hex request4-example.hex \ solicit-example.hex request6-example.hex + +EXTRA_DIST = discover-example.hex request4-example.hex +EXTRA_DIST += solicit-example.hex request6-example.hex