From: Michal Nowikowski Date: Mon, 18 Feb 2019 12:30:40 +0000 (+0100) Subject: perfdhcp avalanche: improvements after review X-Git-Tag: 397-cb-implement-mysqlconfigbackenddhcpv6_base~37 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=f4a2c50d4aae45c7f83440c8d2015481e93bb7ad;p=thirdparty%2Fkea.git perfdhcp avalanche: improvements after review - CommandOptions is no longer a signleton - this makes testing easier - added -i option taking into account in Avalanche scen (execute only DO exchange) - fixed collecting stats in Avalanche scen - improved TestControl tests, moved some of them to BasicScen tests - made PerfSocket testable: it has a base class which is used for mocking in TestControl tests - all references to another singleton, IfaceMgr wrapped into PerfSocket - this makes testing easier - added unit tests for basic and avalanche scenarios, and perf socket - added -Werror to prevent ignore warnings - added more comments --- diff --git a/src/bin/perfdhcp/Makefile.am b/src/bin/perfdhcp/Makefile.am index d595588245..973ff3e581 100644 --- a/src/bin/perfdhcp/Makefile.am +++ b/src/bin/perfdhcp/Makefile.am @@ -3,6 +3,7 @@ SUBDIRS = . tests AM_CPPFLAGS = -I$(top_srcdir)/src/lib -I$(top_builddir)/src/lib AM_CPPFLAGS += -I$(top_srcdir)/src/bin -I$(top_builddir)/src/bin AM_CPPFLAGS += $(BOOST_INCLUDES) +AM_CPPFLAGS += -Werror AM_CXXFLAGS = $(KEA_CXXFLAGS) diff --git a/src/bin/perfdhcp/abstract_scen.h b/src/bin/perfdhcp/abstract_scen.h index bb2f5c892f..d57278cf42 100644 --- a/src/bin/perfdhcp/abstract_scen.h +++ b/src/bin/perfdhcp/abstract_scen.h @@ -20,15 +20,26 @@ namespace perfdhcp { /// This class must be inherited by scenario classes. class AbstractScen : public boost::noncopyable { public: + /// \brief Default and the only constructor of AbstractScen. + /// + /// \param options reference to command options, + /// \param socket reference to a socket. + AbstractScen(CommandOptions& options, BasePerfSocket &socket) : + options_(options), + tc_(options, socket) {}; + /// \brief Run performance test. /// /// Method runs whole performance test. + /// + /// \return execution status. virtual int run() = 0; /// \brief Trivial virtual destructor. virtual ~AbstractScen() {}; protected: + CommandOptions& options_; TestControl tc_; ///< Object for controling sending and receiving packets. }; diff --git a/src/bin/perfdhcp/avalanche_scen.cc b/src/bin/perfdhcp/avalanche_scen.cc index 7145e228e3..da85ef86ed 100644 --- a/src/bin/perfdhcp/avalanche_scen.cc +++ b/src/bin/perfdhcp/avalanche_scen.cc @@ -20,7 +20,6 @@ namespace perfdhcp { int AvalancheScen::resendPackets(ExchangeType xchg_type) { - CommandOptions& options = CommandOptions::instance(); const StatsMgr& stats_mgr(tc_.getStatsMgr()); // get list of sent packets that potentially need to be resent @@ -65,12 +64,12 @@ AvalancheScen::resendPackets(ExchangeType xchg_type) { total_resent_++; // do resend packet - if (options.getIpVersion() == 4) { + if (options_.getIpVersion() == 4) { Pkt4Ptr pkt4 = boost::dynamic_pointer_cast(pkt); - IfaceMgr::instance().send(pkt4); + socket_.send(pkt4); } else { Pkt6Ptr pkt6 = boost::dynamic_pointer_cast(pkt); - IfaceMgr::instance().send(pkt6); + socket_.send(pkt6); } // restore sending time of original packet @@ -104,10 +103,8 @@ AvalancheScen::run() { // and printed during runtime. The whole procedure is stopeed when // all packets got reponses. - CommandOptions& options = CommandOptions::instance(); - - uint32_t clients_num = options.getClientsNum() == 0 ? - 1 : options.getClientsNum(); + uint32_t clients_num = options_.getClientsNum() == 0 ? + 1 : options_.getClientsNum(); StatsMgr& stats_mgr(tc_.getStatsMgr()); @@ -131,12 +128,16 @@ AvalancheScen::run() { if (now - prev_cycle_time > milliseconds(200)) { // check if 0.2s elapsed prev_cycle_time = now; int still_left_cnt = 0; - if (options.getIpVersion() == 4) { + if (options_.getIpVersion() == 4) { still_left_cnt += resendPackets(ExchangeType::DO); - still_left_cnt += resendPackets(ExchangeType::RA); + if (options_.getExchangeMode() == CommandOptions::DORA_SARR) { + still_left_cnt += resendPackets(ExchangeType::RA); + } } else { still_left_cnt += resendPackets(ExchangeType::SA); - still_left_cnt += resendPackets(ExchangeType::RR); + if (options_.getExchangeMode() == CommandOptions::DORA_SARR) { + still_left_cnt += resendPackets(ExchangeType::RR); + } } if (still_left_cnt == 0) { @@ -157,25 +158,44 @@ AvalancheScen::run() { tc_.printStats(); // Print packet timestamps - if (testDiags('t')) { + if (options_.testDiags('t')) { stats_mgr.printTimestamps(); } // Print server id. - if (testDiags('s') && tc_.serverIdReceived()) { + if (options_.testDiags('s') && tc_.serverIdReceived()) { std::cout << "Server id: " << tc_.getServerId() << std::endl; } // Diagnostics flag 'e' means show exit reason. - if (testDiags('e')) { + if (options_.testDiags('e')) { std::cout << "Interrupted" << std::endl; } + // Calculate total stats. + int total_sent_pkts = total_resent_; + int total_rcvd_pkts = 0; + if (options_.getIpVersion() == 4) { + total_sent_pkts += tc_.getStatsMgr().getSentPacketsNum(ExchangeType::DO); + total_rcvd_pkts += tc_.getStatsMgr().getRcvdPacketsNum(ExchangeType::DO); + if (options_.getExchangeMode() == CommandOptions::DORA_SARR) { + total_sent_pkts += tc_.getStatsMgr().getSentPacketsNum(ExchangeType::RA); + total_rcvd_pkts += tc_.getStatsMgr().getRcvdPacketsNum(ExchangeType::RA); + } + } else { + total_sent_pkts += tc_.getStatsMgr().getSentPacketsNum(ExchangeType::SA); + total_rcvd_pkts += tc_.getStatsMgr().getRcvdPacketsNum(ExchangeType::SA); + if (options_.getExchangeMode() == CommandOptions::DORA_SARR) { + total_sent_pkts += tc_.getStatsMgr().getSentPacketsNum(ExchangeType::RR); + total_rcvd_pkts += tc_.getStatsMgr().getRcvdPacketsNum(ExchangeType::RR); + } + } + std::cout << "It took " << duration.length() << " to provision " << clients_num - << " clients. " << (clients_num * 2 + total_resent_) - << " packets were sent, " << total_resent_ - << " retransmissions needed, received " << (clients_num * 2) - << " responses." << std::endl; + << " clients. " << std::endl + << "Requests sent + resent: " << total_sent_pkts << std::endl + << "Requests resent: " << total_resent_ << std::endl + << "Responses received: " << total_rcvd_pkts << std::endl; return (0); } diff --git a/src/bin/perfdhcp/avalanche_scen.h b/src/bin/perfdhcp/avalanche_scen.h index d973ff81a7..c93db852b9 100644 --- a/src/bin/perfdhcp/avalanche_scen.h +++ b/src/bin/perfdhcp/avalanche_scen.h @@ -24,16 +24,27 @@ namespace perfdhcp { /// Full DORA and SARR message sequences are expected. class AvalancheScen : public AbstractScen { public: - /// Default and the only constructor of AvalancheScen. - AvalancheScen(): total_resent_(0) {}; + /// \brief Default and the only constructor of AvalancheScen. + /// + /// \param options reference to command options, + /// \param socket reference to a socket. + AvalancheScen(CommandOptions& options, BasePerfSocket &socket): + AbstractScen(options, socket), + socket_(socket), + total_resent_(0) {}; /// brief\ Run performance test. /// /// Method runs whole performance test. - int run(); + /// + /// \return execution status. + int run() override; private: + // A reference to socket; + BasePerfSocket &socket_; + /// A map xchg type -> (a map of trans id -> retransmissions count. std::unordered_map> retransmissions_; /// A map xchg type -> (a map of trans id -> time of sending first packet. @@ -42,8 +53,13 @@ private: /// Total number of resent packets. int total_resent_; - /// Resend packets for given exchange type that did not receive + /// \\brief Resend packets. + /// + /// It resends packets for given exchange type that did not receive /// a response yet. + /// + /// \param xchg_type exchange type that should be looked for. + /// \return number of packets still waiting for resending. int resendPackets(ExchangeType xchg_type); }; diff --git a/src/bin/perfdhcp/basic_scen.cc b/src/bin/perfdhcp/basic_scen.cc index cda35f644c..b4236244c7 100644 --- a/src/bin/perfdhcp/basic_scen.cc +++ b/src/bin/perfdhcp/basic_scen.cc @@ -27,17 +27,16 @@ BasicScen::checkExitConditions() { const StatsMgr& stats_mgr(tc_.getStatsMgr()); - CommandOptions& options = CommandOptions::instance(); bool test_period_reached = false; // Check if test period passed. - if (options.getPeriod() != 0) { + if (options_.getPeriod() != 0) { time_period period(stats_mgr.getTestPeriod()); - if (period.length().total_seconds() >= options.getPeriod()) { + if (period.length().total_seconds() >= options_.getPeriod()) { test_period_reached = true; } } if (test_period_reached) { - if (testDiags('e')) { + if (options_.testDiags('e')) { std::cout << "reached test-period." << std::endl; } if (!tc_.waitToExit()) { @@ -47,35 +46,35 @@ BasicScen::checkExitConditions() { bool max_requests = false; // Check if we reached maximum number of DISCOVER/SOLICIT sent. - if (options.getNumRequests().size() > 0) { - if (options.getIpVersion() == 4) { + if (options_.getNumRequests().size() > 0) { + if (options_.getIpVersion() == 4) { if (stats_mgr.getSentPacketsNum(ExchangeType::DO) >= - options.getNumRequests()[0]) { + options_.getNumRequests()[0]) { max_requests = true; } - } else if (options.getIpVersion() == 6) { + } else if (options_.getIpVersion() == 6) { if (stats_mgr.getSentPacketsNum(ExchangeType::SA) >= - options.getNumRequests()[0]) { + options_.getNumRequests()[0]) { max_requests = true; } } } // Check if we reached maximum number REQUEST packets. - if (options.getNumRequests().size() > 1) { - if (options.getIpVersion() == 4) { + if (options_.getNumRequests().size() > 1) { + if (options_.getIpVersion() == 4) { if (stats_mgr.getSentPacketsNum(ExchangeType::RA) >= - options.getNumRequests()[1]) { + options_.getNumRequests()[1]) { max_requests = true; } - } else if (options.getIpVersion() == 6) { + } else if (options_.getIpVersion() == 6) { if (stats_mgr.getSentPacketsNum(ExchangeType::RR) >= - options.getNumRequests()[1]) { + options_.getNumRequests()[1]) { max_requests = true; } } } if (max_requests) { - if (testDiags('e')) { + if (options_.testDiags('e')) { std::cout << "Reached max requests limit." << std::endl; } if (!tc_.waitToExit()) { @@ -85,35 +84,35 @@ BasicScen::checkExitConditions() { // 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 (options_.getMaxDrop().size() > 0) { + if (options_.getIpVersion() == 4) { if (stats_mgr.getDroppedPacketsNum(ExchangeType::DO) >= - options.getMaxDrop()[0]) { + options_.getMaxDrop()[0]) { max_drops = true; } - } else if (options.getIpVersion() == 6) { + } else if (options_.getIpVersion() == 6) { if (stats_mgr.getDroppedPacketsNum(ExchangeType::SA) >= - options.getMaxDrop()[0]) { + options_.getMaxDrop()[0]) { max_drops = true; } } } // Check if we reached maximum number of drops of ACK/REPLY packets. - if (options.getMaxDrop().size() > 1) { - if (options.getIpVersion() == 4) { + if (options_.getMaxDrop().size() > 1) { + if (options_.getIpVersion() == 4) { if (stats_mgr.getDroppedPacketsNum(ExchangeType::RA) >= - options.getMaxDrop()[1]) { + options_.getMaxDrop()[1]) { max_drops = true; } - } else if (options.getIpVersion() == 6) { + } else if (options_.getIpVersion() == 6) { if (stats_mgr.getDroppedPacketsNum(ExchangeType::RR) >= - options.getMaxDrop()[1]) { + options_.getMaxDrop()[1]) { max_drops = true; } } } if (max_drops) { - if (testDiags('e')) { + if (options_.testDiags('e')) { std::cout << "Reached maximum drops number." << std::endl; } if (!tc_.waitToExit()) { @@ -123,44 +122,44 @@ BasicScen::checkExitConditions() { // 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 (options_.getMaxDropPercentage().size() > 0) { + if (options_.getIpVersion() == 4) { if ((stats_mgr.getSentPacketsNum(ExchangeType::DO) > 10) && ((100. * stats_mgr.getDroppedPacketsNum(ExchangeType::DO) / stats_mgr.getSentPacketsNum(ExchangeType::DO)) >= - options.getMaxDropPercentage()[0])) { + options_.getMaxDropPercentage()[0])) { max_pdrops = true; } - } else if (options.getIpVersion() == 6) { + } else if (options_.getIpVersion() == 6) { if ((stats_mgr.getSentPacketsNum(ExchangeType::SA) > 10) && ((100. * stats_mgr.getDroppedPacketsNum(ExchangeType::SA) / stats_mgr.getSentPacketsNum(ExchangeType::SA)) >= - options.getMaxDropPercentage()[0])) { + options_.getMaxDropPercentage()[0])) { max_pdrops = true; } } } // Check if we reached maximum drops percentage of ACK/REPLY packets. - if (options.getMaxDropPercentage().size() > 1) { - if (options.getIpVersion() == 4) { + if (options_.getMaxDropPercentage().size() > 1) { + if (options_.getIpVersion() == 4) { if ((stats_mgr.getSentPacketsNum(ExchangeType::RA) > 10) && ((100. * stats_mgr.getDroppedPacketsNum(ExchangeType::RA) / stats_mgr.getSentPacketsNum(ExchangeType::RA)) >= - options.getMaxDropPercentage()[1])) { + options_.getMaxDropPercentage()[1])) { max_pdrops = true; } - } else if (options.getIpVersion() == 6) { + } else if (options_.getIpVersion() == 6) { if ((stats_mgr.getSentPacketsNum(ExchangeType::RR) > 10) && ((100. * stats_mgr.getDroppedPacketsNum(ExchangeType::RR) / stats_mgr.getSentPacketsNum(ExchangeType::RR)) >= - options.getMaxDropPercentage()[1])) { + options_.getMaxDropPercentage()[1])) { max_pdrops = true; } } } if (max_pdrops) { - if (testDiags('e')) { + if (options_.testDiags('e')) { std::cout << "Reached maximum percentage of drops." << std::endl; } if (!tc_.waitToExit()) { @@ -172,21 +171,15 @@ BasicScen::checkExitConditions() { int BasicScen::run() { - CommandOptions& options = CommandOptions::instance(); - - basic_rate_control_.setRate(options.getRate()); - renew_rate_control_.setRate(options.getRenewRate()); - release_rate_control_.setRate(options.getReleaseRate()); - StatsMgr& stats_mgr(tc_.getStatsMgr()); // Preload server with the number of packets. - if (options.getPreload() > 0) { - tc_.sendPackets(options.getPreload(), true); + if (options_.getPreload() > 0) { + tc_.sendPackets(options_.getPreload(), true); } // Fork and run command specified with -w - if (!options.getWrapped().empty()) { + if (!options_.getWrapped().empty()) { tc_.runWrapped(); } @@ -196,7 +189,7 @@ BasicScen::run() { // Calculate number of packets to be sent to stay // catch up with rate. uint64_t packets_due = basic_rate_control_.getOutboundMessageCount(); - if ((packets_due == 0) && testDiags('i')) { + if ((packets_due == 0) && options_.testDiags('i')) { stats_mgr.incrementCounter("shortwait"); } @@ -207,7 +200,7 @@ BasicScen::run() { // If there is nothing to do in this loop iteration then do some sleep to make // CPU idle for a moment, to not consume 100% CPU all the time // but only if it is not that high request rate expected. - if (options.getRate() < 10000 && packets_due == 0 && pkt_count == 0) { + if (options_.getRate() < 10000 && packets_due == 0 && pkt_count == 0) { /// @todo: need to implement adaptive time here, so the sleep time /// is not fixed, but adjusts to current situation. usleep(1); @@ -225,12 +218,12 @@ BasicScen::run() { // If -f option was specified we have to check how many // Renew packets should be sent to catch up with a desired rate. - if (options.getRenewRate() != 0) { + if (options_.getRenewRate() != 0) { uint64_t renew_packets_due = renew_rate_control_.getOutboundMessageCount(); // Send multiple renews to satisfy the desired rate. - if (options.getIpVersion() == 4) { + if (options_.getIpVersion() == 4) { tc_.sendMultipleRequests(renew_packets_due); } else { tc_.sendMultipleMessages6(DHCPV6_RENEW, renew_packets_due); @@ -239,7 +232,7 @@ BasicScen::run() { // If -F option was specified we have to check how many // Release messages should be sent to catch up with a desired rate. - if ((options.getIpVersion() == 6) && (options.getReleaseRate() != 0)) { + if ((options_.getIpVersion() == 6) && (options_.getReleaseRate() != 0)) { uint64_t release_packets_due = release_rate_control_.getOutboundMessageCount(); // Send Release messages. @@ -248,7 +241,7 @@ BasicScen::run() { // Report delay means that user requested printing number // of sent/received/dropped packets repeatedly. - if (options.getReportDelay() > 0) { + if (options_.getReportDelay() > 0) { tc_.printIntermediateStats(); } @@ -265,28 +258,28 @@ BasicScen::run() { tc_.printStats(); - if (!options.getWrapped().empty()) { + if (!options_.getWrapped().empty()) { // true means that we execute wrapped command with 'stop' argument. tc_.runWrapped(true); } // Print packet timestamps - if (testDiags('t')) { + if (options_.testDiags('t')) { stats_mgr.printTimestamps(); } // Print server id. - if (testDiags('s') && tc_.serverIdReceived()) { + if (options_.testDiags('s') && tc_.serverIdReceived()) { std::cout << "Server id: " << tc_.getServerId() << std::endl; } // Diagnostics flag 'e' means show exit reason. - if (testDiags('e')) { + if (options_.testDiags('e')) { std::cout << "Interrupted" << std::endl; } // Print packet templates. Even if -T options have not been specified the // dynamically build packet will be printed if at least one has been sent. - if (testDiags('T')) { + if (options_.testDiags('T')) { tc_.printTemplates(); } diff --git a/src/bin/perfdhcp/basic_scen.h b/src/bin/perfdhcp/basic_scen.h index 31df72eb22..b88986d7df 100644 --- a/src/bin/perfdhcp/basic_scen.h +++ b/src/bin/perfdhcp/basic_scen.h @@ -22,8 +22,17 @@ namespace perfdhcp { /// is continuously loaded with DHCP messages according to given rate. class BasicScen : public AbstractScen { public: - /// Default and the only constructor of BasicScen. - BasicScen() {}; + /// \brief Default and the only constructor of BasicScen. + /// + /// \param options reference to command options, + /// \param socket reference to a socket. + BasicScen(CommandOptions& options, BasePerfSocket &socket): + AbstractScen(options, socket) + { + basic_rate_control_.setRate(options_.getRate()); + renew_rate_control_.setRate(options_.getRenewRate()); + release_rate_control_.setRate(options_.getReleaseRate()); + }; /// brief\ Run performance test. /// @@ -33,11 +42,10 @@ public: /// /// \throw isc::InvalidOperation if command line options are not parsed. /// \throw isc::Unexpected if internal Test Controller error occurred. - /// \return error_code, 3 if number of received packets is not equal - /// to number of sent packets, 0 if everything is ok. - int run(); + /// \return execution status. + int run() override; -private: +protected: /// \brief A rate control class for Discover and Solicit messages. RateControl basic_rate_control_; /// \brief A rate control class for Renew messages. diff --git a/src/bin/perfdhcp/command_options.cc b/src/bin/perfdhcp/command_options.cc index 289b515621..f15b72d41d 100644 --- a/src/bin/perfdhcp/command_options.cc +++ b/src/bin/perfdhcp/command_options.cc @@ -97,12 +97,6 @@ CommandOptions::LeaseType::toText() const { } } -CommandOptions& -CommandOptions::instance() { - static CommandOptions options; - return (options); -} - void CommandOptions::reset() { // Default mac address used in DHCP messages @@ -854,7 +848,7 @@ bool CommandOptions::decodeMacString(const std::string& line) { } void -CommandOptions::validate() const { +CommandOptions::validate() { check((getIpVersion() != 4) && (isBroadcast() != 0), "-B is not compatible with IPv6 (-6)"); check((getIpVersion() != 6) && (isRapidCommit() != 0), @@ -931,6 +925,19 @@ CommandOptions::validate() const { << "WARNING: Better results are achieved when run in multi-threaded mode." << std::endl << "WARNING: To switch use -g multi option." << std::endl; } + + if (scenario_ == Scenario::AVALANCHE) { + check(getClientsNum() <= 0, + "in case of avalanche scenario number\nof clients must be specified" + " using -R option explicitly"); + + // in case of AVALANCHE drops ie. long responses should not be observed by perfdhcp + double dt[2] = { 1000.0, 1000.0 }; + drop_time_.assign(dt, dt + 2); + if (drop_time_set_) { + std::cout << "INFO: in avalanche scenario drop time is ignored" << std::endl; + } + } } void @@ -1265,15 +1272,6 @@ CommandOptions::version() const { std::cout << "VERSION: " << VERSION << std::endl; } -bool -testDiags(const char diag) { - std::string diags(CommandOptions::instance().getDiags()); - if (diags.find(diag) != std::string::npos) { - return (true); - } - return (false); -} - } // namespace perfdhcp } // namespace isc diff --git a/src/bin/perfdhcp/command_options.h b/src/bin/perfdhcp/command_options.h index 92ca961cf4..693a2458e2 100644 --- a/src/bin/perfdhcp/command_options.h +++ b/src/bin/perfdhcp/command_options.h @@ -30,6 +30,14 @@ enum class Scenario { class CommandOptions : public boost::noncopyable { public: + /// \brief Default Constructor. + /// + /// Private constructor as this is a singleton class. + /// Use CommandOptions::instance() to get instance of it. + CommandOptions() { + reset(); + } + /// @brief A vector holding MAC addresses. typedef std::vector > MacAddrsVector; @@ -111,12 +119,6 @@ public: DORA_SARR }; - /// CommandOptions is a singleton class. This method returns reference - /// to its sole instance. - /// - /// \return the only existing instance of command options - static CommandOptions& instance(); - /// \brief Reset to defaults /// /// Reset data members to default values. This is specifically @@ -363,6 +365,18 @@ public: /// \return server name. std::string getServerName() const { return server_name_; } + + /// \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. + bool testDiags(const char diag) { + if (getDiags().find(diag) != std::string::npos) { + return (true); + } + return (false); + } + /// \brief Print command line arguments. void printCommandLine() const; @@ -377,15 +391,6 @@ public: void version() const; private: - - /// \brief Default Constructor. - /// - /// Private constructor as this is a singleton class. - /// Use CommandOptions::instance() to get instance of it. - CommandOptions() { - reset(); - } - /// \brief Initializes class members based on the command line. /// /// Reads each command line parameter and sets class member values. @@ -400,7 +405,7 @@ private: /// \brief Validates initialized options. /// /// \throws isc::InvalidParameter if command line validation fails. - void validate() const; + void validate(); /// \brief Throws !InvalidParameter exception if condition is true. /// @@ -669,13 +674,6 @@ private: Scenario scenario_; }; -/// \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. -bool -testDiags(const char diag); - } // namespace perfdhcp } // namespace isc diff --git a/src/bin/perfdhcp/main.cc b/src/bin/perfdhcp/main.cc index 2ab854208a..aa002d78ab 100644 --- a/src/bin/perfdhcp/main.cc +++ b/src/bin/perfdhcp/main.cc @@ -19,7 +19,7 @@ using namespace isc::perfdhcp; int main(int argc, char* argv[]) { - CommandOptions& command_options = CommandOptions::instance(); + CommandOptions command_options; std::string diags(command_options.getDiags()); int ret_code = 0; try { @@ -36,7 +36,7 @@ main(int argc, char* argv[]) { } catch(isc::Exception& e) { ret_code = 1; command_options.usage(); - std::cerr << "Error parsing command line options: " + std::cerr << "\nERROR: parsing command line options: " << e.what() << std::endl; if (diags.find('e') != std::string::npos) { std::cerr << "Fatal error" << std::endl; @@ -45,16 +45,17 @@ main(int argc, char* argv[]) { } try{ auto scenario = command_options.getScenario(); + PerfSocket socket(command_options); if (scenario == Scenario::BASIC) { - BasicScen scen; + BasicScen scen(command_options, socket); ret_code = scen.run(); } else if (scenario == Scenario::AVALANCHE) { - AvalancheScen scen; + AvalancheScen scen(command_options, socket); ret_code = scen.run(); } } catch (std::exception& e) { ret_code = 1; - std::cerr << "Error running perfdhcp: " << e.what() << std::endl; + std::cerr << "\nERROR: running perfdhcp: " << e.what() << std::endl; if (diags.find('e') != std::string::npos) { std::cerr << "Fatal error" << std::endl; } diff --git a/src/bin/perfdhcp/perf_socket.cc b/src/bin/perfdhcp/perf_socket.cc index e9a55dfa2b..a27296682a 100644 --- a/src/bin/perfdhcp/perf_socket.cc +++ b/src/bin/perfdhcp/perf_socket.cc @@ -20,17 +20,14 @@ using namespace isc::asiolink; namespace isc { namespace perfdhcp { -PerfSocket::PerfSocket() : - SocketInfo(asiolink::IOAddress("127.0.0.1"), 0, openSocket()), - ifindex_(0) -{ +PerfSocket::PerfSocket(CommandOptions& options) { + sockfd_ = openSocket(options); initSocketData(); } int -PerfSocket::openSocket() const { - CommandOptions& options = CommandOptions::instance(); +PerfSocket::openSocket(CommandOptions& options) const { std::string localname = options.getLocalName(); std::string servername = options.getServerName(); uint16_t port = options.getLocalPort(); @@ -149,5 +146,42 @@ PerfSocket::initSocketData() { isc_throw(BadValue, "interface for specified socket descriptor not found"); } +Pkt4Ptr +PerfSocket::receive4(uint32_t timeout_sec, uint32_t timeout_usec) { + Pkt4Ptr pkt = IfaceMgr::instance().receive4(timeout_sec, timeout_usec); + if (pkt) { + /// @todo: Add packet exception handling here. Right now any + /// malformed packet will cause perfdhcp to abort. + pkt->unpack(); + } + return (pkt); +} + +Pkt6Ptr +PerfSocket::receive6(uint32_t timeout_sec, uint32_t timeout_usec) { + Pkt6Ptr pkt = IfaceMgr::instance().receive6(timeout_sec, timeout_usec); + if (pkt) { + /// @todo: Add packet exception handling here. Right now any + /// malformed packet will cause perfdhcp to abort. + pkt->unpack(); + } + return (pkt); +} + +bool +PerfSocket::send(const Pkt4Ptr& pkt) { + return IfaceMgr::instance().send(pkt); +} + +bool +PerfSocket::send(const Pkt6Ptr& pkt) { + return IfaceMgr::instance().send(pkt); +} + +IfacePtr +PerfSocket::getIface() { + return (IfaceMgr::instance().getIface(ifindex_)); +} + } } diff --git a/src/bin/perfdhcp/perf_socket.h b/src/bin/perfdhcp/perf_socket.h index a49cc92e32..83f8b971b8 100644 --- a/src/bin/perfdhcp/perf_socket.h +++ b/src/bin/perfdhcp/perf_socket.h @@ -7,11 +7,34 @@ #ifndef PERF_SOCKET_H #define PERF_SOCKET_H +#include + +#include +#include #include +#include namespace isc { namespace perfdhcp { +class BasePerfSocket : public dhcp::SocketInfo { +public: + /// Interface index. + uint16_t ifindex_; + + BasePerfSocket() : + SocketInfo(asiolink::IOAddress("127.0.0.1"), 0, 0), + ifindex_(0) {} + + /// \brief Destructor of the socket wrapper class. + virtual ~BasePerfSocket() = default; + + virtual dhcp::Pkt4Ptr receive4(uint32_t timeout_sec, uint32_t timeout_usec) = 0; + virtual dhcp::Pkt6Ptr receive6(uint32_t timeout_sec, uint32_t timeout_usec) = 0; + virtual bool send(const dhcp::Pkt4Ptr& pkt) = 0; + virtual bool send(const dhcp::Pkt6Ptr& pkt) = 0; + virtual dhcp::IfacePtr getIface() = 0; +}; /// \brief Socket wrapper structure. /// @@ -22,23 +45,52 @@ namespace perfdhcp { /// when exception occurs). This structure extends parent /// structure with new field ifindex_ that holds interface /// index where socket is bound to. -struct PerfSocket : public dhcp::SocketInfo { - /// Interface index. - uint16_t ifindex_; - +class PerfSocket : public BasePerfSocket { +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. - PerfSocket(); + PerfSocket(CommandOptions& options); /// \brief Destructor of the socket wrapper class. /// /// Destructor closes wrapped socket. virtual ~PerfSocket(); -private: + /// \brief Receive DHCPv4 packet from interface. + /// + /// \param timeout_sec number of seconds for waiting for a packet, + /// \param timeout_sec number of microseconds for waiting for a packet, + /// \return received packet or nullptr if timed out + virtual dhcp::Pkt4Ptr receive4(uint32_t timeout_sec, uint32_t timeout_usec) override; + + /// \brief Receive DHCPv6 packet from interface. + /// + /// \param timeout_sec number of seconds for waiting for a packet, + /// \param timeout_sec number of microseconds for waiting for a packet, + /// \return received packet or nullptr if timed out + virtual dhcp::Pkt6Ptr receive6(uint32_t timeout_sec, uint32_t timeout_usec) override; + + /// \brief Send DHCPv4 packet through interface. + /// + /// \param pkt a packet for sending + /// \return true if operation succeeded + virtual bool send(const dhcp::Pkt4Ptr& pkt) override; + + /// \brief Send DHCPv6 packet through interface. + /// + /// \param pkt a packet for sending + /// \return true if operation succeeded + virtual bool send(const dhcp::Pkt6Ptr& pkt) override; + + /// \brief Get interface from IfaceMgr. + /// + /// \return shared pointer to Iface. + virtual dhcp::IfacePtr getIface() override; + +protected: /// \brief Initialize socket data. /// /// This method initializes members of the class that Interface @@ -66,7 +118,7 @@ private: /// for the v6 socket. /// \throw isc::Unexpected if internal unexpected error occurred. /// \return socket descriptor. - int openSocket() const; + int openSocket(CommandOptions& options) const; }; } diff --git a/src/bin/perfdhcp/receiver.cc b/src/bin/perfdhcp/receiver.cc index 68cff6bfd1..d55830220c 100644 --- a/src/bin/perfdhcp/receiver.cc +++ b/src/bin/perfdhcp/receiver.cc @@ -61,7 +61,7 @@ Receiver::getPkt() { // In multi thread mode read packet from the queue which is feed by Receiver thread. util::thread::Mutex::Locker lock(pkt_queue_mutex_); if (pkt_queue_.empty()) { - if (CommandOptions::instance().getIpVersion() == 4) { + if (ip_version_ == 4) { return Pkt4Ptr(); } else { return Pkt6Ptr(); @@ -109,21 +109,15 @@ Receiver::readPktFromSocket() { timeout = 1000; } try { - if (CommandOptions::instance().getIpVersion() == 4) { - pkt = IfaceMgr::instance().receive4(0, timeout); + if (ip_version_ == 4) { + pkt = socket_.receive4(0, timeout); } else { - pkt = IfaceMgr::instance().receive6(0, timeout); + pkt = socket_.receive6(0, timeout); } } catch (const Exception& e) { cerr << "Failed to receive DHCP packet: " << e.what() << endl; } - if (pkt) { - /// @todo: Add packet exception handling here. Right now any - /// malformed packet will cause perfdhcp to abort. - pkt->unpack(); - } - return (pkt); } diff --git a/src/bin/perfdhcp/receiver.h b/src/bin/perfdhcp/receiver.h index 4a0afc5a54..e51e4f4076 100644 --- a/src/bin/perfdhcp/receiver.h +++ b/src/bin/perfdhcp/receiver.h @@ -46,15 +46,23 @@ private: /// \brief Mutex for controlling access to the queue. util::thread::Mutex pkt_queue_mutex_; + BasePerfSocket &socket_; + /// \brief Single- or thread-mode indicator. bool single_threaded_; + uint8_t ip_version_; + public: /// \brief Receiver constructor. /// /// \param socket A socket for receiving packets. - Receiver() : - single_threaded_(CommandOptions::instance().isSingleThreaded()) { + /// \param single_threaded A flag indicating running mode. + /// \param ip_version An IP version: 4 or 6 + Receiver(BasePerfSocket &socket, bool single_threaded, uint8_t ip_version) : + socket_(socket), + single_threaded_(single_threaded), + ip_version_(ip_version) { } /// \brief Destructor. diff --git a/src/bin/perfdhcp/stats_mgr.cc b/src/bin/perfdhcp/stats_mgr.cc index bd741e31ed..51c706804a 100644 --- a/src/bin/perfdhcp/stats_mgr.cc +++ b/src/bin/perfdhcp/stats_mgr.cc @@ -5,7 +5,6 @@ // file, You can obtain one at http://mozilla.org/MPL/2.0/. #include -#include namespace isc { @@ -78,7 +77,9 @@ ExchangeStats::updateDelays(const dhcp::PktPtr& sent_packet, rcvd_time.is_not_a_date_time()) { isc_throw(Unexpected, "Timestamp must be set for sent and " - "received packet to measure RTT"); + "received packet to measure RTT," + << " sent: " << sent_time + << " recv: " << rcvd_time); } boost::posix_time::time_period period(sent_time, rcvd_time); // We don't bother calculating deltas in nanoseconds. It is much @@ -313,16 +314,15 @@ ExchangeStats::printTimestamps() { } } -StatsMgr::StatsMgr() : +StatsMgr::StatsMgr(CommandOptions& options) : exchanges_(), - boot_time_(boost::posix_time::microsec_clock::universal_time()) + boot_time_(boost::posix_time::microsec_clock::universal_time()), + options_(options) { - CommandOptions& options = CommandOptions::instance(); - // Check if packet archive mode is required. If user // requested diagnostics option -x t we have to enable // it so as StatsMgr preserves all packets. - archive_enabled_ = testDiags('t') ? true : false; + archive_enabled_ = options.testDiags('t') ? true : false; if (options.getIpVersion() == 4) { addExchangeStats(ExchangeType::DO, options.getDropTime()[0]); @@ -345,7 +345,7 @@ StatsMgr::StatsMgr() : addExchangeStats(ExchangeType::RL); } } - if (testDiags('i')) { + if (options.testDiags('i')) { addCustomCounter("shortwait", "Short waits for packets"); } } diff --git a/src/bin/perfdhcp/stats_mgr.h b/src/bin/perfdhcp/stats_mgr.h index b05a75b800..351e51be08 100644 --- a/src/bin/perfdhcp/stats_mgr.h +++ b/src/bin/perfdhcp/stats_mgr.h @@ -9,6 +9,7 @@ #include #include +#include #include #include @@ -649,7 +650,7 @@ public: /// the test. If this is not selected archiving should be disabled /// for performance reasons and to avoid waste of memory for storing /// large list of archived packets. - StatsMgr(); + StatsMgr(CommandOptions& options); /// \brief Specify new exchange type. /// @@ -1109,6 +1110,8 @@ private: bool archive_enabled_; boost::posix_time::ptime boot_time_; ///< Time when test is started. + + CommandOptions& options_; }; /// Pointer to Statistics Manager; diff --git a/src/bin/perfdhcp/test_control.cc b/src/bin/perfdhcp/test_control.cc index 9a5cad8310..1493a8f1e6 100644 --- a/src/bin/perfdhcp/test_control.cc +++ b/src/bin/perfdhcp/test_control.cc @@ -45,8 +45,7 @@ bool TestControl::interrupted_ = false; bool TestControl::waitToExit() const { static ptime exit_time = ptime(not_a_date_time); - CommandOptions& options = CommandOptions::instance(); - uint32_t wait_time = options.getExitWaitTime(); + uint32_t wait_time = options_.getExitWaitTime(); // If we care and not all packets are in yet if (wait_time && !haveAllPacketsBeenReceived()) { @@ -67,9 +66,8 @@ TestControl::waitToExit() const { bool TestControl::haveAllPacketsBeenReceived() const { - const CommandOptions& options = CommandOptions::instance(); - const uint8_t& ipversion = options.getIpVersion(); - const std::vector& num_request = options.getNumRequests(); + const uint8_t& ipversion = options_.getIpVersion(); + const std::vector& num_request = options_.getNumRequests(); const size_t& num_request_size = num_request.size(); if (num_request_size == 0) { @@ -95,10 +93,9 @@ TestControl::haveAllPacketsBeenReceived() const { void TestControl::cleanCachedPackets() { - CommandOptions& options = CommandOptions::instance(); // When Renews are not sent, Reply packets are not cached so there // is nothing to do. - if (options.getRenewRate() == 0) { + if (options_.getRenewRate() == 0) { return; } @@ -115,9 +112,9 @@ TestControl::cleanCachedPackets() { // since we want to randomize leases to be renewed so leave 5 // times more packets to randomize from. /// @todo The cache size might be controlled from the command line. - if (reply_storage_.size() > 5 * options.getRenewRate()) { + if (reply_storage_.size() > 5 * options_.getRenewRate()) { reply_storage_.clear(reply_storage_.size() - - 5 * options.getRenewRate()); + 5 * options_.getRenewRate()); } // Remember when we performed a cleanup for the last time. // We want to do the next cleanup not earlier than in one second. @@ -132,7 +129,7 @@ TestControl::copyIaOptions(const Pkt6Ptr& pkt_from, Pkt6Ptr& pkt_to) { " for the copyIaOptions function"); } // IA_NA - if (CommandOptions::instance().getLeaseType() + if (options_.getLeaseType() .includes(CommandOptions::LeaseType::ADDRESS)) { OptionPtr option = pkt_from->getOption(D6O_IA_NA); if (!option) { @@ -142,7 +139,7 @@ TestControl::copyIaOptions(const Pkt6Ptr& pkt_from, Pkt6Ptr& pkt_to) { pkt_to->addOption(option); } // IA_PD - if (CommandOptions::instance().getLeaseType() + if (options_.getLeaseType() .includes(CommandOptions::LeaseType::PREFIX)) { OptionPtr option = pkt_from->getOption(D6O_IA_PD); if (!option) { @@ -311,9 +308,7 @@ TestControl::factoryRequestList4(Option::Universe u, std::vector TestControl::generateMacAddress(uint8_t& randomized) { - CommandOptions& options = CommandOptions::instance(); - - const CommandOptions::MacAddrsVector& macs = options.getMacsFromFile(); + const CommandOptions::MacAddrsVector& macs = options_.getMacsFromFile(); // if we are using the -M option return a random one from the list... if (macs.size() > 0) { uint16_t r = number_generator_(); @@ -324,12 +319,12 @@ TestControl::generateMacAddress(uint8_t& randomized) { } else { // ... otherwise use the standard behavior - uint32_t clients_num = options.getClientsNum(); + uint32_t clients_num = options_.getClientsNum(); if (clients_num < 2) { - return (options.getMacTemplate()); + return (options_.getMacTemplate()); } // Get the base MAC address. We are going to randomize part of it. - std::vector mac_addr(options.getMacTemplate()); + std::vector mac_addr(options_.getMacTemplate()); if (mac_addr.size() != HW_ETHER_LEN) { isc_throw(BadValue, "invalid MAC address template specified"); } @@ -368,9 +363,8 @@ TestControl::generateClientId(const dhcp::HWAddrPtr& hwaddr) const { std::vector TestControl::generateDuid(uint8_t& randomized) { - CommandOptions& options = CommandOptions::instance(); std::vector mac_addr(generateMacAddress(randomized)); - const CommandOptions::MacAddrsVector& macs = options.getMacsFromFile(); + const CommandOptions::MacAddrsVector& macs = options_.getMacsFromFile(); // pick a random mac address if we are using option -M.. if (macs.size() > 0) { uint16_t r = number_generator_(); @@ -399,12 +393,12 @@ TestControl::generateDuid(uint8_t& randomized) { std::copy(mac.begin(), mac.end(), duid.begin() + 4); return (duid); } else { - uint32_t clients_num = options.getClientsNum(); + uint32_t clients_num = options_.getClientsNum(); if ((clients_num == 0) || (clients_num == 1)) { - return (options.getDuidTemplate()); + return (options_.getDuidTemplate()); } // Get the base DUID. We are going to randomize part of it. - std::vector duid(options.getDuidTemplate()); + std::vector duid(options_.getDuidTemplate()); /// @todo: add support for DUIDs of different sizes. duid.resize(duid.size()); std::copy(mac_addr.begin(), mac_addr.end(), @@ -415,10 +409,10 @@ TestControl::generateDuid(uint8_t& randomized) { int TestControl::getElapsedTimeOffset() const { - int elp_offset = CommandOptions::instance().getIpVersion() == 4 ? + int elp_offset = options_.getIpVersion() == 4 ? DHCPV4_ELAPSED_TIME_OFFSET : DHCPV6_ELAPSED_TIME_OFFSET; - if (CommandOptions::instance().getElapsedTimeOffset() > 0) { - elp_offset = CommandOptions::instance().getElapsedTimeOffset(); + if (options_.getElapsedTimeOffset() > 0) { + elp_offset = options_.getElapsedTimeOffset(); } return (elp_offset); } @@ -440,30 +434,30 @@ TestControl::getElapsedTime(const T& pkt1, const T& pkt2) { int TestControl::getRandomOffset(const int arg_idx) const { - int rand_offset = CommandOptions::instance().getIpVersion() == 4 ? + int rand_offset = options_.getIpVersion() == 4 ? DHCPV4_RANDOMIZATION_OFFSET : DHCPV6_RANDOMIZATION_OFFSET; - if (CommandOptions::instance().getRandomOffset().size() > arg_idx) { - rand_offset = CommandOptions::instance().getRandomOffset()[arg_idx]; + if (options_.getRandomOffset().size() > arg_idx) { + rand_offset = options_.getRandomOffset()[arg_idx]; } return (rand_offset); } int TestControl::getRequestedIpOffset() const { - int rip_offset = CommandOptions::instance().getIpVersion() == 4 ? + int rip_offset = options_.getIpVersion() == 4 ? DHCPV4_REQUESTED_IP_OFFSET : DHCPV6_IA_NA_OFFSET; - if (CommandOptions::instance().getRequestedIpOffset() > 0) { - rip_offset = CommandOptions::instance().getRequestedIpOffset(); + if (options_.getRequestedIpOffset() > 0) { + rip_offset = options_.getRequestedIpOffset(); } return (rip_offset); } int TestControl::getServerIdOffset() const { - int srvid_offset = CommandOptions::instance().getIpVersion() == 4 ? + int srvid_offset = options_.getIpVersion() == 4 ? DHCPV4_SERVERID_OFFSET : DHCPV6_SERVERID_OFFSET; - if (CommandOptions::instance().getServerIdOffset() > 0) { - srvid_offset = CommandOptions::instance().getServerIdOffset(); + if (options_.getServerIdOffset() > 0) { + srvid_offset = options_.getServerIdOffset(); } return (srvid_offset); } @@ -478,10 +472,10 @@ TestControl::getTemplateBuffer(const size_t idx) const { int TestControl::getTransactionIdOffset(const int arg_idx) const { - int xid_offset = CommandOptions::instance().getIpVersion() == 4 ? + int xid_offset = options_.getIpVersion() == 4 ? DHCPV4_TRANSID_OFFSET : DHCPV6_TRANSID_OFFSET; - if (CommandOptions::instance().getTransactionIdOffset().size() > arg_idx) { - xid_offset = CommandOptions::instance().getTransactionIdOffset()[arg_idx]; + if (options_.getTransactionIdOffset().size() > arg_idx) { + xid_offset = options_.getTransactionIdOffset()[arg_idx]; } return (xid_offset); } @@ -504,8 +498,7 @@ TestControl::initPacketTemplates() { template_packets_v4_.clear(); template_packets_v6_.clear(); template_buffers_.clear(); - CommandOptions& options = CommandOptions::instance(); - std::vector template_files = options.getTemplateFiles(); + std::vector template_files = options_.getTemplateFiles(); for (std::vector::const_iterator it = template_files.begin(); it != template_files.end(); ++it) { readPacketTemplate(*it); @@ -515,9 +508,8 @@ TestControl::initPacketTemplates() { void TestControl::sendPackets(const uint64_t packets_num, const bool preload /* = false */) { - CommandOptions& options = CommandOptions::instance(); for (uint64_t i = packets_num; i > 0; --i) { - if (options.getIpVersion() == 4) { + if (options_.getIpVersion() == 4) { // No template packets means that no -T option was specified. // We have to build packets ourselves. if (template_buffers_.empty()) { @@ -564,15 +556,14 @@ TestControl::sendMultipleMessages6(const uint32_t msg_type, void TestControl::printDiagnostics() const { - CommandOptions& options = CommandOptions::instance(); - if (testDiags('a')) { + if (options_.testDiags('a')) { // Print all command line parameters. - options.printCommandLine(); + options_.printCommandLine(); // Print MAC and DUID. - std::cout << "Set MAC to " << vector2Hex(options.getMacTemplate(), "::") + std::cout << "Set MAC to " << vector2Hex(options_.getMacTemplate(), "::") << std::endl; - if (options.getDuidTemplate().size() > 0) { - std::cout << "Set DUID to " << vector2Hex(options.getDuidTemplate()) << std::endl; + if (options_.getDuidTemplate().size() > 0) { + std::cout << "Set DUID to " << vector2Hex(options_.getDuidTemplate()) << std::endl; } } } @@ -581,7 +572,7 @@ void TestControl::printTemplate(const uint8_t packet_type) const { std::string hex_buf; int arg_idx = 0; - if (CommandOptions::instance().getIpVersion() == 4) { + if (options_.getIpVersion() == 4) { if (packet_type == DHCPREQUEST) { arg_idx = 1; } @@ -595,7 +586,7 @@ TestControl::printTemplate(const uint8_t packet_type) const { std::vector buf(out_buf_data, out_buf_data + out_buf.getLength()); hex_buf = vector2Hex(buf); } - } else if (CommandOptions::instance().getIpVersion() == 6) { + } else if (options_.getIpVersion() == 6) { if (packet_type == DHCPV6_REQUEST) { arg_idx = 1; } @@ -636,11 +627,10 @@ TestControl::printTemplate(const uint8_t packet_type) const { void TestControl::printTemplates() const { - CommandOptions& options = CommandOptions::instance(); - if (options.getIpVersion() == 4) { + if (options_.getIpVersion() == 4) { printTemplate(DHCPDISCOVER); printTemplate(DHCPREQUEST); - } else if (options.getIpVersion() == 6) { + } else if (options_.getIpVersion() == 6) { printTemplate(DHCPV6_SOLICIT); printTemplate(DHCPV6_REQUEST); } @@ -649,22 +639,21 @@ TestControl::printTemplates() const { void TestControl::printRate() const { double rate = 0; - CommandOptions& options = CommandOptions::instance(); std::string exchange_name = "4-way exchanges"; ExchangeType xchg_type = ExchangeType::DO; - if (options.getIpVersion() == 4) { + if (options_.getIpVersion() == 4) { xchg_type = - options.getExchangeMode() == CommandOptions::DO_SA ? + options_.getExchangeMode() == CommandOptions::DO_SA ? ExchangeType::DO : ExchangeType::RA; if (xchg_type == ExchangeType::DO) { exchange_name = "DISCOVER-OFFER"; } - } else if (options.getIpVersion() == 6) { + } else if (options_.getIpVersion() == 6) { xchg_type = - options.getExchangeMode() == CommandOptions::DO_SA ? + options_.getExchangeMode() == CommandOptions::DO_SA ? ExchangeType::SA : ExchangeType::RR; if (xchg_type == ExchangeType::SA) { - exchange_name = options.isRapidCommit() ? "Solicit-Reply" : + exchange_name = options_.isRapidCommit() ? "Solicit-Reply" : "Solicit-Advertise"; } } @@ -674,8 +663,8 @@ TestControl::printRate() const { std::ostringstream s; s << "***Rate statistics***" << std::endl; s << "Rate: " << rate << " " << exchange_name << "/second"; - if (options.getRate() > 0) { - s << ", expected rate: " << options.getRate() << std::endl; + if (options_.getRate() > 0) { + s << ", expected rate: " << options_.getRate() << std::endl; } std::cout << s.str() << std::endl; @@ -683,8 +672,7 @@ TestControl::printRate() const { void TestControl::printIntermediateStats() { - CommandOptions& options = CommandOptions::instance(); - int delay = options.getReportDelay(); + 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) { @@ -697,7 +685,7 @@ void TestControl::printStats() const { printRate(); stats_mgr_.printStats(); - if (testDiags('i')) { + if (options_.testDiags('i')) { stats_mgr_.printCustomCounters(); } } @@ -771,8 +759,7 @@ TestControl::processReceivedPacket4(const Pkt4Ptr& pkt4) { if (pkt4->getType() == DHCPOFFER) { PktPtr pkt = stats_mgr_.passRcvdPacket(ExchangeType::DO, pkt4); Pkt4Ptr discover_pkt4(boost::dynamic_pointer_cast(pkt)); - CommandOptions::ExchangeMode xchg_mode = - CommandOptions::instance().getExchangeMode(); + CommandOptions::ExchangeMode xchg_mode = options_.getExchangeMode(); if ((xchg_mode == CommandOptions::DORA_SARR) && discover_pkt4) { if (template_buffers_.size() < 2) { sendRequest4(discover_pkt4, pkt4); @@ -814,8 +801,7 @@ TestControl::processReceivedPacket6(const Pkt6Ptr& pkt6) { if (packet_type == DHCPV6_ADVERTISE) { PktPtr pkt = stats_mgr_.passRcvdPacket(ExchangeType::SA, pkt6); Pkt6Ptr solicit_pkt6(boost::dynamic_pointer_cast(pkt)); - CommandOptions::ExchangeMode xchg_mode = - CommandOptions::instance().getExchangeMode(); + CommandOptions::ExchangeMode xchg_mode = options_.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 @@ -869,7 +855,7 @@ TestControl::consumeReceivedPackets() { PktPtr pkt; while ((pkt = receiver_.getPkt())) { pkt_count += 1; - if (CommandOptions::instance().getIpVersion() == 4) { + if (options_.getIpVersion() == 4) { Pkt4Ptr pkt4 = boost::dynamic_pointer_cast(pkt); processReceivedPacket4(pkt4); } else { @@ -940,8 +926,7 @@ TestControl::registerOptionFactories6() const { void TestControl::registerOptionFactories() const { - CommandOptions& options = CommandOptions::instance(); - switch(options.getIpVersion()) { + switch(options_.getIpVersion()) { case 4: registerOptionFactories4(); break; @@ -966,21 +951,24 @@ TestControl::reset() { interrupted_ = false; } -TestControl::TestControl() : - number_generator_(0, CommandOptions::instance().getMacsFromFile().size()) +TestControl::TestControl(CommandOptions& options, BasePerfSocket &socket) : + number_generator_(0, options.getMacsFromFile().size()), + socket_(socket), + receiver_(socket, options.isSingleThreaded(), options.getIpVersion()), + stats_mgr_(options), + options_(options) { // 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 // was not called prior to starting the test. This is fatal // error. - if (options.getIpVersion() == 0) { + if (options_.getIpVersion() == 0) { isc_throw(InvalidOperation, "command options must be parsed before running a test"); - } else if (options.getIpVersion() == 4) { + } else if (options_.getIpVersion() == 4) { // Turn off packet queueing. IfaceMgr::instance().configureDHCPPacketQueue(AF_INET, data::ElementPtr()); setTransidGenerator(NumberGeneratorPtr(new SequentialGenerator())); @@ -990,8 +978,8 @@ TestControl::TestControl() : setTransidGenerator(NumberGeneratorPtr(new SequentialGenerator(0x00FFFFFF))); } - uint32_t clients_num = options.getClientsNum() == 0 ? - 1 : options.getClientsNum(); + uint32_t clients_num = options_.getClientsNum() == 0 ? + 1 : options_.getClientsNum(); setMacAddrGenerator(NumberGeneratorPtr(new SequentialGenerator(clients_num))); // Diagnostics are command line options mainly. @@ -1001,8 +989,8 @@ TestControl::TestControl() : // Initialize packet templates. initPacketTemplates(); // Initialize randomization seed. - if (options.isSeeded()) { - srandom(options.getSeed()); + if (options_.isSeeded()) { + srandom(options_.getSeed()); } else { // Seed with current time. time_period duration(from_iso_string("20111231T235959"), @@ -1016,15 +1004,14 @@ TestControl::TestControl() : void TestControl::runWrapped(bool do_stop /*= false */) const { - CommandOptions& options = CommandOptions::instance(); - if (!options.getWrapped().empty()) { + if (!options_.getWrapped().empty()) { pid_t pid = 0; signal(SIGCHLD, handleChild); pid = fork(); if (pid < 0) { isc_throw(Unexpected, "unable to fork"); } else if (pid == 0) { - execlp(options.getWrapped().c_str(), + execlp(options_.getWrapped().c_str(), do_stop ? "stop" : "start", NULL); } @@ -1033,7 +1020,7 @@ TestControl::runWrapped(bool do_stop /*= false */) const { void TestControl::saveFirstPacket(const Pkt4Ptr& pkt) { - if (testDiags('T')) { + if (options_.testDiags('T')) { if (template_packets_v4_.find(pkt->getType()) == template_packets_v4_.end()) { template_packets_v4_[pkt->getType()] = pkt; } @@ -1042,7 +1029,7 @@ TestControl::saveFirstPacket(const Pkt4Ptr& pkt) { void TestControl::saveFirstPacket(const Pkt6Ptr& pkt) { - if (testDiags('T')) { + if (options_.testDiags('T')) { if (template_packets_v6_.find(pkt->getType()) == template_packets_v6_.end()) { template_packets_v6_[pkt->getType()] = pkt; } @@ -1086,7 +1073,7 @@ TestControl::sendDiscover4(const bool preload /*= false*/) { addExtraOpts(pkt4); pkt4->pack(); - IfaceMgr::instance().send(pkt4); + socket_.send(pkt4); if (!preload) { stats_mgr_.passSentPacket(ExchangeType::DO, pkt4); } @@ -1130,7 +1117,7 @@ TestControl::sendDiscover4(const std::vector& template_buf, // Pack the input packet buffer to output buffer so as it can // be sent to server. pkt4->rawPack(); - IfaceMgr::instance().send(boost::static_pointer_cast(pkt4)); + socket_.send(boost::static_pointer_cast(pkt4)); if (!preload) { // Update packet stats. stats_mgr_.passSentPacket(ExchangeType::DO, @@ -1156,7 +1143,7 @@ TestControl::sendRequestFromAck() { msg->pack(); // And send it. - IfaceMgr::instance().send(msg); + socket_.send(msg); stats_mgr_.passSentPacket(ExchangeType::RNA, msg); return (true); } @@ -1182,7 +1169,7 @@ TestControl::sendMessageFromReply(const uint16_t msg_type) { msg->pack(); // And send it. - IfaceMgr::instance().send(msg); + socket_.send(msg); stats_mgr_.passSentPacket((msg_type == DHCPV6_RENEW ? ExchangeType::RN : ExchangeType::RL), msg); return (true); @@ -1197,7 +1184,7 @@ TestControl::sendRequest4(const dhcp::Pkt4Ptr& discover_pkt4, // Use first flags indicates that we want to use the server // id captured in first packet. - if (CommandOptions::instance().isUseFirst() && + if (options_.isUseFirst() && (first_packet_serverid_.size() > 0)) { pkt4->addOption(Option::factory(Option::V4, DHO_DHCP_SERVER_IDENTIFIER, first_packet_serverid_)); @@ -1244,7 +1231,7 @@ TestControl::sendRequest4(const dhcp::Pkt4Ptr& discover_pkt4, pkt4->setSecs(static_cast(elapsed_time / 1000)); // Prepare on wire data to send. pkt4->pack(); - IfaceMgr::instance().send(pkt4); + socket_.send(pkt4); stats_mgr_.passSentPacket(ExchangeType::RA, pkt4); saveFirstPacket(pkt4); } @@ -1297,7 +1284,7 @@ TestControl::sendRequest4(const std::vector& template_buf, size_t sid_offset = getServerIdOffset(); // Use first flags indicates that we want to use the server // id captured in first packet. - if (CommandOptions::instance().isUseFirst() && + if (options_.isUseFirst() && (first_packet_serverid_.size() > 0)) { boost::shared_ptr opt_serverid(new LocalizedOption(Option::V4, @@ -1351,7 +1338,7 @@ TestControl::sendRequest4(const std::vector& template_buf, // Prepare on-wire data. pkt4->rawPack(); - IfaceMgr::instance().send(boost::static_pointer_cast(pkt4)); + socket_.send(boost::static_pointer_cast(pkt4)); // Update packet stats. stats_mgr_.passSentPacket(ExchangeType::RA, boost::static_pointer_cast(pkt4)); @@ -1375,7 +1362,7 @@ TestControl::sendRequest6(const Pkt6Ptr& advertise_pkt6) { // Use first flags indicates that we want to use the server // id captured in first packet. - if (CommandOptions::instance().isUseFirst() && + if (options_.isUseFirst() && (first_packet_serverid_.size() > 0)) { pkt6->addOption(Option::factory(Option::V6, D6O_SERVERID, first_packet_serverid_)); @@ -1405,7 +1392,7 @@ TestControl::sendRequest6(const Pkt6Ptr& advertise_pkt6) { // Prepare on-wire data. pkt6->pack(); - IfaceMgr::instance().send(pkt6); + socket_.send(pkt6); stats_mgr_.passSentPacket(ExchangeType::RR, pkt6); saveFirstPacket(pkt6); } @@ -1433,7 +1420,7 @@ TestControl::sendRequest6(const std::vector& template_buf, size_t sid_offset = getServerIdOffset(); // Use first flags indicates that we want to use the server // id captured in first packet. - if (CommandOptions::instance().isUseFirst() && + if (options_.isUseFirst() && (first_packet_serverid_.size() > 0)) { boost::shared_ptr opt_serverid(new LocalizedOption(Option::V6, @@ -1509,7 +1496,7 @@ TestControl::sendRequest6(const std::vector& template_buf, // Prepare on wire data. pkt6->rawPack(); // Send packet. - IfaceMgr::instance().send(pkt6); + socket_.send(pkt6); // Update packet stats. stats_mgr_.passSentPacket(ExchangeType::RR, pkt6); @@ -1518,7 +1505,7 @@ TestControl::sendRequest6(const std::vector& template_buf, // contents will be printed. Here we check if this packet has been already // collected. If it hasn't we save this packet so as we can print its // contents when test is finished. - if (testDiags('T') && + if (options_.testDiags('T') && (template_packets_v6_.find(DHCPV6_REQUEST) == template_packets_v6_.end())) { template_packets_v6_[DHCPV6_REQUEST] = pkt6; } @@ -1536,7 +1523,7 @@ TestControl::sendSolicit6(const bool preload /*= false*/) { isc_throw(Unexpected, "failed to create SOLICIT packet"); } pkt6->addOption(Option::factory(Option::V6, D6O_ELAPSED_TIME)); - if (CommandOptions::instance().isRapidCommit()) { + if (options_.isRapidCommit()) { pkt6->addOption(Option::factory(Option::V6, D6O_RAPID_COMMIT)); } pkt6->addOption(Option::factory(Option::V6, D6O_CLIENTID, duid)); @@ -1546,13 +1533,11 @@ TestControl::sendSolicit6(const bool preload /*= false*/) { // IPv6 address (with IA_NA) or IPv6 prefix (IA_PD) or both. // IA_NA - if (CommandOptions::instance().getLeaseType() - .includes(CommandOptions::LeaseType::ADDRESS)) { + if (options_.getLeaseType().includes(CommandOptions::LeaseType::ADDRESS)) { pkt6->addOption(Option::factory(Option::V6, D6O_IA_NA)); } // IA_PD - if (CommandOptions::instance().getLeaseType() - .includes(CommandOptions::LeaseType::PREFIX)) { + if (options_.getLeaseType().includes(CommandOptions::LeaseType::PREFIX)) { pkt6->addOption(Option::factory(Option::V6, D6O_IA_PD)); } @@ -1562,7 +1547,7 @@ TestControl::sendSolicit6(const bool preload /*= false*/) { addExtraOpts(pkt6); pkt6->pack(); - IfaceMgr::instance().send(pkt6); + socket_.send(pkt6); if (!preload) { stats_mgr_.passSentPacket(ExchangeType::SA, pkt6); } @@ -1605,7 +1590,7 @@ TestControl::sendSolicit6(const std::vector& template_buf, addExtraOpts(pkt6); // Send solicit packet. - IfaceMgr::instance().send(pkt6); + socket_.send(pkt6); if (!preload) { // Update packet stats. stats_mgr_.passSentPacket(ExchangeType::SA, pkt6); @@ -1616,9 +1601,8 @@ TestControl::sendSolicit6(const std::vector& template_buf, void TestControl::setDefaults4(const Pkt4Ptr& pkt) { - CommandOptions& options = CommandOptions::instance(); // Interface name. - IfacePtr iface = IfaceMgr::instance().getIface(socket_.ifindex_); + IfacePtr iface = socket_.getIface(); if (iface == NULL) { isc_throw(BadValue, "unable to find interface with given index"); } @@ -1628,13 +1612,13 @@ TestControl::setDefaults4(const Pkt4Ptr& pkt) { // Local client's port (68) pkt->setLocalPort(DHCP4_CLIENT_PORT); // Server's port (67) - if (options.getRemotePort()) { - pkt->setRemotePort(options.getRemotePort()); + if (options_.getRemotePort()) { + pkt->setRemotePort(options_.getRemotePort()); } else { pkt->setRemotePort(DHCP4_SERVER_PORT); } // The remote server's name or IP. - pkt->setRemoteAddr(IOAddress(options.getServerName())); + pkt->setRemoteAddr(IOAddress(options_.getServerName())); // Set local address. pkt->setLocalAddr(IOAddress(socket_.addr_)); // Set relay (GIADDR) address to local address. @@ -1645,9 +1629,8 @@ TestControl::setDefaults4(const Pkt4Ptr& pkt) { void TestControl::setDefaults6(const Pkt6Ptr& pkt) { - CommandOptions& options = CommandOptions::instance(); // Interface name. - IfacePtr iface = IfaceMgr::instance().getIface(socket_.ifindex_); + IfacePtr iface = socket_.getIface(); if (iface == NULL) { isc_throw(BadValue, "unable to find interface with given index"); } @@ -1657,20 +1640,20 @@ TestControl::setDefaults6(const Pkt6Ptr& pkt) { // Local client's port (547) pkt->setLocalPort(DHCP6_CLIENT_PORT); // Server's port (548) - if (options.getRemotePort()) { - pkt->setRemotePort(options.getRemotePort()); + if (options_.getRemotePort()) { + pkt->setRemotePort(options_.getRemotePort()); } else { pkt->setRemotePort(DHCP6_SERVER_PORT); } // Set local address. pkt->setLocalAddr(socket_.addr_); // The remote server's name or IP. - pkt->setRemoteAddr(IOAddress(options.getServerName())); + pkt->setRemoteAddr(IOAddress(options_.getServerName())); // only act as a relay agent when told so. /// @todo: support more level of encapsulation, at the moment we only support /// one, via -A1 option. - if (options.isUseRelayedV6()) { + if (options_.isUseRelayedV6()) { Pkt6::RelayInfo relay_info; relay_info.msg_type_ = DHCPV6_RELAY_FORW; relay_info.hop_count_ = 1; @@ -1683,8 +1666,7 @@ TestControl::setDefaults6(const Pkt6Ptr& pkt) { void TestControl::addExtraOpts(const Pkt4Ptr& pkt) { // All all extra options that the user may have specified - CommandOptions& options = CommandOptions::instance(); - const dhcp::OptionCollection& extra_opts = options.getExtraOpts(); + const dhcp::OptionCollection& extra_opts = options_.getExtraOpts(); for (auto entry : extra_opts) { pkt->addOption(entry.second); } @@ -1693,8 +1675,7 @@ TestControl::addExtraOpts(const Pkt4Ptr& pkt) { void TestControl::addExtraOpts(const Pkt6Ptr& pkt) { // All all extra options that the user may have specified - CommandOptions& options = CommandOptions::instance(); - const dhcp::OptionCollection& extra_opts = options.getExtraOpts(); + const dhcp::OptionCollection& extra_opts = options_.getExtraOpts(); for (auto entry : extra_opts) { pkt->addOption(entry.second); } diff --git a/src/bin/perfdhcp/test_control.h b/src/bin/perfdhcp/test_control.h index 73dc968d91..61778fa881 100644 --- a/src/bin/perfdhcp/test_control.h +++ b/src/bin/perfdhcp/test_control.h @@ -11,6 +11,8 @@ #include #include #include +#include +#include #include #include @@ -124,7 +126,7 @@ public: class TestControl : public boost::noncopyable { public: /// \brief Default constructor. - TestControl(); + TestControl(CommandOptions& options, BasePerfSocket &socket); /// Packet template buffer. typedef std::vector TemplateBuffer; @@ -925,7 +927,7 @@ protected: /// spaces or hexadecimal digits. void readPacketTemplate(const std::string& file_name); - PerfSocket socket_; + BasePerfSocket &socket_; Receiver receiver_; boost::posix_time::ptime last_report_; ///< Last intermediate report time. @@ -950,6 +952,8 @@ protected: std::map template_packets_v6_; static bool interrupted_; ///< Is program interrupted. + + CommandOptions& options_; }; } // namespace perfdhcp diff --git a/src/bin/perfdhcp/tests/Makefile.am b/src/bin/perfdhcp/tests/Makefile.am index 73c78ee8d0..848492bc98 100644 --- a/src/bin/perfdhcp/tests/Makefile.am +++ b/src/bin/perfdhcp/tests/Makefile.am @@ -4,6 +4,7 @@ AM_CPPFLAGS = -I$(top_builddir)/src/lib -I$(top_srcdir)/src/lib -I$(top_builddir AM_CPPFLAGS += -I$(srcdir)/.. -I$(builddir)/.. AM_CPPFLAGS += -DTEST_DATA_DIR=\"$(abs_srcdir)/testdata\" AM_CPPFLAGS += $(BOOST_INCLUDES) +AM_CPPFLAGS += -Werror AM_CXXFLAGS = $(KEA_CXXFLAGS) if USE_STATIC_LINK @@ -31,6 +32,9 @@ run_unittests_SOURCES += rate_control_unittest.cc run_unittests_SOURCES += stats_mgr_unittest.cc run_unittests_SOURCES += test_control_unittest.cc run_unittests_SOURCES += receiver_unittest.cc +run_unittests_SOURCES += perf_socket_unittest.cc +run_unittests_SOURCES += basic_scen_unittest.cc +run_unittests_SOURCES += avalanche_scen_unittest.cc run_unittests_SOURCES += command_options_helper.h run_unittests_CPPFLAGS = $(AM_CPPFLAGS) $(GTEST_INCLUDES) diff --git a/src/bin/perfdhcp/tests/avalanche_scen_unittest.cc b/src/bin/perfdhcp/tests/avalanche_scen_unittest.cc new file mode 100644 index 0000000000..de8e54342f --- /dev/null +++ b/src/bin/perfdhcp/tests/avalanche_scen_unittest.cc @@ -0,0 +1,254 @@ +// Copyright (C) 2012-2019 Internet Systems Consortium, Inc. ("ISC") +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +#include + +#include "command_options_helper.h" +#include "../avalanche_scen.h" + +#include +#include +#include +#include +#include + +#include +#include + +#include +#include +#include +#include +#include +#include + +using namespace std; +using namespace boost::posix_time; +using namespace isc; +using namespace isc::dhcp; +using namespace isc::perfdhcp; + +/// \brief FakeScenPerfSocket class that mocks PerfSocket. +/// +/// It stubs send and receive operations and collects statistics. +/// Beside that it simulates DHCP server responses for received +/// packets. +class FakeScenPerfSocket: public BasePerfSocket { +public: + /// \brief Default constructor for FakeScenPerfSocket. + FakeScenPerfSocket(CommandOptions &opt) : + opt_(opt), + iface_(boost::make_shared("fake", 0)), + sent_cnt_(0), + recv_cnt_(0) {}; + + CommandOptions &opt_; + + IfacePtr iface_; ///< Local fake interface. + + int sent_cnt_; ///< Counter of sent packets. + int recv_cnt_; ///< Counter of received packets. + + /// List of pairs containing responses + /// planned to send to perfdhcp. + std::list> planned_responses_; + + /// \brief Simulate receiving DHCPv4 packet. + virtual dhcp::Pkt4Ptr receive4(uint32_t timeout_sec, uint32_t timeout_usec) override { + (void)timeout_sec; // silence compile 'unused parameter' warning; + (void)timeout_usec; // silence compile 'unused parameter' warning; + recv_cnt_++; + + if (planned_responses_.empty()) { + return Pkt4Ptr(); + } + auto msg = planned_responses_.front(); + planned_responses_.pop_front(); + auto msg_type = std::get<0>(msg); + Pkt4Ptr pkt(new Pkt4(msg_type, std::get<1>(msg))); + OptionPtr opt_serverid = Option::factory(Option::V4, + DHO_DHCP_SERVER_IDENTIFIER, + OptionBuffer(4, 1)); + pkt->setYiaddr(asiolink::IOAddress("127.0.0.1")); + pkt->addOption(opt_serverid); + pkt->updateTimestamp(); + return (pkt); + }; + + /// \brief Simulate receiving DHCPv6 packet. + virtual dhcp::Pkt6Ptr receive6(uint32_t timeout_sec, uint32_t timeout_usec) override { + (void)timeout_sec; // silence compile 'unused parameter' warning; + (void)timeout_usec; // silence compile 'unused parameter' warning; + recv_cnt_++; + + if (planned_responses_.empty()) { + return Pkt6Ptr(); + } + auto msg = planned_responses_.front(); + planned_responses_.pop_front(); + auto msg_type = std::get<0>(msg); + Pkt6Ptr pkt(new Pkt6(msg_type, std::get<1>(msg))); + // Add IA_NA if requested by the client. + if (opt_.getLeaseType().includes(CommandOptions::LeaseType::ADDRESS)) { + OptionPtr opt_ia_na = Option::factory(Option::V6, D6O_IA_NA); + pkt->addOption(opt_ia_na); + } + // Add IA_PD if requested by the client. + if (opt_.getLeaseType().includes(CommandOptions::LeaseType::PREFIX)) { + OptionPtr opt_ia_pd = Option::factory(Option::V6, D6O_IA_PD); + pkt->addOption(opt_ia_pd); + } + OptionPtr opt_serverid(new Option(Option::V6, D6O_SERVERID)); + std::vector duid({0, 1, 2, 3, 4, 5, 6, 7, 8, 9}); + OptionPtr opt_clientid(Option::factory(Option::V6, D6O_CLIENTID, duid)); + pkt->addOption(opt_serverid); + pkt->addOption(opt_clientid); + pkt->updateTimestamp(); + return (pkt); + }; + + /// \brief Simulate sending DHCPv4 packet. + virtual bool send(const dhcp::Pkt4Ptr& pkt) override { + sent_cnt_++; + pkt->updateTimestamp(); + if (pkt->getType() == DHCPDISCOVER) { + planned_responses_.push_back(std::make_tuple(DHCPOFFER, pkt->getTransid())); + } else if (pkt->getType() == DHCPREQUEST) { + planned_responses_.push_back(std::make_tuple(DHCPACK, pkt->getTransid())); + } else { + assert(0); + } + return true; + }; + + /// \brief Simulate sending DHCPv6 packet. + virtual bool send(const dhcp::Pkt6Ptr& pkt) override { + sent_cnt_++; + pkt->updateTimestamp(); + if (pkt->getType() == DHCPV6_SOLICIT) { + planned_responses_.push_back(std::make_tuple(DHCPV6_ADVERTISE, pkt->getTransid())); + } else if (pkt->getType() == DHCPV6_REQUEST) { + planned_responses_.push_back(std::make_tuple(DHCPV6_REPLY, pkt->getTransid())); + } else { + assert(0); + } + return true; + }; + + /// \brief Override getting interface. + virtual IfacePtr getIface() override { return iface_; } + + void reset() { + sent_cnt_ = 0; + recv_cnt_ = 0; + } +}; + + +/// \brief NakedAvalancheScen class. +/// +/// It exposes AvalancheScen internals for UT. +class NakedAvalancheScen: public AvalancheScen { +public: + using AvalancheScen::tc_; + + FakeScenPerfSocket fake_sock_; + + NakedAvalancheScen(CommandOptions &opt) : AvalancheScen(opt, fake_sock_), fake_sock_(opt) {}; + +}; + + +/// \brief Test Fixture Class +/// +/// This test fixture class is used to perform +/// unit tests on perfdhcp AvalancheScenTest class. +class AvalancheScenTest : public virtual ::testing::Test +{ +public: + AvalancheScenTest() { } + + /// \brief Parse command line string with CommandOptions. + /// + /// \param cmdline command line string to be parsed. + /// \throw isc::Unexpected if unexpected error occurred. + /// \throw isc::InvalidParameter if command line is invalid. + void processCmdLine(CommandOptions &opt, const std::string& cmdline) const { + CommandOptionsHelper::process(opt, cmdline); + } + + /// \brief Get full path to a file in testdata directory. + /// + /// \param filename filename being appended to absolute + /// path to testdata directory + /// + /// \return full path to a file in testdata directory. + std::string getFullPath(const std::string& filename) const { + std::ostringstream stream; + stream << TEST_DATA_DIR << "/" << filename; + return (stream.str()); + } +}; + + +TEST_F(AvalancheScenTest, Packet4Exchange) { + CommandOptions opt; + processCmdLine(opt, "perfdhcp -l fake -4 -R 10 --scenario avalanche -g single 127.0.0.1"); + NakedAvalancheScen as(opt); + + as.run(); + + EXPECT_EQ(as.fake_sock_.sent_cnt_, 20); // Discovery + Request + EXPECT_EQ(as.tc_.getStatsMgr().getSentPacketsNum(ExchangeType::DO), 10); + EXPECT_EQ(as.tc_.getStatsMgr().getRcvdPacketsNum(ExchangeType::DO), 10); + EXPECT_EQ(as.tc_.getStatsMgr().getSentPacketsNum(ExchangeType::RA), 10); + EXPECT_EQ(as.tc_.getStatsMgr().getRcvdPacketsNum(ExchangeType::RA), 10); +} + + +TEST_F(AvalancheScenTest, Packet4ExchangeOnlyDO) { + CommandOptions opt; + processCmdLine(opt, "perfdhcp -l fake -4 -R 10 -i --scenario avalanche -g single 127.0.0.1"); + NakedAvalancheScen as(opt); + + as.run(); + + EXPECT_EQ(as.fake_sock_.sent_cnt_, 10); // Discovery + Request + EXPECT_EQ(as.tc_.getStatsMgr().getSentPacketsNum(ExchangeType::DO), 10); + EXPECT_EQ(as.tc_.getStatsMgr().getRcvdPacketsNum(ExchangeType::DO), 10); + EXPECT_EQ(as.tc_.getStatsMgr().getSentPacketsNum(ExchangeType::RA), 0); + EXPECT_EQ(as.tc_.getStatsMgr().getRcvdPacketsNum(ExchangeType::RA), 0); +} + + +TEST_F(AvalancheScenTest, Packet6Exchange) { + CommandOptions opt; + processCmdLine(opt, "perfdhcp -l fake -6 -R 10 --scenario avalanche -g single -R 20 -L 10547 ::1"); + NakedAvalancheScen as(opt); + + as.run(); + + EXPECT_GE(as.fake_sock_.sent_cnt_, 20); // Solicit + Request + EXPECT_GE(as.tc_.getStatsMgr().getSentPacketsNum(ExchangeType::SA), 10); + EXPECT_GE(as.tc_.getStatsMgr().getRcvdPacketsNum(ExchangeType::SA), 10); + EXPECT_GE(as.tc_.getStatsMgr().getSentPacketsNum(ExchangeType::RR), 10); + EXPECT_GE(as.tc_.getStatsMgr().getRcvdPacketsNum(ExchangeType::RR), 10); +} + + +TEST_F(AvalancheScenTest, Packet6ExchangeOnlySA) { + CommandOptions opt; + processCmdLine(opt, "perfdhcp -l fake -6 -R 10 -i --scenario avalanche -g single -R 20 -L 10547 ::1"); + NakedAvalancheScen as(opt); + + as.run(); + + EXPECT_GE(as.fake_sock_.sent_cnt_, 10); // Solicit + Request + EXPECT_GE(as.tc_.getStatsMgr().getSentPacketsNum(ExchangeType::SA), 10); + EXPECT_GE(as.tc_.getStatsMgr().getRcvdPacketsNum(ExchangeType::SA), 10); + EXPECT_GE(as.tc_.getStatsMgr().getSentPacketsNum(ExchangeType::RR), 0); + EXPECT_GE(as.tc_.getStatsMgr().getRcvdPacketsNum(ExchangeType::RR), 0); +} diff --git a/src/bin/perfdhcp/tests/basic_scen_unittest.cc b/src/bin/perfdhcp/tests/basic_scen_unittest.cc new file mode 100644 index 0000000000..a3f2f553f1 --- /dev/null +++ b/src/bin/perfdhcp/tests/basic_scen_unittest.cc @@ -0,0 +1,321 @@ +// Copyright (C) 2012-2019 Internet Systems Consortium, Inc. ("ISC") +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +#include + +#include "command_options_helper.h" +#include "../basic_scen.h" + +#include +#include +#include +#include +#include + +#include +#include + +#include +#include +#include +#include +#include +#include + +using namespace std; +using namespace boost::posix_time; +using namespace isc; +using namespace isc::dhcp; +using namespace isc::perfdhcp; + +/// \brief FakeScenPerfSocket class that mocks PerfSocket. +/// +/// It stubs send and receive operations and collects statistics. +/// Beside that it simulates DHCP server responses for received +/// packets. +class FakeScenPerfSocket: public BasePerfSocket { +public: + /// \brief Default constructor for FakeScenPerfSocket. + FakeScenPerfSocket(CommandOptions &opt) : + opt_(opt), + iface_(boost::make_shared("fake", 0)), + sent_cnt_(0), + recv_cnt_(0), + start_dropping_after_cnt_(100000) {}; + + CommandOptions &opt_; + + IfacePtr iface_; ///< Local fake interface. + + int sent_cnt_; ///< Counter of sent packets. + int recv_cnt_; ///< Counter of received packets. + + /// List of pairs containing responses + /// planned to send to perfdhcp. + std::list> planned_responses_; + + /// Limit for sent packets. After this limit not more packets + /// are sent. This simulate dropping responses. + int start_dropping_after_cnt_; + + /// \brief Simulate receiving DHCPv4 packet. + virtual dhcp::Pkt4Ptr receive4(uint32_t timeout_sec, uint32_t timeout_usec) override { + (void)timeout_sec; // silence compile 'unused parameter' warning; + (void)timeout_usec; // silence compile 'unused parameter' warning; + recv_cnt_++; + + if (planned_responses_.empty() || sent_cnt_ >= start_dropping_after_cnt_) { + return Pkt4Ptr(); + } + auto msg = planned_responses_.front(); + planned_responses_.pop_front(); + auto msg_type = std::get<0>(msg); + Pkt4Ptr pkt(new Pkt4(msg_type, std::get<1>(msg))); + OptionPtr opt_serverid = Option::factory(Option::V4, + DHO_DHCP_SERVER_IDENTIFIER, + OptionBuffer(4, 1)); + pkt->setYiaddr(asiolink::IOAddress("127.0.0.1")); + pkt->addOption(opt_serverid); + pkt->updateTimestamp(); + return (pkt); + }; + + /// \brief Simulate receiving DHCPv6 packet. + virtual dhcp::Pkt6Ptr receive6(uint32_t timeout_sec, uint32_t timeout_usec) override { + (void)timeout_sec; // silence compile 'unused parameter' warning; + (void)timeout_usec; // silence compile 'unused parameter' warning; + recv_cnt_++; + + if (planned_responses_.empty() || sent_cnt_ >= start_dropping_after_cnt_) { + return Pkt6Ptr(); + } + auto msg = planned_responses_.front(); + planned_responses_.pop_front(); + auto msg_type = std::get<0>(msg); + Pkt6Ptr pkt(new Pkt6(msg_type, std::get<1>(msg))); + // Add IA_NA if requested by the client. + if (opt_.getLeaseType().includes(CommandOptions::LeaseType::ADDRESS)) { + OptionPtr opt_ia_na = Option::factory(Option::V6, D6O_IA_NA); + pkt->addOption(opt_ia_na); + } + // Add IA_PD if requested by the client. + if (opt_.getLeaseType().includes(CommandOptions::LeaseType::PREFIX)) { + OptionPtr opt_ia_pd = Option::factory(Option::V6, D6O_IA_PD); + pkt->addOption(opt_ia_pd); + } + OptionPtr opt_serverid(new Option(Option::V6, D6O_SERVERID)); + std::vector duid({0, 1, 2, 3, 4, 5, 6, 7, 8, 9}); + OptionPtr opt_clientid(Option::factory(Option::V6, D6O_CLIENTID, duid)); + pkt->addOption(opt_serverid); + pkt->addOption(opt_clientid); + pkt->updateTimestamp(); + return (pkt); + }; + + /// \brief Simulate sending DHCPv4 packet. + virtual bool send(const dhcp::Pkt4Ptr& pkt) override { + sent_cnt_++; + pkt->updateTimestamp(); + if (sent_cnt_ >= start_dropping_after_cnt_) { + return true; + } + if (pkt->getType() == DHCPDISCOVER) { + planned_responses_.push_back(std::make_tuple(DHCPOFFER, pkt->getTransid())); + } else if (pkt->getType() == DHCPREQUEST) { + planned_responses_.push_back(std::make_tuple(DHCPACK, pkt->getTransid())); + } else { + assert(0); + } + return true; + }; + + /// \brief Simulate sending DHCPv6 packet. + virtual bool send(const dhcp::Pkt6Ptr& pkt) override { + sent_cnt_++; + pkt->updateTimestamp(); + if (sent_cnt_ >= start_dropping_after_cnt_) { + return true; + } + if (pkt->getType() == DHCPV6_SOLICIT) { + planned_responses_.push_back(std::make_tuple(DHCPV6_ADVERTISE, pkt->getTransid())); + } else if (pkt->getType() == DHCPV6_REQUEST) { + planned_responses_.push_back(std::make_tuple(DHCPV6_REPLY, pkt->getTransid())); + } else { + assert(0); + } + return true; + }; + + /// \brief Override getting interface. + virtual IfacePtr getIface() override { return iface_; } + + void reset() { + sent_cnt_ = 0; + recv_cnt_ = 0; + } +}; + + +/// \brief NakedBasicScen class. +/// +/// It exposes BasicScen internals for UT. +class NakedBasicScen: public BasicScen { +public: + using BasicScen::basic_rate_control_; + using BasicScen::renew_rate_control_; + using BasicScen::release_rate_control_; + using BasicScen::tc_; + + FakeScenPerfSocket fake_sock_; + + NakedBasicScen(CommandOptions &opt) : BasicScen(opt, fake_sock_), fake_sock_(opt) {}; + +}; + + +/// \brief Test Fixture Class +/// +/// This test fixture class is used to perform +/// unit tests on perfdhcp BasicScenTest class. +class BasicScenTest : public virtual ::testing::Test +{ +public: + BasicScenTest() { } + + /// \brief Parse command line string with CommandOptions. + /// + /// \param cmdline command line string to be parsed. + /// \throw isc::Unexpected if unexpected error occurred. + /// \throw isc::InvalidParameter if command line is invalid. + void processCmdLine(CommandOptions &opt, const std::string& cmdline) const { + CommandOptionsHelper::process(opt, cmdline); + } + + /// \brief Get full path to a file in testdata directory. + /// + /// \param filename filename being appended to absolute + /// path to testdata directory + /// + /// \return full path to a file in testdata directory. + std::string getFullPath(const std::string& filename) const { + std::ostringstream stream; + stream << TEST_DATA_DIR << "/" << filename; + return (stream.str()); + } +}; + + +// This test verifies that the class members are reset to expected values. +TEST_F(BasicScenTest, initial_settings) { + CommandOptions opt; + processCmdLine(opt, "perfdhcp -6 -l ethx -r 50 -f 30 -F 10 all"); + NakedBasicScen bs(opt); + + EXPECT_EQ(50, bs.basic_rate_control_.getRate()); + EXPECT_EQ(30, bs.renew_rate_control_.getRate()); + EXPECT_EQ(10, bs.release_rate_control_.getRate()); +} + + +TEST_F(BasicScenTest, Packet4Exchange) { + CommandOptions opt; + processCmdLine(opt, "perfdhcp -l fake -r 100 -n 10 -g single 127.0.0.1"); + NakedBasicScen bs(opt); + bs.run(); + // The command line restricts the number of iterations to 10 + // with -n 10 parameter. + EXPECT_GE(bs.fake_sock_.sent_cnt_, 20); // Discovery + Request + EXPECT_GE(bs.tc_.getStatsMgr().getSentPacketsNum(ExchangeType::DO), 10); + EXPECT_GE(bs.tc_.getStatsMgr().getRcvdPacketsNum(ExchangeType::DO), 10); + EXPECT_GE(bs.tc_.getStatsMgr().getSentPacketsNum(ExchangeType::RA), 10); + EXPECT_GE(bs.tc_.getStatsMgr().getRcvdPacketsNum(ExchangeType::RA), 10); +} + + +TEST_F(BasicScenTest, Packet4ExchangeMaxDrop10Proc) { + CommandOptions opt; + + // With the following command line we restrict the maximum + // number of dropped packets to 10% of all. + // Use templates for this test. + processCmdLine(opt, "perfdhcp -l fake -r 100 -R 20 -n 100" + " -D 10% -L 10547 -g single" + // TODO: seems to be broken as it crashes building pkt + // " -T " + getFullPath("discover-example.hex") + // + " -T " + getFullPath("request4-example.hex") + " 127.0.0.1"); + // The number iterations is restricted by the percentage of + // dropped packets (-D 10%). + NakedBasicScen bs(opt); + bs.fake_sock_.start_dropping_after_cnt_ = 10; + bs.run(); + EXPECT_GE(bs.fake_sock_.sent_cnt_, 15); // Discovery + Request + EXPECT_LE(bs.fake_sock_.sent_cnt_, 20); // Discovery + Request + + EXPECT_GE(bs.tc_.getStatsMgr().getSentPacketsNum(ExchangeType::DO), 1); + EXPECT_LE(bs.tc_.getStatsMgr().getSentPacketsNum(ExchangeType::DO), 15); + + EXPECT_GE(bs.tc_.getStatsMgr().getRcvdPacketsNum(ExchangeType::DO), 1); + EXPECT_LE(bs.tc_.getStatsMgr().getRcvdPacketsNum(ExchangeType::DO), 15); + + EXPECT_GE(bs.tc_.getStatsMgr().getSentPacketsNum(ExchangeType::RA), 1); + EXPECT_LE(bs.tc_.getStatsMgr().getSentPacketsNum(ExchangeType::RA), 15); + + EXPECT_GE(bs.tc_.getStatsMgr().getRcvdPacketsNum(ExchangeType::RA), 1); + EXPECT_LE(bs.tc_.getStatsMgr().getRcvdPacketsNum(ExchangeType::RA), 15); +} + + +TEST_F(BasicScenTest, Packet6Exchange) { + // Set number of iterations to 10. + CommandOptions opt; + processCmdLine(opt, "perfdhcp -l fake -6 -r 100 -n 10 -g single -R 20 -L 10547 ::1"); + // Set number of received packets equal to number of iterations. + // This simulates no packet drops. + NakedBasicScen bs(opt); + bs.run(); + EXPECT_GE(bs.fake_sock_.sent_cnt_, 20); // Solicit + Request + EXPECT_GE(bs.tc_.getStatsMgr().getSentPacketsNum(ExchangeType::SA), 10); + EXPECT_GE(bs.tc_.getStatsMgr().getRcvdPacketsNum(ExchangeType::SA), 10); + EXPECT_GE(bs.tc_.getStatsMgr().getSentPacketsNum(ExchangeType::RR), 10); + EXPECT_GE(bs.tc_.getStatsMgr().getRcvdPacketsNum(ExchangeType::RR), 10); +} + +TEST_F(BasicScenTest, Packet6ExchangeMaxDrop3Pkt) { + CommandOptions opt; + // The maximum number of dropped packets is 3 (because of -D 3). + processCmdLine(opt, "perfdhcp -l fake" + " -6 -r 100 -n 100 -R 20 -D 3 -L 10547" + // TODO: seems to be broken as it crashes building pkt + // " -T " + getFullPath("solicit-example.hex") + // + " -T " + getFullPath("request6-example.hex") + " ::1"); + + // Simulate the number of Solicit-Advertise-Request-Reply (SARR) exchanges. + // The test function generates server's responses and passes it to the + // TestControl class methods for processing. The number of exchanges + // actually performed is controller by 'start_dropping_after_cnt_'. + // All exchanged packets carry the IA_NA option + // to simulate the IPv6 address acquisition and to verify that the + // IA_NA options returned by the server are processed correctly. + NakedBasicScen bs(opt); + bs.fake_sock_.start_dropping_after_cnt_ = 10; + bs.run(); + EXPECT_GE(bs.fake_sock_.sent_cnt_, 10); + EXPECT_LE(bs.fake_sock_.sent_cnt_, 20); + + EXPECT_GE(bs.tc_.getStatsMgr().getSentPacketsNum(ExchangeType::SA), 1); + EXPECT_LE(bs.tc_.getStatsMgr().getSentPacketsNum(ExchangeType::SA), 15); + + EXPECT_GE(bs.tc_.getStatsMgr().getRcvdPacketsNum(ExchangeType::SA), 1); + EXPECT_LE(bs.tc_.getStatsMgr().getRcvdPacketsNum(ExchangeType::SA), 15); + + EXPECT_GE(bs.tc_.getStatsMgr().getSentPacketsNum(ExchangeType::RR), 1); + EXPECT_LE(bs.tc_.getStatsMgr().getSentPacketsNum(ExchangeType::RR), 15); + + EXPECT_GE(bs.tc_.getStatsMgr().getRcvdPacketsNum(ExchangeType::RR), 1); + EXPECT_LE(bs.tc_.getStatsMgr().getRcvdPacketsNum(ExchangeType::RR), 15); +} diff --git a/src/bin/perfdhcp/tests/command_options_helper.h b/src/bin/perfdhcp/tests/command_options_helper.h index 7469076f36..2184240b9c 100644 --- a/src/bin/perfdhcp/tests/command_options_helper.h +++ b/src/bin/perfdhcp/tests/command_options_helper.h @@ -84,8 +84,7 @@ public: /// /// \param cmdline command line provided as single string. /// \return true if program has been run in help or version mode ('h' or 'v' flag). - static bool process(const std::string& cmdline) { - CommandOptions& opt = CommandOptions::instance(); + static bool process(CommandOptions& opt, const std::string& cmdline) { int argc = 0; char** argv = tokenizeString(cmdline, argc); ArgvPtr args(argv, argc); diff --git a/src/bin/perfdhcp/tests/command_options_unittest.cc b/src/bin/perfdhcp/tests/command_options_unittest.cc index 148bbb3d3d..3b0bc79b04 100644 --- a/src/bin/perfdhcp/tests/command_options_unittest.cc +++ b/src/bin/perfdhcp/tests/command_options_unittest.cc @@ -149,8 +149,8 @@ protected: /// \param cmdline Command line to parse. /// \throws std::bad allocation if tokenization failed. /// \return true if program has been run in help or version mode ('h' or 'v' flag). - bool process(const std::string& cmdline) { - return (CommandOptionsHelper::process(cmdline)); + bool process(CommandOptions& opt, const std::string& cmdline) { + return (CommandOptionsHelper::process(opt, cmdline)); } /// \brief Get full path to a file in testdata directory. @@ -164,314 +164,306 @@ protected: stream << TEST_DATA_DIR << "/" << filename; return (stream.str()); } +}; + +TEST_F(CommandOptionsTest, Defaults) { + CommandOptions opt; + EXPECT_NO_THROW(process(opt, "perfdhcp 192.168.0.1")); + EXPECT_EQ(4, opt.getIpVersion()); + EXPECT_EQ(CommandOptions::DORA_SARR, opt.getExchangeMode()); + EXPECT_TRUE(opt.getLeaseType().is(CommandOptions::LeaseType::ADDRESS)); + EXPECT_EQ(0, opt.getRate()); + EXPECT_EQ(0, opt.getRenewRate()); + EXPECT_EQ(0, opt.getReleaseRate()); + EXPECT_EQ(0, opt.getReportDelay()); + EXPECT_EQ(0, opt.getClientsNum()); + // default mac + const uint8_t mac[6] = { 0x00, 0x0C, 0x01, 0x02, 0x03, 0x04 }; + std::vector v1 = opt.getMacTemplate(); + ASSERT_EQ(6, v1.size()); + EXPECT_TRUE(std::equal(v1.begin(), v1.end(), mac)); - /// \brief Check default initialized values - /// - /// Check if initialized values are correct - void checkDefaults() { - CommandOptions& opt = CommandOptions::instance(); - EXPECT_NO_THROW(process("perfdhcp 192.168.0.1")); - EXPECT_EQ(4, opt.getIpVersion()); - EXPECT_EQ(CommandOptions::DORA_SARR, opt.getExchangeMode()); - EXPECT_TRUE(opt.getLeaseType().is(CommandOptions::LeaseType::ADDRESS)); - EXPECT_EQ(0, opt.getRate()); - EXPECT_EQ(0, opt.getRenewRate()); - EXPECT_EQ(0, opt.getReleaseRate()); - EXPECT_EQ(0, opt.getReportDelay()); - EXPECT_EQ(0, opt.getClientsNum()); - - // default mac - const uint8_t mac[6] = { 0x00, 0x0C, 0x01, 0x02, 0x03, 0x04 }; - std::vector v1 = opt.getMacTemplate(); - 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; - // 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, - 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()); - EXPECT_EQ(0, opt.getPeriod()); - for (size_t i = 0; i < opt.getDropTime().size(); ++i) { - EXPECT_DOUBLE_EQ(1, opt.getDropTime()[i]); - } - ASSERT_EQ(opt.getMaxDrop().size(), opt.getMaxDropPercentage().size()); - for (size_t i = 0; i < opt.getMaxDrop().size(); ++i) { - EXPECT_EQ(0, opt.getMaxDrop()[i]); - EXPECT_EQ(0, opt.getMaxDropPercentage()[i]); - } - EXPECT_EQ("", opt.getLocalName()); - EXPECT_FALSE(opt.isInterface()); - EXPECT_EQ(0, opt.getPreload()); - EXPECT_EQ(0, opt.getLocalPort()); - EXPECT_FALSE(opt.isSeeded()); - EXPECT_EQ(0, opt.getSeed()); - EXPECT_FALSE(opt.isBroadcast()); - EXPECT_FALSE(opt.isRapidCommit()); - EXPECT_FALSE(opt.isUseFirst()); - EXPECT_EQ(0, opt.getTemplateFiles().size()); - EXPECT_EQ(0, opt.getTransactionIdOffset().size()); - EXPECT_EQ(0, opt.getRandomOffset().size()); - EXPECT_GT(0, opt.getElapsedTimeOffset()); - EXPECT_GT(0, opt.getServerIdOffset()); - EXPECT_GT(0, opt.getRequestedIpOffset()); - EXPECT_EQ("", opt.getDiags()); - EXPECT_EQ("", opt.getWrapped()); - EXPECT_EQ("192.168.0.1", opt.getServerName()); + // 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; + // 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, + 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); } -}; -TEST_F(CommandOptionsTest, Defaults) { - EXPECT_NO_THROW(process("perfdhcp all")); - checkDefaults(); + EXPECT_EQ(0, opt.getBase().size()); + EXPECT_EQ(0, opt.getNumRequests().size()); + EXPECT_EQ(0, opt.getPeriod()); + for (size_t i = 0; i < opt.getDropTime().size(); ++i) { + EXPECT_DOUBLE_EQ(1, opt.getDropTime()[i]); + } + ASSERT_EQ(opt.getMaxDrop().size(), opt.getMaxDropPercentage().size()); + for (size_t i = 0; i < opt.getMaxDrop().size(); ++i) { + EXPECT_EQ(0, opt.getMaxDrop()[i]); + EXPECT_EQ(0, opt.getMaxDropPercentage()[i]); + } + EXPECT_EQ("", opt.getLocalName()); + EXPECT_FALSE(opt.isInterface()); + EXPECT_EQ(0, opt.getPreload()); + EXPECT_EQ(0, opt.getLocalPort()); + EXPECT_FALSE(opt.isSeeded()); + EXPECT_EQ(0, opt.getSeed()); + EXPECT_FALSE(opt.isBroadcast()); + EXPECT_FALSE(opt.isRapidCommit()); + EXPECT_FALSE(opt.isUseFirst()); + EXPECT_EQ(0, opt.getTemplateFiles().size()); + EXPECT_EQ(0, opt.getTransactionIdOffset().size()); + EXPECT_EQ(0, opt.getRandomOffset().size()); + EXPECT_GT(0, opt.getElapsedTimeOffset()); + EXPECT_GT(0, opt.getServerIdOffset()); + EXPECT_GT(0, opt.getRequestedIpOffset()); + EXPECT_EQ("", opt.getDiags()); + EXPECT_EQ("", opt.getWrapped()); + EXPECT_EQ("192.168.0.1", opt.getServerName()); } TEST_F(CommandOptionsTest, HelpVersion) { // The parser is supposed to return true if 'h' or 'v' options // are specified. - EXPECT_TRUE(process("perfdhcp -h")); - EXPECT_TRUE(process("perfdhcp -v")); - EXPECT_TRUE(process("perfdhcp -h -v")); - EXPECT_TRUE(process("perfdhcp -6 -l ethx -h all")); - EXPECT_TRUE(process("perfdhcp -l ethx -v all")); + CommandOptions opt; + EXPECT_TRUE(process(opt, "perfdhcp -h")); + EXPECT_TRUE(process(opt, "perfdhcp -v")); + EXPECT_TRUE(process(opt, "perfdhcp -h -v")); + EXPECT_TRUE(process(opt, "perfdhcp -6 -l ethx -h all")); + EXPECT_TRUE(process(opt, "perfdhcp -l ethx -v all")); // No 'h' or 'v' option specified. The false value // should be returned. - EXPECT_FALSE(process("perfdhcp -l ethx all")); + EXPECT_FALSE(process(opt, "perfdhcp -l ethx all")); } TEST_F(CommandOptionsTest, UseFirst) { - CommandOptions& opt = CommandOptions::instance(); - EXPECT_NO_THROW(process("perfdhcp -1 -B -l ethx all")); + CommandOptions opt; + EXPECT_NO_THROW(process(opt, "perfdhcp -1 -B -l ethx all")); EXPECT_TRUE(opt.isUseFirst()); } TEST_F(CommandOptionsTest, UseRelayV6) { - CommandOptions& opt = CommandOptions::instance(); - EXPECT_NO_THROW(process("perfdhcp -6 -A1 -l ethx all")); + CommandOptions opt; + EXPECT_NO_THROW(process(opt, "perfdhcp -6 -A1 -l ethx all")); EXPECT_TRUE(opt.isUseRelayedV6()); // -4 and -A must not coexist - EXPECT_THROW(process("perfdhcp -4 -A1 -l ethx all"), isc::InvalidParameter); + EXPECT_THROW(process(opt, "perfdhcp -4 -A1 -l ethx all"), isc::InvalidParameter); } TEST_F(CommandOptionsTest, IpVersion) { - CommandOptions& opt = CommandOptions::instance(); - EXPECT_NO_THROW(process("perfdhcp -6 -l ethx -c -i all")); + CommandOptions opt; + EXPECT_NO_THROW(process(opt, "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 all"); + process(opt, "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 all"), isc::InvalidParameter); + EXPECT_THROW(process(opt, "perfdhcp -4 -6 -l ethx all"), isc::InvalidParameter); // -6 and -B must not coexist - EXPECT_THROW(process("perfdhcp -6 -B -l ethx all"), isc::InvalidParameter); + EXPECT_THROW(process(opt, "perfdhcp -6 -B -l ethx all"), isc::InvalidParameter); // -c and -4 (default) must not coexist - EXPECT_THROW(process("perfdhcp -c -l ethx all"), isc::InvalidParameter); + EXPECT_THROW(process(opt, "perfdhcp -c -l ethx all"), isc::InvalidParameter); } TEST_F(CommandOptionsTest, LeaseType) { - CommandOptions& opt = CommandOptions::instance(); + CommandOptions opt; // Check that the -e address-only works for IPv6. - ASSERT_NO_THROW(process("perfdhcp -6 -l etx -e address-only all")); + ASSERT_NO_THROW(process(opt, "perfdhcp -6 -l etx -e address-only all")); EXPECT_EQ(6, opt.getIpVersion()); EXPECT_EQ("etx", opt.getLocalName()); EXPECT_TRUE(opt.getLeaseType().is(CommandOptions::LeaseType::ADDRESS)); // Check that the -e address-only works for IPv4. - ASSERT_NO_THROW(process("perfdhcp -4 -l etx -e address-only all")); + ASSERT_NO_THROW(process(opt, "perfdhcp -4 -l etx -e address-only all")); EXPECT_EQ(4, opt.getIpVersion()); EXPECT_EQ("etx", opt.getLocalName()); EXPECT_TRUE(opt.getLeaseType().is(CommandOptions::LeaseType::ADDRESS)); // Check that the -e prefix-only works. - ASSERT_NO_THROW(process("perfdhcp -6 -l etx -e prefix-only all")); + ASSERT_NO_THROW(process(opt, "perfdhcp -6 -l etx -e prefix-only all")); EXPECT_EQ(6, opt.getIpVersion()); EXPECT_EQ("etx", opt.getLocalName()); EXPECT_TRUE(opt.getLeaseType().is(CommandOptions::LeaseType::PREFIX)); // Check that -e prefix-only must not coexist with -4 option. - EXPECT_THROW(process("perfdhcp -4 -l ethx -e prefix-only all"), + EXPECT_THROW(process(opt, "perfdhcp -4 -l ethx -e prefix-only all"), InvalidParameter); // Check that -e prefix-only must not coexist with -T options. - EXPECT_THROW(process("perfdhcp -6 -l ethx -e prefix-only -T file1.hex" + EXPECT_THROW(process(opt, "perfdhcp -6 -l ethx -e prefix-only -T file1.hex" " -T file2.hex -E 4 all"), InvalidParameter); } TEST_F(CommandOptionsTest, Rate) { - CommandOptions& opt = CommandOptions::instance(); - EXPECT_NO_THROW(process("perfdhcp -4 -r 10 -l ethx all")); + CommandOptions opt; + EXPECT_NO_THROW(process(opt, "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 all"), + EXPECT_THROW(process(opt, "perfdhcp -4 -r 0 -l ethx all"), isc::InvalidParameter); } TEST_F(CommandOptionsTest, RenewRate) { - CommandOptions& opt = CommandOptions::instance(); + CommandOptions opt; // If -f is specified together with -r the command line should // be accepted and the renew rate should be set. - EXPECT_NO_THROW(process("perfdhcp -6 -r 10 -f 10 -l ethx all")); + EXPECT_NO_THROW(process(opt, "perfdhcp -6 -r 10 -f 10 -l ethx all")); EXPECT_EQ(10, opt.getRenewRate()); // Check that the release rate can be set to different value than // rate specified as -r. Also, swap -f and -r to make sure // that order doesn't matter. - EXPECT_NO_THROW(process("perfdhcp -6 -f 5 -r 10 -l ethx all")); + EXPECT_NO_THROW(process(opt, "perfdhcp -6 -f 5 -r 10 -l ethx all")); EXPECT_EQ(5, opt.getRenewRate()); // Renew rate should also be accepted for DHCPv4 case. - EXPECT_NO_THROW(process("perfdhcp -4 -f 5 -r 10 -l ethx all")); + EXPECT_NO_THROW(process(opt, "perfdhcp -4 -f 5 -r 10 -l ethx all")); EXPECT_EQ(5, opt.getRenewRate()); // The renew rate should not be greater than the rate. - EXPECT_THROW(process("perfdhcp -6 -r 10 -f 11 -l ethx all"), + EXPECT_THROW(process(opt, "perfdhcp -6 -r 10 -f 11 -l ethx all"), isc::InvalidParameter); // The renew-rate of 0 is invalid. - EXPECT_THROW(process("perfdhcp -6 -r 10 -f 0 -l ethx all"), + EXPECT_THROW(process(opt, "perfdhcp -6 -r 10 -f 0 -l ethx all"), isc::InvalidParameter); // The negative renew-rate is invalid. - EXPECT_THROW(process("perfdhcp -6 -r 10 -f -5 -l ethx all"), + EXPECT_THROW(process(opt, "perfdhcp -6 -r 10 -f -5 -l ethx all"), isc::InvalidParameter); // If -r is not specified the -f should not // be accepted. - EXPECT_THROW(process("perfdhcp -6 -f 10 -l ethx all"), + EXPECT_THROW(process(opt, "perfdhcp -6 -f 10 -l ethx all"), isc::InvalidParameter); // Renew rate should be specified. - EXPECT_THROW(process("perfdhcp -6 -r 10 -f -l ethx all"), + EXPECT_THROW(process(opt, "perfdhcp -6 -r 10 -f -l ethx all"), isc::InvalidParameter); // -f and -i are mutually exclusive - EXPECT_THROW(process("perfdhcp -6 -r 10 -f 10 -l ethx -i all"), + EXPECT_THROW(process(opt, "perfdhcp -6 -r 10 -f 10 -l ethx -i all"), isc::InvalidParameter); } TEST_F(CommandOptionsTest, ReleaseRate) { - CommandOptions& opt = CommandOptions::instance(); + CommandOptions opt; // If -F is specified together with -r the command line should // be accepted and the release rate should be set. - EXPECT_NO_THROW(process("perfdhcp -6 -r 10 -F 10 -l ethx all")); + EXPECT_NO_THROW(process(opt, "perfdhcp -6 -r 10 -F 10 -l ethx all")); EXPECT_EQ(10, opt.getReleaseRate()); // Check that the release rate can be set to different value than // rate specified as -r. Also, swap -F and -r to make sure // that order doesn't matter. - EXPECT_NO_THROW(process("perfdhcp -6 -F 5 -r 10 -l ethx all")); + EXPECT_NO_THROW(process(opt, "perfdhcp -6 -F 5 -r 10 -l ethx all")); EXPECT_EQ(5, opt.getReleaseRate()); // The release rate should not be greater than the rate. - EXPECT_THROW(process("perfdhcp -6 -r 10 -F 11 -l ethx all"), + EXPECT_THROW(process(opt, "perfdhcp -6 -r 10 -F 11 -l ethx all"), isc::InvalidParameter); // The release-rate of 0 is invalid. - EXPECT_THROW(process("perfdhcp -6 -r 10 -F 0 -l ethx all"), + EXPECT_THROW(process(opt, "perfdhcp -6 -r 10 -F 0 -l ethx all"), isc::InvalidParameter); // The negative release-rate is invalid. - EXPECT_THROW(process("perfdhcp -6 -r 10 -F -5 -l ethx all"), + EXPECT_THROW(process(opt, "perfdhcp -6 -r 10 -F -5 -l ethx all"), isc::InvalidParameter); // If -r is not specified the -F should not // be accepted. - EXPECT_THROW(process("perfdhcp -6 -F 10 -l ethx all"), + EXPECT_THROW(process(opt, "perfdhcp -6 -F 10 -l ethx all"), isc::InvalidParameter); // Currently the -F can be specified for IPv6 mode // only. - EXPECT_THROW(process("perfdhcp -4 -r 10 -F 10 -l ethx all"), + EXPECT_THROW(process(opt, "perfdhcp -4 -r 10 -F 10 -l ethx all"), isc::InvalidParameter); // Release rate should be specified. - EXPECT_THROW(process("perfdhcp -6 -r 10 -F -l ethx all"), + EXPECT_THROW(process(opt, "perfdhcp -6 -r 10 -F -l ethx all"), isc::InvalidParameter); // -F and -i are mutually exclusive - EXPECT_THROW(process("perfdhcp -6 -r 10 -F 10 -l ethx -i all"), + EXPECT_THROW(process(opt, "perfdhcp -6 -r 10 -F 10 -l ethx -i all"), isc::InvalidParameter); } TEST_F(CommandOptionsTest, ReleaseRenew) { - CommandOptions& opt = CommandOptions::instance(); + CommandOptions opt; // It should be possible to specify the -F, -f and -r options. - EXPECT_NO_THROW(process("perfdhcp -6 -r 10 -F 3 -f 5 -l ethx all")); + EXPECT_NO_THROW(process(opt, "perfdhcp -6 -r 10 -F 3 -f 5 -l ethx all")); EXPECT_EQ(10, opt.getRate()); EXPECT_EQ(3, opt.getReleaseRate()); EXPECT_EQ(5, opt.getRenewRate()); // It should be possible to specify the -F and -f with the values which // sum is equal to the rate specified as -r. - EXPECT_NO_THROW(process("perfdhcp -6 -r 8 -F 3 -f 5 -l ethx all")); + EXPECT_NO_THROW(process(opt, "perfdhcp -6 -r 8 -F 3 -f 5 -l ethx all")); EXPECT_EQ(8, opt.getRate()); EXPECT_EQ(3, opt.getReleaseRate()); EXPECT_EQ(5, opt.getRenewRate()); // Check that the sum of the release and renew rate is not greater // than the rate specified as -r. - EXPECT_THROW(process("perfdhcp -6 -F 6 -f 5 -r 10 -l ethx all"), + EXPECT_THROW(process(opt, "perfdhcp -6 -F 6 -f 5 -r 10 -l ethx all"), isc::InvalidParameter); } TEST_F(CommandOptionsTest, ReportDelay) { - CommandOptions& opt = CommandOptions::instance(); - EXPECT_NO_THROW(process("perfdhcp -t 17 -l ethx all")); + CommandOptions opt; + EXPECT_NO_THROW(process(opt, "perfdhcp -t 17 -l ethx all")); EXPECT_EQ(17, opt.getReportDelay()); // Negative test cases // -t must be positive integer - EXPECT_THROW(process("perfdhcp -t -8 -l ethx all"), + EXPECT_THROW(process(opt, "perfdhcp -t -8 -l ethx all"), isc::InvalidParameter); - EXPECT_THROW(process("perfdhcp -t 0 -l ethx all"), + EXPECT_THROW(process(opt, "perfdhcp -t 0 -l ethx all"), isc::InvalidParameter); - EXPECT_THROW(process("perfdhcp -t s -l ethx all"), + EXPECT_THROW(process(opt, "perfdhcp -t s -l ethx all"), isc::InvalidParameter); } TEST_F(CommandOptionsTest, ClientsNum) { - CommandOptions& opt = CommandOptions::instance(); - EXPECT_NO_THROW(process("perfdhcp -R 200 -l ethx all")); + CommandOptions opt; + EXPECT_NO_THROW(process(opt, "perfdhcp -R 200 -l ethx all")); EXPECT_EQ(200, opt.getClientsNum()); - process("perfdhcp -R 0 -l ethx all"); + process(opt, "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 all"), + EXPECT_THROW(process(opt, "perfdhcp -R -5 -l ethx all"), isc::InvalidParameter); - EXPECT_THROW(process("perfdhcp -R gs -l ethx all"), + EXPECT_THROW(process(opt, "perfdhcp -R gs -l ethx all"), isc::InvalidParameter); } TEST_F(CommandOptionsTest, Base) { - CommandOptions& opt = CommandOptions::instance(); + CommandOptions opt; 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 DUID and MAC together. - EXPECT_NO_THROW(process("perfdhcp -b DUID=0101010101010101010110111F14" + EXPECT_NO_THROW(process(opt, "perfdhcp -b DUID=0101010101010101010110111F14" " -b MAC=10::20::30::40::50::60" " -l 127.0.0.1 all")); std::vector v1 = opt.getMacTemplate(); @@ -480,13 +472,13 @@ TEST_F(CommandOptionsTest, Base) { 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") + process(opt, "perfdhcp -b duid=0101010101010101010110111F14 -l 127.0.0.1 all") ); 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" + EXPECT_NO_THROW(process(opt, "perfdhcp -b DuiD=0101010101010101010110111F14" " -b Mac=10::20::30::40::50::60" " -l 127.0.0.1 all")); v1 = opt.getMacTemplate(); @@ -494,99 +486,99 @@ TEST_F(CommandOptionsTest, Base) { 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" + EXPECT_NO_THROW(process(opt, "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" + EXPECT_NO_THROW(process(opt, "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 " + EXPECT_THROW(process(opt, "perfdhcp -6 -l ethx -b " "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"), + EXPECT_THROW(process(opt, "perfdhcp -b mac=10::2::3x::4::5::6 -l ethx all"), isc::InvalidParameter); // Base is not specified - EXPECT_THROW(process("perfdhcp -b -l ethx all"), + EXPECT_THROW(process(opt, "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"), + EXPECT_THROW(process(opt, "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"), + EXPECT_THROW(process(opt, "perfdhcp -l ethx -b duid=00010203 all"), isc::InvalidParameter); // Odd number of digits. - EXPECT_THROW(process("perfdhcp -l ethx -b duid=000102030405060 all"), + EXPECT_THROW(process(opt, "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"), + EXPECT_THROW(process(opt, "perfdhcp -l ethx -b mac=00:01:02:04 all"), isc::InvalidParameter); } TEST_F(CommandOptionsTest, DropTime) { - CommandOptions& opt = CommandOptions::instance(); - EXPECT_NO_THROW(process("perfdhcp -l ethx -d 12 all")); + CommandOptions opt; + EXPECT_NO_THROW(process(opt, "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]); - EXPECT_NO_THROW(process("perfdhcp -l ethx -d 2 -d 4.7 all")); + EXPECT_NO_THROW(process(opt, "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 all"), + EXPECT_THROW(process(opt, "perfdhcp -l ethx -d -2 -d 4.7 all"), isc::InvalidParameter); - EXPECT_THROW(process("perfdhcp -l ethx -d -9.1 -d 0 all"), + EXPECT_THROW(process(opt, "perfdhcp -l ethx -d -9.1 -d 0 all"), isc::InvalidParameter); } TEST_F(CommandOptionsTest, TimeOffset) { - CommandOptions& opt = CommandOptions::instance(); - EXPECT_NO_THROW(process("perfdhcp -l ethx -T file1.x -T file2.x -E 4 all")); + CommandOptions opt; + EXPECT_NO_THROW(process(opt, "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 all"), + EXPECT_THROW(process(opt, "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 all"), + EXPECT_THROW(process(opt, "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 all"), + EXPECT_THROW(process(opt, "perfdhcp -l ethx -E -3 -T file.x all"), isc::InvalidParameter); } TEST_F(CommandOptionsTest, ExchangeMode) { - CommandOptions& opt = CommandOptions::instance(); - process("perfdhcp -l ethx -i all"); + CommandOptions opt; + process(opt, "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 all"), + EXPECT_THROW(process(opt, "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 all"), + EXPECT_THROW(process(opt, "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"), + EXPECT_THROW(process(opt, "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"), + EXPECT_THROW(process(opt, "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"), + EXPECT_THROW(process(opt, "perfdhcp -i -l ethx -I 2 -T file.x all"), isc::InvalidParameter); } TEST_F(CommandOptionsTest, Offsets) { - CommandOptions& opt = CommandOptions::instance(); - EXPECT_NO_THROW(process("perfdhcp -E5 -4 -I 2 -S3 -O 30 -X7 -l ethx " + CommandOptions opt; + EXPECT_NO_THROW(process(opt, "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()); @@ -600,163 +592,163 @@ TEST_F(CommandOptionsTest, Offsets) { // Negative test cases // IP offset/IA_NA offset must be positive - EXPECT_THROW(process("perfdhcp -6 -I 0 -l ethx all"), + EXPECT_THROW(process(opt, "perfdhcp -6 -I 0 -l ethx all"), isc::InvalidParameter); - EXPECT_THROW(process("perfdhcp -6 -I -4 -l ethx all"), + EXPECT_THROW(process(opt, "perfdhcp -6 -I -4 -l ethx all"), isc::InvalidParameter); // TODO - other negative cases } TEST_F(CommandOptionsTest, LocalPort) { - CommandOptions& opt = CommandOptions::instance(); - EXPECT_NO_THROW(process("perfdhcp -l ethx -L 2000 all")); + CommandOptions opt; + EXPECT_NO_THROW(process(opt, "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 all"), + EXPECT_THROW(process(opt, "perfdhcp -l ethx -L -2 all"), isc::InvalidParameter); - EXPECT_THROW(process("perfdhcp -l ethx -L all"), + EXPECT_THROW(process(opt, "perfdhcp -l ethx -L all"), isc::InvalidParameter); - EXPECT_THROW(process("perfdhcp -l ethx -L 65540 all"), + EXPECT_THROW(process(opt, "perfdhcp -l ethx -L 65540 all"), isc::InvalidParameter); } TEST_F(CommandOptionsTest, Preload) { - CommandOptions& opt = CommandOptions::instance(); - EXPECT_NO_THROW(process("perfdhcp -1 -P 3 -l ethx all")); + CommandOptions opt; + EXPECT_NO_THROW(process(opt, "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 all"), + EXPECT_THROW(process(opt, "perfdhcp -P -1 -l ethx all"), isc::InvalidParameter); - EXPECT_THROW(process("perfdhcp -P -3 -l ethx all"), + EXPECT_THROW(process(opt, "perfdhcp -P -3 -l ethx all"), isc::InvalidParameter); } TEST_F(CommandOptionsTest, Seed) { - CommandOptions& opt = CommandOptions::instance(); - EXPECT_NO_THROW(process("perfdhcp -6 -P 2 -s 23 -l ethx all")); + CommandOptions opt; + EXPECT_NO_THROW(process(opt, "perfdhcp -6 -P 2 -s 23 -l ethx all")); EXPECT_EQ(23, opt.getSeed()); EXPECT_TRUE(opt.isSeeded()); - EXPECT_NO_THROW(process("perfdhcp -6 -P 2 -s 0 -l ethx all")); + EXPECT_NO_THROW(process(opt, "perfdhcp -6 -P 2 -s 0 -l ethx all")); EXPECT_EQ(0, opt.getSeed()); EXPECT_FALSE(opt.isSeeded()); // Negative test cases // Seed must be non-negative integer - EXPECT_THROW(process("perfdhcp -6 -P 2 -s -5 -l ethx all"), + EXPECT_THROW(process(opt, "perfdhcp -6 -P 2 -s -5 -l ethx all"), isc::InvalidParameter); - EXPECT_THROW(process("perfdhcp -6 -P 2 -s -l ethx all"), + EXPECT_THROW(process(opt, "perfdhcp -6 -P 2 -s -l ethx all"), isc::InvalidParameter); } TEST_F(CommandOptionsTest, TemplateFiles) { - CommandOptions& opt = CommandOptions::instance(); - EXPECT_NO_THROW(process("perfdhcp -T file1.x -l ethx all")); + CommandOptions opt; + EXPECT_NO_THROW(process(opt, "perfdhcp -T file1.x -l ethx all")); ASSERT_EQ(1, opt.getTemplateFiles().size()); EXPECT_EQ("file1.x", opt.getTemplateFiles()[0]); - EXPECT_NO_THROW(process("perfdhcp -T file1.x -s 12 -w start -T file2.x -4 -l ethx all")); + EXPECT_NO_THROW(process(opt, "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 -T -l ethx all"), + EXPECT_THROW(process(opt, "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 " + EXPECT_THROW(process(opt, "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(); - EXPECT_NO_THROW(process("perfdhcp -B -w start -i -l ethx all")); + CommandOptions opt; + EXPECT_NO_THROW(process(opt, "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 all"), + EXPECT_THROW(process(opt, "perfdhcp -B -i -l ethx -w all"), isc::InvalidParameter); } TEST_F(CommandOptionsTest, Diagnostics) { - CommandOptions& opt = CommandOptions::instance(); - EXPECT_NO_THROW(process("perfdhcp -l ethx -i -x asTe all")); + CommandOptions opt; + EXPECT_NO_THROW(process(opt, "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 all"), + EXPECT_THROW(process(opt, "perfdhcp -l ethx -i -x all"), isc::InvalidParameter); } TEST_F(CommandOptionsTest, MaxDrop) { - CommandOptions& opt = CommandOptions::instance(); - EXPECT_NO_THROW(process("perfdhcp -D 25 -l ethx all")); + CommandOptions opt; + EXPECT_NO_THROW(process(opt, "perfdhcp -D 25 -l ethx all")); EXPECT_EQ(25, opt.getMaxDrop()[0]); - EXPECT_NO_THROW(process("perfdhcp -D 25 -l ethx -D 15 all")); + EXPECT_NO_THROW(process(opt, "perfdhcp -D 25 -l ethx -D 15 all")); EXPECT_EQ(25, opt.getMaxDrop()[0]); EXPECT_EQ(15, opt.getMaxDrop()[1]); - EXPECT_NO_THROW(process("perfdhcp -D 15% -l ethx all")); + EXPECT_NO_THROW(process(opt, "perfdhcp -D 15% -l ethx all")); EXPECT_EQ(15, opt.getMaxDropPercentage()[0]); - EXPECT_NO_THROW(process("perfdhcp -D 15% -D25% -l ethx all")); + EXPECT_NO_THROW(process(opt, "perfdhcp -D 15% -D25% -l ethx all")); EXPECT_EQ(15, opt.getMaxDropPercentage()[0]); EXPECT_EQ(25, opt.getMaxDropPercentage()[1]); - EXPECT_NO_THROW(process("perfdhcp -D 1% -D 99% -l ethx all")); + EXPECT_NO_THROW(process(opt, "perfdhcp -D 1% -D 99% -l ethx 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 -D 3 all"), + EXPECT_THROW(process(opt, "perfdhcp -D 0% -D 1 -l ethx -D 3 all"), isc::InvalidParameter); // Too many -D options - EXPECT_THROW(process("perfdhcp -D 99% -D 13% -l ethx -D 10% all"), + EXPECT_THROW(process(opt, "perfdhcp -D 99% -D 13% -l ethx -D 10% all"), isc::InvalidParameter); // Percentage is out of bounds - EXPECT_THROW(process("perfdhcp -D101% -D 13% -l ethx all"), + EXPECT_THROW(process(opt, "perfdhcp -D101% -D 13% -l ethx all"), isc::InvalidParameter); - EXPECT_THROW(process("perfdhcp -D0% -D 13% -l ethx all"), + EXPECT_THROW(process(opt, "perfdhcp -D0% -D 13% -l ethx all"), isc::InvalidParameter); } TEST_F(CommandOptionsTest, NumRequest) { - CommandOptions& opt = CommandOptions::instance(); - EXPECT_NO_THROW(process("perfdhcp -n 1000 -l ethx all")); + CommandOptions opt; + EXPECT_NO_THROW(process(opt, "perfdhcp -n 1000 -l ethx all")); EXPECT_EQ(1000, opt.getNumRequests()[0]); - EXPECT_NO_THROW(process("perfdhcp -n 5 -n 500 -l ethx all")); + EXPECT_NO_THROW(process(opt, "perfdhcp -n 5 -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 all"), + EXPECT_THROW(process(opt, "perfdhcp -n 1 -n 2 -l ethx -n3 all"), isc::InvalidParameter); // Num request must be positive integer - EXPECT_THROW(process("perfdhcp -n 1 -n -22 -l ethx all"), + EXPECT_THROW(process(opt, "perfdhcp -n 1 -n -22 -l ethx all"), isc::InvalidParameter); - EXPECT_THROW(process("perfdhcp -n 0 -l ethx all"), + EXPECT_THROW(process(opt, "perfdhcp -n 0 -l ethx all"), isc::InvalidParameter); } TEST_F(CommandOptionsTest, Period) { - CommandOptions& opt = CommandOptions::instance(); - EXPECT_NO_THROW(process("perfdhcp -p 120 -l ethx all")); + CommandOptions opt; + EXPECT_NO_THROW(process(opt, "perfdhcp -p 120 -l ethx all")); EXPECT_EQ(120, opt.getPeriod()); // Negative test cases // Test period must be positive integer - EXPECT_THROW(process("perfdhcp -p 0 -l ethx all"), + EXPECT_THROW(process(opt, "perfdhcp -p 0 -l ethx all"), isc::InvalidParameter); - EXPECT_THROW(process("perfdhcp -p -3 -l ethx all"), + EXPECT_THROW(process(opt, "perfdhcp -p -3 -l ethx all"), isc::InvalidParameter); } @@ -770,7 +762,7 @@ TEST_F(CommandOptionsTest, Interface) { dhcp::IfaceMgr& iface_mgr = dhcp::IfaceMgr::instance(); const dhcp::IfaceMgr::IfaceCollection& ifaces = iface_mgr.getIfaces(); std::string iface_name; - CommandOptions& opt = CommandOptions::instance(); + CommandOptions opt; // The local loopback interface should be available. // If no interface have been found for any reason we should // not fail this test. @@ -778,23 +770,23 @@ TEST_F(CommandOptionsTest, Interface) { // 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")); + ASSERT_NO_THROW(process(opt, "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); + EXPECT_THROW(process(opt, "perfdhcp -4"), isc::InvalidParameter); } } TEST_F(CommandOptionsTest, Server) { - CommandOptions& opt = CommandOptions::instance(); + CommandOptions opt; // 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")); + ASSERT_NO_THROW(process(opt, "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()); @@ -804,40 +796,41 @@ TEST_F(CommandOptionsTest, Server) { // 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")); + ASSERT_NO_THROW(process(opt, "perfdhcp -6 all")); EXPECT_EQ(ALL_DHCP_RELAY_AGENTS_AND_SERVERS, 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")); + ASSERT_NO_THROW(process(opt, "perfdhcp -6 servers")); EXPECT_EQ(ALL_DHCP_SERVERS, 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")); + ASSERT_NO_THROW(process(opt, "perfdhcp -6 abc")); EXPECT_EQ("abc", opt.getServerName()); } TEST_F(CommandOptionsTest, LoadMacsFromFile) { - CommandOptions &opt = CommandOptions::instance(); + CommandOptions opt; - std::string mac_list_full_path = getFullPath("mac-list.txt"); - std::ostringstream cmd; - cmd << "perfdhcp -M " << mac_list_full_path << " abc"; - EXPECT_NO_THROW(process(cmd.str())); - EXPECT_EQ(mac_list_full_path, opt.getMacListFile()); + std::string mac_list_full_path = getFullPath("mac-list.txt"); + std::ostringstream cmd; + cmd << "perfdhcp -M " << mac_list_full_path << " abc"; + EXPECT_NO_THROW(process(opt, cmd.str())); + EXPECT_EQ(mac_list_full_path, opt.getMacListFile()); - const CommandOptions::MacAddrsVector& m = opt.getMacsFromFile(); - EXPECT_EQ(4, m.size()); + const CommandOptions::MacAddrsVector& m = opt.getMacsFromFile(); + EXPECT_EQ(4, m.size()); } TEST_F(CommandOptionsTest, LoadMacsFromFileNegativeCases) { - // Negative test cases - // Too many -M parameters, expected only 1 - EXPECT_THROW(process("perfdhcp -M foo -M foo1 all"), isc::InvalidParameter); - // -M option can't use with -b option - EXPECT_THROW(process("perfdhcp -M foo -b mac=1234 all"), - isc::InvalidParameter); + CommandOptions opt; + // Negative test cases + // Too many -M parameters, expected only 1 + EXPECT_THROW(process(opt, "perfdhcp -M foo -M foo1 all"), isc::InvalidParameter); + // -M option can't use with -b option + EXPECT_THROW(process(opt, "perfdhcp -M foo -b mac=1234 all"), + isc::InvalidParameter); } diff --git a/src/bin/perfdhcp/tests/perf_socket_unittest.cc b/src/bin/perfdhcp/tests/perf_socket_unittest.cc new file mode 100644 index 0000000000..5db34f77f8 --- /dev/null +++ b/src/bin/perfdhcp/tests/perf_socket_unittest.cc @@ -0,0 +1,56 @@ +// Copyright (C) 2019 Internet Systems Consortium, Inc. ("ISC") +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +#include + +#include "command_options_helper.h" +#include "../perf_socket.h" + +#include +#include +#include +#include +#include + +#include +#include + +#include +#include +#include +#include +#include +#include + +using namespace std; +using namespace boost::posix_time; +using namespace isc; +using namespace isc::dhcp; +using namespace isc::perfdhcp; + + +/// \brief Test Fixture Class +/// +/// This test fixture class is used to perform +/// unit tests on perfdhcp PerfSocketTest class. +class PerfSocketTest : public virtual ::testing::Test +{ +public: + PerfSocketTest() { } +}; + + +TEST_F(PerfSocketTest, Basic) { + CommandOptions opt; + + // make sure we catch -6 paired with v4 address + CommandOptionsHelper::process(opt, "perfdhcp -l 127.0.0.1 -6 192.168.1.1"); + EXPECT_THROW(PerfSocket sock(opt), isc::InvalidParameter); + + // make sure we catch -4 paired with v6 address + CommandOptionsHelper::process(opt, "perfdhcp -l 127.0.0.1 -4 ff02::1:2"); + EXPECT_THROW(PerfSocket sock(opt), isc::InvalidParameter); +} diff --git a/src/bin/perfdhcp/tests/receiver_unittest.cc b/src/bin/perfdhcp/tests/receiver_unittest.cc index 0217b8e542..940505cb5e 100644 --- a/src/bin/perfdhcp/tests/receiver_unittest.cc +++ b/src/bin/perfdhcp/tests/receiver_unittest.cc @@ -17,12 +17,12 @@ using namespace isc; using namespace isc::perfdhcp; TEST(Receiver, singleThreaded) { - CommandOptionsHelper::process("perfdhcp -g single -l 127.0.0.1 all"); - ASSERT_TRUE(CommandOptions::instance().isSingleThreaded()); + CommandOptions opt; + CommandOptionsHelper::process(opt, "perfdhcp -g single -l 127.0.0.1 all"); + ASSERT_TRUE(opt.isSingleThreaded()); - PerfSocket sock(123); - - Receiver receiver(sock); + PerfSocket socket(opt); + Receiver receiver(socket, opt.isSingleThreaded(), opt.getIpVersion()); ASSERT_NO_THROW(receiver.start()); @@ -34,12 +34,12 @@ TEST(Receiver, singleThreaded) { TEST(Receiver, multiThreaded) { - CommandOptionsHelper::process("perfdhcp -g multi -l 127.0.0.1 all"); - ASSERT_FALSE(CommandOptions::instance().isSingleThreaded()); - - PerfSocket sock(123); + CommandOptions opt; + CommandOptionsHelper::process(opt, "perfdhcp -g multi -l 127.0.0.1 all"); + ASSERT_FALSE(opt.isSingleThreaded()); - Receiver receiver(sock); + PerfSocket socket(opt); + Receiver receiver(socket, opt.isSingleThreaded(), opt.getIpVersion()); ASSERT_NO_THROW(receiver.start()); diff --git a/src/bin/perfdhcp/tests/stats_mgr_unittest.cc b/src/bin/perfdhcp/tests/stats_mgr_unittest.cc index 41de29527f..2759a2e129 100644 --- a/src/bin/perfdhcp/tests/stats_mgr_unittest.cc +++ b/src/bin/perfdhcp/tests/stats_mgr_unittest.cc @@ -6,19 +6,21 @@ #include -#include +#include "command_options_helper.h" + +#include #include #include #include #include #include -#include "../stats_mgr.h" #include #include #include +#include using namespace std; using namespace isc; @@ -27,9 +29,6 @@ using namespace isc::perfdhcp; namespace { -typedef StatsMgr StatsMgr4; -typedef StatsMgr StatsMgr6; - const uint32_t common_transid = 123; /// @brief Number of packets to be used for testing packets collecting. @@ -118,8 +117,8 @@ public: /// \param num_packets packets to be passed to Statistics Manager. /// \param receive simulated packets are received (if true) /// or sent (if false) - void passMultiplePackets6(const boost::shared_ptr stats_mgr, - StatsMgr6::ExchangeType xchg_type, + void passMultiplePackets6(const boost::shared_ptr stats_mgr, + ExchangeType xchg_type, const uint8_t packet_type, const int num_packets, const bool receive = false) { @@ -148,30 +147,23 @@ public: /// /// \param stats_mgr Statistics Manager instance. /// \param delay delay in seconds between DISCOVER and OFFER packets. - void passDOPacketsWithDelay(const boost::shared_ptr stats_mgr, + void passDOPacketsWithDelay(const boost::shared_ptr stats_mgr, unsigned int delay, uint32_t transid) { boost::shared_ptr sent_packet(createPacket4(DHCPDISCOVER, transid)); ASSERT_NO_THROW( - stats_mgr->passSentPacket(StatsMgr4::XCHG_DO, sent_packet) + stats_mgr->passSentPacket(ExchangeType::DO, sent_packet) ); - // There is way to differentiate timestamps of two packets other than - // sleep for before we create another packet. Packet is using current - // time to update its timestamp. - // Sleeping for X seconds will guarantee that delay between packets - // will be greater than 1 second. Note that posix time value is - // transformed to double value and it makes it hard to determine - // actual value to expect. - std::cout << "Sleeping for " << delay << "s to test packet delays" - << std::endl; - sleep(delay); + // Simulate time difference by changing time of sent packet + auto ts = sent_packet->getTimestamp() - boost::posix_time::seconds(delay); + sent_packet->setTimestamp(ts); boost::shared_ptr rcvd_packet(createPacket4(DHCPOFFER, transid)); ASSERT_NO_THROW( - stats_mgr->passRcvdPacket(StatsMgr4::XCHG_DO, rcvd_packet); + stats_mgr->passRcvdPacket(ExchangeType::DO, rcvd_packet); ); // Calculate period between packets. @@ -187,10 +179,11 @@ public: /// @param transid_index Index in the table of transaction ids which /// points to the transaction id to be selected for the DHCPOFFER. void testSendReceiveCollected(const size_t transid_index) { - boost::scoped_ptr stats_mgr(new StatsMgr4()); + CommandOptions opt; + boost::scoped_ptr stats_mgr(new StatsMgr(opt)); // The second parameter indicates that transactions older than // 2 seconds should be removed and respective packets collected. - stats_mgr->addExchangeStats(StatsMgr4::XCHG_DO, 2); + stats_mgr->addExchangeStats(ExchangeType::DO, 2); // Transaction ids of packets to be sent. All transaction ids // belong to the same bucket (match the transid & 1023 hashing @@ -209,7 +202,7 @@ public: sent_packet->modifyTimestamp(-10); } ASSERT_NO_THROW( - stats_mgr->passSentPacket(StatsMgr4::XCHG_DO, sent_packet) + stats_mgr->passSentPacket(ExchangeType::DO, sent_packet) ) << "failure for transaction id " << transid[i]; } @@ -217,18 +210,18 @@ public: Pkt4ModifiablePtr rcvd_packet(createPacket4(DHCPOFFER, transid[transid_index])); ASSERT_NO_THROW( - stats_mgr->passRcvdPacket(StatsMgr4::XCHG_DO, rcvd_packet); + stats_mgr->passRcvdPacket(ExchangeType::DO, rcvd_packet); ); // There is exactly one case (transaction id) for which perfdhcp // will find a packet using ordered lookup. In this case, no // packets will be collected. Otherwise, for any unordered lookup // all packets from a bucket should be collected. - if (stats_mgr->getUnorderedLookups(StatsMgr4::XCHG_DO) > 0) { + if (stats_mgr->getUnorderedLookups(ExchangeType::DO) > 0) { // All packets in the bucket having transaction id // index below TEST_COLLECTED_PKT_NUM / 2 should be removed. EXPECT_EQ(TEST_COLLECTED_PKT_NUM / 2, - stats_mgr->getCollectedNum(StatsMgr4::XCHG_DO)); + stats_mgr->getCollectedNum(ExchangeType::DO)); } // Make sure that we can still use the StatsMgr. It is possible @@ -242,11 +235,11 @@ public: Pkt4ModifiablePtr rcvd_packet(createPacket4(DHCPOFFER, transid[i] + 1)); ASSERT_NO_THROW( - stats_mgr->passSentPacket(StatsMgr4::XCHG_DO, sent_packet) + stats_mgr->passSentPacket(ExchangeType::DO, sent_packet) ) << "failure for transaction id " << transid[i]; ASSERT_NO_THROW( - stats_mgr->passRcvdPacket(StatsMgr4::XCHG_DO, rcvd_packet); + stats_mgr->passRcvdPacket(ExchangeType::DO, rcvd_packet); ) << "failure for transaction id " << transid[i]; } @@ -256,86 +249,89 @@ public: // timeout. Therefore, we have to count both received packets and // orphans. EXPECT_EQ(TEST_COLLECTED_PKT_NUM + 1, - stats_mgr->getRcvdPacketsNum(StatsMgr4::XCHG_DO) + - stats_mgr->getOrphans(StatsMgr4::XCHG_DO)); + stats_mgr->getRcvdPacketsNum(ExchangeType::DO) + + stats_mgr->getOrphans(ExchangeType::DO)); } }; TEST_F(StatsMgrTest, Constructor) { - boost::scoped_ptr stats_mgr(new StatsMgr4()); - stats_mgr->addExchangeStats(StatsMgr4::XCHG_DO); + CommandOptions opt; + boost::scoped_ptr stats_mgr(new StatsMgr(opt)); + stats_mgr->addExchangeStats(ExchangeType::DO); EXPECT_DOUBLE_EQ( std::numeric_limits::max(), - stats_mgr->getMinDelay(StatsMgr4::XCHG_DO) + stats_mgr->getMinDelay(ExchangeType::DO) ); - EXPECT_DOUBLE_EQ(0, stats_mgr->getMaxDelay(StatsMgr4::XCHG_DO)); - EXPECT_EQ(0, stats_mgr->getOrphans(StatsMgr4::XCHG_DO)); - EXPECT_EQ(0, stats_mgr->getOrderedLookups(StatsMgr4::XCHG_DO)); - EXPECT_EQ(0, stats_mgr->getUnorderedLookups(StatsMgr4::XCHG_DO)); - EXPECT_EQ(0, stats_mgr->getSentPacketsNum(StatsMgr4::XCHG_DO)); - EXPECT_EQ(0, stats_mgr->getRcvdPacketsNum(StatsMgr4::XCHG_DO)); - EXPECT_EQ(0, stats_mgr->getCollectedNum(StatsMgr4::XCHG_DO)); - - EXPECT_THROW(stats_mgr->getAvgDelay(StatsMgr4::XCHG_DO), InvalidOperation); - EXPECT_THROW(stats_mgr->getStdDevDelay(StatsMgr4::XCHG_DO), + EXPECT_DOUBLE_EQ(0, stats_mgr->getMaxDelay(ExchangeType::DO)); + EXPECT_EQ(0, stats_mgr->getOrphans(ExchangeType::DO)); + EXPECT_EQ(0, stats_mgr->getOrderedLookups(ExchangeType::DO)); + EXPECT_EQ(0, stats_mgr->getUnorderedLookups(ExchangeType::DO)); + EXPECT_EQ(0, stats_mgr->getSentPacketsNum(ExchangeType::DO)); + EXPECT_EQ(0, stats_mgr->getRcvdPacketsNum(ExchangeType::DO)); + EXPECT_EQ(0, stats_mgr->getCollectedNum(ExchangeType::DO)); + + EXPECT_THROW(stats_mgr->getAvgDelay(ExchangeType::DO), InvalidOperation); + EXPECT_THROW(stats_mgr->getStdDevDelay(ExchangeType::DO), InvalidOperation); - EXPECT_THROW(stats_mgr->getAvgUnorderedLookupSetSize(StatsMgr4::XCHG_DO), + EXPECT_THROW(stats_mgr->getAvgUnorderedLookupSetSize(ExchangeType::DO), InvalidOperation); } TEST_F(StatsMgrTest, Exchange) { - boost::scoped_ptr stats_mgr(new StatsMgr4()); + CommandOptions opt; + boost::scoped_ptr stats_mgr(new StatsMgr(opt)); boost::shared_ptr sent_packet(createPacket4(DHCPDISCOVER, common_transid)); boost::shared_ptr rcvd_packet(createPacket4(DHCPOFFER, common_transid)); // This is expected to throw because XCHG_DO was not yet // added to Stats Manager for tracking. - ASSERT_FALSE(stats_mgr->hasExchangeStats(StatsMgr4::XCHG_DO)); - ASSERT_FALSE(stats_mgr->hasExchangeStats(StatsMgr4::XCHG_RA)); + ASSERT_FALSE(stats_mgr->hasExchangeStats(ExchangeType::DO)); + ASSERT_FALSE(stats_mgr->hasExchangeStats(ExchangeType::RA)); EXPECT_THROW( - stats_mgr->passSentPacket(StatsMgr4::XCHG_DO, sent_packet), + stats_mgr->passSentPacket(ExchangeType::DO, sent_packet), BadValue ); EXPECT_THROW( - stats_mgr->passRcvdPacket(StatsMgr4::XCHG_DO, rcvd_packet), + stats_mgr->passRcvdPacket(ExchangeType::DO, rcvd_packet), BadValue ); // Adding DISCOVER-OFFER exchanges to be tracked by Stats Manager. - stats_mgr->addExchangeStats(StatsMgr4::XCHG_DO); - ASSERT_TRUE(stats_mgr->hasExchangeStats(StatsMgr4::XCHG_DO)); - ASSERT_FALSE(stats_mgr->hasExchangeStats(StatsMgr4::XCHG_RA)); + stats_mgr->addExchangeStats(ExchangeType::DO); + ASSERT_TRUE(stats_mgr->hasExchangeStats(ExchangeType::DO)); + ASSERT_FALSE(stats_mgr->hasExchangeStats(ExchangeType::RA)); // The following two attempts are expected to throw because // invalid exchange types are passed (XCHG_RA instead of XCHG_DO) EXPECT_THROW( - stats_mgr->passSentPacket(StatsMgr4::XCHG_RA, sent_packet), + stats_mgr->passSentPacket(ExchangeType::RA, sent_packet), BadValue ); EXPECT_THROW( - stats_mgr->passRcvdPacket(StatsMgr4::XCHG_RA, rcvd_packet), + stats_mgr->passRcvdPacket(ExchangeType::RA, rcvd_packet), BadValue ); // The following two attempts are expected to run fine because // right exchange type is specified. EXPECT_NO_THROW( - stats_mgr->passSentPacket(StatsMgr4::XCHG_DO, sent_packet) + stats_mgr->passSentPacket(ExchangeType::DO, sent_packet) ); EXPECT_NO_THROW( - stats_mgr->passRcvdPacket(StatsMgr4::XCHG_DO, rcvd_packet) + stats_mgr->passRcvdPacket(ExchangeType::DO, rcvd_packet) ); } TEST_F(StatsMgrTest, MultipleExchanges) { - boost::shared_ptr stats_mgr(new StatsMgr6()); - stats_mgr->addExchangeStats(StatsMgr6::XCHG_SA); - stats_mgr->addExchangeStats(StatsMgr6::XCHG_RR); + CommandOptions opt; + boost::shared_ptr stats_mgr(new StatsMgr(opt)); + stats_mgr->addExchangeStats(ExchangeType::SA); + stats_mgr->addExchangeStats(ExchangeType::RR); // Simulate sending number of solicit packets. const int solicit_packets_num = 10; - passMultiplePackets6(stats_mgr, StatsMgr6::XCHG_SA, DHCPV6_SOLICIT, + passMultiplePackets6(stats_mgr, ExchangeType::SA, DHCPV6_SOLICIT, solicit_packets_num); // Simulate sending number of request packets. It is important that @@ -343,60 +339,62 @@ TEST_F(StatsMgrTest, MultipleExchanges) { // packets. We can now check if right number packets went to // the right exchange type group. const int request_packets_num = 5; - passMultiplePackets6(stats_mgr, StatsMgr6::XCHG_RR, DHCPV6_REQUEST, + passMultiplePackets6(stats_mgr, ExchangeType::RR, DHCPV6_REQUEST, request_packets_num); // Check if all packets are successfully passed to packet lists. EXPECT_EQ(solicit_packets_num, - stats_mgr->getSentPacketsNum(StatsMgr6::XCHG_SA)); + stats_mgr->getSentPacketsNum(ExchangeType::SA)); EXPECT_EQ(request_packets_num, - stats_mgr->getSentPacketsNum(StatsMgr6::XCHG_RR)); + stats_mgr->getSentPacketsNum(ExchangeType::RR)); // Simulate reception of multiple packets for both SOLICIT-ADVERTISE // and REQUEST-REPLY exchanges. Assume no packet drops. const bool receive_packets = true; - passMultiplePackets6(stats_mgr, StatsMgr6::XCHG_SA, DHCPV6_ADVERTISE, + passMultiplePackets6(stats_mgr, ExchangeType::SA, DHCPV6_ADVERTISE, solicit_packets_num, receive_packets); - passMultiplePackets6(stats_mgr, StatsMgr6::XCHG_RR, DHCPV6_REPLY, + passMultiplePackets6(stats_mgr, ExchangeType::RR, DHCPV6_REPLY, request_packets_num, receive_packets); // Verify that all received packets are counted. EXPECT_EQ(solicit_packets_num, - stats_mgr->getRcvdPacketsNum(StatsMgr6::XCHG_SA)); + stats_mgr->getRcvdPacketsNum(ExchangeType::SA)); EXPECT_EQ(request_packets_num, - stats_mgr->getRcvdPacketsNum(StatsMgr6::XCHG_RR)); + stats_mgr->getRcvdPacketsNum(ExchangeType::RR)); } TEST_F(StatsMgrTest, SendReceiveSimple) { - boost::scoped_ptr stats_mgr(new StatsMgr4()); + CommandOptions opt; + boost::scoped_ptr stats_mgr(new StatsMgr(opt)); boost::shared_ptr sent_packet(createPacket4(DHCPDISCOVER, common_transid)); boost::shared_ptr rcvd_packet(createPacket4(DHCPOFFER, common_transid)); - stats_mgr->addExchangeStats(StatsMgr4::XCHG_DO); + stats_mgr->addExchangeStats(ExchangeType::DO); // The following attempt is expected to pass because the right // exchange type is used. ASSERT_NO_THROW( - stats_mgr->passSentPacket(StatsMgr4::XCHG_DO, sent_packet) + stats_mgr->passSentPacket(ExchangeType::DO, sent_packet) ); // It is ok, to pass to received packets here. First one will // be matched with sent packet. The latter one will not be // matched with sent packet but orphans counter will simply // increase. ASSERT_NO_THROW( - stats_mgr->passRcvdPacket(StatsMgr4::XCHG_DO, rcvd_packet) + stats_mgr->passRcvdPacket(ExchangeType::DO, rcvd_packet) ); ASSERT_NO_THROW( - stats_mgr->passRcvdPacket(StatsMgr4::XCHG_DO, rcvd_packet) + stats_mgr->passRcvdPacket(ExchangeType::DO, rcvd_packet) ); - EXPECT_EQ(1, stats_mgr->getOrphans(StatsMgr4::XCHG_DO)); + EXPECT_EQ(1, stats_mgr->getOrphans(ExchangeType::DO)); } TEST_F(StatsMgrTest, SendReceiveUnordered) { + CommandOptions opt; const int packets_num = 10; - boost::scoped_ptr stats_mgr(new StatsMgr4()); - stats_mgr->addExchangeStats(StatsMgr4::XCHG_DO); + boost::scoped_ptr stats_mgr(new StatsMgr(opt)); + stats_mgr->addExchangeStats(ExchangeType::DO); // Transaction ids of 10 packets to be sent and received. uint32_t transid[packets_num] = @@ -405,7 +403,7 @@ TEST_F(StatsMgrTest, SendReceiveUnordered) { boost::shared_ptr sent_packet(createPacket4(DHCPDISCOVER, transid[i])); ASSERT_NO_THROW( - stats_mgr->passSentPacket(StatsMgr4::XCHG_DO, sent_packet) + stats_mgr->passSentPacket(ExchangeType::DO, sent_packet) ); } @@ -416,17 +414,17 @@ TEST_F(StatsMgrTest, SendReceiveUnordered) { rcvd_packet(createPacket4(DHCPDISCOVER, transid[packets_num - 1 - i])); ASSERT_NO_THROW( - stats_mgr->passRcvdPacket(StatsMgr4::XCHG_DO, rcvd_packet); + stats_mgr->passRcvdPacket(ExchangeType::DO, rcvd_packet); ); } // All packets are expected to match (we did not drop any) - EXPECT_EQ(0, stats_mgr->getOrphans(StatsMgr4::XCHG_DO)); + EXPECT_EQ(0, stats_mgr->getOrphans(ExchangeType::DO)); // Most of the time we have to do unordered lookups except for the last // one. Packets are removed from the sent list every time we have a match // so eventually we come up with the single packet that caching iterator // is pointing to. This is counted as ordered lookup. - EXPECT_EQ(1, stats_mgr->getOrderedLookups(StatsMgr4::XCHG_DO)); - EXPECT_EQ(9, stats_mgr->getUnorderedLookups(StatsMgr4::XCHG_DO)); + EXPECT_EQ(1, stats_mgr->getOrderedLookups(ExchangeType::DO)); + EXPECT_EQ(9, stats_mgr->getUnorderedLookups(ExchangeType::DO)); } TEST_F(StatsMgrTest, SendReceiveCollected) { @@ -438,33 +436,34 @@ TEST_F(StatsMgrTest, SendReceiveCollected) { } TEST_F(StatsMgrTest, Orphans) { + CommandOptions opt; const int packets_num = 6; - boost::scoped_ptr stats_mgr(new StatsMgr4()); - stats_mgr->addExchangeStats(StatsMgr4::XCHG_DO); + boost::scoped_ptr stats_mgr(new StatsMgr(opt)); + stats_mgr->addExchangeStats(ExchangeType::DO); // We skip every second packet to simulate drops. for (int i = 0; i < packets_num; i += 2) { boost::shared_ptr sent_packet(createPacket4(DHCPDISCOVER, i)); ASSERT_NO_THROW( - stats_mgr->passSentPacket(StatsMgr4::XCHG_DO, sent_packet) + stats_mgr->passSentPacket(ExchangeType::DO, sent_packet) ); } // We pass all received packets. for (int i = 0; i < packets_num; ++i) { boost::shared_ptr rcvd_packet(createPacket4(DHCPOFFER, i)); ASSERT_NO_THROW( - stats_mgr->passRcvdPacket(StatsMgr4::XCHG_DO, rcvd_packet); + stats_mgr->passRcvdPacket(ExchangeType::DO, rcvd_packet); ); } // The half of received packets are expected not to have matching // sent packet. - EXPECT_EQ(packets_num / 2, stats_mgr->getOrphans(StatsMgr4::XCHG_DO)); + EXPECT_EQ(packets_num / 2, stats_mgr->getOrphans(ExchangeType::DO)); } TEST_F(StatsMgrTest, Delays) { - - boost::shared_ptr stats_mgr(new StatsMgr4()); - stats_mgr->addExchangeStats(StatsMgr4::XCHG_DO, 5); + CommandOptions opt; + boost::shared_ptr stats_mgr(new StatsMgr(opt)); + stats_mgr->addExchangeStats(ExchangeType::DO, 5); // Send DISCOVER, wait 2s and receive OFFER. This will affect // counters in Stats Manager. @@ -472,16 +471,16 @@ TEST_F(StatsMgrTest, Delays) { // Initially min delay is equal to MAX_DOUBLE. After first packets // are passed, it is expected to set to actual value. - EXPECT_LT(stats_mgr->getMinDelay(StatsMgr4::XCHG_DO), + EXPECT_LT(stats_mgr->getMinDelay(ExchangeType::DO), std::numeric_limits::max()); - EXPECT_GT(stats_mgr->getMinDelay(StatsMgr4::XCHG_DO), 1); + EXPECT_GT(stats_mgr->getMinDelay(ExchangeType::DO), 1); // Max delay is supposed to the same value as minimum // or maximum delay. - EXPECT_GT(stats_mgr->getMaxDelay(StatsMgr4::XCHG_DO), 1); + EXPECT_GT(stats_mgr->getMaxDelay(ExchangeType::DO), 1); // Delay sums are now the same as minimum or maximum delay. - EXPECT_GT(stats_mgr->getAvgDelay(StatsMgr4::XCHG_DO), 1); + EXPECT_GT(stats_mgr->getAvgDelay(ExchangeType::DO), 1); // Simulate another DISCOVER-OFFER exchange with delay between // sent and received packets. Delay is now shorter than earlier @@ -489,11 +488,12 @@ TEST_F(StatsMgrTest, Delays) { const unsigned int delay2 = 1; passDOPacketsWithDelay(stats_mgr, delay2, common_transid + 1); // Standard deviation is expected to be non-zero. - EXPECT_GT(stats_mgr->getStdDevDelay(StatsMgr4::XCHG_DO), 0); + EXPECT_GT(stats_mgr->getStdDevDelay(ExchangeType::DO), 0); } TEST_F(StatsMgrTest, CustomCounters) { - boost::scoped_ptr stats_mgr(new StatsMgr4()); + CommandOptions opt; + boost::scoped_ptr stats_mgr(new StatsMgr(opt)); // Specify counter keys and names. const std::string too_short_key("tooshort"); @@ -518,13 +518,13 @@ TEST_F(StatsMgrTest, CustomCounters) { } // Check counter's current value and name. - StatsMgr4::CustomCounterPtr tooshort_counter = + CustomCounterPtr tooshort_counter = stats_mgr->getCounter(too_short_key); EXPECT_EQ(too_short_name, tooshort_counter->getName()); EXPECT_EQ(tooshort_num, tooshort_counter->getValue()); // Check counter's current value and name. - StatsMgr4::CustomCounterPtr toolate_counter = + CustomCounterPtr toolate_counter = stats_mgr->getCounter(too_late_key); EXPECT_EQ(too_late_name, toolate_counter->getName()); EXPECT_EQ(toolate_num, toolate_counter->getValue()); @@ -536,15 +536,16 @@ TEST_F(StatsMgrTest, PrintStats) { << "capabilities. It is expected that some counters " << "will be printed during this test. It may also " << "cause spurious errors." << std::endl; - boost::shared_ptr stats_mgr(new StatsMgr6()); - stats_mgr->addExchangeStats(StatsMgr6::XCHG_SA); + CommandOptions opt; + boost::shared_ptr stats_mgr(new StatsMgr(opt)); + stats_mgr->addExchangeStats(ExchangeType::SA); // Simulate sending and receiving one packet. Otherwise printing // functions will complain about lack of packets. const int packets_num = 1; - passMultiplePackets6(stats_mgr, StatsMgr6::XCHG_SA, DHCPV6_SOLICIT, + passMultiplePackets6(stats_mgr, ExchangeType::SA, DHCPV6_SOLICIT, packets_num); - passMultiplePackets6(stats_mgr, StatsMgr6::XCHG_SA, DHCPV6_ADVERTISE, + passMultiplePackets6(stats_mgr, ExchangeType::SA, DHCPV6_ADVERTISE, packets_num, true); // This function will print statistics even if packets are not @@ -559,9 +560,9 @@ TEST_F(StatsMgrTest, PrintStats) { // Now, we create another statistics manager instance and enable // packets archiving mode. - const bool archive_packets = true; - boost::shared_ptr stats_mgr2(new StatsMgr6(archive_packets)); - stats_mgr2->addExchangeStats(StatsMgr6::XCHG_SA); + CommandOptionsHelper::process(opt, "perfdhcp -x t 127.0.0.1"); + boost::shared_ptr stats_mgr2(new StatsMgr(opt)); + stats_mgr2->addExchangeStats(ExchangeType::SA); // Timestamps should now get printed because packets have been preserved. EXPECT_NO_THROW(stats_mgr2->printTimestamps()); diff --git a/src/bin/perfdhcp/tests/test_control_unittest.cc b/src/bin/perfdhcp/tests/test_control_unittest.cc index fc9393bb5c..a3215a4814 100644 --- a/src/bin/perfdhcp/tests/test_control_unittest.cc +++ b/src/bin/perfdhcp/tests/test_control_unittest.cc @@ -31,6 +31,62 @@ using namespace isc; using namespace isc::dhcp; using namespace isc::perfdhcp; +/// \brief FakePerfSocket class that mocks PerfSocket. +/// +/// It stubs send and receive operations and collects statistics. +class FakePerfSocket: public BasePerfSocket { +public: + /// \brief Default constructor for FakePerfSocket. + FakePerfSocket() : + iface_(boost::make_shared("fake", 0)), + sent_cnt_(0), + recv_cnt_(0) {}; + + IfacePtr iface_; ///< Local fake interface. + + int sent_cnt_; ///< Counter of sent packets + int recv_cnt_; ///< Counter of received packets. + + /// \brief Simulate receiving DHCPv4 packet. + virtual dhcp::Pkt4Ptr receive4(uint32_t timeout_sec, uint32_t timeout_usec) override { + (void)timeout_sec; // silence compile 'unused parameter' warning; + (void)timeout_usec; // silence compile 'unused parameter' warning; + recv_cnt_++; + return(dhcp::Pkt4Ptr()); + }; + + /// \brief Simulate receiving DHCPv6 packet. + virtual dhcp::Pkt6Ptr receive6(uint32_t timeout_sec, uint32_t timeout_usec) override { + (void)timeout_sec; // silence compile 'unused parameter' warning; + (void)timeout_usec; // silence compile 'unused parameter' warning; + recv_cnt_++; + return(dhcp::Pkt6Ptr()); + }; + + /// \brief Simulate sending DHCPv4 packet. + virtual bool send(const dhcp::Pkt4Ptr& pkt) override { + sent_cnt_++; + pkt->updateTimestamp(); + return true; + }; + + /// \brief Simulate sending DHCPv6 packet. + virtual bool send(const dhcp::Pkt6Ptr& pkt) override { + sent_cnt_++; + pkt->updateTimestamp(); + return true; + }; + + /// \brief Override getting interface. + virtual IfacePtr getIface() override { return iface_; } + + void reset() { + sent_cnt_ = 0; + recv_cnt_ = 0; + } +}; + + /// \brief Test Control class with protected members made public. /// /// This class makes protected TestControl class's members public @@ -74,7 +130,6 @@ public: /// \brief Pointer to incremental generator. typedef boost::shared_ptr IncrementalGeneratorPtr; - using TestControl::checkExitConditions; using TestControl::createMessageFromReply; using TestControl::createRequestFromAck; using TestControl::factoryElapsedTime6; @@ -88,8 +143,6 @@ public: using TestControl::generateMacAddress; using TestControl::getTemplateBuffer; using TestControl::initPacketTemplates; - using TestControl::initializeStatsMgr; - using TestControl::openSocket; using TestControl::processReceivedPacket4; using TestControl::processReceivedPacket6; using TestControl::registerOptionFactories; @@ -103,9 +156,7 @@ public: using TestControl::sendSolicit6; using TestControl::setDefaults4; using TestControl::setDefaults6; - using TestControl::basic_rate_control_; - using TestControl::renew_rate_control_; - using TestControl::release_rate_control_; + using TestControl::socket_; using TestControl::last_report_; using TestControl::transid_gen_; using TestControl::macaddr_gen_; @@ -115,15 +166,20 @@ public: using TestControl::template_packets_v6_; using TestControl::ack_storage_; using TestControl::sendRequestFromAck; + using TestControl::options_; + using TestControl::stats_mgr_; + + FakePerfSocket fake_sock_; - NakedTestControl() : TestControl() { - uint32_t clients_num = CommandOptions::instance().getClientsNum() == 0 ? - 1 : CommandOptions::instance().getClientsNum(); + NakedTestControl(CommandOptions &opt) : TestControl(opt, fake_sock_) { + uint32_t clients_num = opt.getClientsNum() == 0 ? + 1 : opt.getClientsNum(); setMacAddrGenerator(NumberGeneratorPtr(new TestControl::SequentialGenerator(clients_num))); }; }; + /// \brief Test Fixture Class /// /// This test fixture class is used to perform @@ -189,25 +245,6 @@ public: return (true); } - /// \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 { - BOOST_FOREACH(IfacePtr iface, IfaceMgr::instance().getIfaces()) { - if (iface->flag_loopback_) { - return (iface->getName()); - } - } - return (""); - } - /// \brief Get full path to a file in testdata directory. /// /// \param filename filename being appended to absolute @@ -316,10 +353,10 @@ public: /// 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(); + void testDuid(CommandOptions &opt) const { + int clients_num = opt.getClientsNum(); // Initialize Test Control class. - NakedTestControl tc; + NakedTestControl tc(opt); // 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. @@ -439,21 +476,18 @@ public: /// /// \param iterations_num number of exchanges to simulate. /// \param receive_num number of received OFFER packets. - /// \param iterations_performed actual number of iterations. /// \param tc test control instance void testPkt4Exchange(int iterations_num, int receive_num, bool use_templates, - int& iterations_performed, NakedTestControl& tc) const { - int sock_handle = 0; - tc.initializeStatsMgr(); + //int sock_handle = 0; // Use templates files to crate packets. if (use_templates) { tc.initPacketTemplates(); - ASSERT_NO_THROW(tc.getTemplateBuffer(0)); - ASSERT_NO_THROW(tc.getTemplateBuffer(1)); + tc.getTemplateBuffer(0); + tc.getTemplateBuffer(1); } // Incremental transaction id generator will generate @@ -464,17 +498,14 @@ public: NakedTestControl::IncrementalGeneratorPtr generator(new NakedTestControl::IncrementalGenerator()); tc.setTransidGenerator(generator); - // Socket is needed to send packets through the interface. - ASSERT_NO_THROW(sock_handle = tc.openSocket()); - PerfSocket sock(sock_handle); for (int i = 0; i < iterations_num; ++i) { // Get next transaction id, without actually using it. The same // id wll be used by the TestControl class for DHCPDISCOVER. uint32_t transid = generator->getNext(); if (use_templates) { - ASSERT_NO_THROW(tc.sendDiscover4(sock, tc.getTemplateBuffer(0))); + tc.sendDiscover4(tc.getTemplateBuffer(0)); } else { - ASSERT_NO_THROW(tc.sendDiscover4(sock)); + tc.sendDiscover4(); } // Do not simulate responses for packets later @@ -482,13 +513,8 @@ public: // packet drops. if (i < receive_num) { boost::shared_ptr offer_pkt4(createOfferPkt4(transid)); - ASSERT_NO_THROW(tc.processReceivedPacket4(sock, offer_pkt4)); + tc.processReceivedPacket4(offer_pkt4); } - if (tc.checkExitConditions()) { - iterations_performed = i + 1; - break; - } - iterations_performed = i + 1; } } @@ -504,21 +530,18 @@ public: /// /// \param iterations_num number of exchanges to simulate. /// \param receive_num number of received OFFER packets. - /// \param iterations_performed actual number of iterations. /// \param tc test control instance void testPkt6Exchange(int iterations_num, int receive_num, bool use_templates, - int& iterations_performed, NakedTestControl& tc) const { - int sock_handle = 0; - tc.initializeStatsMgr(); + //int sock_handle = 0; // Use templates files to crate packets. if (use_templates) { tc.initPacketTemplates(); - ASSERT_NO_THROW(tc.getTemplateBuffer(0)); - ASSERT_NO_THROW(tc.getTemplateBuffer(1)); + tc.getTemplateBuffer(0); + tc.getTemplateBuffer(1); } // Incremental transaction id generator will generate @@ -529,33 +552,24 @@ public: TestControl::NumberGeneratorPtr generator(new NakedTestControl::IncrementalGenerator()); tc.setTransidGenerator(generator); - // Socket is needed to send packets through the interface. - ASSERT_NO_THROW(sock_handle = tc.openSocket()); - PerfSocket sock(sock_handle); 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. if (use_templates) { - ASSERT_NO_THROW(tc.sendSolicit6(sock, tc.getTemplateBuffer(0))); + tc.sendSolicit6(tc.getTemplateBuffer(0)); } else { - ASSERT_NO_THROW(tc.sendSolicit6(sock)); + tc.sendSolicit6(); } ++transid; if (i < receive_num) { boost::shared_ptr - advertise_pkt6(createAdvertisePkt6(transid)); + advertise_pkt6(createAdvertisePkt6(tc, transid)); // Receive ADVERTISE and send REQUEST. - ASSERT_NO_THROW(tc.processReceivedPacket6(sock, - advertise_pkt6)); + tc.processReceivedPacket6(advertise_pkt6); ++transid; } - if (tc.checkExitConditions()) { - iterations_performed = i + 1; - break; - } - iterations_performed = i + 1; } } @@ -567,13 +581,13 @@ public: /// 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(); + void testMacAddress(CommandOptions &opt) const { + int clients_num = opt.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 multiple clients are simulated or if it // does not change when single client is simulated. - MacAddress old_mac(CommandOptions::instance().getMacTemplate()); + MacAddress old_mac(opt.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 @@ -583,7 +597,7 @@ public: // Number of unique MACs. size_t unique_macs = 0; // Initialize Test Controller. - NakedTestControl tc; + NakedTestControl tc(opt); size_t total_dist = 0; // Keep generated MACs in this container. std::list > macs; @@ -650,34 +664,24 @@ public: /// attempt to send more renew messages than the number of leases acquired /// will fail. void testSendRenew4() { - std::string loopback_iface(getLocalLoopback()); - if (loopback_iface.empty()) { - std::cout << "Skipping the test because loopback interface could" - " not be detected" << std::endl; - return; - } // Build a command line. Depending on the message type, we will use // -f or -F parameter. + CommandOptions opt; std::ostringstream s; - s << "perfdhcp -4 -l " << loopback_iface << " -r 10 -f"; + s << "perfdhcp -4 -l fake -r 10 -f"; s << " 10 -R 10 -L 10067 -n 10 127.0.0.1"; - ASSERT_NO_THROW(processCmdLine(s.str())); + processCmdLine(opt, s.str()); // Create a test controller class. - NakedTestControl tc; - tc.initializeStatsMgr(); + NakedTestControl tc(opt); // Set the transaction id generator to sequential to control to // guarantee that transaction ids are predictable. boost::shared_ptr generator(new NakedTestControl::IncrementalGenerator()); tc.setTransidGenerator(generator); - // Socket has to be created so as we can actually send packets. - int sock_handle = 0; - ASSERT_NO_THROW(sock_handle = tc.openSocket()); - PerfSocket sock(sock_handle); // Send a number of DHCPDISCOVER messages. Each generated message will // be assigned a different transaction id, starting from 1 to 10. - tc.sendPackets(sock, 10); + tc.sendPackets(10); // Simulate DHCPOFFER responses from the server. Each DHCPOFFER is // assigned a transaction id from the range of 1 to 10, so as they @@ -689,7 +693,7 @@ public: // will trigger a corresponding DHCPREQUEST. They will be assigned // transaction ids from the range from 11 to 20 (the range of // 1 to 10 has been used by DHCPDISCOVER-DHCPOFFER). - ASSERT_NO_THROW(tc.processReceivedPacket4(sock, offer)); + tc.processReceivedPacket4(offer); } // Requests have been sent, so now let's simulate responses from the @@ -702,32 +706,26 @@ public: // -f option has been specified, received Reply // messages are held so as renew messages can be sent for // existing leases. - ASSERT_NO_THROW(tc.processReceivedPacket4(sock, ack)); + tc.processReceivedPacket4(ack); } uint64_t msg_num; // Try to send 5 messages. It should be successful because 10 // DHCPREQUEST messages has been received. For each of them we // should be able to send renewal. - ASSERT_NO_THROW( - msg_num = tc.sendMultipleRequests(sock, 5) - ); + msg_num = tc.sendMultipleRequests(5); // Make sure that we have sent 5 messages. EXPECT_EQ(5, msg_num); // Try to do it again. We should still have 5 Reply packets for // which renews haven't been sent yet. - ASSERT_NO_THROW( - msg_num = tc.sendMultipleRequests(sock, 5) - ); + msg_num = tc.sendMultipleRequests(5); EXPECT_EQ(5, msg_num); // We used all the DHCPACK packets (we sent renew or release for each of // them already). Therefore, no further renew messages should be sent // before we acquire new leases. - ASSERT_NO_THROW( - msg_num = tc.sendMultipleRequests(sock, 5) - ); + msg_num = tc.sendMultipleRequests(5); // Make sure that no message has been sent. EXPECT_EQ(0, msg_num); } @@ -737,12 +735,13 @@ public: void testCreateRequest() { // This command line specifies that the Release/Renew messages should // be sent with the same rate as the Solicit messages. + CommandOptions opt; std::ostringstream s; s << "perfdhcp -4 -l lo -r 10 -f 10"; s << " -R 10 -L 10067 -n 10 127.0.0.1"; - ASSERT_NO_THROW(processCmdLine(s.str())); + processCmdLine(opt, s.str()); // Create a test controller class. - NakedTestControl tc; + NakedTestControl tc(opt); // Set the transaction id generator which will be used by the // createRenew or createRelease function to generate transaction id. boost::shared_ptr @@ -753,7 +752,7 @@ public: // Create DHCPREQUEST from DHCPACK. Pkt4Ptr request; - ASSERT_NO_THROW(request = tc.createRequestFromAck(ack)); + request = tc.createRequestFromAck(ack); // Make sure that the DHCPACK has been successfully created and that // it holds expected data. @@ -785,13 +784,14 @@ public: void testCreateRenewRelease(const uint16_t msg_type) { // This command line specifies that the Release/Renew messages should // be sent with the same rate as the Solicit messages. + CommandOptions opt; std::ostringstream s; s << "perfdhcp -6 -l lo -r 10 "; s << (msg_type == DHCPV6_RELEASE ? "-F" : "-f") << " 10 "; s << "-R 10 -L 10547 -n 10 -e address-and-prefix ::1"; - ASSERT_NO_THROW(processCmdLine(s.str())); + processCmdLine(opt, s.str()); // Create a test controller class. - NakedTestControl tc; + NakedTestControl tc(opt); // Set the transaction id generator which will be used by the // createRenew or createRelease function to generate transaction id. boost::shared_ptr @@ -800,11 +800,11 @@ public: // Create a Reply packet. The createRelease or createReply function will // need Reply packet to create a corresponding Release or Reply. - Pkt6Ptr reply = createReplyPkt6(1); + Pkt6Ptr reply = createReplyPkt6(tc, 1); Pkt6Ptr msg; // Check that the message is created. - ASSERT_NO_THROW(msg = tc.createMessageFromReply(msg_type, reply)); + msg = tc.createMessageFromReply(msg_type, reply); ASSERT_TRUE(msg); // Check that the message type and transaction id is correct. @@ -856,47 +856,37 @@ public: /// \param msg_type A type of the message which is simulated to be sent /// (DHCPV6_RENEW or DHCPV6_RELEASE). void testSendRenewRelease(const uint16_t msg_type) { - std::string loopback_iface(getLocalLoopback()); - if (loopback_iface.empty()) { - std::cout << "Skipping the test because loopback interface could" - " not be detected" << std::endl; - return; - } // Build a command line. Depending on the message type, we will use // -f or -F parameter. + CommandOptions opt; std::ostringstream s; - s << "perfdhcp -6 -l " << loopback_iface << " -r 10 "; + s << "perfdhcp -6 -l fake -r 10 "; s << (msg_type == DHCPV6_RENEW ? "-f" : "-F"); s << " 10 -R 10 -L 10547 -n 10 ::1"; - ASSERT_NO_THROW(processCmdLine(s.str())); + processCmdLine(opt, s.str()); // Create a test controller class. - NakedTestControl tc; - tc.initializeStatsMgr(); + NakedTestControl tc(opt); // Set the transaction id generator to sequential to control to // guarantee that transaction ids are predictable. boost::shared_ptr generator(new NakedTestControl::IncrementalGenerator()); tc.setTransidGenerator(generator); - // Socket has to be created so as we can actually send packets. - int sock_handle = 0; - ASSERT_NO_THROW(sock_handle = tc.openSocket()); - PerfSocket sock(sock_handle); // Send a number of Solicit messages. Each generated Solicit will be // assigned a different transaction id, starting from 1 to 10. - tc.sendPackets(sock, 10); + tc.sendPackets(10); // Simulate Advertise responses from the server. Each advertise is // assigned a transaction id from the range of 1 to 10, so as they // match the transaction ids from the Solicit messages. for (unsigned i = generator->getNext() - 10; i < generator->getNext(); ++i) { - Pkt6Ptr advertise(createAdvertisePkt6(i)); + Pkt6Ptr advertise(createAdvertisePkt6(tc, i)); // If Advertise is matched with the Solicit the call below will // trigger a corresponding Request. They will be assigned // transaction ids from the range from 11 to 20 (the range of // 1 to 10 has been used by Solicit-Advertise). - ASSERT_NO_THROW(tc.processReceivedPacket6(sock, advertise)); + tc.processReceivedPacket6(advertise); } // Requests have been sent, so now let's simulate responses from the @@ -904,37 +894,31 @@ public: // ids from the range from 11 to 20. for (unsigned i = generator->getNext() - 10; i < generator->getNext(); ++i) { - Pkt6Ptr reply(createReplyPkt6(i)); + Pkt6Ptr reply(createReplyPkt6(tc, i)); // Each Reply packet corresponds to the new lease acquired. Since // -f option has been specified, received Reply // messages are held so as Renew messages can be sent for // existing leases. - ASSERT_NO_THROW(tc.processReceivedPacket6(sock, reply)); + tc.processReceivedPacket6(reply); } uint64_t msg_num; // Try to send 5 messages. It should be successful because 10 Reply // messages has been received. For each of them we should be able to // send Renew or Release. - ASSERT_NO_THROW( - msg_num = tc.sendMultipleMessages6(sock, msg_type, 5) - ); + msg_num = tc.sendMultipleMessages6(msg_type, 5); // Make sure that we have sent 5 messages. EXPECT_EQ(5, msg_num); // Try to do it again. We should still have 5 Reply packets for // which Renews or Releases haven't been sent yet. - ASSERT_NO_THROW( - msg_num = tc.sendMultipleMessages6(sock, msg_type, 5) - ); + msg_num = tc.sendMultipleMessages6(msg_type, 5); EXPECT_EQ(5, msg_num); // We used all the Reply packets (we sent Renew or Release for each of // them already). Therefore, no further Renew or Release messages should // be sent before we acquire new leases. - ASSERT_NO_THROW( - msg_num = tc.sendMultipleMessages6(sock, msg_type, 5) - ); + msg_num = tc.sendMultipleMessages6(msg_type, 5); // Make sure that no message has been sent. EXPECT_EQ(0, msg_num); @@ -945,8 +929,8 @@ public: /// \param cmdline command line string to be parsed. /// \throw isc::Unexpected if unexpected error occurred. /// \throw isc::InvalidParameter if command line is invalid. - void processCmdLine(const std::string& cmdline) const { - CommandOptionsHelper::process(cmdline); + void processCmdLine(CommandOptions &opt, const std::string& cmdline) const { + CommandOptionsHelper::process(opt, cmdline); } /// \brief Create DHCPOFFER or DHCPACK packet. @@ -991,22 +975,19 @@ public: /// \param transid transaction id. /// \return instance of the packet. Pkt6Ptr - createAdvertisePkt6(const uint32_t transid) const { + createAdvertisePkt6(NakedTestControl &tc, const uint32_t transid) const { boost::shared_ptr advertise(new Pkt6(DHCPV6_ADVERTISE, transid)); // Add IA_NA if requested by the client. - if (CommandOptions::instance().getLeaseType() - .includes(CommandOptions::LeaseType::ADDRESS)) { + if (tc.options_.getLeaseType().includes(CommandOptions::LeaseType::ADDRESS)) { OptionPtr opt_ia_na = Option::factory(Option::V6, D6O_IA_NA); advertise->addOption(opt_ia_na); } // Add IA_PD if requested by the client. - if (CommandOptions::instance().getLeaseType() - .includes(CommandOptions::LeaseType::PREFIX)) { + if (tc.options_.getLeaseType().includes(CommandOptions::LeaseType::PREFIX)) { OptionPtr opt_ia_pd = Option::factory(Option::V6, D6O_IA_PD); advertise->addOption(opt_ia_pd); } OptionPtr opt_serverid(new Option(Option::V6, D6O_SERVERID)); - NakedTestControl tc; uint8_t randomized = 0; std::vector duid(tc.generateDuid(randomized)); OptionPtr opt_clientid(Option::factory(Option::V6, D6O_CLIENTID, duid)); @@ -1017,22 +998,19 @@ public: } Pkt6Ptr - createReplyPkt6(const uint32_t transid) const { + createReplyPkt6(NakedTestControl &tc, const uint32_t transid) const { Pkt6Ptr reply(new Pkt6(DHCPV6_REPLY, transid)); // Add IA_NA if requested by the client. - if (CommandOptions::instance().getLeaseType() - .includes(CommandOptions::LeaseType::ADDRESS)) { + if (tc.options_.getLeaseType().includes(CommandOptions::LeaseType::ADDRESS)) { OptionPtr opt_ia_na = Option::factory(Option::V6, D6O_IA_NA); reply->addOption(opt_ia_na); } // Add IA_PD if requested by the client. - if (CommandOptions::instance().getLeaseType() - .includes(CommandOptions::LeaseType::PREFIX)) { + if (tc.options_.getLeaseType().includes(CommandOptions::LeaseType::PREFIX)) { OptionPtr opt_ia_pd = Option::factory(Option::V6, D6O_IA_PD); reply->addOption(opt_ia_pd); } OptionPtr opt_serverid(new Option(Option::V6, D6O_SERVERID)); - NakedTestControl tc; uint8_t randomized = 0; std::vector duid(tc.generateDuid(randomized)); OptionPtr opt_clientid(Option::factory(Option::V6, D6O_CLIENTID, duid)); @@ -1079,12 +1057,10 @@ public: // This test verifies that the class members are reset to expected values. TEST_F(TestControlTest, reset) { - ASSERT_NO_THROW(processCmdLine("perfdhcp -6 -l ethx -r 50 -f 30 -F 10 all")); - NakedTestControl tc; + CommandOptions opt; + processCmdLine(opt, "perfdhcp -4 127.0.0.1"); + NakedTestControl tc(opt); tc.reset(); - EXPECT_EQ(50, tc.basic_rate_control_.getRate()); - EXPECT_EQ(30, tc.renew_rate_control_.getRate()); - EXPECT_EQ(10, tc.release_rate_control_.getRate()); EXPECT_FALSE(tc.last_report_.is_not_a_date_time()); EXPECT_FALSE(tc.transid_gen_); EXPECT_FALSE(tc.macaddr_gen_); @@ -1103,9 +1079,11 @@ TEST_F(TestControlTest, generateClientId) { HWAddrPtr hwaddr_ptr(new HWAddr(hwaddr, 5)); // Use generated HW address to generate client id. - NakedTestControl tc; + CommandOptions opt; + processCmdLine(opt, "perfdhcp -4 127.0.0.1"); + NakedTestControl tc(opt); OptionPtr opt_client_id; - ASSERT_NO_THROW(opt_client_id = tc.generateClientId(hwaddr_ptr)); + opt_client_id = tc.generateClientId(hwaddr_ptr); ASSERT_TRUE(opt_client_id); // Extract the client id data. @@ -1124,21 +1102,22 @@ TEST_F(TestControlTest, generateClientId) { 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(); + CommandOptions opt; + processCmdLine(opt, "perfdhcp -l 127.0.0.1 all"); + testDuid(opt); // Simulate 50 clients. Different DUID will be generated. - ASSERT_NO_THROW(processCmdLine("perfdhcp -l 127.0.0.1 -R 50 all")); - testDuid(); + processCmdLine(opt, "perfdhcp -l 127.0.0.1 -R 50 all"); + testDuid(opt); // Checks that the random mac address returned by generateDuid // is in the list of mac addresses in the mac-list.txt data file std::string mac_list_full_path = getFullPath("mac-list.txt"); std::ostringstream cmd; - cmd << "perfdhcp -M " << mac_list_full_path << " abc"; - ASSERT_NO_THROW(processCmdLine(cmd.str())); + cmd << "perfdhcp -M " << mac_list_full_path << " 127.0.0.1"; + processCmdLine(opt, cmd.str()); // Initialize Test Controller. - NakedTestControl tc; + NakedTestControl tc(opt); uint8_t randomized = 0; std::vector generated_duid = tc.generateDuid(randomized); @@ -1148,8 +1127,7 @@ TEST_F(TestControlTest, GenerateDuid) { ASSERT_EQ(duid->getType(), DUID::DUID_LL); // Make sure it's on the list - CommandOptions& options = CommandOptions::instance(); - const CommandOptions::MacAddrsVector& macs = options.getMacsFromFile(); + const CommandOptions::MacAddrsVector& macs = opt.getMacsFromFile(); // DUID LL comprises 2 bytes of duid type, 2 bytes of hardware type, // then 6 bytes of HW address. vector mac(6); @@ -1159,50 +1137,40 @@ TEST_F(TestControlTest, GenerateDuid) { ASSERT_TRUE(std::find(macs.begin(), macs.end(), mac) != macs.end()); } -TEST_F(TestControlTest, MisMatchVersionServer) { - NakedTestControl tc; - - // make sure we catch -6 paired with v4 address - ASSERT_NO_THROW(processCmdLine("perfdhcp -l 127.0.0.1 -6 192.168.1.1")); - EXPECT_THROW(tc.openSocket(), isc::InvalidParameter); - - // make sure we catch -4 paired with v6 address - ASSERT_NO_THROW(processCmdLine("perfdhcp -l 127.0.0.1 -4 ff02::1:2")); - EXPECT_THROW(tc.openSocket(), isc::InvalidParameter); -} - TEST_F(TestControlTest, GenerateMacAddress) { + CommandOptions opt; // Simulate one client only. Always the same MAC address will be // generated. - ASSERT_NO_THROW(processCmdLine("perfdhcp -l 127.0.0.1 all")); - testMacAddress(); + processCmdLine(opt, "perfdhcp -l 127.0.0.1 all"); + testMacAddress(opt); // Simulate 50 clients. Different MAC addresses will be generated. - ASSERT_NO_THROW(processCmdLine("perfdhcp -l 127.0.0.1 -R 50 all")); - testMacAddress(); + processCmdLine(opt, "perfdhcp -l 127.0.0.1 -R 50 all"); + testMacAddress(opt); // Checks that the random mac address returned by generateMacAddress // is in the list of mac addresses in the mac-list.txt data file std::string mac_list_full_path = getFullPath("mac-list.txt"); std::ostringstream cmd; - cmd << "perfdhcp -M " << mac_list_full_path << " abc"; - ASSERT_NO_THROW(processCmdLine(cmd.str())); + cmd << "perfdhcp -M " << mac_list_full_path << " 127.0.0.1"; + processCmdLine(opt, cmd.str()); // Initialize Test Controller. - NakedTestControl tc; + NakedTestControl tc(opt); uint8_t randomized = 0; // Generate MAC address and sanity check its size. std::vector mac = tc.generateMacAddress(randomized); ASSERT_EQ(6, mac.size()); // Make sure that the generated MAC address belongs to the MAC addresses // read from a file. - CommandOptions& options = CommandOptions::instance(); - const CommandOptions::MacAddrsVector& macs = options.getMacsFromFile(); + const CommandOptions::MacAddrsVector& macs = opt.getMacsFromFile(); ASSERT_TRUE(std::find(macs.begin(), macs.end(), mac) != macs.end()); } TEST_F(TestControlTest, Options4) { using namespace isc::dhcp; - NakedTestControl tc; + CommandOptions opt; + processCmdLine(opt, "perfdhcp -4 127.0.0.1"); + NakedTestControl tc(opt); // 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. @@ -1216,7 +1184,7 @@ TEST_F(TestControlTest, Options4) { 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()); + msg_type = opt_msg_type->getUint8(); EXPECT_EQ(DHCPDISCOVER, msg_type); // Create another option: DHCP_PARAMETER_REQUEST_LIST @@ -1254,11 +1222,12 @@ TEST_F(TestControlTest, Options4) { TEST_F(TestControlTest, Options6) { using namespace isc::dhcp; + CommandOptions opt; // Lets override the IP version to test V6 options (-6 parameter) - ASSERT_NO_THROW(processCmdLine("perfdhcp -l 127.0.0.1 -6 all")); + processCmdLine(opt, "perfdhcp -l lo -6 ::1"); - NakedTestControl tc; + NakedTestControl tc(opt); tc.registerOptionFactories(); // Validate the D6O_ELAPSED_TIME option. @@ -1268,7 +1237,7 @@ TEST_F(TestControlTest, Options6) { 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()); + elapsed_time = opt_elapsed_time->getUint16(); EXPECT_EQ(0, elapsed_time); // With the factory function we may also specify the actual @@ -1295,7 +1264,7 @@ TEST_F(TestControlTest, Options6) { 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()); + elapsed_time = opt_elapsed_time2->getUint16(); // Check the expected value of elapsed time. EXPECT_EQ(0x0101, elapsed_time); @@ -1308,7 +1277,7 @@ TEST_F(TestControlTest, Options6) { EXPECT_THROW(opt_rapid_commit->getUint8(), isc::OutOfRange); // Validate the D6O_CLIENTID option. - OptionBuffer duid(CommandOptions::instance().getDuidTemplate()); + OptionBuffer duid(opt.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()); @@ -1362,182 +1331,125 @@ TEST_F(TestControlTest, Options6) { } 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 + - " -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()); - PerfSocket 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.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.addr_), pkt4->getLocalAddr()); - EXPECT_EQ(asiolink::IOAddress(sock.addr_), pkt4->getGiaddr()); - } else { - std::cout << "Unable to find the loopback interface. Skip test. " - << std::endl; - } + CommandOptions opt; + processCmdLine(opt, "perfdhcp -l fake -L 10547 all"); + NakedTestControl tc(opt); + uint32_t transid = 123; + boost::shared_ptr pkt4(new Pkt4(DHCPDISCOVER, transid)); + // Set parameters on outgoing packet. + tc.setDefaults4(pkt4); + // Validate that packet has been setup correctly. + EXPECT_EQ(tc.fake_sock_.iface_->getName(), pkt4->getIface()); + EXPECT_EQ(tc.fake_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(tc.socket_.addr_), pkt4->getLocalAddr()); + EXPECT_EQ(asiolink::IOAddress(tc.socket_.addr_), pkt4->getGiaddr()); } 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 + - " -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()); - PerfSocket 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.ifindex_, pkt6->getIndex()); - EXPECT_EQ(DHCP6_CLIENT_PORT, pkt6->getLocalPort()); - EXPECT_EQ(DHCP6_SERVER_PORT, pkt6->getRemotePort()); - EXPECT_EQ(sock.addr_, pkt6->getLocalAddr()); - EXPECT_EQ(asiolink::IOAddress("FF05::1:3"), pkt6->getRemoteAddr()); - // Packet must not be relayed. - EXPECT_TRUE(pkt6->relay_info_.empty()); - - } else { - std::cout << "Unable to find the loopback interface. Skip test. " - << std::endl; - } + CommandOptions opt; + processCmdLine(opt, "perfdhcp -6 -l fake -L 10547 servers"); + NakedTestControl tc(opt); + uint32_t transid = 123; + boost::shared_ptr pkt6(new Pkt6(DHCPV6_SOLICIT, transid)); + // Set packet's parameters. + tc.setDefaults6(pkt6); + // Validate if parameters have been set correctly. + EXPECT_EQ(tc.fake_sock_.iface_->getName(), pkt6->getIface()); + EXPECT_EQ(tc.socket_.ifindex_, pkt6->getIndex()); + EXPECT_EQ(DHCP6_CLIENT_PORT, pkt6->getLocalPort()); + EXPECT_EQ(DHCP6_SERVER_PORT, pkt6->getRemotePort()); + EXPECT_EQ(tc.socket_.addr_, pkt6->getLocalAddr()); + EXPECT_EQ(asiolink::IOAddress("FF05::1:3"), pkt6->getRemoteAddr()); + // Packet must not be relayed. + EXPECT_TRUE(pkt6->relay_info_.empty()); } TEST_F(TestControlTest, Packet6Relayed) { - // 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 + - " -A1 -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()); - PerfSocket 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.ifindex_, pkt6->getIndex()); - EXPECT_EQ(DHCP6_CLIENT_PORT, pkt6->getLocalPort()); - EXPECT_EQ(DHCP6_SERVER_PORT, pkt6->getRemotePort()); - EXPECT_EQ(sock.addr_, pkt6->getLocalAddr()); - EXPECT_EQ(asiolink::IOAddress("FF05::1:3"), pkt6->getRemoteAddr()); - // Packet should be relayed. - EXPECT_EQ(pkt6->relay_info_.size(), 1); - EXPECT_EQ(pkt6->relay_info_[0].hop_count_, 1); - EXPECT_EQ(pkt6->relay_info_[0].msg_type_, DHCPV6_RELAY_FORW); - EXPECT_EQ(pkt6->relay_info_[0].linkaddr_, sock.addr_); - EXPECT_EQ(pkt6->relay_info_[0].peeraddr_, sock.addr_); - } else { - std::cout << "Unable to find the loopback interface. Skip test. " - << std::endl; - } + CommandOptions opt; + processCmdLine(opt, "perfdhcp -6 -l fake -A1 -L 10547 servers"); + NakedTestControl tc(opt); + uint32_t transid = 123; + boost::shared_ptr pkt6(new Pkt6(DHCPV6_SOLICIT, transid)); + // Set packet's parameters. + tc.setDefaults6(pkt6); + // Validate if parameters have been set correctly. + EXPECT_EQ(tc.fake_sock_.iface_->getName(), pkt6->getIface()); + EXPECT_EQ(tc.socket_.ifindex_, pkt6->getIndex()); + EXPECT_EQ(DHCP6_CLIENT_PORT, pkt6->getLocalPort()); + EXPECT_EQ(DHCP6_SERVER_PORT, pkt6->getRemotePort()); + EXPECT_EQ(tc.socket_.addr_, pkt6->getLocalAddr()); + EXPECT_EQ(asiolink::IOAddress("FF05::1:3"), pkt6->getRemoteAddr()); + // Packet should be relayed. + EXPECT_EQ(pkt6->relay_info_.size(), 1); + EXPECT_EQ(pkt6->relay_info_[0].hop_count_, 1); + EXPECT_EQ(pkt6->relay_info_[0].msg_type_, DHCPV6_RELAY_FORW); + EXPECT_EQ(pkt6->relay_info_[0].linkaddr_, tc.socket_.addr_); + EXPECT_EQ(pkt6->relay_info_[0].peeraddr_, tc.socket_.addr_); } 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 -L 10547 127.0.0.1"); - // The actual number of iterations will be stored in the - // following variable. - int iterations_performed = 0; + CommandOptions opt; + processCmdLine(opt, "perfdhcp -l fake -r 100 -n 10 -R 20 -L 10547 127.0.0.1"); bool use_templates = false; - NakedTestControl tc; - testPkt4Exchange(iterations_num, iterations_num, use_templates, iterations_performed, tc); - // 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 10% of all. - // Use templates for this test. - processCmdLine("perfdhcp -l " + loopback_iface - + " -r 100 -R 20 -n 20 -D 10% -L 10547" - + " -T " + getFullPath("discover-example.hex") + NakedTestControl tc(opt); + testPkt4Exchange(iterations_num, iterations_num, use_templates, tc); + + EXPECT_EQ(tc.fake_sock_.sent_cnt_, iterations_num * 2); // Discovery + Request + EXPECT_EQ(tc.stats_mgr_.getSentPacketsNum(ExchangeType::DO), iterations_num); + EXPECT_EQ(tc.stats_mgr_.getRcvdPacketsNum(ExchangeType::DO), iterations_num); + EXPECT_EQ(tc.stats_mgr_.getSentPacketsNum(ExchangeType::RA), iterations_num); + EXPECT_EQ(tc.stats_mgr_.getRcvdPacketsNum(ExchangeType::RA), 0); +} + +TEST_F(TestControlTest, Packet4ExchangeFromTemplate) { + const int iterations_num = 100; + CommandOptions opt; + + processCmdLine(opt, "perfdhcp -l fake -r 100 -R 20 -n 20 -L 10547" + " -T " + getFullPath("discover-example.hex") + " -T " + getFullPath("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; - use_templates = true; - testPkt4Exchange(iterations_num, received_num, use_templates, iterations_performed, tc); - EXPECT_EQ(12, iterations_performed); + bool use_templates = true; + NakedTestControl tc(opt); + testPkt4Exchange(iterations_num, received_num, use_templates, tc); + + EXPECT_EQ(tc.fake_sock_.sent_cnt_, iterations_num + received_num); // Discovery + Request + EXPECT_EQ(tc.stats_mgr_.getSentPacketsNum(ExchangeType::DO), iterations_num); + EXPECT_EQ(tc.stats_mgr_.getRcvdPacketsNum(ExchangeType::DO), received_num); + EXPECT_EQ(tc.stats_mgr_.getSentPacketsNum(ExchangeType::RA), received_num); + EXPECT_EQ(tc.stats_mgr_.getRcvdPacketsNum(ExchangeType::RA), 0); } -TEST_F(TestControlTest, Packet6ExchangeFromTemplate) { - // 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; - } - +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 -L 10547 ::1"); - int iterations_performed = 0; - // Set number of received packets equal to number of iterations. - // This simulates no packet drops. + CommandOptions opt; + processCmdLine(opt, "perfdhcp -l fake -6 -r 100 -n 10 -R 20 -L 10547 ::1"); bool use_templates = false; - NakedTestControl tc; - testPkt6Exchange(iterations_num, iterations_num, use_templates, - iterations_performed, tc); - // 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" - + " -T " + getFullPath("solicit-example.hex") + NakedTestControl tc(opt); + testPkt6Exchange(iterations_num, iterations_num, use_templates, tc); + + EXPECT_EQ(tc.fake_sock_.sent_cnt_, iterations_num * 2); // Solicit + Request + EXPECT_EQ(tc.stats_mgr_.getSentPacketsNum(ExchangeType::SA), iterations_num); + EXPECT_EQ(tc.stats_mgr_.getRcvdPacketsNum(ExchangeType::SA), iterations_num); + EXPECT_EQ(tc.stats_mgr_.getSentPacketsNum(ExchangeType::RR), iterations_num); + EXPECT_EQ(tc.stats_mgr_.getRcvdPacketsNum(ExchangeType::RR), 0); +} + +TEST_F(TestControlTest, Packet6ExchangeFromTemplate) { + const int iterations_num = 100; + CommandOptions opt; + processCmdLine(opt, "perfdhcp -l fake -6 -r 100 -n 10 -R 20 -L 10547" + " -T " + getFullPath("solicit-example.hex") + " -T " + getFullPath("request6-example.hex ::1")); + NakedTestControl tc(opt); + // 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 @@ -1545,127 +1457,89 @@ TEST_F(TestControlTest, Packet6ExchangeFromTemplate) { const int received_num = 3; // Simulate the number of Solicit-Advertise-Request-Reply (SARR) exchanges. // The test function generates server's responses and passes it to the - // TestControl class methods for processing. The number of exchanges - // actually performed is returned in 'iterations_performed' argument. If - // processing is successful, the number of performed iterations should be - // equal to the number of exchanges specified with the '-n' command line - // parameter (10 in this case). All exchanged packets carry the IA_NA option - // to simulate the IPv6 address acquisition and to verify that the - // IA_NA options returned by the server are processed correctly. - testPkt6Exchange(iterations_num, received_num, use_templates, - iterations_performed, tc); - EXPECT_EQ(6, iterations_performed); + // TestControl class methods for processing. All exchanged packets carry + // the IA_NA option to simulate the IPv6 address acquisition and to verify + // that the IA_NA options returned by the server are processed correctly. + bool use_templates = true; + testPkt6Exchange(iterations_num, received_num, use_templates, tc); + + EXPECT_EQ(tc.fake_sock_.sent_cnt_, iterations_num + received_num); // Solicit + Advertise + EXPECT_EQ(tc.stats_mgr_.getSentPacketsNum(ExchangeType::SA), iterations_num); + EXPECT_EQ(tc.stats_mgr_.getRcvdPacketsNum(ExchangeType::SA), received_num); + EXPECT_EQ(tc.stats_mgr_.getSentPacketsNum(ExchangeType::RR), received_num); + EXPECT_EQ(tc.stats_mgr_.getRcvdPacketsNum(ExchangeType::RR), 0); } -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; - } - +TEST_F(TestControlTest, Packet6ExchangeAddressOnly) { const int iterations_num = 100; - // Set number of iterations to 10. - processCmdLine("perfdhcp -l " + loopback_iface - + " -e address-only" - + " -6 -r 100 -n 10 -R 20 -L 10547 ::1"); - int iterations_performed = 0; + CommandOptions opt; + processCmdLine(opt, "perfdhcp -l fake -e address-only" + " -6 -r 100 -n 10 -R 20 -L 10547 ::1"); // Set number of received packets equal to number of iterations. // This simulates no packet drops. bool use_templates = false; // Simulate the number of Solicit-Advertise-Request-Reply (SARR) exchanges. // The test function generates server's responses and passes it to the - // TestControl class methods for processing. The number of exchanges - // actually performed is returned in 'iterations_performed' argument. If - // processing is successful, the number of performed iterations should be - // equal to the number of exchanges specified with the '-n' command line - // parameter (10 in this case). All exchanged packets carry the IA_NA option - // to simulate the IPv6 address acquisition and to verify that the IA_NA - // options returned by the server are processed correctly. - NakedTestControl tc; - testPkt6Exchange(iterations_num, iterations_num, use_templates, - iterations_performed, tc); - // Actual number of iterations should be 10. - EXPECT_EQ(10, iterations_performed); + // TestControl class methods for processing. All exchanged packets carry + // the IA_NA option to simulate the IPv6 address acquisition and to verify + // that the IA_NA options returned by the server are processed correctly. + NakedTestControl tc(opt); + testPkt6Exchange(iterations_num, iterations_num, use_templates, tc); + + EXPECT_EQ(tc.fake_sock_.sent_cnt_, iterations_num * 2); // Solicit + Request + EXPECT_EQ(tc.stats_mgr_.getSentPacketsNum(ExchangeType::SA), iterations_num); + EXPECT_EQ(tc.stats_mgr_.getRcvdPacketsNum(ExchangeType::SA), iterations_num); + EXPECT_EQ(tc.stats_mgr_.getSentPacketsNum(ExchangeType::RR), iterations_num); + EXPECT_EQ(tc.stats_mgr_.getRcvdPacketsNum(ExchangeType::RR), 0); } TEST_F(TestControlTest, Packet6ExchangePrefixDelegation) { - // 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 - + " -e prefix-only" - + " -6 -r 100 -n 10 -R 20 -L 10547 ::1"); - int iterations_performed = 0; + CommandOptions opt; + processCmdLine(opt, "perfdhcp -l fake -e prefix-only" + " -6 -r 100 -n 10 -R 20 -L 10547 ::1"); // Set number of received packets equal to number of iterations. // This simulates no packet drops. bool use_templates = false; // Simulate the number of Solicit-Advertise-Request-Reply (SARR) exchanges. // The test function generates server's responses and passes it to the - // TestControl class methods for processing. The number of exchanges - // actually performed is returned in 'iterations_performed' argument. If - // processing is successful, the number of performed iterations should be - // equal to the number of exchanges specified with the '-n' command line - // parameter (10 in this case). All exchanged packets carry the IA_PD option - // to simulate the Prefix Delegation and to verify that the IA_PD options - // returned by the server are processed correctly. - NakedTestControl tc; - testPkt6Exchange(iterations_num, iterations_num, use_templates, - iterations_performed, tc); - // Actual number of iterations should be 10. - EXPECT_EQ(10, iterations_performed); + // TestControl class methods for processing. All exchanged packets carry + // the IA_PD option to simulate the Prefix Delegation and to verify that + // the IA_PD options returned by the server are processed correctly. + NakedTestControl tc(opt); + testPkt6Exchange(iterations_num, iterations_num, use_templates, tc); + + EXPECT_EQ(tc.fake_sock_.sent_cnt_, iterations_num * 2); // Discovery + Request + EXPECT_EQ(tc.stats_mgr_.getSentPacketsNum(ExchangeType::SA), iterations_num); + EXPECT_EQ(tc.stats_mgr_.getRcvdPacketsNum(ExchangeType::SA), iterations_num); + EXPECT_EQ(tc.stats_mgr_.getSentPacketsNum(ExchangeType::RR), iterations_num); + EXPECT_EQ(tc.stats_mgr_.getRcvdPacketsNum(ExchangeType::RR), 0); } TEST_F(TestControlTest, Packet6ExchangeAddressAndPrefix) { - // 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 - + " -e address-and-prefix" - + " -6 -r 100 -n 10 -R 20 -L 10547 ::1"); - int iterations_performed = 0; + CommandOptions opt; + processCmdLine(opt, "perfdhcp -l fake -e address-and-prefix" + " -6 -r 100 -n 10 -R 20 -L 10547 ::1"); // Set number of received packets equal to number of iterations. // This simulates no packet drops. bool use_templates = false; // Simulate the number of Solicit-Advertise-Request-Reply (SARR) exchanges. // The test function generates server's responses and passes it to the - // TestControl class methods for processing. The number of exchanges - // actually performed is returned in 'iterations_performed' argument. If - // processing is successful, the number of performed iterations should be - // equal to the number of exchanges specified with the '-n' command line - // parameter (10 in this case). All exchanged packets carry either IA_NA - // or IA_PD options to simulate the address and prefix acquisition with - // the single message and to verify that the IA_NA and IA_PD options - // returned by the server are processed correctly. - NakedTestControl tc; - testPkt6Exchange(iterations_num, iterations_num, use_templates, - iterations_performed, tc); - // Actual number of iterations should be 10. - EXPECT_EQ(10, iterations_performed); + // TestControl class methods for processing. All exchanged packets carry + // either IA_NA or IA_PD options to simulate the address and prefix + // acquisition with the single message and to verify that the IA_NA + // and IA_PD options returned by the server are processed correctly. + NakedTestControl tc(opt); + testPkt6Exchange(iterations_num, iterations_num, use_templates, tc); + + EXPECT_EQ(tc.fake_sock_.sent_cnt_, iterations_num * 2); // Solicit + Request + EXPECT_EQ(tc.stats_mgr_.getSentPacketsNum(ExchangeType::SA), iterations_num); + EXPECT_EQ(tc.stats_mgr_.getRcvdPacketsNum(ExchangeType::SA), iterations_num); + EXPECT_EQ(tc.stats_mgr_.getSentPacketsNum(ExchangeType::RR), iterations_num); + EXPECT_EQ(tc.stats_mgr_.getRcvdPacketsNum(ExchangeType::RR), 0); } TEST_F(TestControlTest, PacketTemplates) { @@ -1683,17 +1557,16 @@ TEST_F(TestControlTest, PacketTemplates) { ASSERT_TRUE(createTemplateFile(file1, template1, template1.size() * 2)); ASSERT_TRUE(createTemplateFile(file2, template2, template2.size() * 2)); - NakedTestControl tc; + CommandOptions opt; + processCmdLine(opt, "perfdhcp -l 127.0.0.1" + " -T " + file1 + " -T " + file2 + " all"); + NakedTestControl tc(opt); - ASSERT_NO_THROW( - processCmdLine("perfdhcp -l 127.0.0.1" - " -T " + file1 + " -T " + file2 + " all") - ); - ASSERT_NO_THROW(tc.initPacketTemplates()); + tc.initPacketTemplates(); TestControl::TemplateBuffer buf1; TestControl::TemplateBuffer buf2; - ASSERT_NO_THROW(buf1 = tc.getTemplateBuffer(0)); - ASSERT_NO_THROW(buf2 = tc.getTemplateBuffer(1)); + buf1 = tc.getTemplateBuffer(0); + 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())); @@ -1704,25 +1577,19 @@ TEST_F(TestControlTest, PacketTemplates) { // Size of the file is 2 times larger than binary data size and it is always // even number. Substracting 1 makes file size odd. ASSERT_TRUE(createTemplateFile(file3, template1, template1.size() * 2 - 1)); - ASSERT_NO_THROW( - processCmdLine("perfdhcp -l 127.0.0.1 -T " + file3 + " all") - ); + processCmdLine(opt, "perfdhcp -l 127.0.0.1 -T " + file3 + " all"); EXPECT_THROW(tc.initPacketTemplates(), isc::OutOfRange); // Try to read empty file. std::string file4("test4.hex"); ASSERT_TRUE(createTemplateFile(file4, template2, 0)); - ASSERT_NO_THROW( - processCmdLine("perfdhcp -l 127.0.0.1 -T " + file4 + " all") - ); + processCmdLine(opt, "perfdhcp -l 127.0.0.1 -T " + file4 + " all"); EXPECT_THROW(tc.initPacketTemplates(), isc::OutOfRange); // Try reading file with non hexadecimal characters. std::string file5("test5.hex"); ASSERT_TRUE(createTemplateFile(file5, template1, template1.size() * 2, true)); - ASSERT_NO_THROW( - processCmdLine("perfdhcp -l 127.0.0.1 -T " + file5 + " all") - ); + processCmdLine(opt, "perfdhcp -l 127.0.0.1 -T " + file5 + " all"); EXPECT_THROW(tc.initPacketTemplates(), isc::BadValue); } @@ -1769,24 +1636,18 @@ TEST_F(TestControlTest, sendDiscoverExtraOpts) { // -xT - save first packet of each type for templates (useful for packet inspection) // -o 200,abcdef1234 - send option 200 with hex content: ab:cd:ef:12:34 // -o 201,00 - send option 201 with hex content: 00 - std::string loopback(getLocalLoopback()); - ASSERT_NO_THROW(processCmdLine("perfdhcp -4 -l " + loopback - + " -xT -L 10068 -o 200,abcdef1234 -o 201,00 -r 1 127.0.0.1")); + CommandOptions opt; + processCmdLine(opt, "perfdhcp -4 -l fake -xT -L 10068" + " -o 200,abcdef1234 -o 201,00 -r 1 127.0.0.1"); // Create test control and set up some basic defaults. - NakedTestControl tc; - tc.initializeStatsMgr(); + NakedTestControl tc(opt); tc.registerOptionFactories(); NakedTestControl::IncrementalGeneratorPtr gen(new NakedTestControl::IncrementalGenerator()); tc.setTransidGenerator(gen); - // Socket is needed to send packets through the interface. - int sock_handle = 0; - ASSERT_NO_THROW(sock_handle = tc.openSocket()); - PerfSocket sock(sock_handle); - // Make tc send the packet. The first packet of each type is saved in templates. - ASSERT_NO_THROW(tc.sendDiscover4(sock)); + tc.sendDiscover4(); // Let's find the packet and see if it includes the right option. auto pkt_it = tc.template_packets_v4_.find(DHCPDISCOVER); @@ -1798,32 +1659,26 @@ TEST_F(TestControlTest, sendDiscoverExtraOpts) { // Test checks if regular packet exchange inserts the extra v4 options // specified on command line. TEST_F(TestControlTest, Packet4ExchangeExtraOpts) { - - // 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(getLocalLoopback()); - if (loopback.empty()) { - std::cout << "Unable to find the loopback interface. Skip test." << std::endl; - return; - } - // Important paramters here: // -xT - save first packet of each type for templates (useful for packet inspection) // -o 200,abcdef1234 - send option 200 with hex content: ab:cd:ef:12:34 // -o 201,00 - send option 201 with hex content: 00 const int iterations_num = 1; - processCmdLine("perfdhcp -l " + loopback + " -4 " + - "-o 200,abcdef1234 -o 201,00 " + + CommandOptions opt; + processCmdLine(opt, "perfdhcp -l fake -4 -o 200,abcdef1234 -o 201,00 " "-r 100 -n 10 -R 20 -xT -L 10547 127.0.0.1"); - int iterations_performed = 0; - NakedTestControl tc; + NakedTestControl tc(opt); tc.registerOptionFactories(); // Do the actual exchange. - testPkt4Exchange(iterations_num, iterations_num, false, iterations_performed, tc); - EXPECT_EQ(1, iterations_performed); + testPkt4Exchange(iterations_num, iterations_num, false, tc); + + EXPECT_EQ(tc.fake_sock_.sent_cnt_, iterations_num * 2); // Discovery + Request + EXPECT_EQ(tc.stats_mgr_.getSentPacketsNum(ExchangeType::DO), iterations_num); + EXPECT_EQ(tc.stats_mgr_.getRcvdPacketsNum(ExchangeType::DO), iterations_num); + EXPECT_EQ(tc.stats_mgr_.getSentPacketsNum(ExchangeType::RA), iterations_num); + EXPECT_EQ(tc.stats_mgr_.getRcvdPacketsNum(ExchangeType::RA), 0); // Check if Discover was recored and if it contains options 200 and 201. auto disc = tc.template_packets_v4_.find(DHCPDISCOVER); @@ -1839,38 +1694,30 @@ TEST_F(TestControlTest, Packet4ExchangeExtraOpts) { // Test checks if regular packet exchange inserts the extra v6 options // specified on command line. TEST_F(TestControlTest, Packet6ExchangeExtraOpts) { - // 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(getLocalLoopback()); - if (loopback.empty()) { - std::cout << "Unable to find the loopback interface. Skip test." - << std::endl; - return; - } - // Important paramters here: // -xT - save first packet of each type for templates (useful for packet inspection) // -o 200,abcdef1234 - send option 200 with hex content: ab:cd:ef:12:34 // -o 201,00 - send option 201 with hex content: 00 const int iterations_num = 1; - processCmdLine("perfdhcp -l " + loopback - + " -6 -e address-only" - + " -xT -o 200,abcdef1234 -o 201,00 " - + " -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. + CommandOptions opt; + processCmdLine(opt, "perfdhcp -l fake" + " -6 -e address-only" + " -xT -o 200,abcdef1234 -o 201,00 " + " -r 100 -n 10 -R 20 -L 10547 ::1"); // Simulate the number of Solicit-Advertise-Request-Reply (SARR) exchanges. // The test function generates server's responses and passes it to the - // TestControl class methods for processing. The number of exchanges - // actually performed is returned in 'iterations_performed' argument. + // TestControl class methods for processing. // First packet of each type is recorded as a template packet. The check // inspects this template to see if the expected options are really there. - NakedTestControl tc; - testPkt6Exchange(iterations_num, iterations_num, false, - iterations_performed, tc); + NakedTestControl tc(opt); + testPkt6Exchange(iterations_num, iterations_num, false, tc); + + EXPECT_EQ(tc.fake_sock_.sent_cnt_, iterations_num * 2); // Solicit + Request + EXPECT_EQ(tc.stats_mgr_.getSentPacketsNum(ExchangeType::SA), iterations_num); + EXPECT_EQ(tc.stats_mgr_.getRcvdPacketsNum(ExchangeType::SA), iterations_num); + EXPECT_EQ(tc.stats_mgr_.getSentPacketsNum(ExchangeType::RR), iterations_num); + EXPECT_EQ(tc.stats_mgr_.getRcvdPacketsNum(ExchangeType::RR), 0); // Check if Solicit was recored and if it contains options 200 and 201. auto sol = tc.template_packets_v6_.find(DHCPV6_SOLICIT); @@ -1881,5 +1728,4 @@ TEST_F(TestControlTest, Packet6ExchangeExtraOpts) { auto req = tc.template_packets_v6_.find(DHCPV6_REQUEST); ASSERT_TRUE(req != tc.template_packets_v6_.end()); checkOptions20x(req->second); - } diff --git a/src/lib/dhcp/pkt.h b/src/lib/dhcp/pkt.h index 959eaf9a81..4dcca2c912 100644 --- a/src/lib/dhcp/pkt.h +++ b/src/lib/dhcp/pkt.h @@ -399,7 +399,7 @@ public: /// @brief Set packet timestamp. /// /// Sets packet timestamp to arbitrary value. - /// It is used by perfdhcp tool and should be used elsewhere. + /// It is used by perfdhcp tool and should not be used elsewhere. void setTimestamp(boost::posix_time::ptime& timestamp) { timestamp_ = timestamp; }