summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMichal Nowikowski <godfryd@isc.org>2019-02-12 15:55:51 +0100
committerMichal Nowikowski <godfryd@isc.org>2019-02-19 21:54:31 +0100
commitad43417ae266171b7d68585ab60729506b032be0 (patch)
tree29633f679394c8238ea2e133209e9b000878ea08
parent[448-update-cb-cmds-to-handle-parameter-types] Extended SimpleRequiredKeyword... (diff)
downloadkea-ad43417ae266171b7d68585ab60729506b032be0.tar.xz
kea-ad43417ae266171b7d68585ab60729506b032be0.zip
added avalanche scenario to perfdhcp
-rw-r--r--src/bin/perfdhcp/Makefile.am4
-rw-r--r--src/bin/perfdhcp/avalanche_scen.cc157
-rw-r--r--src/bin/perfdhcp/avalanche_scen.h48
-rw-r--r--src/bin/perfdhcp/basic_scen.cc301
-rw-r--r--src/bin/perfdhcp/basic_scen.h64
-rw-r--r--src/bin/perfdhcp/command_options.cc42
-rw-r--r--src/bin/perfdhcp/command_options.h17
-rw-r--r--src/bin/perfdhcp/main.cc13
-rw-r--r--src/bin/perfdhcp/perf_socket.cc115
-rw-r--r--src/bin/perfdhcp/perf_socket.h30
-rw-r--r--src/bin/perfdhcp/receiver.h7
-rw-r--r--src/bin/perfdhcp/stats_mgr.cc358
-rw-r--r--src/bin/perfdhcp/stats_mgr.h1460
-rw-r--r--src/bin/perfdhcp/test_control.cc768
-rw-r--r--src/bin/perfdhcp/test_control.h248
-rw-r--r--src/bin/perfdhcp/tests/stats_mgr_unittest.cc18
16 files changed, 1888 insertions, 1762 deletions
diff --git a/src/bin/perfdhcp/Makefile.am b/src/bin/perfdhcp/Makefile.am
index c1e4531912..436f7c3910 100644
--- a/src/bin/perfdhcp/Makefile.am
+++ b/src/bin/perfdhcp/Makefile.am
@@ -23,10 +23,12 @@ libperfdhcp_la_SOURCES += perf_pkt4.cc perf_pkt4.h
libperfdhcp_la_SOURCES += packet_storage.h
libperfdhcp_la_SOURCES += pkt_transform.cc pkt_transform.h
libperfdhcp_la_SOURCES += rate_control.cc rate_control.h
-libperfdhcp_la_SOURCES += stats_mgr.h
+libperfdhcp_la_SOURCES += stats_mgr.cc stats_mgr.h
libperfdhcp_la_SOURCES += test_control.cc test_control.h
libperfdhcp_la_SOURCES += receiver.cc receiver.h
libperfdhcp_la_SOURCES += perf_socket.cc perf_socket.h
+libperfdhcp_la_SOURCES += avalanche_scen.cc avalanche_scen.h
+libperfdhcp_la_SOURCES += basic_scen.cc basic_scen.h
sbin_PROGRAMS = perfdhcp
perfdhcp_SOURCES = main.cc
diff --git a/src/bin/perfdhcp/avalanche_scen.cc b/src/bin/perfdhcp/avalanche_scen.cc
new file mode 100644
index 0000000000..91f69da033
--- /dev/null
+++ b/src/bin/perfdhcp/avalanche_scen.cc
@@ -0,0 +1,157 @@
+// 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 <perfdhcp/avalanche_scen.h>
+
+
+#include <boost/date_time/posix_time/posix_time.hpp>
+
+using namespace std;
+using namespace boost::posix_time;
+using namespace isc;
+using namespace isc::dhcp;
+
+
+namespace isc {
+namespace perfdhcp {
+
+int
+AvalancheScen::resendPackets(ExchangeType xchg_type) {
+ CommandOptions& options = CommandOptions::instance();
+ const StatsMgr& stats_mgr(tc_.getStatsMgr());
+ auto sent_packets_its = stats_mgr.getSentPackets(xchg_type);
+ auto begin_it = std::get<0>(sent_packets_its);
+ auto end_it = std::get<1>(sent_packets_its);
+
+ auto& retrans = retransmissions_[xchg_type];
+
+ int still_left_cnt = 0;
+ int resent_cnt = 0;
+ for (auto it = begin_it; it != end_it; ++it) {
+ still_left_cnt++;
+ dhcp::PktPtr pkt = *it;
+ auto trans_id = pkt->getTransid();
+ int rx_times = 0;
+ auto r_it = retrans.find(trans_id);
+ if (r_it != retrans.end()) {
+ rx_times = (*r_it).second;
+ }
+
+ int delay = (1 << rx_times); // in seconds
+ if (delay > 64) {
+ delay = 64;
+ }
+ delay *= 1000; // to miliseconds
+ delay += random() % 2000 - 1000; // adjust by random from -1000..1000 range
+ auto now = microsec_clock::universal_time();
+ if (now - pkt->getTimestamp() > milliseconds(delay)) {
+ //if (rx_times > 2) {
+ // std::cout << xchg_type << " RX " << trans_id << ", times " << rx_times << ", delay " << delay << std::endl;
+ //}
+ resent_cnt++;
+ total_resent_++;
+
+ if (options.getIpVersion() == 4) {
+ Pkt4Ptr pkt4 = boost::dynamic_pointer_cast<Pkt4>(pkt);
+ IfaceMgr::instance().send(pkt4);
+ } else {
+ Pkt6Ptr pkt6 = boost::dynamic_pointer_cast<Pkt6>(pkt);
+ IfaceMgr::instance().send(pkt6);
+ }
+ //std::cout << "resent " << xchg_type << " " << pkt->getTransid() << " @ " << pkt->getTimestamp() << std::endl;
+
+ rx_times++;
+ retrans[trans_id] = rx_times;
+ }
+ }
+ if (resent_cnt > 0) {
+ auto now = microsec_clock::universal_time();
+ std::cout << now << " " << xchg_type << ": still waiting for " << still_left_cnt << " answers, resent " << resent_cnt << ", retrying " << retrans.size() << std::endl;
+ }
+ return still_left_cnt;
+}
+
+
+
+int
+AvalancheScen::run() {
+ CommandOptions& options = CommandOptions::instance();
+
+ uint32_t clients_num = options.getClientsNum() == 0 ?
+ 1 : options.getClientsNum();
+
+ // StatsMgr& stats_mgr(tc_.getStatsMgr());
+
+ tc_.start();
+
+ auto start = microsec_clock::universal_time();
+
+ // Initiate new DHCP packet exchanges.
+ tc_.sendPackets(clients_num);
+
+ auto now = microsec_clock::universal_time();
+ auto prev_cycle_time = now;
+ for (;;) {
+ // Pull some packets from receiver thread, process them, update some stats
+ // and respond to the server if needed.
+ tc_.consumeReceivedPackets();
+ usleep(100);
+
+ now = microsec_clock::universal_time();
+ if (now - prev_cycle_time > milliseconds(200)) { // check if 0.2s elapsed
+ prev_cycle_time = now;
+ auto still_left_cnt_do = resendPackets(ExchangeType::DO);
+ auto still_left_cnt_ra = resendPackets(ExchangeType::RA);
+ if (still_left_cnt_do + still_left_cnt_ra == 0) {
+ break;
+ }
+ }
+
+ // If we are sending Renews to the server, the Reply packets are cached
+ // so as leases for which we send Renews can be identified. The major
+ // issue with this approach is that most of the time we are caching
+ // more packets than we actually need. This function removes excessive
+ // Reply messages to reduce the memory and CPU utilization. Note that
+ // searches in the long list of Reply packets increases CPU utilization.
+ //tc_.cleanCachedPackets();
+ }
+
+ auto stop = microsec_clock::universal_time();
+ boost::posix_time::time_period duration(start, stop);
+
+ tc_.stop();
+
+ tc_.printStats();
+
+ // // Print packet timestamps
+ // if (testDiags('t')) {
+ // stats_mgr.printTimestamps();
+ // }
+
+ // Print server id.
+ if (testDiags('s') && tc_.serverIdReceived()) {
+ std::cout << "Server id: " << tc_.getServerId() << std::endl;
+ }
+
+ // Diagnostics flag 'e' means show exit reason.
+ if (testDiags('e')) {
+ std::cout << "Interrupted" << std::endl;
+ }
+
+ 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;
+
+ int ret_code = 0;
+ // // Check if any packet drops occurred.
+ // ret_code = stats_mgr.droppedPackets() ? 3 : 0;
+ return (ret_code);
+}
+
+}
+}
diff --git a/src/bin/perfdhcp/avalanche_scen.h b/src/bin/perfdhcp/avalanche_scen.h
new file mode 100644
index 0000000000..66cf040286
--- /dev/null
+++ b/src/bin/perfdhcp/avalanche_scen.h
@@ -0,0 +1,48 @@
+// 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/.
+
+#ifndef AVALANCHE_SCEN_H
+#define AVALANCHE_SCEN_H
+
+#include <config.h>
+
+#include <perfdhcp/test_control.h>
+
+
+namespace isc {
+namespace perfdhcp {
+
+
+class AvalancheScen : public boost::noncopyable {
+public:
+ AvalancheScen(): tc_(true), total_resent_(0) {};
+
+ /// brief\ Run performance test.
+ ///
+ /// Method runs whole performance test. Command line options must
+ /// be parsed prior to running this function. Otherwise function will
+ /// throw exception.
+ ///
+ /// \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();
+
+private:
+ TestControl tc_;
+
+ std::unordered_map<ExchangeType, std::unordered_map<uint32_t, int>> retransmissions_;
+ int total_resent_;
+
+ int resendPackets(ExchangeType xchg_type);
+
+};
+
+}
+}
+
+#endif // AVALANCHE_SCEN_H
diff --git a/src/bin/perfdhcp/basic_scen.cc b/src/bin/perfdhcp/basic_scen.cc
new file mode 100644
index 0000000000..cda35f644c
--- /dev/null
+++ b/src/bin/perfdhcp/basic_scen.cc
@@ -0,0 +1,301 @@
+// 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 <perfdhcp/basic_scen.h>
+
+
+#include <boost/date_time/posix_time/posix_time.hpp>
+
+using namespace std;
+using namespace boost::posix_time;
+using namespace isc;
+using namespace isc::dhcp;
+
+
+namespace isc {
+namespace perfdhcp {
+
+
+bool
+BasicScen::checkExitConditions() {
+ if (tc_.interrupted()) {
+ return (true);
+ }
+
+ const StatsMgr& stats_mgr(tc_.getStatsMgr());
+
+ CommandOptions& options = CommandOptions::instance();
+ bool test_period_reached = false;
+ // Check if test period passed.
+ if (options.getPeriod() != 0) {
+ time_period period(stats_mgr.getTestPeriod());
+ if (period.length().total_seconds() >= options.getPeriod()) {
+ test_period_reached = true;
+ }
+ }
+ if (test_period_reached) {
+ if (testDiags('e')) {
+ std::cout << "reached test-period." << std::endl;
+ }
+ if (!tc_.waitToExit()) {
+ return true;
+ }
+ }
+
+ bool max_requests = false;
+ // Check if we reached maximum number of DISCOVER/SOLICIT sent.
+ if (options.getNumRequests().size() > 0) {
+ if (options.getIpVersion() == 4) {
+ if (stats_mgr.getSentPacketsNum(ExchangeType::DO) >=
+ options.getNumRequests()[0]) {
+ max_requests = true;
+ }
+ } else if (options.getIpVersion() == 6) {
+ if (stats_mgr.getSentPacketsNum(ExchangeType::SA) >=
+ options.getNumRequests()[0]) {
+ max_requests = true;
+ }
+ }
+ }
+ // Check if we reached maximum number REQUEST packets.
+ if (options.getNumRequests().size() > 1) {
+ if (options.getIpVersion() == 4) {
+ if (stats_mgr.getSentPacketsNum(ExchangeType::RA) >=
+ options.getNumRequests()[1]) {
+ max_requests = true;
+ }
+ } else if (options.getIpVersion() == 6) {
+ if (stats_mgr.getSentPacketsNum(ExchangeType::RR) >=
+ options.getNumRequests()[1]) {
+ max_requests = true;
+ }
+ }
+ }
+ if (max_requests) {
+ if (testDiags('e')) {
+ std::cout << "Reached max requests limit." << std::endl;
+ }
+ if (!tc_.waitToExit()) {
+ return true;
+ }
+ }
+
+ // Check if we reached maximum number of drops of OFFER/ADVERTISE packets.
+ bool max_drops = false;
+ if (options.getMaxDrop().size() > 0) {
+ if (options.getIpVersion() == 4) {
+ if (stats_mgr.getDroppedPacketsNum(ExchangeType::DO) >=
+ options.getMaxDrop()[0]) {
+ max_drops = true;
+ }
+ } else if (options.getIpVersion() == 6) {
+ if (stats_mgr.getDroppedPacketsNum(ExchangeType::SA) >=
+ 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 (stats_mgr.getDroppedPacketsNum(ExchangeType::RA) >=
+ options.getMaxDrop()[1]) {
+ max_drops = true;
+ }
+ } else if (options.getIpVersion() == 6) {
+ if (stats_mgr.getDroppedPacketsNum(ExchangeType::RR) >=
+ options.getMaxDrop()[1]) {
+ max_drops = true;
+ }
+ }
+ }
+ if (max_drops) {
+ if (testDiags('e')) {
+ std::cout << "Reached maximum drops number." << std::endl;
+ }
+ if (!tc_.waitToExit()) {
+ return true;
+ }
+ }
+
+ // Check if we reached maximum drops percentage of OFFER/ADVERTISE packets.
+ bool max_pdrops = false;
+ if (options.getMaxDropPercentage().size() > 0) {
+ if (options.getIpVersion() == 4) {
+ if ((stats_mgr.getSentPacketsNum(ExchangeType::DO) > 10) &&
+ ((100. * stats_mgr.getDroppedPacketsNum(ExchangeType::DO) /
+ stats_mgr.getSentPacketsNum(ExchangeType::DO)) >=
+ options.getMaxDropPercentage()[0])) {
+ max_pdrops = true;
+
+ }
+ } 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])) {
+ max_pdrops = true;
+ }
+ }
+ }
+ // Check if we reached maximum drops percentage of ACK/REPLY packets.
+ 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])) {
+ max_pdrops = true;
+ }
+ } 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])) {
+ max_pdrops = true;
+ }
+ }
+ }
+ if (max_pdrops) {
+ if (testDiags('e')) {
+ std::cout << "Reached maximum percentage of drops." << std::endl;
+ }
+ if (!tc_.waitToExit()) {
+ return true;
+ }
+ }
+ return (false);
+}
+
+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);
+ }
+
+ // Fork and run command specified with -w<wrapped-command>
+ if (!options.getWrapped().empty()) {
+ tc_.runWrapped();
+ }
+
+ tc_.start();
+
+ for (;;) {
+ // 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')) {
+ stats_mgr.incrementCounter("shortwait");
+ }
+
+ // Pull some packets from receiver thread, process them, update some stats
+ // and respond to the server if needed.
+ auto pkt_count = tc_.consumeReceivedPackets();
+
+ // 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) {
+ /// @todo: need to implement adaptive time here, so the sleep time
+ /// is not fixed, but adjusts to current situation.
+ usleep(1);
+ }
+
+ // If test period finished, maximum number of packet drops
+ // has been reached or test has been interrupted we have to
+ // finish the test.
+ if (checkExitConditions()) {
+ break;
+ }
+
+ // Initiate new DHCP packet exchanges.
+ tc_.sendPackets(packets_due);
+
+ // If -f<renew-rate> 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) {
+ uint64_t renew_packets_due =
+ renew_rate_control_.getOutboundMessageCount();
+
+ // Send multiple renews to satisfy the desired rate.
+ if (options.getIpVersion() == 4) {
+ tc_.sendMultipleRequests(renew_packets_due);
+ } else {
+ tc_.sendMultipleMessages6(DHCPV6_RENEW, renew_packets_due);
+ }
+ }
+
+ // If -F<release-rate> 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)) {
+ uint64_t release_packets_due =
+ release_rate_control_.getOutboundMessageCount();
+ // Send Release messages.
+ tc_.sendMultipleMessages6(DHCPV6_RELEASE, release_packets_due);
+ }
+
+ // Report delay means that user requested printing number
+ // of sent/received/dropped packets repeatedly.
+ if (options.getReportDelay() > 0) {
+ tc_.printIntermediateStats();
+ }
+
+ // If we are sending Renews to the server, the Reply packets are cached
+ // so as leases for which we send Renews can be identified. The major
+ // issue with this approach is that most of the time we are caching
+ // more packets than we actually need. This function removes excessive
+ // Reply messages to reduce the memory and CPU utilization. Note that
+ // searches in the long list of Reply packets increases CPU utilization.
+ tc_.cleanCachedPackets();
+ }
+
+ tc_.stop();
+
+ tc_.printStats();
+
+ if (!options.getWrapped().empty()) {
+ // true means that we execute wrapped command with 'stop' argument.
+ tc_.runWrapped(true);
+ }
+
+ // Print packet timestamps
+ if (testDiags('t')) {
+ stats_mgr.printTimestamps();
+ }
+
+ // Print server id.
+ if (testDiags('s') && tc_.serverIdReceived()) {
+ std::cout << "Server id: " << tc_.getServerId() << std::endl;
+ }
+
+ // Diagnostics flag 'e' means show exit reason.
+ if (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')) {
+ tc_.printTemplates();
+ }
+
+ int ret_code = 0;
+ // Check if any packet drops occurred.
+ ret_code = stats_mgr.droppedPackets() ? 3 : 0;
+ return (ret_code);
+}
+
+
+}
+}
diff --git a/src/bin/perfdhcp/basic_scen.h b/src/bin/perfdhcp/basic_scen.h
new file mode 100644
index 0000000000..7a5d115d4e
--- /dev/null
+++ b/src/bin/perfdhcp/basic_scen.h
@@ -0,0 +1,64 @@
+// 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/.
+
+#ifndef BASIC_SCEN_H
+#define BASIC_SCEN_H
+
+#include <config.h>
+
+#include <perfdhcp/test_control.h>
+
+
+namespace isc {
+namespace perfdhcp {
+
+
+class BasicScen : public boost::noncopyable {
+public:
+ BasicScen() : tc_(false) {};
+
+ /// \brief Check if test exit conditions fulfilled.
+ ///
+ /// Method checks if the test exit conditions are fulfilled.
+ /// Exit conditions are checked periodically from the
+ /// main loop. Program should break the main loop when
+ /// this method returns true. It is calling function
+ /// responsibility to break main loop gracefully and
+ /// cleanup after test execution.
+ ///
+ /// \return true if any of the exit conditions is fulfilled.
+ bool checkExitConditions();
+
+ /// brief\ Run performance test.
+ ///
+ /// Method runs whole performance test. Command line options must
+ /// be parsed prior to running this function. Otherwise function will
+ /// throw exception.
+ ///
+ /// \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();
+
+private:
+ TestControl tc_;
+
+ /// \brief A rate control class for Discover and Solicit messages.
+ RateControl basic_rate_control_;
+ /// \brief A rate control class for Renew messages.
+ RateControl renew_rate_control_;
+ /// \brief A rate control class for Release messages.
+ RateControl release_rate_control_;
+
+ int resendPackets(ExchangeType xchg_type);
+
+};
+
+}
+}
+
+#endif // BASIC_SCEN_H
diff --git a/src/bin/perfdhcp/command_options.cc b/src/bin/perfdhcp/command_options.cc
index eb562d97d2..60397f6b58 100644
--- a/src/bin/perfdhcp/command_options.cc
+++ b/src/bin/perfdhcp/command_options.cc
@@ -24,6 +24,7 @@
#include <unistd.h>
#include <fstream>
#include <thread>
+#include <getopt.h>
#ifdef HAVE_OPTRESET
extern int optreset;
@@ -161,6 +162,7 @@ CommandOptions::reset() {
} else {
single_thread_mode_ = false;
}
+ scenario_ = Scenario::BASIC;
}
bool
@@ -209,6 +211,8 @@ CommandOptions::parse(int argc, char** const argv, bool print_cmd_line) {
return (help_or_version_mode);
}
+const int LONG_OPT_SCENARIO = 300;
+
bool
CommandOptions::initialize(int argc, char** argv, bool print_cmd_line) {
int opt = 0; // Subsequent options returned by getopt()
@@ -225,10 +229,17 @@ CommandOptions::initialize(int argc, char** argv, bool print_cmd_line) {
stream << "perfdhcp";
int num_mac_list_files = 0;
+ struct option long_options[] = {
+ {"scenario", required_argument, 0, LONG_OPT_SCENARIO},
+ {0, 0, 0, 0}
+ };
+
// In this section we collect argument values from command line
// they will be tuned and validated elsewhere
- while((opt = getopt(argc, argv, "hv46A:r:t:R:b:n:p:d:D:l:P:a:L:N:M:"
- "s:iBc1T:X:O:o:E:S:I:x:W:w:e:f:F:g:")) != -1) {
+ while((opt = getopt_long(argc, argv,
+ "hv46A:r:t:R:b:n:p:d:D:l:P:a:L:N:M:s:iBc1"
+ "T:X:O:o:E:S:I:x:W:w:e:f:F:g:",
+ long_options, NULL)) != -1) {
stream << " -" << static_cast<char>(opt);
if (optarg) {
stream << " " << optarg;
@@ -544,6 +555,17 @@ CommandOptions::initialize(int argc, char** argv, bool print_cmd_line) {
xid_offset_.push_back(offset_arg);
break;
+ case LONG_OPT_SCENARIO: {
+ auto optarg_text = std::string(optarg);
+ if (optarg_text == "basic") {
+ scenario_ = Scenario::BASIC;
+ } else if (optarg_text == "avalanche") {
+ scenario_ = Scenario::AVALANCHE;
+ } else {
+ isc_throw(InvalidParameter, "scenario value '" << optarg << "' is wrong - should be 'basic' or 'avalanche'");
+ }
+ break;
+ }
default:
isc_throw(isc::InvalidParameter, "wrong command line option");
}
@@ -594,6 +616,12 @@ CommandOptions::initialize(int argc, char** argv, bool print_cmd_line) {
std::cout << "Running: " << stream.str() << std::endl;
}
+ if (scenario_ == Scenario::BASIC) {
+ std::cout << "Scenario: basic." << std::endl;
+ } else if (scenario_ == Scenario::AVALANCHE) {
+ std::cout << "Scenario: avalanche." << std::endl;
+ }
+
if (!isSingleThreaded()) {
std::cout << "Multi-thread mode enabled." << std::endl;
}
@@ -1231,5 +1259,15 @@ 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 deb2808267..efbfdafbfb 100644
--- a/src/bin/perfdhcp/command_options.h
+++ b/src/bin/perfdhcp/command_options.h
@@ -17,6 +17,11 @@
namespace isc {
namespace perfdhcp {
+enum class Scenario {
+ BASIC,
+ AVALANCHE
+};
+
/// \brief Command Options.
///
/// This class is responsible for parsing the command-line and storing the
@@ -348,6 +353,8 @@ public:
/// \return true if single-threaded mode is enabled.
bool isSingleThreaded() const { return single_thread_mode_; }
+ Scenario getScenario() const { return scenario_; }
+
/// \brief Returns server name.
///
/// \return server name.
@@ -654,8 +661,18 @@ private:
/// @brief Option to switch modes between single-threaded and multi-threaded.
bool single_thread_mode_;
+
+ /// @brief Selected performance scenario. Default is basic.
+ 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 7548ee76cd..2ab854208a 100644
--- a/src/bin/perfdhcp/main.cc
+++ b/src/bin/perfdhcp/main.cc
@@ -6,7 +6,8 @@
#include <config.h>
-#include <perfdhcp/test_control.h>
+#include <perfdhcp/avalanche_scen.h>
+#include <perfdhcp/basic_scen.h>
#include <perfdhcp/command_options.h>
#include <exceptions/exceptions.h>
@@ -43,8 +44,14 @@ main(int argc, char* argv[]) {
return (ret_code);
}
try{
- TestControl& test_control = TestControl::instance();
- ret_code = test_control.run();
+ auto scenario = command_options.getScenario();
+ if (scenario == Scenario::BASIC) {
+ BasicScen scen;
+ ret_code = scen.run();
+ } else if (scenario == Scenario::AVALANCHE) {
+ AvalancheScen scen;
+ ret_code = scen.run();
+ }
} catch (std::exception& e) {
ret_code = 1;
std::cerr << "Error running perfdhcp: " << e.what() << std::endl;
diff --git a/src/bin/perfdhcp/perf_socket.cc b/src/bin/perfdhcp/perf_socket.cc
index c5b01cfa2e..e9a55dfa2b 100644
--- a/src/bin/perfdhcp/perf_socket.cc
+++ b/src/bin/perfdhcp/perf_socket.cc
@@ -6,25 +6,126 @@
#include <perfdhcp/perf_socket.h>
+#include <perfdhcp/command_options.h>
#include <dhcp/iface_mgr.h>
+#include <asiolink/io_address.h>
#include <boost/foreach.hpp>
using namespace isc::dhcp;
+using namespace isc::asiolink;
namespace isc {
namespace perfdhcp {
-PerfSocket::PerfSocket(const int socket) :
-SocketInfo(asiolink::IOAddress("127.0.0.1"), 0, socket),
- ifindex_(0), valid_(true) {
- try {
- initSocketData();
- } catch (const Exception&) {
- valid_ = false;
+PerfSocket::PerfSocket() :
+ SocketInfo(asiolink::IOAddress("127.0.0.1"), 0, openSocket()),
+ ifindex_(0)
+{
+ initSocketData();
+}
+
+
+int
+PerfSocket::openSocket() const {
+ CommandOptions& options = CommandOptions::instance();
+ std::string localname = options.getLocalName();
+ std::string servername = options.getServerName();
+ uint16_t port = options.getLocalPort();
+ int sock = 0;
+
+ uint8_t family = (options.getIpVersion() == 6) ? AF_INET6 : AF_INET;
+ IOAddress remoteaddr(servername);
+
+ // Check for mismatch between IP option and server address
+ if (family != remoteaddr.getFamily()) {
+ isc_throw(InvalidParameter,
+ "Values for IP version: " <<
+ static_cast<unsigned int>(options.getIpVersion()) <<
+ " and server address: " << servername << " are mismatched.");
+ }
+
+ if (port == 0) {
+ if (family == AF_INET6) {
+ // need server port (547) because the server is acting as a relay agent
+ port = DHCP6_CLIENT_PORT;
+ // if acting as a relay agent change port.
+ if (options.isUseRelayedV6()) {
+ port = DHCP6_SERVER_PORT;
+ }
+ } else if (options.getIpVersion() == 4) {
+ port = 67; /// @todo: find out why port 68 is wrong here.
+ }
+ }
+
+ // Local name is specified along with '-l' option.
+ // It may point to interface name or local address.
+ if (!localname.empty()) {
+ // CommandOptions should be already aware whether local name
+ // is interface name or address because it uses IfaceMgr to
+ // scan interfaces and get's their names.
+ if (options.isInterface()) {
+ sock = IfaceMgr::instance().openSocketFromIface(localname,
+ port,
+ family);
+ } else {
+ IOAddress localaddr(localname);
+ sock = IfaceMgr::instance().openSocketFromAddress(localaddr,
+ port);
+ }
+ } else if (!servername.empty()) {
+ // If only server name is given we will need to try to resolve
+ // the local address to bind socket to based on remote address.
+ sock = IfaceMgr::instance().openSocketFromRemoteAddress(remoteaddr,
+ port);
+ }
+ if (sock <= 0) {
+ isc_throw(BadValue, "unable to open socket to communicate with "
+ "DHCP server");
+ }
+
+ // IfaceMgr does not set broadcast option on the socket. We rely
+ // on CommandOptions object to find out if socket has to have
+ // broadcast enabled.
+ if ((options.getIpVersion() == 4) && options.isBroadcast()) {
+ int broadcast_enable = 1;
+ int ret = setsockopt(sock, SOL_SOCKET, SO_BROADCAST,
+ &broadcast_enable, sizeof(broadcast_enable));
+ if (ret < 0) {
+ isc_throw(InvalidOperation,
+ "unable to set broadcast option on the socket");
+ }
+ } else if (options.getIpVersion() == 6) {
+ // If remote address is multicast we need to enable it on
+ // the socket that has been created.
+ if (remoteaddr.isV6Multicast()) {
+ int hops = 1;
+ int ret = setsockopt(sock, IPPROTO_IPV6, IPV6_MULTICAST_HOPS,
+ &hops, sizeof(hops));
+ // If user specified interface name with '-l' the
+ // IPV6_MULTICAST_IF has to be set.
+ if ((ret >= 0) && options.isInterface()) {
+ IfacePtr iface =
+ IfaceMgr::instance().getIface(options.getLocalName());
+ if (iface == NULL) {
+ isc_throw(Unexpected, "unknown interface "
+ << options.getLocalName());
+ }
+ int idx = iface->getIndex();
+ ret = setsockopt(sock, IPPROTO_IPV6, IPV6_MULTICAST_IF,
+ &idx, sizeof(idx));
+ }
+ if (ret < 0) {
+ isc_throw(InvalidOperation,
+ "unable to enable multicast on socket " << sock
+ << ". errno = " << errno);
+ }
+ }
}
+
+ return (sock);
}
PerfSocket::~PerfSocket() {
diff --git a/src/bin/perfdhcp/perf_socket.h b/src/bin/perfdhcp/perf_socket.h
index de593d317f..a49cc92e32 100644
--- a/src/bin/perfdhcp/perf_socket.h
+++ b/src/bin/perfdhcp/perf_socket.h
@@ -25,19 +25,13 @@ namespace perfdhcp {
struct PerfSocket : public dhcp::SocketInfo {
/// Interface index.
uint16_t ifindex_;
- /// Is socket valid. It will not be valid if the provided socket
- /// descriptor does not point to valid socket.
- bool valid_;
/// \brief Constructor of socket wrapper class.
///
/// This constructor uses provided socket descriptor to
/// find the name of the interface where socket has been
- /// bound to. If provided socket descriptor is invalid then
- /// valid_ field is set to false;
- ///
- /// \param socket socket descriptor.
- PerfSocket(const int socket);
+ /// bound to.
+ PerfSocket();
/// \brief Destructor of the socket wrapper class.
///
@@ -53,6 +47,26 @@ private:
/// \throw isc::BadValue if interface for specified socket
/// descriptor does not exist.
void initSocketData();
+
+ /// \brief Open socket to communicate with DHCP server.
+ ///
+ /// Method opens socket and binds it to local address. Function will
+ /// use either interface name, local address or server address
+ /// to create a socket, depending on what is available (specified
+ /// from the command line). If socket can't be created for any
+ /// reason, exception is thrown.
+ /// If destination address is broadcast (for DHCPv4) or multicast
+ /// (for DHCPv6) than broadcast or multicast option is set on
+ /// the socket. Opened socket is registered and managed by IfaceMgr.
+ ///
+ /// \throw isc::BadValue if socket can't be created for given
+ /// interface, local address or remote address.
+ /// \throw isc::InvalidOperation if broadcast option can't be
+ /// set for the v4 socket or if multicast option can't be set
+ /// for the v6 socket.
+ /// \throw isc::Unexpected if internal unexpected error occurred.
+ /// \return socket descriptor.
+ int openSocket() const;
};
}
diff --git a/src/bin/perfdhcp/receiver.h b/src/bin/perfdhcp/receiver.h
index d05ee2493a..4a0afc5a54 100644
--- a/src/bin/perfdhcp/receiver.h
+++ b/src/bin/perfdhcp/receiver.h
@@ -33,10 +33,6 @@ namespace perfdhcp {
/// in main thread packets can be consumed from the queue using getPkt
/// method.
class Receiver {
-public:
- /// \brief Socket for receiving.
- const PerfSocket& socket_;
-
private:
/// \brief Flag indicating if thread should run (true) or not (false).
boost::atomic_flag run_flag_;
@@ -57,8 +53,7 @@ public:
/// \brief Receiver constructor.
///
/// \param socket A socket for receiving packets.
- Receiver(const PerfSocket& socket) :
- socket_(socket),
+ Receiver() :
single_threaded_(CommandOptions::instance().isSingleThreaded()) {
}
diff --git a/src/bin/perfdhcp/stats_mgr.cc b/src/bin/perfdhcp/stats_mgr.cc
new file mode 100644
index 0000000000..bc8fe641a1
--- /dev/null
+++ b/src/bin/perfdhcp/stats_mgr.cc
@@ -0,0 +1,358 @@
+// 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 <perfdhcp/stats_mgr.h>
+#include <perfdhcp/command_options.h>
+
+
+namespace isc {
+namespace perfdhcp {
+
+std::ostream& operator<<(std::ostream& os, ExchangeType xchg_type)
+{
+ switch(xchg_type) {
+ case ExchangeType::DO:
+ return(os << "DISCOVER-OFFER");
+ case ExchangeType::RA:
+ return(os << "REQUEST-ACK");
+ case ExchangeType::RNA:
+ return(os << "REQUEST-ACK (renewal)");
+ case ExchangeType::SA:
+ return(os << "SOLICIT-ADVERTISE");
+ case ExchangeType::RR:
+ return(os << "REQUEST-REPLY");
+ case ExchangeType::RN:
+ return(os << "RENEW-REPLY");
+ case ExchangeType::RL:
+ return(os << "RELEASE-REPLY");
+ default:
+ return(os << "Unknown exchange type");
+ }
+}
+
+
+ExchangeStats::ExchangeStats(const ExchangeType xchg_type,
+ const double drop_time,
+ const bool archive_enabled,
+ const boost::posix_time::ptime boot_time,
+ bool ignore_timestamp_reorder)
+ : xchg_type_(xchg_type),
+ sent_packets_(),
+ rcvd_packets_(),
+ archived_packets_(),
+ archive_enabled_(archive_enabled),
+ drop_time_(drop_time),
+ min_delay_(std::numeric_limits<double>::max()),
+ max_delay_(0.),
+ sum_delay_(0.),
+ sum_delay_squared_(0.),
+ orphans_(0),
+ collected_(0),
+ unordered_lookup_size_sum_(0),
+ unordered_lookups_(0),
+ ordered_lookups_(0),
+ sent_packets_num_(0),
+ rcvd_packets_num_(0),
+ boot_time_(boot_time),
+ ignore_timestamp_reorder_(ignore_timestamp_reorder)
+{
+ next_sent_ = sent_packets_.begin();
+}
+
+
+void
+ExchangeStats::updateDelays(const dhcp::PktPtr& sent_packet,
+ const dhcp::PktPtr& rcvd_packet) {
+ if (!sent_packet) {
+ isc_throw(BadValue, "Sent packet is null");
+ }
+ if (!rcvd_packet) {
+ isc_throw(BadValue, "Received packet is null");
+ }
+
+ boost::posix_time::ptime sent_time = sent_packet->getTimestamp();
+ boost::posix_time::ptime rcvd_time = rcvd_packet->getTimestamp();
+
+ if (sent_time.is_not_a_date_time() ||
+ rcvd_time.is_not_a_date_time()) {
+ isc_throw(Unexpected,
+ "Timestamp must be set for sent and "
+ "received packet to measure RTT");
+ }
+ boost::posix_time::time_period period(sent_time, rcvd_time);
+ // We don't bother calculating deltas in nanoseconds. It is much
+ // more convenient to use seconds instead because we are going to
+ // sum them up.
+ double delta =
+ static_cast<double>(period.length().total_nanoseconds()) / 1e9;
+
+ if (!ignore_timestamp_reorder_ && delta < 0) {
+ isc_throw(Unexpected, "Sent packet's timestamp must not be "
+ "greater than received packet's timestamp in "
+ << xchg_type_ << ".\nTime difference: "
+ << delta << ", sent: " << sent_time << ", rcvd: "
+ << rcvd_time << ".\nTrans ID: " << sent_packet->getTransid()
+ << ".");
+ }
+
+ // Record the minimum delay between sent and received packets.
+ if (delta < min_delay_) {
+ min_delay_ = delta;
+ }
+ // Record the maximum delay between sent and received packets.
+ if (delta > max_delay_) {
+ max_delay_ = delta;
+ }
+ // Update delay sum and square sum. That will be used to calculate
+ // mean delays.
+ sum_delay_ += delta;
+ sum_delay_squared_ += delta * delta;
+}
+
+dhcp::PktPtr
+ExchangeStats::matchPackets(const dhcp::PktPtr& rcvd_packet) {
+ using namespace boost::posix_time;
+
+ if (!rcvd_packet) {
+ isc_throw(BadValue, "Received packet is null");
+ }
+
+ if (sent_packets_.size() == 0) {
+ // List of sent packets is empty so there is no sense
+ // to continue looking fo the packet. It also means
+ // that the received packet we got has no corresponding
+ // sent packet so orphans counter has to be updated.
+ ++orphans_;
+ return(dhcp::PktPtr());
+ } else if (next_sent_ == sent_packets_.end()) {
+ // Even if there are still many unmatched packets on the
+ // list we might hit the end of it because of unordered
+ // lookups. The next logical step is to reset iterator.
+ next_sent_ = sent_packets_.begin();
+ }
+
+ // With this variable we will be signalling success or failure
+ // to find the packet.
+ bool packet_found = false;
+ // Most likely responses are sent from the server in the same
+ // order as client's requests to the server. We are caching
+ // next sent packet and first try to match it with the next
+ // incoming packet. We are successful if there is no
+ // packet drop or out of order packets sent. This is actually
+ // the fastest way to look for packets.
+ if ((*next_sent_)->getTransid() == rcvd_packet->getTransid()) {
+ ++ordered_lookups_;
+ packet_found = true;
+ } else {
+ // If we are here, it means that we were unable to match the
+ // next incoming packet with next sent packet so we need to
+ // take a little more expensive approach to look packets using
+ // alternative index (transaction id & 1023).
+ PktListTransidHashIndex& idx = sent_packets_.template get<1>();
+ // Packets are grouped using transaction id masked with value
+ // of 1023. For instance, packets with transaction id equal to
+ // 1, 1024 ... will belong to the same group (a.k.a. bucket).
+ // When using alternative index we don't find the packet but
+ // bucket of packets and we need to iterate through the bucket
+ // to find the one that has desired transaction id.
+ std::pair<PktListTransidHashIterator,PktListTransidHashIterator> p =
+ idx.equal_range(hashTransid(rcvd_packet));
+ // We want to keep statistics of unordered lookups to make
+ // sure that there is a right balance between number of
+ // unordered lookups and ordered lookups. If number of unordered
+ // lookups is high it may mean that many packets are lost or
+ // sent out of order.
+ ++unordered_lookups_;
+ // We also want to keep the mean value of the bucket. The lower
+ // bucket size the better. If bucket sizes appear to big we
+ // might want to increase number of buckets.
+ unordered_lookup_size_sum_ += std::distance(p.first, p.second);
+ bool non_expired_found = false;
+ // Removal can be done only after the loop
+ PktListRemovalQueue to_remove;
+ for (PktListTransidHashIterator it = p.first; it != p.second; ++it) {
+ // If transaction id is matching, we found the original
+ // packet sent to the server. Therefore, we reset the
+ // 'next sent' pointer to point to this location. We
+ // also indicate that the matching packet is found.
+ // Even though the packet has been found, we continue
+ // iterating over the bucket to remove all those packets
+ // that are timed out.
+ if (!packet_found && ((*it)->getTransid() == rcvd_packet->getTransid())) {
+ packet_found = true;
+ next_sent_ = sent_packets_.template project<0>(it);
+ }
+
+ if (!non_expired_found) {
+ // Check if the packet should be removed due to timeout.
+ // This includes the packet matching the received one.
+ ptime now = microsec_clock::universal_time();
+ ptime packet_time = (*it)->getTimestamp();
+ time_period packet_period(packet_time, now);
+ if (!packet_period.is_null()) {
+ double period_fractional =
+ packet_period.length().total_seconds() +
+ (static_cast<double>(packet_period.length().fractional_seconds())
+ / packet_period.length().ticks_per_second());
+ if (drop_time_ > 0 && (period_fractional > drop_time_)) {
+ // Push the iterator on the removal queue.
+ to_remove.push(it);
+
+ } else {
+ // We found first non-expired transaction. All other
+ // transactions within this bucket are considered
+ // non-expired because packets are held in the
+ // order of addition within the bucket.
+ non_expired_found = true;
+ }
+ }
+ }
+
+ // If we found the packet and all expired transactions,
+ // there is nothing more to do.
+ if (non_expired_found && packet_found) {
+ break;
+ }
+ }
+
+ // Deal with the removal queue.
+ while (!to_remove.empty()) {
+ PktListTransidHashIterator it = to_remove.front();
+ to_remove.pop();
+ // If timed out packet is not the one matching server response,
+ // we simply remove it and keep the pointer to the 'next sent'
+ // packet as it was. If the timed out packet appears to be the
+ // one that is matching the server response, we still want to
+ // remove it, but we need to update the 'next sent' pointer to
+ // point to a valid location.
+ if (sent_packets_.template project<0>(it) != next_sent_) {
+ eraseSent(sent_packets_.template project<0>(it));
+ } else {
+ next_sent_ = eraseSent(sent_packets_.template project<0>(it));
+ // We removed the matching packet because of the timeout. It
+ // means that there is no match anymore.
+ packet_found = false;
+ }
+ ++collected_;
+ }
+ }
+
+ if (!packet_found) {
+ // If we are here, it means that both ordered lookup and
+ // unordered lookup failed. Searched packet is not on the list.
+ ++orphans_;
+ return(dhcp::PktPtr());
+ }
+
+ // Packet is matched so we count it. We don't count unmatched packets
+ // as they are counted as orphans with a separate counter.
+ ++rcvd_packets_num_;
+ dhcp::PktPtr sent_packet(*next_sent_);
+ // If packet was found, we assume it will be never searched
+ // again. We want to delete this packet from the list to
+ // improve performance of future searches.
+ next_sent_ = eraseSent(next_sent_);
+ return(sent_packet);
+}
+
+
+void
+ExchangeStats::printTimestamps() {
+ // If archive mode is disabled there is no sense to proceed
+ // because we don't have packets and their timestamps.
+ if (!archive_enabled_) {
+ isc_throw(isc::InvalidOperation,
+ "packets archive mode is disabled");
+ }
+ if (rcvd_packets_num_ == 0) {
+ std::cout << "Unavailable! No packets received." << std::endl;
+ }
+ // We will be using boost::posix_time extensively here
+ using namespace boost::posix_time;
+
+ // Iterate through all received packets.
+ for (PktListIterator it = rcvd_packets_.begin();
+ it != rcvd_packets_.end();
+ ++it) {
+ dhcp::PktPtr rcvd_packet = *it;
+ PktListTransidHashIndex& idx =
+ archived_packets_.template get<1>();
+ std::pair<PktListTransidHashIterator,
+ PktListTransidHashIterator> p =
+ idx.equal_range(hashTransid(rcvd_packet));
+ for (PktListTransidHashIterator it_archived = p.first;
+ it_archived != p.second;
+ ++it_archived) {
+ if ((*it_archived)->getTransid() ==
+ rcvd_packet->getTransid()) {
+ dhcp::PktPtr sent_packet = *it_archived;
+ // Get sent and received packet times.
+ ptime sent_time = sent_packet->getTimestamp();
+ ptime rcvd_time = rcvd_packet->getTimestamp();
+ // All sent and received packets should have timestamps
+ // set but if there is a bug somewhere and packet does
+ // not have timestamp we want to catch this here.
+ if (sent_time.is_not_a_date_time() ||
+ rcvd_time.is_not_a_date_time()) {
+ isc_throw(InvalidOperation,
+ "packet time is not set");
+ }
+ // Calculate durations of packets from beginning of epoch.
+ time_period sent_period(boot_time_, sent_time);
+ time_period rcvd_period(boot_time_, rcvd_time);
+ // Print timestamps for sent and received packet.
+ std::cout << "sent / received: "
+ << to_iso_string(sent_period.length())
+ << " / "
+ << to_iso_string(rcvd_period.length())
+ << std::endl;
+ break;
+ }
+ }
+ }
+}
+
+StatsMgr::StatsMgr(bool ignore_timestamp_reorder) :
+ exchanges_(),
+ boot_time_(boost::posix_time::microsec_clock::universal_time()),
+ ignore_timestamp_reorder_(ignore_timestamp_reorder)
+{
+ 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;
+
+ if (options.getIpVersion() == 4) {
+ addExchangeStats(ExchangeType::DO, options.getDropTime()[0]);
+ if (options.getExchangeMode() == CommandOptions::DORA_SARR) {
+ addExchangeStats(ExchangeType::RA, options.getDropTime()[1]);
+ }
+ if (options.getRenewRate() != 0) {
+ addExchangeStats(ExchangeType::RNA);
+ }
+
+ } else if (options.getIpVersion() == 6) {
+ addExchangeStats(ExchangeType::SA, options.getDropTime()[0]);
+ if (options.getExchangeMode() == CommandOptions::DORA_SARR) {
+ addExchangeStats(ExchangeType::RR, options.getDropTime()[1]);
+ }
+ if (options.getRenewRate() != 0) {
+ addExchangeStats(ExchangeType::RN);
+ }
+ if (options.getReleaseRate() != 0) {
+ addExchangeStats(ExchangeType::RL);
+ }
+ }
+ if (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 00a58904f9..7bb1bf3a21 100644
--- a/src/bin/perfdhcp/stats_mgr.h
+++ b/src/bin/perfdhcp/stats_mgr.h
@@ -7,8 +7,7 @@
#ifndef STATS_MGR_H
#define STATS_MGR_H
-#include <dhcp/pkt4.h>
-#include <dhcp/pkt6.h>
+#include <dhcp/pkt.h>
#include <exceptions/exceptions.h>
#include <boost/noncopyable.hpp>
@@ -28,861 +27,622 @@
namespace isc {
namespace perfdhcp {
-/// \brief Statistics Manager
+/// DHCP packet exchange types.
+enum class ExchangeType {
+ DO, ///< DHCPv4 DISCOVER-OFFER
+ RA, ///< DHCPv4 REQUEST-ACK
+ RNA, ///< DHCPv4 REQUEST-ACK (renewal)
+ SA, ///< DHCPv6 SOLICIT-ADVERTISE
+ RR, ///< DHCPv6 REQUEST-REPLY
+ RN, ///< DHCPv6 RENEW-REPLY
+ RL ///< DHCPv6 RELEASE-REPLY
+};
+
+/// \brief Return name of the exchange.
///
-/// This class template is a storage for various performance statistics
-/// collected during performance tests execution with perfdhcp tool.
+/// Function returns name of the specified exchange type.
+/// This function is mainly for logging purposes.
///
-/// Statistics Manager holds lists of sent and received packets and
-/// groups them into exchanges. For example: DHCPDISCOVER message and
-/// corresponding DHCPOFFER messages belong to one exchange, DHCPREQUEST
-/// and corresponding DHCPACK message belong to another exchange etc.
-/// In order to update statistics for a particular exchange type, client
-/// class passes sent and received packets. Internally, Statistics Manager
-/// tries to match transaction id of received packet with sent packet
-/// stored on the list of sent packets. When packets are matched the
-/// round trip time can be calculated.
+/// \param xchg_type exchange type.
+/// \return string representing name of the exchange.
+std::ostream& operator<<(std::ostream& os, ExchangeType xchg_type);
+
+/// \brief Custom Counter
///
-/// \param T class representing DHCPv4 or DHCPv6 packet.
-template <class T = dhcp::Pkt4>
-class StatsMgr : public boost::noncopyable {
+/// This class represents custom statistics counters. Client class
+/// may create unlimited number of counters. Such counters are
+/// being stored in map in Statistics Manager and access using
+/// unique string key.
+class CustomCounter {
public:
+ /// \brief Constructor.
+ ///
+ /// This constructor sets counter name. This name is used in
+ /// log file to report value of each counter.
+ ///
+ /// \param name name of the counter used in log file.
+ CustomCounter(const std::string& name) :
+ counter_(0),
+ name_(name) { };
+
+ /// \brief Increment operator.
+ const CustomCounter& operator++() {
+ ++counter_;
+ return (*this);
+ }
- /// \brief Custom Counter
- ///
- /// This class represents custom statistics counters. Client class
- /// may create unlimited number of counters. Such counters are
- /// being stored in map in Statistics Manager and access using
- /// unique string key.
- class CustomCounter {
- public:
- /// \brief Constructor.
- ///
- /// This constructor sets counter name. This name is used in
- /// log file to report value of each counter.
- ///
- /// \param name name of the counter used in log file.
- CustomCounter(const std::string& name) :
- counter_(0),
- name_(name) { };
-
- /// \brief Increment operator.
- const CustomCounter& operator++() {
- ++counter_;
- return (*this);
- }
-
- /// \brief Increment operator.
- const CustomCounter& operator++(int) {
- CustomCounter& this_counter(*this);
- operator++();
- return (this_counter);
- }
+ /// \brief Increment operator.
+ const CustomCounter& operator++(int) {
+ CustomCounter& this_counter(*this);
+ operator++();
+ return (this_counter);
+ }
- const CustomCounter& operator+=(int val) {
- counter_ += val;
- return (*this);
- }
+ const CustomCounter& operator+=(int val) {
+ counter_ += val;
+ return (*this);
+ }
- /// \brief Return counter value.
- ///
- /// Method returns counter value.
- ///
+ /// \brief Return counter value.
+ ///
+ /// Method returns counter value.
+ ///
/// \return counter value.
- uint64_t getValue() const { return(counter_); }
+ uint64_t getValue() const { return(counter_); }
- /// \brief Return counter name.
- ///
- /// Method returns counter name.
- ///
+ /// \brief Return counter name.
+ ///
+ /// Method returns counter name.
+ ///
/// \return counter name.
- const std::string& getName() const { return(name_); }
- private:
- /// \brief Default constructor.
- ///
- /// Default constructor is private because we don't want client
- /// class to call it because we want client class to specify
- /// counter's name.
- CustomCounter() { };
-
- uint64_t counter_; ///< Counter's value.
- std::string name_; ///< Counter's name.
- };
-
- typedef typename boost::shared_ptr<CustomCounter> CustomCounterPtr;
-
- /// DHCP packet exchange types.
- enum ExchangeType {
- XCHG_DO, ///< DHCPv4 DISCOVER-OFFER
- XCHG_RA, ///< DHCPv4 REQUEST-ACK
- XCHG_RNA, ///< DHCPv4 REQUEST-ACK (renewal)
- XCHG_SA, ///< DHCPv6 SOLICIT-ADVERTISE
- XCHG_RR, ///< DHCPv6 REQUEST-REPLY
- XCHG_RN, ///< DHCPv6 RENEW-REPLY
- XCHG_RL ///< DHCPv6 RELEASE-REPLY
- };
-
- /// \brief Exchange Statistics.
- ///
- /// This class collects statistics for exchanges. Parent class
- /// may define number of different packet exchanges like:
- /// DHCPv4 DISCOVER-OFFER, DHCPv6 SOLICIT-ADVERTISE etc. Performance
- /// statistics will be collected for each of those separately in
- /// corresponding instance of ExchangeStats.
- class ExchangeStats {
- public:
-
- /// \brief Hash transaction id of the packet.
- ///
- /// Function hashes transaction id of the packet. Hashing is
- /// non-unique. Many packets may have the same hash value and thus
- /// they belong to the same packet buckets. Packet buckets are
- /// used for unordered packets search with multi index container.
- ///
- /// \param packet packet which transaction id is to be hashed.
- /// \throw isc::BadValue if packet is null.
- /// \return transaction id hash.
- static uint32_t hashTransid(const boost::shared_ptr<T>& packet) {
- if (!packet) {
- isc_throw(BadValue, "Packet is null");
- }
- return(packet->getTransid() & 1023);
+ const std::string& getName() const { return(name_); }
+private:
+ /// \brief Default constructor.
+ ///
+ /// Default constructor is private because we don't want client
+ /// class to call it because we want client class to specify
+ /// counter's name.
+ CustomCounter() { };
+
+ uint64_t counter_; ///< Counter's value.
+ std::string name_; ///< Counter's name.
+};
+
+typedef typename boost::shared_ptr<CustomCounter> CustomCounterPtr;
+
+/// Map containing custom counters.
+typedef typename std::map<std::string, CustomCounterPtr> CustomCountersMap;
+
+/// Iterator for \ref CustomCountersMap.
+typedef typename CustomCountersMap::const_iterator CustomCountersMapIterator;
+
+
+/// \brief Exchange Statistics.
+///
+/// This class collects statistics for exchanges. Parent class
+/// may define number of different packet exchanges like:
+/// DHCPv4 DISCOVER-OFFER, DHCPv6 SOLICIT-ADVERTISE etc. Performance
+/// statistics will be collected for each of those separately in
+/// corresponding instance of ExchangeStats.
+class ExchangeStats {
+public:
+
+ /// \brief Hash transaction id of the packet.
+ ///
+ /// Function hashes transaction id of the packet. Hashing is
+ /// non-unique. Many packets may have the same hash value and thus
+ /// they belong to the same packet buckets. Packet buckets are
+ /// used for unordered packets search with multi index container.
+ ///
+ /// \param packet packet which transaction id is to be hashed.
+ /// \throw isc::BadValue if packet is null.
+ /// \return transaction id hash.
+ static uint32_t hashTransid(const dhcp::PktPtr& packet) {
+ if (!packet) {
+ isc_throw(BadValue, "Packet is null");
}
+ return(packet->getTransid() & 1023);
+ }
- /// \brief List of packets (sent or received).
- ///
- /// List of packets based on multi index container allows efficient
- /// search of packets based on their sequence (order in which they
- /// were inserted) as well as based on their hashed transaction id.
- /// The first index (sequenced) provides the way to use container
- /// as a regular list (including iterators, removal of elements from
- /// the middle of the collection etc.). This index is meant to be used
- /// more frequently than the latter one and it is based on the
- /// assumption that responses from the DHCP server are received in
- /// order. In this case, when next packet is received it can be
- /// matched with next packet on the list of sent packets. This
- /// prevents intensive searches on the list of sent packets every
- /// time new packet arrives. In many cases however packets can be
- /// dropped by the server or may be sent out of order and we still
- /// want to have ability to search packets using transaction id.
- /// The second index can be used for this purpose. This index is
- /// hashing transaction ids using custom function \ref hashTransid.
- /// Note that other possibility would be to simply specify index
- /// that uses transaction id directly (instead of hashing with
- /// \ref hashTransid). In this case however we have chosen to use
- /// hashing function because it shortens the index size to just
- /// 1023 values maximum. Search operation on this index generally
- /// returns the range of packets that have the same transaction id
- /// hash assigned but most often these ranges will be short so further
- /// search within a range to find a packet with particular transaction
- /// id will not be intensive.
- ///
- /// Example 1: Add elements to the list
- /// \code
- /// PktList packets_collection();
- /// boost::shared_ptr<Pkt4> pkt1(new Pkt4(...));
- /// boost::shared_ptr<Pkt4> pkt2(new Pkt4(...));
- /// // Add new packet to the container, it will be available through
- /// // both indexes
- /// packets_collection.push_back(pkt1);
- /// // Here is another way to add packet to the container. The result
- /// // is exactly the same as previously.
- /// packets_collection.template get<0>().push_back(pkt2);
- /// \endcode
- ///
- /// Example 2: Access elements through sequential index
- /// \code
- /// PktList packets_collection();
- /// ... # Add elements to the container
- /// for (PktListIterator it = packets_collection.begin();
- /// it != packets_collection.end();
- /// ++it) {
- /// boost::shared_ptr<Pkt4> pkt = *it;
- /// # Do something with packet;
- /// }
- /// \endcode
- ///
- /// Example 3: Access elements through ordered index by hash
- /// \code
- /// // Get the instance of the second search index.
- /// PktListTransidHashIndex& idx = sent_packets_.template get<1>();
- /// // Get the range (bucket) of packets sharing the same transaction
- /// // id hash.
- /// std::pair<PktListTransidHashIterator,PktListTransidHashIterator> p =
- /// idx.equal_range(hashTransid(rcvd_packet));
- /// // Iterate through the returned bucket.
- /// for (PktListTransidHashIterator it = p.first; it != p.second;
- /// ++it) {
- /// boost::shared_ptr pkt = *it;
- /// ... # Do something with the packet (e.g. check transaction id)
- /// }
- /// \endcode
- typedef boost::multi_index_container<
- // Container holds shared_ptr<Pkt4> or shared_ptr<Pkt6> objects.
- boost::shared_ptr<T>,
- // List container indexes.
- boost::multi_index::indexed_by<
- // Sequenced index provides the way to use this container
- // in the same way as std::list.
- boost::multi_index::sequenced<>,
- // The other index keeps products of transaction id.
- // Elements with the same hash value are grouped together
- // into buckets and transactions are ordered from the
- // oldest to latest within a bucket.
- boost::multi_index::ordered_non_unique<
- // Specify hash function to get the product of
- // transaction id. This product is obtained by calling
- // hashTransid() function.
- boost::multi_index::global_fun<
- // Hashing function takes shared_ptr<Pkt4> or
- // shared_ptr<Pkt6> as argument.
- const boost::shared_ptr<T>&,
- // ... and returns uint32 value.
- uint32_t,
- // ... and here is a reference to it.
- &ExchangeStats::hashTransid
+ /// \brief List of packets (sent or received).
+ ///
+ /// List of packets based on multi index container allows efficient
+ /// search of packets based on their sequence (order in which they
+ /// were inserted) as well as based on their hashed transaction id.
+ /// The first index (sequenced) provides the way to use container
+ /// as a regular list (including iterators, removal of elements from
+ /// the middle of the collection etc.). This index is meant to be used
+ /// more frequently than the latter one and it is based on the
+ /// assumption that responses from the DHCP server are received in
+ /// order. In this case, when next packet is received it can be
+ /// matched with next packet on the list of sent packets. This
+ /// prevents intensive searches on the list of sent packets every
+ /// time new packet arrives. In many cases however packets can be
+ /// dropped by the server or may be sent out of order and we still
+ /// want to have ability to search packets using transaction id.
+ /// The second index can be used for this purpose. This index is
+ /// hashing transaction ids using custom function \ref hashTransid.
+ /// Note that other possibility would be to simply specify index
+ /// that uses transaction id directly (instead of hashing with
+ /// \ref hashTransid). In this case however we have chosen to use
+ /// hashing function because it shortens the index size to just
+ /// 1023 values maximum. Search operation on this index generally
+ /// returns the range of packets that have the same transaction id
+ /// hash assigned but most often these ranges will be short so further
+ /// search within a range to find a packet with particular transaction
+ /// id will not be intensive.
+ ///
+ /// Example 1: Add elements to the list
+ /// \code
+ /// PktList packets_collection();
+ /// boost::shared_ptr<Pkt4> pkt1(new Pkt4(...));
+ /// boost::shared_ptr<Pkt4> pkt2(new Pkt4(...));
+ /// // Add new packet to the container, it will be available through
+ /// // both indexes
+ /// packets_collection.push_back(pkt1);
+ /// // Here is another way to add packet to the container. The result
+ /// // is exactly the same as previously.
+ /// packets_collection.template get<0>().push_back(pkt2);
+ /// \endcode
+ ///
+ /// Example 2: Access elements through sequential index
+ /// \code
+ /// PktList packets_collection();
+ /// ... # Add elements to the container
+ /// for (PktListIterator it = packets_collection.begin();
+ /// it != packets_collection.end();
+ /// ++it) {
+ /// boost::shared_ptr<Pkt4> pkt = *it;
+ /// # Do something with packet;
+ /// }
+ /// \endcode
+ ///
+ /// Example 3: Access elements through ordered index by hash
+ /// \code
+ /// // Get the instance of the second search index.
+ /// PktListTransidHashIndex& idx = sent_packets_.template get<1>();
+ /// // Get the range (bucket) of packets sharing the same transaction
+ /// // id hash.
+ /// std::pair<PktListTransidHashIterator,PktListTransidHashIterator> p =
+ /// idx.equal_range(hashTransid(rcvd_packet));
+ /// // Iterate through the returned bucket.
+ /// for (PktListTransidHashIterator it = p.first; it != p.second;
+ /// ++it) {
+ /// boost::shared_ptr pkt = *it;
+ /// ... # Do something with the packet (e.g. check transaction id)
+ /// }
+ /// \endcode
+ typedef boost::multi_index_container<
+ // Container holds shared_ptr<Pkt4> or shared_ptr<Pkt6> objects.
+ dhcp::PktPtr,
+ // List container indexes.
+ boost::multi_index::indexed_by<
+ // Sequenced index provides the way to use this container
+ // in the same way as std::list.
+ boost::multi_index::sequenced<>,
+ // The other index keeps products of transaction id.
+ // Elements with the same hash value are grouped together
+ // into buckets and transactions are ordered from the
+ // oldest to latest within a bucket.
+ boost::multi_index::ordered_non_unique<
+ // Specify hash function to get the product of
+ // transaction id. This product is obtained by calling
+ // hashTransid() function.
+ boost::multi_index::global_fun<
+ // Hashing function takes shared_ptr<Pkt4> or
+ // shared_ptr<Pkt6> as argument.
+ const dhcp::PktPtr&,
+ // ... and returns uint32 value.
+ uint32_t,
+ // ... and here is a reference to it.
+ &ExchangeStats::hashTransid
>
>
>
> PktList;
- /// Packet list iterator for sequential access to elements.
- typedef typename PktList::iterator PktListIterator;
- /// Packet list index to search packets using transaction id hash.
- typedef typename PktList::template nth_index<1>::type
- PktListTransidHashIndex;
- /// Packet list iterator to access packets using transaction id hash.
- typedef typename PktListTransidHashIndex::const_iterator
- PktListTransidHashIterator;
- /// Packet list iterator queue for removal.
- typedef typename std::queue<PktListTransidHashIterator>
- PktListRemovalQueue;
-
- /// \brief Constructor
- ///
- /// \param xchg_type exchange type
- /// \param drop_time maximum time elapsed before packet is
- /// assumed dropped. Negative value disables it.
- /// \param archive_enabled if true packets archive mode is enabled.
- /// In this mode all packets are stored throughout the test execution.
- /// \param boot_time Holds the timestamp when perfdhcp has been started.
- ExchangeStats(const ExchangeType xchg_type,
- const double drop_time,
- const bool archive_enabled,
- const boost::posix_time::ptime boot_time)
- : xchg_type_(xchg_type),
- sent_packets_(),
- rcvd_packets_(),
- archived_packets_(),
- archive_enabled_(archive_enabled),
- drop_time_(drop_time),
- min_delay_(std::numeric_limits<double>::max()),
- max_delay_(0.),
- sum_delay_(0.),
- sum_delay_squared_(0.),
- orphans_(0),
- collected_(0),
- unordered_lookup_size_sum_(0),
- unordered_lookups_(0),
- ordered_lookups_(0),
- sent_packets_num_(0),
- rcvd_packets_num_(0),
- boot_time_(boot_time)
- {
- next_sent_ = sent_packets_.begin();
+ /// Packet list iterator for sequential access to elements.
+ typedef typename PktList::iterator PktListIterator;
+ /// Packet list index to search packets using transaction id hash.
+ typedef typename PktList::template nth_index<1>::type
+ PktListTransidHashIndex;
+ /// Packet list iterator to access packets using transaction id hash.
+ typedef typename PktListTransidHashIndex::const_iterator
+ PktListTransidHashIterator;
+ /// Packet list iterator queue for removal.
+ typedef typename std::queue<PktListTransidHashIterator>
+ PktListRemovalQueue;
+
+ /// \brief Constructor
+ ///
+ /// \param xchg_type exchange type
+ /// \param drop_time maximum time elapsed before packet is
+ /// assumed dropped. Negative value disables it.
+ /// \param archive_enabled if true packets archive mode is enabled.
+ /// In this mode all packets are stored throughout the test execution.
+ /// \param boot_time Holds the timestamp when perfdhcp has been started.
+ ExchangeStats(const ExchangeType xchg_type,
+ const double drop_time,
+ const bool archive_enabled,
+ const boost::posix_time::ptime boot_time,
+ bool ignore_timestamp_reorder);
+
+ /// \brief Add new packet to list of sent packets.
+ ///
+ /// Method adds new packet to list of sent packets.
+ ///
+ /// \param packet packet object to be added.
+ /// \throw isc::BadValue if packet is null.
+ void appendSent(const dhcp::PktPtr& packet) {
+ if (!packet) {
+ isc_throw(BadValue, "Packet is null");
}
+ ++sent_packets_num_;
+ sent_packets_.template get<0>().push_back(packet);
+ }
- /// \brief Add new packet to list of sent packets.
- ///
- /// Method adds new packet to list of sent packets.
- ///
- /// \param packet packet object to be added.
- /// \throw isc::BadValue if packet is null.
- void appendSent(const boost::shared_ptr<T>& packet) {
- if (!packet) {
- isc_throw(BadValue, "Packet is null");
- }
- ++sent_packets_num_;
- sent_packets_.template get<0>().push_back(packet);
+ /// \brief Add new packet to list of received packets.
+ ///
+ /// Method adds new packet to list of received packets.
+ ///
+ /// \param packet packet object to be added.
+ /// \throw isc::BadValue if packet is null.
+ void appendRcvd(const dhcp::PktPtr& packet) {
+ if (!packet) {
+ isc_throw(BadValue, "Packet is null");
}
+ rcvd_packets_.push_back(packet);
+ }
- /// \brief Add new packet to list of received packets.
- ///
- /// Method adds new packet to list of received packets.
- ///
- /// \param packet packet object to be added.
- /// \throw isc::BadValue if packet is null.
- void appendRcvd(const boost::shared_ptr<T>& packet) {
- if (!packet) {
- isc_throw(BadValue, "Packet is null");
- }
- rcvd_packets_.push_back(packet);
+ /// \brief Update delay counters.
+ ///
+ /// Method updates delay counters based on timestamps of
+ /// sent and received packets.
+ ///
+ /// \param sent_packet sent packet
+ /// \param rcvd_packet received packet
+ /// \throw isc::BadValue if sent or received packet is null.
+ /// \throw isc::Unexpected if failed to calculate timestamps
+ void updateDelays(const dhcp::PktPtr& sent_packet,
+ const dhcp::PktPtr& rcvd_packet);
+
+ /// \brief Match received packet with the corresponding sent packet.
+ ///
+ /// Method finds packet with specified transaction id on the list
+ /// of sent packets. It is used to match received packet with
+ /// corresponding sent packet.
+ /// Since packets from the server most often come in the same order
+ /// as they were sent by client, this method will first check if
+ /// next sent packet matches. If it doesn't, function will search
+ /// the packet using indexing by transaction id. This reduces
+ /// packet search time significantly.
+ ///
+ /// \param rcvd_packet received packet to be matched with sent packet.
+ /// \throw isc::BadValue if received packet is null.
+ /// \return packet having specified transaction or NULL if packet
+ /// not found
+ dhcp::PktPtr matchPackets(const dhcp::PktPtr& rcvd_packet);
+
+ /// \brief Return minimum delay between sent and received packet.
+ ///
+ /// Method returns minimum delay between sent and received packet.
+ ///
+ /// \return minimum delay between packets.
+ double getMinDelay() const { return(min_delay_); }
+
+ /// \brief Return maximum delay between sent and received packet.
+ ///
+ /// Method returns maximum delay between sent and received packet.
+ ///
+ /// \return maximum delay between packets.
+ double getMaxDelay() const { return(max_delay_); }
+
+ /// \brief Return average packet delay.
+ ///
+ /// Method returns average packet delay. If no packets have been
+ /// received for this exchange avg delay can't be calculated and
+ /// thus method throws exception.
+ ///
+ /// \throw isc::InvalidOperation if no packets for this exchange
+ /// have been received yet.
+ /// \return average packet delay.
+ double getAvgDelay() const {
+ if (rcvd_packets_num_ == 0) {
+ isc_throw(InvalidOperation, "no packets received");
}
+ return(sum_delay_ / rcvd_packets_num_);
+ }
- /// \brief Update delay counters.
- ///
- /// Method updates delay counters based on timestamps of
- /// sent and received packets.
- ///
- /// \param sent_packet sent packet
- /// \param rcvd_packet received packet
- /// \throw isc::BadValue if sent or received packet is null.
- /// \throw isc::Unexpected if failed to calculate timestamps
- void updateDelays(const boost::shared_ptr<T>& sent_packet,
- const boost::shared_ptr<T>& rcvd_packet) {
- if (!sent_packet) {
- isc_throw(BadValue, "Sent packet is null");
- }
- if (!rcvd_packet) {
- isc_throw(BadValue, "Received packet is null");
- }
+ /// \brief Return standard deviation of packet delay.
+ ///
+ /// Method returns standard deviation of packet delay. If no
+ /// packets have been received for this exchange, the standard
+ /// deviation can't be calculated and thus method throws
+ /// exception.
+ ///
+ /// \throw isc::InvalidOperation if number of received packets
+ /// for the exchange is equal to zero.
+ /// \return standard deviation of packet delay.
+ double getStdDevDelay() const {
+ if (rcvd_packets_num_ == 0) {
+ isc_throw(InvalidOperation, "no packets received");
+ }
+ return(sqrt(sum_delay_squared_ / rcvd_packets_num_ -
+ getAvgDelay() * getAvgDelay()));
+ }
- boost::posix_time::ptime sent_time = sent_packet->getTimestamp();
- boost::posix_time::ptime rcvd_time = rcvd_packet->getTimestamp();
+ /// \brief Return number of orphan packets.
+ ///
+ /// Method returns number of received packets that had no matching
+ /// sent packet. It is possible that such packet was late or not
+ /// for us.
+ ///
+ /// \return number of orphan received packets.
+ uint64_t getOrphans() const { return(orphans_); }
- if (sent_time.is_not_a_date_time() ||
- rcvd_time.is_not_a_date_time()) {
- isc_throw(Unexpected,
- "Timestamp must be set for sent and "
- "received packet to measure RTT");
- }
- boost::posix_time::time_period period(sent_time, rcvd_time);
- // We don't bother calculating deltas in nanoseconds. It is much
- // more convenient to use seconds instead because we are going to
- // sum them up.
- double delta =
- static_cast<double>(period.length().total_nanoseconds()) / 1e9;
-
- if (delta < 0) {
- isc_throw(Unexpected, "Sent packet's timestamp must not be "
- "greater than received packet's timestamp");
- }
+ /// \brief Return number of garbage collected packets.
+ ///
+ /// Method returns number of garbage collected timed out
+ /// packets. Packet is assumed timed out when duration
+ /// between sending it to server and receiving server's
+ /// response is greater than value specified with -d<value>
+ /// command line argument.
+ ///
+ /// \return number of garbage collected packets.
+ uint64_t getCollectedNum() const { return(collected_); }
- // Record the minimum delay between sent and received packets.
- if (delta < min_delay_) {
- min_delay_ = delta;
- }
- // Record the maximum delay between sent and received packets.
- if (delta > max_delay_) {
- max_delay_ = delta;
- }
- // Update delay sum and square sum. That will be used to calculate
- // mean delays.
- sum_delay_ += delta;
- sum_delay_squared_ += delta * delta;
+ /// \brief Return average unordered lookup set size.
+ ///
+ /// Method returns average unordered lookup set size.
+ /// This value changes every time \ref ExchangeStats::matchPackets
+ /// function performs unordered packet lookup.
+ ///
+ /// \throw isc::InvalidOperation if there have been no unordered
+ /// lookups yet.
+ /// \return average unordered lookup set size.
+ double getAvgUnorderedLookupSetSize() const {
+ if (unordered_lookups_ == 0) {
+ isc_throw(InvalidOperation, "no unordered lookups");
}
+ return(static_cast<double>(unordered_lookup_size_sum_) /
+ static_cast<double>(unordered_lookups_));
+ }
- /// \brief Match received packet with the corresponding sent packet.
- ///
- /// Method finds packet with specified transaction id on the list
- /// of sent packets. It is used to match received packet with
- /// corresponding sent packet.
- /// Since packets from the server most often come in the same order
- /// as they were sent by client, this method will first check if
- /// next sent packet matches. If it doesn't, function will search
- /// the packet using indexing by transaction id. This reduces
- /// packet search time significantly.
- ///
- /// \param rcvd_packet received packet to be matched with sent packet.
- /// \throw isc::BadValue if received packet is null.
- /// \return packet having specified transaction or NULL if packet
- /// not found
- boost::shared_ptr<T>
- matchPackets(const boost::shared_ptr<T>& rcvd_packet) {
- using namespace boost::posix_time;
-
- if (!rcvd_packet) {
- isc_throw(BadValue, "Received packet is null");
- }
+ /// \brief Return number of unordered sent packets lookups
+ ///
+ /// Method returns number of unordered sent packet lookups.
+ /// Unordered lookup is used when received packet was sent
+ /// out of order by server - transaction id of received
+ /// packet does not match transaction id of next sent packet.
+ ///
+ /// \return number of unordered lookups.
+ uint64_t getUnorderedLookups() const { return(unordered_lookups_); }
- if (sent_packets_.size() == 0) {
- // List of sent packets is empty so there is no sense
- // to continue looking fo the packet. It also means
- // that the received packet we got has no corresponding
- // sent packet so orphans counter has to be updated.
- ++orphans_;
- return(boost::shared_ptr<T>());
- } else if (next_sent_ == sent_packets_.end()) {
- // Even if there are still many unmatched packets on the
- // list we might hit the end of it because of unordered
- // lookups. The next logical step is to reset iterator.
- next_sent_ = sent_packets_.begin();
- }
+ /// \brief Return number of ordered sent packets lookups
+ ///
+ /// Method returns number of ordered sent packet lookups.
+ /// Ordered lookup is used when packets are received in the
+ /// same order as they were sent to the server.
+ /// If packets are skipped or received out of order, lookup
+ /// function will use unordered lookup (with hash table).
+ ///
+ /// \return number of ordered lookups.
+ uint64_t getOrderedLookups() const { return(ordered_lookups_); }
- // With this variable we will be signalling success or failure
- // to find the packet.
- bool packet_found = false;
- // Most likely responses are sent from the server in the same
- // order as client's requests to the server. We are caching
- // next sent packet and first try to match it with the next
- // incoming packet. We are successful if there is no
- // packet drop or out of order packets sent. This is actually
- // the fastest way to look for packets.
- if ((*next_sent_)->getTransid() == rcvd_packet->getTransid()) {
- ++ordered_lookups_;
- packet_found = true;
- } else {
- // If we are here, it means that we were unable to match the
- // next incoming packet with next sent packet so we need to
- // take a little more expensive approach to look packets using
- // alternative index (transaction id & 1023).
- PktListTransidHashIndex& idx = sent_packets_.template get<1>();
- // Packets are grouped using transaction id masked with value
- // of 1023. For instance, packets with transaction id equal to
- // 1, 1024 ... will belong to the same group (a.k.a. bucket).
- // When using alternative index we don't find the packet but
- // bucket of packets and we need to iterate through the bucket
- // to find the one that has desired transaction id.
- std::pair<PktListTransidHashIterator,PktListTransidHashIterator> p =
- idx.equal_range(hashTransid(rcvd_packet));
- // We want to keep statistics of unordered lookups to make
- // sure that there is a right balance between number of
- // unordered lookups and ordered lookups. If number of unordered
- // lookups is high it may mean that many packets are lost or
- // sent out of order.
- ++unordered_lookups_;
- // We also want to keep the mean value of the bucket. The lower
- // bucket size the better. If bucket sizes appear to big we
- // might want to increase number of buckets.
- unordered_lookup_size_sum_ += std::distance(p.first, p.second);
- bool non_expired_found = false;
- // Removal can be done only after the loop
- PktListRemovalQueue to_remove;
- for (PktListTransidHashIterator it = p.first; it != p.second;
- ++it) {
- // If transaction id is matching, we found the original
- // packet sent to the server. Therefore, we reset the
- // 'next sent' pointer to point to this location. We
- // also indicate that the matching packet is found.
- // Even though the packet has been found, we continue
- // iterating over the bucket to remove all those packets
- // that are timed out.
- if (!packet_found && ((*it)->getTransid() == rcvd_packet->getTransid())) {
- packet_found = true;
- next_sent_ = sent_packets_.template project<0>(it);
- }
-
- if (!non_expired_found) {
- // Check if the packet should be removed due to timeout.
- // This includes the packet matching the received one.
- ptime now = microsec_clock::universal_time();
- ptime packet_time = (*it)->getTimestamp();
- time_period packet_period(packet_time, now);
- if (!packet_period.is_null()) {
- double period_fractional =
- packet_period.length().total_seconds() +
- (static_cast<double>(packet_period.length().fractional_seconds())
- / packet_period.length().ticks_per_second());
- if (drop_time_ > 0 && (period_fractional > drop_time_)) {
- // Push the iterator on the removal queue.
- to_remove.push(it);
-
- } else {
- // We found first non-expired transaction. All other
- // transactions within this bucket are considered
- // non-expired because packets are held in the
- // order of addition within the bucket.
- non_expired_found = true;
- }
- }
- }
-
- // If we found the packet and all expired transactions,
- // there is nothing more to do.
- if (non_expired_found && packet_found) {
- break;
- }
- }
-
- // Deal with the removal queue.
- while (!to_remove.empty()) {
- PktListTransidHashIterator it = to_remove.front();
- to_remove.pop();
- // If timed out packet is not the one matching server response,
- // we simply remove it and keep the pointer to the 'next sent'
- // packet as it was. If the timed out packet appears to be the
- // one that is matching the server response, we still want to
- // remove it, but we need to update the 'next sent' pointer to
- // point to a valid location.
- if (sent_packets_.template project<0>(it) != next_sent_) {
- eraseSent(sent_packets_.template project<0>(it));
- } else {
- next_sent_ = eraseSent(sent_packets_.template project<0>(it));
- // We removed the matching packet because of the timeout. It
- // means that there is no match anymore.
- packet_found = false;
- }
- ++collected_;
- }
- }
+ /// \brief Return total number of sent packets
+ ///
+ /// Method returns total number of sent packets.
+ ///
+ /// \return number of sent packets.
+ uint64_t getSentPacketsNum() const { return(sent_packets_num_); }
- if (!packet_found) {
- // If we are here, it means that both ordered lookup and
- // unordered lookup failed. Searched packet is not on the list.
- ++orphans_;
- return(boost::shared_ptr<T>());
- }
+ /// \brief Return total number of received packets
+ ///
+ /// Method returns total number of received packets.
+ ///
+ /// \return number of received packets.
+ uint64_t getRcvdPacketsNum() const { return(rcvd_packets_num_); }
- // Packet is matched so we count it. We don't count unmatched packets
- // as they are counted as orphans with a separate counter.
- ++rcvd_packets_num_;
- boost::shared_ptr<T> sent_packet(*next_sent_);
- // If packet was found, we assume it will be never searched
- // again. We want to delete this packet from the list to
- // improve performance of future searches.
- next_sent_ = eraseSent(next_sent_);
- return(sent_packet);
+ /// \brief Return number of dropped packets.
+ ///
+ /// Method returns number of dropped packets.
+ ///
+ /// \return number of dropped packets.
+ uint64_t getDroppedPacketsNum() const {
+ uint64_t drops = 0;
+ if (getSentPacketsNum() > getRcvdPacketsNum()) {
+ drops = getSentPacketsNum() - getRcvdPacketsNum();
}
+ return(drops);
+ }
- /// \brief Return minimum delay between sent and received packet.
- ///
- /// Method returns minimum delay between sent and received packet.
- ///
- /// \return minimum delay between packets.
- double getMinDelay() const { return(min_delay_); }
-
- /// \brief Return maximum delay between sent and received packet.
- ///
- /// Method returns maximum delay between sent and received packet.
- ///
- /// \return maximum delay between packets.
- double getMaxDelay() const { return(max_delay_); }
-
- /// \brief Return average packet delay.
- ///
- /// Method returns average packet delay. If no packets have been
- /// received for this exchange avg delay can't be calculated and
- /// thus method throws exception.
- ///
- /// \throw isc::InvalidOperation if no packets for this exchange
- /// have been received yet.
- /// \return average packet delay.
- double getAvgDelay() const {
- if (rcvd_packets_num_ == 0) {
- isc_throw(InvalidOperation, "no packets received");
- }
- return(sum_delay_ / rcvd_packets_num_);
- }
+ /// \brief Print main statistics for packet exchange.
+ ///
+ /// Method prints main statistics for particular exchange.
+ /// Statistics includes: number of sent and received packets,
+ /// number of dropped packets and number of orphans.
+ ///
+ /// \todo Currently the number of orphans is not displayed because
+ /// Reply messages received for Renew and Releases are counted as
+ /// orphans for the 4-way exchanges, which is wrong. We will need to
+ /// move the orphans counting out of the Statistics Manager so as
+ /// orphans counter is increased only if the particular message is
+ /// not identified as a response to any of the messages sent by
+ /// perfdhcp.
+ void printMainStats() const {
+ using namespace std;
+ auto sent = getSentPacketsNum();
+ auto drops = getDroppedPacketsNum();
+ double drops_ratio = 100.0 * static_cast<double>(drops) / static_cast<double>(sent);
+
+ cout << "sent packets: " << sent << endl
+ << "received packets: " << getRcvdPacketsNum() << endl
+ << "drops: " << drops << endl
+ << "drops ratio: " << drops_ratio << " %" << endl
+ << "orphans: " << getOrphans() << endl;
+ }
- /// \brief Return standard deviation of packet delay.
- ///
- /// Method returns standard deviation of packet delay. If no
- /// packets have been received for this exchange, the standard
- /// deviation can't be calculated and thus method throws
- /// exception.
- ///
- /// \throw isc::InvalidOperation if number of received packets
- /// for the exchange is equal to zero.
- /// \return standard deviation of packet delay.
- double getStdDevDelay() const {
- if (rcvd_packets_num_ == 0) {
- isc_throw(InvalidOperation, "no packets received");
- }
- return(sqrt(sum_delay_squared_ / rcvd_packets_num_ -
- getAvgDelay() * getAvgDelay()));
+ /// \brief Print round trip time packets statistics.
+ ///
+ /// Method prints round trip time packets statistics. Statistics
+ /// includes minimum packet delay, maximum packet delay, average
+ /// packet delay and standard deviation of delays. Packet delay
+ /// is a duration between sending a packet to server and receiving
+ /// response from server.
+ void printRTTStats() const {
+ using namespace std;
+ try {
+ cout << fixed << setprecision(3)
+ << "min delay: " << getMinDelay() * 1e3 << " ms" << endl
+ << "avg delay: " << getAvgDelay() * 1e3 << " ms" << endl
+ << "max delay: " << getMaxDelay() * 1e3 << " ms" << endl
+ << "std deviation: " << getStdDevDelay() * 1e3 << " ms"
+ << endl
+ << "collected packets: " << getCollectedNum() << endl;
+ } catch (const Exception&) {
+ cout << "Delay summary unavailable! No packets received." << endl;
}
+ }
- /// \brief Return number of orphan packets.
- ///
- /// Method returns number of received packets that had no matching
- /// sent packet. It is possible that such packet was late or not
- /// for us.
- ///
- /// \return number of orphan received packets.
- uint64_t getOrphans() const { return(orphans_); }
-
- /// \brief Return number of garbage collected packets.
- ///
- /// Method returns number of garbage collected timed out
- /// packets. Packet is assumed timed out when duration
- /// between sending it to server and receiving server's
- /// response is greater than value specified with -d<value>
- /// command line argument.
- ///
- /// \return number of garbage collected packets.
- uint64_t getCollectedNum() const { return(collected_); }
-
- /// \brief Return average unordered lookup set size.
- ///
- /// Method returns average unordered lookup set size.
- /// This value changes every time \ref ExchangeStats::matchPackets
- /// function performs unordered packet lookup.
- ///
- /// \throw isc::InvalidOperation if there have been no unordered
- /// lookups yet.
- /// \return average unordered lookup set size.
- double getAvgUnorderedLookupSetSize() const {
- if (unordered_lookups_ == 0) {
- isc_throw(InvalidOperation, "no unordered lookups");
- }
- return(static_cast<double>(unordered_lookup_size_sum_) /
- static_cast<double>(unordered_lookups_));
- }
+ //// \brief Print timestamps for sent and received packets.
+ ///
+ /// Method prints timestamps for all sent and received packets for
+ /// packet exchange. In order to run this method the packets
+ /// archiving mode has to be enabled during object constructions.
+ /// Otherwise sent packets are not stored during tests execution
+ /// and this method has no ability to get and print their timestamps.
+ ///
+ /// \throw isc::InvalidOperation if found packet with no timestamp or
+ /// if packets archive mode is disabled.
+ void printTimestamps();
- /// \brief Return number of unordered sent packets lookups
- ///
- /// Method returns number of unordered sent packet lookups.
- /// Unordered lookup is used when received packet was sent
- /// out of order by server - transaction id of received
- /// packet does not match transaction id of next sent packet.
- ///
- /// \return number of unordered lookups.
- uint64_t getUnorderedLookups() const { return(unordered_lookups_); }
-
- /// \brief Return number of ordered sent packets lookups
- ///
- /// Method returns number of ordered sent packet lookups.
- /// Ordered lookup is used when packets are received in the
- /// same order as they were sent to the server.
- /// If packets are skipped or received out of order, lookup
- /// function will use unordered lookup (with hash table).
- ///
- /// \return number of ordered lookups.
- uint64_t getOrderedLookups() const { return(ordered_lookups_); }
-
- /// \brief Return total number of sent packets
- ///
- /// Method returns total number of sent packets.
- ///
- /// \return number of sent packets.
- uint64_t getSentPacketsNum() const { return(sent_packets_num_); }
-
- /// \brief Return total number of received packets
- ///
- /// Method returns total number of received packets.
- ///
- /// \return number of received packets.
- uint64_t getRcvdPacketsNum() const { return(rcvd_packets_num_); }
-
- /// \brief Return number of dropped packets.
- ///
- /// Method returns number of dropped packets.
- ///
- /// \return number of dropped packets.
- uint64_t getDroppedPacketsNum() const {
- uint64_t drops = 0;
- if (getSentPacketsNum() > getRcvdPacketsNum()) {
- drops = getSentPacketsNum() - getRcvdPacketsNum();
- }
- return(drops);
- }
+ std::tuple<PktListIterator, PktListIterator> getSentPackets() {
+ return(std::make_tuple(sent_packets_.begin(), sent_packets_.end()));
+ }
- /// \brief Print main statistics for packet exchange.
- ///
- /// Method prints main statistics for particular exchange.
- /// Statistics includes: number of sent and received packets,
- /// number of dropped packets and number of orphans.
- ///
- /// \todo Currently the number of orphans is not displayed because
- /// Reply messages received for Renew and Releases are counted as
- /// orphans for the 4-way exchanges, which is wrong. We will need to
- /// move the orphans counting out of the Statistics Manager so as
- /// orphans counter is increased only if the particular message is
- /// not identified as a response to any of the messages sent by
- /// perfdhcp.
- void printMainStats() const {
- using namespace std;
- auto sent = getSentPacketsNum();
- auto drops = getDroppedPacketsNum();
- double drops_ratio = 100.0 * static_cast<double>(drops) / static_cast<double>(sent);
-
- cout << "sent packets: " << sent << endl
- << "received packets: " << getRcvdPacketsNum() << endl
- << "drops: " << drops << endl
- << "drops ratio: " << drops_ratio << " %" << endl;
- // << "orphans: " << getOrphans() << endl;
- }
- /// \brief Print round trip time packets statistics.
- ///
- /// Method prints round trip time packets statistics. Statistics
- /// includes minimum packet delay, maximum packet delay, average
- /// packet delay and standard deviation of delays. Packet delay
- /// is a duration between sending a packet to server and receiving
- /// response from server.
- void printRTTStats() const {
- using namespace std;
- try {
- cout << fixed << setprecision(3)
- << "min delay: " << getMinDelay() * 1e3 << " ms" << endl
- << "avg delay: " << getAvgDelay() * 1e3 << " ms" << endl
- << "max delay: " << getMaxDelay() * 1e3 << " ms" << endl
- << "std deviation: " << getStdDevDelay() * 1e3 << " ms"
- << endl
- << "collected packets: " << getCollectedNum() << endl;
- } catch (const Exception&) {
- cout << "Delay summary unavailable! No packets received." << endl;
- }
- }
+// Private stuff of ExchangeStats class
+private:
- //// \brief Print timestamps for sent and received packets.
- ///
- /// Method prints timestamps for all sent and received packets for
- /// packet exchange. In order to run this method the packets
- /// archiving mode has to be enabled during object constructions.
- /// Otherwise sent packets are not stored during tests execution
- /// and this method has no ability to get and print their timestamps.
- ///
- /// \throw isc::InvalidOperation if found packet with no timestamp or
- /// if packets archive mode is disabled.
- void printTimestamps() {
- // If archive mode is disabled there is no sense to proceed
- // because we don't have packets and their timestamps.
- if (!archive_enabled_) {
- isc_throw(isc::InvalidOperation,
- "packets archive mode is disabled");
- }
- if (rcvd_packets_num_ == 0) {
- std::cout << "Unavailable! No packets received." << std::endl;
- }
- // We will be using boost::posix_time extensively here
- using namespace boost::posix_time;
-
- // Iterate through all received packets.
- for (PktListIterator it = rcvd_packets_.begin();
- it != rcvd_packets_.end();
- ++it) {
- boost::shared_ptr<T> rcvd_packet = *it;
- PktListTransidHashIndex& idx =
- archived_packets_.template get<1>();
- std::pair<PktListTransidHashIterator,
- PktListTransidHashIterator> p =
- idx.equal_range(hashTransid(rcvd_packet));
- for (PktListTransidHashIterator it_archived = p.first;
- it_archived != p.second;
- ++it_archived) {
- if ((*it_archived)->getTransid() ==
- rcvd_packet->getTransid()) {
- boost::shared_ptr<T> sent_packet = *it_archived;
- // Get sent and received packet times.
- ptime sent_time = sent_packet->getTimestamp();
- ptime rcvd_time = rcvd_packet->getTimestamp();
- // All sent and received packets should have timestamps
- // set but if there is a bug somewhere and packet does
- // not have timestamp we want to catch this here.
- if (sent_time.is_not_a_date_time() ||
- rcvd_time.is_not_a_date_time()) {
- isc_throw(InvalidOperation,
- "packet time is not set");
- }
- // Calculate durations of packets from beginning of epoch.
- time_period sent_period(boot_time_, sent_time);
- time_period rcvd_period(boot_time_, rcvd_time);
- // Print timestamps for sent and received packet.
- std::cout << "sent / received: "
- << to_iso_string(sent_period.length())
- << " / "
- << to_iso_string(rcvd_period.length())
- << std::endl;
- break;
- }
- }
- }
- }
+ /// \brief Private default constructor.
+ ///
+ /// Default constructor is private because we want the client
+ /// class to specify exchange type explicitly.
+ ExchangeStats();
- private:
-
- /// \brief Private default constructor.
- ///
- /// Default constructor is private because we want the client
- /// class to specify exchange type explicitly.
- ExchangeStats();
-
- /// \brief Erase packet from the list of sent packets.
- ///
- /// Method erases packet from the list of sent packets.
- ///
- /// \param it iterator pointing to packet to be erased.
- /// \return iterator pointing to packet following erased
- /// packet or sent_packets_.end() if packet not found.
- PktListIterator eraseSent(const PktListIterator it) {
- if (archive_enabled_) {
- // We don't want to keep list of all sent packets
- // because it will affect packet lookup performance.
- // If packet is matched with received packet we
- // move it to list of archived packets. List of
- // archived packets may be used for diagnostics
- // when test is completed.
- archived_packets_.push_back(*it);
- }
- // get<0>() template returns sequential index to
- // container.
- return(sent_packets_.template get<0>().erase(it));
+ /// \brief Erase packet from the list of sent packets.
+ ///
+ /// Method erases packet from the list of sent packets.
+ ///
+ /// \param it iterator pointing to packet to be erased.
+ /// \return iterator pointing to packet following erased
+ /// packet or sent_packets_.end() if packet not found.
+ PktListIterator eraseSent(const PktListIterator it) {
+ if (archive_enabled_) {
+ // We don't want to keep list of all sent packets
+ // because it will affect packet lookup performance.
+ // If packet is matched with received packet we
+ // move it to list of archived packets. List of
+ // archived packets may be used for diagnostics
+ // when test is completed.
+ archived_packets_.push_back(*it);
}
+ // get<0>() template returns sequential index to
+ // container.
+ return(sent_packets_.template get<0>().erase(it));
+ }
- ExchangeType xchg_type_; ///< Packet exchange type.
- PktList sent_packets_; ///< List of sent packets.
-
- /// Iterator pointing to the packet on sent list which will most
- /// likely match next received packet. This is based on the
- /// assumption that server responds in order to incoming packets.
- PktListIterator next_sent_;
-
- PktList rcvd_packets_; ///< List of received packets.
-
- /// List of archived packets. All sent packets that have
- /// been matched with received packet are moved to this
- /// list for diagnostics purposes.
- PktList archived_packets_;
-
- /// Indicates all packets have to be preserved after matching.
- /// By default this is disabled which means that when received
- /// packet is matched with sent packet both are deleted. This
- /// is important when test is executed for extended period of
- /// time and high memory usage might be the issue.
- /// When timestamps listing is specified from the command line
- /// (using diagnostics selector), all packets have to be preserved
- /// so as the printing method may read their timestamps and
- /// print it to user. In such usage model it will be rare to
- /// run test for extended period of time so it should be fine
- /// to keep all packets archived throughout the test.
- bool archive_enabled_;
-
- /// Maximum time elapsed between sending and receiving packet
- /// before packet is assumed dropped.
- double drop_time_;
-
- double min_delay_; ///< Minimum delay between sent
- ///< and received packets.
- double max_delay_; ///< Maximum delay between sent
- ///< and received packets.
- double sum_delay_; ///< Sum of delays between sent
- ///< and received packets.
- double sum_delay_squared_; ///< Squared sum of delays between
- ///< sent and received packets.
-
- uint64_t orphans_; ///< Number of orphan received packets.
-
- uint64_t collected_; ///< Number of garbage collected packets.
-
- /// Sum of unordered lookup sets. Needed to calculate mean size of
- /// lookup set. It is desired that number of unordered lookups is
- /// minimal for performance reasons. Tracking number of lookups and
- /// mean size of the lookup set should give idea of packets search
- /// complexity.
- uint64_t unordered_lookup_size_sum_;
-
- uint64_t unordered_lookups_; ///< Number of unordered sent packets
- ///< lookups.
- uint64_t ordered_lookups_; ///< Number of ordered sent packets
- ///< lookups.
-
- uint64_t sent_packets_num_; ///< Total number of sent packets.
- uint64_t rcvd_packets_num_; ///< Total number of received packets.
- boost::posix_time::ptime boot_time_; ///< Time when test is started.
- };
-
- /// Pointer to ExchangeStats.
- typedef boost::shared_ptr<ExchangeStats> ExchangeStatsPtr;
- /// Map containing all specified exchange types.
- typedef typename std::map<ExchangeType, ExchangeStatsPtr> ExchangesMap;
- /// Iterator pointing to \ref ExchangesMap
- typedef typename ExchangesMap::const_iterator ExchangesMapIterator;
- /// Map containing custom counters.
- typedef typename std::map<std::string, CustomCounterPtr> CustomCountersMap;
- /// Iterator for \ref CustomCountersMap.
- typedef typename CustomCountersMap::const_iterator CustomCountersMapIterator;
+ ExchangeType xchg_type_; ///< Packet exchange type.
+ PktList sent_packets_; ///< List of sent packets.
+
+ /// Iterator pointing to the packet on sent list which will most
+ /// likely match next received packet. This is based on the
+ /// assumption that server responds in order to incoming packets.
+ PktListIterator next_sent_;
+
+ PktList rcvd_packets_; ///< List of received packets.
+
+ /// List of archived packets. All sent packets that have
+ /// been matched with received packet are moved to this
+ /// list for diagnostics purposes.
+ PktList archived_packets_;
+
+ /// Indicates all packets have to be preserved after matching.
+ /// By default this is disabled which means that when received
+ /// packet is matched with sent packet both are deleted. This
+ /// is important when test is executed for extended period of
+ /// time and high memory usage might be the issue.
+ /// When timestamps listing is specified from the command line
+ /// (using diagnostics selector), all packets have to be preserved
+ /// so as the printing method may read their timestamps and
+ /// print it to user. In such usage model it will be rare to
+ /// run test for extended period of time so it should be fine
+ /// to keep all packets archived throughout the test.
+ bool archive_enabled_;
+
+ /// Maximum time elapsed between sending and receiving packet
+ /// before packet is assumed dropped.
+ double drop_time_;
+
+ double min_delay_; ///< Minimum delay between sent
+ ///< and received packets.
+ double max_delay_; ///< Maximum delay between sent
+ ///< and received packets.
+ double sum_delay_; ///< Sum of delays between sent
+ ///< and received packets.
+ double sum_delay_squared_; ///< Squared sum of delays between
+ ///< sent and received packets.
+
+ uint64_t orphans_; ///< Number of orphan received packets.
+
+ uint64_t collected_; ///< Number of garbage collected packets.
+
+ /// Sum of unordered lookup sets. Needed to calculate mean size of
+ /// lookup set. It is desired that number of unordered lookups is
+ /// minimal for performance reasons. Tracking number of lookups and
+ /// mean size of the lookup set should give idea of packets search
+ /// complexity.
+ uint64_t unordered_lookup_size_sum_;
+
+ uint64_t unordered_lookups_; ///< Number of unordered sent packets
+ ///< lookups.
+ uint64_t ordered_lookups_; ///< Number of ordered sent packets
+ ///< lookups.
+
+ uint64_t sent_packets_num_; ///< Total number of sent packets.
+ uint64_t rcvd_packets_num_; ///< Total number of received packets.
+ boost::posix_time::ptime boot_time_; ///< Time when test is started.
+
+ bool ignore_timestamp_reorder_;
+};
+
+/// Pointer to ExchangeStats.
+typedef boost::shared_ptr<ExchangeStats> ExchangeStatsPtr;
+
+/// Map containing all specified exchange types.
+typedef typename std::map<ExchangeType, ExchangeStatsPtr> ExchangesMap;
+/// Iterator pointing to \ref ExchangesMap
+typedef typename ExchangesMap::const_iterator ExchangesMapIterator;
+
+
+/// \brief Statistics Manager
+///
+/// This class template is a storage for various performance statistics
+/// collected during performance tests execution with perfdhcp tool.
+///
+/// Statistics Manager holds lists of sent and received packets and
+/// groups them into exchanges. For example: DHCPDISCOVER message and
+/// corresponding DHCPOFFER messages belong to one exchange, DHCPREQUEST
+/// and corresponding DHCPACK message belong to another exchange etc.
+/// In order to update statistics for a particular exchange type, client
+/// class passes sent and received packets. Internally, Statistics Manager
+/// tries to match transaction id of received packet with sent packet
+/// stored on the list of sent packets. When packets are matched the
+/// round trip time can be calculated.
+///
+class StatsMgr : public boost::noncopyable {
+public:
/// \brief Constructor.
///
/// This constructor by default disables packets archiving mode.
@@ -893,14 +653,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.
- ///
- /// \param archive_enabled true indicates that packets
- /// archive mode is enabled.
- StatsMgr(const bool archive_enabled = false) :
- exchanges_(),
- archive_enabled_(archive_enabled),
- boot_time_(boost::posix_time::microsec_clock::universal_time()) {
- }
+ StatsMgr(bool ignore_timestamp_reorder);
/// \brief Specify new exchange type.
///
@@ -921,7 +674,8 @@ public:
ExchangeStatsPtr(new ExchangeStats(xchg_type,
drop_time,
archive_enabled_,
- boot_time_));
+ boot_time_,
+ ignore_timestamp_reorder_));
}
/// \brief Check if the exchange type has been specified.
@@ -1010,7 +764,7 @@ public:
/// \throw isc::BadValue if invalid exchange type specified or
/// packet is null.
void passSentPacket(const ExchangeType xchg_type,
- const boost::shared_ptr<T>& packet) {
+ const dhcp::PktPtr& packet) {
ExchangeStatsPtr xchg_stats = getExchangeStats(xchg_type);
xchg_stats->appendSent(packet);
}
@@ -1028,12 +782,11 @@ public:
/// or packet is null.
/// \throw isc::Unexpected if corresponding packet was not
/// found on the list of sent packets.
- boost::shared_ptr<T>
+ dhcp::PktPtr
passRcvdPacket(const ExchangeType xchg_type,
- const boost::shared_ptr<T>& packet) {
+ const dhcp::PktPtr& packet) {
ExchangeStatsPtr xchg_stats = getExchangeStats(xchg_type);
- boost::shared_ptr<T> sent_packet
- = xchg_stats->matchPackets(packet);
+ dhcp::PktPtr sent_packet = xchg_stats->matchPackets(packet);
if (sent_packet) {
xchg_stats->updateDelays(sent_packet, packet);
@@ -1219,35 +972,7 @@ public:
return test_period;
}
- /// \brief Return name of the exchange.
- ///
- /// Method returns name of the specified exchange type.
- /// This function is mainly for logging purposes.
- ///
- /// \param xchg_type exchange type.
- /// \return string representing name of the exchange.
- static std::string exchangeToString(ExchangeType xchg_type) {
- switch(xchg_type) {
- case XCHG_DO:
- return("DISCOVER-OFFER");
- case XCHG_RA:
- return("REQUEST-ACK");
- case XCHG_RNA:
- return("REQUEST-ACK (renewal)");
- case XCHG_SA:
- return("SOLICIT-ADVERTISE");
- case XCHG_RR:
- return("REQUEST-REPLY");
- case XCHG_RN:
- return("RENEW-REPLY");
- case XCHG_RL:
- return("RELEASE-REPLY");
- default:
- return("Unknown exchange type");
- }
- }
-
- /// \brief Print statistics counters for all exchange types.
+ /// \brief Print statistics counters for all exchange types.
///
/// Method prints statistics for all exchange types.
/// Statistics includes:
@@ -1260,7 +985,7 @@ public:
///
/// \throw isc::InvalidOperation if no exchange type added to
/// track statistics.
- void printStats() const {
+ void printStats() const {
if (exchanges_.empty()) {
isc_throw(isc::InvalidOperation,
"no exchange type added for tracking");
@@ -1269,7 +994,7 @@ public:
it != exchanges_.end();
++it) {
ExchangeStatsPtr xchg_stats = it->second;
- std::cout << "***Statistics for: " << exchangeToString(it->first)
+ std::cout << "***Statistics for: " << it->first
<< "***" << std::endl;
xchg_stats->printMainStats();
std::cout << std::endl;
@@ -1325,7 +1050,7 @@ public:
++it) {
ExchangeStatsPtr xchg_stats = it->second;
std::cout << "***Timestamps for packets: "
- << exchangeToString(it->first)
+ << it->first
<< "***" << std::endl;
xchg_stats->printTimestamps();
std::cout << std::endl;
@@ -1351,6 +1076,12 @@ public:
}
}
+ std::tuple<typename ExchangeStats::PktListIterator, typename ExchangeStats::PktListIterator> getSentPackets(const ExchangeType xchg_type) const {
+ ExchangeStatsPtr xchg_stats = getExchangeStats(xchg_type);
+ std::tuple<typename ExchangeStats::PktListIterator, typename ExchangeStats::PktListIterator> sent_packets_its = xchg_stats->getSentPackets();
+ return(sent_packets_its);
+ }
+
private:
/// \brief Return exchange stats object for given exchange type
@@ -1383,18 +1114,13 @@ private:
bool archive_enabled_;
boost::posix_time::ptime boot_time_; ///< Time when test is started.
+
+ bool ignore_timestamp_reorder_;
};
-/// Statistics Manager for DHCPv4.
-typedef StatsMgr<dhcp::Pkt4> StatsMgr4;
/// Pointer to Statistics Manager for DHCPv4;
-typedef boost::shared_ptr<StatsMgr4> StatsMgr4Ptr;
-/// Statistics Manager for DHCPv6.
-typedef StatsMgr<dhcp::Pkt6> StatsMgr6;
-/// Pointer to Statistics Manager for DHCPv6.
-typedef boost::shared_ptr<StatsMgr6> StatsMgr6Ptr;
-/// Packet exchange type.
-typedef StatsMgr<>::ExchangeType ExchangeType;
+typedef boost::shared_ptr<StatsMgr> StatsMgrPtr;
+
} // namespace perfdhcp
} // namespace isc
diff --git a/src/bin/perfdhcp/test_control.cc b/src/bin/perfdhcp/test_control.cc
index 55aaacbc2d..a37394c78c 100644
--- a/src/bin/perfdhcp/test_control.cc
+++ b/src/bin/perfdhcp/test_control.cc
@@ -13,7 +13,6 @@
#include <perfdhcp/perf_pkt6.h>
#include <exceptions/exceptions.h>
-#include <asiolink/io_address.h>
#include <dhcp/libdhcp++.h>
#include <dhcp/iface_mgr.h>
#include <dhcp/dhcp4.h>
@@ -43,19 +42,6 @@ namespace perfdhcp {
bool TestControl::interrupted_ = false;
-/// \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) {
- std::string diags(CommandOptions::instance().getDiags());
- if (diags.find(diag) != std::string::npos) {
- return (true);
- }
- return (false);
-}
-
bool
TestControl::waitToExit() const {
static ptime exit_time = ptime(not_a_date_time);
@@ -97,27 +83,16 @@ TestControl::haveAllPacketsBeenReceived() const {
}
if (ipversion == 4) {
- responses = stats_mgr4_->getRcvdPacketsNum(StatsMgr4::XCHG_DO) +
- stats_mgr4_->getRcvdPacketsNum(StatsMgr4::XCHG_RA);
+ responses = stats_mgr_.getRcvdPacketsNum(ExchangeType::DO) +
+ stats_mgr_.getRcvdPacketsNum(ExchangeType::RA);
} else {
- responses = stats_mgr6_->getRcvdPacketsNum(StatsMgr6::XCHG_SA) +
- stats_mgr6_->getRcvdPacketsNum(StatsMgr6::XCHG_RR);
+ responses = stats_mgr_.getRcvdPacketsNum(ExchangeType::SA) +
+ stats_mgr_.getRcvdPacketsNum(ExchangeType::RR);
}
return (responses == requests);
}
-TestControl&
-TestControl::instance() {
- static TestControl test_control;
- return (test_control);
-}
-
-TestControl::TestControl()
- : number_generator_(0, CommandOptions::instance().getMacsFromFile().size()) {
- reset();
-}
-
void
TestControl::cleanCachedPackets() {
CommandOptions& options = CommandOptions::instance();
@@ -187,161 +162,6 @@ TestControl::byte2Hex(const uint8_t b) const {
return (stream.str());
}
-bool
-TestControl::checkExitConditions() const {
- if (interrupted_) {
- return (true);
- }
- CommandOptions& options = CommandOptions::instance();
- bool test_period_reached = false;
- // Check if test period passed.
- if (options.getPeriod() != 0) {
- if (options.getIpVersion() == 4) {
- time_period period(stats_mgr4_->getTestPeriod());
- if (period.length().total_seconds() >= options.getPeriod()) {
- test_period_reached = true;
- }
- } else if (options.getIpVersion() == 6) {
- time_period period = stats_mgr6_->getTestPeriod();
- if (period.length().total_seconds() >= options.getPeriod()) {
- test_period_reached = true;
- }
- }
- }
- if (test_period_reached) {
- if (testDiags('e')) {
- std::cout << "reached test-period." << std::endl;
- }
- if (!waitToExit()) {
- return true;
- }
- }
-
- bool max_requests = false;
- // Check if we reached maximum number of DISCOVER/SOLICIT sent.
- if (options.getNumRequests().size() > 0) {
- if (options.getIpVersion() == 4) {
- if (getSentPacketsNum(StatsMgr4::XCHG_DO) >=
- options.getNumRequests()[0]) {
- max_requests = true;
- }
- } else if (options.getIpVersion() == 6) {
- if (stats_mgr6_->getSentPacketsNum(StatsMgr6::XCHG_SA) >=
- options.getNumRequests()[0]) {
- max_requests = true;
- }
- }
- }
- // Check if we reached maximum number REQUEST packets.
- if (options.getNumRequests().size() > 1) {
- if (options.getIpVersion() == 4) {
- if (stats_mgr4_->getSentPacketsNum(StatsMgr4::XCHG_RA) >=
- options.getNumRequests()[1]) {
- max_requests = true;
- }
- } else if (options.getIpVersion() == 6) {
- if (stats_mgr6_->getSentPacketsNum(StatsMgr6::XCHG_RR) >=
- options.getNumRequests()[1]) {
- max_requests = true;
- }
- }
- }
- if (max_requests) {
- if (testDiags('e')) {
- std::cout << "Reached max requests limit." << std::endl;
- }
- if (!waitToExit()) {
- return true;
- }
- }
-
- // Check if we reached maximum number of drops of OFFER/ADVERTISE packets.
- bool max_drops = false;
- if (options.getMaxDrop().size() > 0) {
- if (options.getIpVersion() == 4) {
- if (stats_mgr4_->getDroppedPacketsNum(StatsMgr4::XCHG_DO) >=
- options.getMaxDrop()[0]) {
- max_drops = true;
- }
- } else if (options.getIpVersion() == 6) {
- if (stats_mgr6_->getDroppedPacketsNum(StatsMgr6::XCHG_SA) >=
- 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 (stats_mgr4_->getDroppedPacketsNum(StatsMgr4::XCHG_RA) >=
- options.getMaxDrop()[1]) {
- max_drops = true;
- }
- } else if (options.getIpVersion() == 6) {
- if (stats_mgr6_->getDroppedPacketsNum(StatsMgr6::XCHG_RR) >=
- options.getMaxDrop()[1]) {
- max_drops = true;
- }
- }
- }
- if (max_drops) {
- if (testDiags('e')) {
- std::cout << "Reached maximum drops number." << std::endl;
- }
- if (!waitToExit()) {
- return true;
- }
- }
-
- // Check if we reached maximum drops percentage of OFFER/ADVERTISE packets.
- bool max_pdrops = false;
- if (options.getMaxDropPercentage().size() > 0) {
- if (options.getIpVersion() == 4) {
- if ((stats_mgr4_->getSentPacketsNum(StatsMgr4::XCHG_DO) > 10) &&
- ((100. * stats_mgr4_->getDroppedPacketsNum(StatsMgr4::XCHG_DO) /
- stats_mgr4_->getSentPacketsNum(StatsMgr4::XCHG_DO)) >=
- options.getMaxDropPercentage()[0])) {
- max_pdrops = true;
-
- }
- } else if (options.getIpVersion() == 6) {
- if ((stats_mgr6_->getSentPacketsNum(StatsMgr6::XCHG_SA) > 10) &&
- ((100. * stats_mgr6_->getDroppedPacketsNum(StatsMgr6::XCHG_SA) /
- stats_mgr6_->getSentPacketsNum(StatsMgr6::XCHG_SA)) >=
- options.getMaxDropPercentage()[0])) {
- max_pdrops = true;
- }
- }
- }
- // Check if we reached maximum drops percentage of ACK/REPLY packets.
- if (options.getMaxDropPercentage().size() > 1) {
- if (options.getIpVersion() == 4) {
- if ((stats_mgr4_->getSentPacketsNum(StatsMgr4::XCHG_RA) > 10) &&
- ((100. * stats_mgr4_->getDroppedPacketsNum(StatsMgr4::XCHG_RA) /
- stats_mgr4_->getSentPacketsNum(StatsMgr4::XCHG_RA)) >=
- options.getMaxDropPercentage()[1])) {
- max_pdrops = true;
- }
- } else if (options.getIpVersion() == 6) {
- if ((stats_mgr6_->getSentPacketsNum(StatsMgr6::XCHG_RR) > 10) &&
- ((100. * stats_mgr6_->getDroppedPacketsNum(StatsMgr6::XCHG_RR) /
- stats_mgr6_->getSentPacketsNum(StatsMgr6::XCHG_RR)) >=
- options.getMaxDropPercentage()[1])) {
- max_pdrops = true;
- }
- }
- }
- if (max_pdrops) {
- if (testDiags('e')) {
- std::cout << "Reached maximum percentage of drops." << std::endl;
- }
- if (!waitToExit()) {
- return true;
- }
- }
- return (false);
-}
-
Pkt4Ptr
TestControl::createRequestFromAck(const dhcp::Pkt4Ptr& ack) {
if (!ack) {
@@ -638,26 +458,6 @@ TestControl::getRequestedIpOffset() const {
return (rip_offset);
}
-uint64_t
-TestControl::getRcvdPacketsNum(ExchangeType xchg_type) const {
- uint8_t ip_version = CommandOptions::instance().getIpVersion();
- if (ip_version == 4) {
- return (stats_mgr4_->getRcvdPacketsNum(xchg_type));
- }
- return (stats_mgr6_->
- getRcvdPacketsNum(static_cast<StatsMgr6::ExchangeType>(xchg_type)));
-}
-
-uint64_t
-TestControl::getSentPacketsNum(ExchangeType xchg_type) const {
- uint8_t ip_version = CommandOptions::instance().getIpVersion();
- if (ip_version == 4) {
- return (stats_mgr4_->getSentPacketsNum(xchg_type));
- }
- return (stats_mgr6_->
- getSentPacketsNum(static_cast<StatsMgr6::ExchangeType>(xchg_type)));
-}
-
int
TestControl::getServerIdOffset() const {
int srvid_offset = CommandOptions::instance().getIpVersion() == 4 ?
@@ -713,153 +513,7 @@ TestControl::initPacketTemplates() {
}
void
-TestControl::initializeStatsMgr() {
- 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.
- const bool archive_mode = testDiags('t') ? true : false;
- if (options.getIpVersion() == 4) {
- stats_mgr4_.reset();
- stats_mgr4_ = StatsMgr4Ptr(new StatsMgr4(archive_mode));
- stats_mgr4_->addExchangeStats(StatsMgr4::XCHG_DO,
- options.getDropTime()[0]);
- if (options.getExchangeMode() == CommandOptions::DORA_SARR) {
- stats_mgr4_->addExchangeStats(StatsMgr4::XCHG_RA,
- options.getDropTime()[1]);
- }
- if (options.getRenewRate() != 0) {
- stats_mgr4_->addExchangeStats(StatsMgr4::XCHG_RNA);
- }
-
- } else if (options.getIpVersion() == 6) {
- stats_mgr6_.reset();
- stats_mgr6_ = StatsMgr6Ptr(new StatsMgr6(archive_mode));
- stats_mgr6_->addExchangeStats(StatsMgr6::XCHG_SA,
- options.getDropTime()[0]);
- if (options.getExchangeMode() == CommandOptions::DORA_SARR) {
- stats_mgr6_->addExchangeStats(StatsMgr6::XCHG_RR,
- options.getDropTime()[1]);
- }
- if (options.getRenewRate() != 0) {
- stats_mgr6_->addExchangeStats(StatsMgr6::XCHG_RN);
- }
- if (options.getReleaseRate() != 0) {
- stats_mgr6_->addExchangeStats(StatsMgr6::XCHG_RL);
- }
- }
- if (testDiags('i')) {
- if (options.getIpVersion() == 4) {
- stats_mgr4_->addCustomCounter("shortwait", "Short waits for packets");
- } else if (options.getIpVersion() == 6) {
- stats_mgr6_->addCustomCounter("shortwait", "Short waits for packets");
- }
- }
-}
-
-int
-TestControl::openSocket() const {
- CommandOptions& options = CommandOptions::instance();
- std::string localname = options.getLocalName();
- std::string servername = options.getServerName();
- uint16_t port = options.getLocalPort();
- int sock = 0;
-
- uint8_t family = (options.getIpVersion() == 6) ? AF_INET6 : AF_INET;
- IOAddress remoteaddr(servername);
-
- // Check for mismatch between IP option and server address
- if (family != remoteaddr.getFamily()) {
- isc_throw(InvalidParameter,
- "Values for IP version: " <<
- static_cast<unsigned int>(options.getIpVersion()) <<
- " and server address: " << servername << " are mismatched.");
- }
-
- if (port == 0) {
- if (family == AF_INET6) {
- // need server port (547) because the server is acting as a relay agent
- port = DHCP6_CLIENT_PORT;
- // if acting as a relay agent change port.
- if (options.isUseRelayedV6()) {
- port = DHCP6_SERVER_PORT;
- }
- } else if (options.getIpVersion() == 4) {
- port = 67; /// @todo: find out why port 68 is wrong here.
- }
- }
-
- // Local name is specified along with '-l' option.
- // It may point to interface name or local address.
- if (!localname.empty()) {
- // CommandOptions should be already aware whether local name
- // is interface name or address because it uses IfaceMgr to
- // scan interfaces and get's their names.
- if (options.isInterface()) {
- sock = IfaceMgr::instance().openSocketFromIface(localname,
- port,
- family);
- } else {
- IOAddress localaddr(localname);
- sock = IfaceMgr::instance().openSocketFromAddress(localaddr,
- port);
- }
- } else if (!servername.empty()) {
- // If only server name is given we will need to try to resolve
- // the local address to bind socket to based on remote address.
- sock = IfaceMgr::instance().openSocketFromRemoteAddress(remoteaddr,
- port);
- }
- if (sock <= 0) {
- isc_throw(BadValue, "unable to open socket to communicate with "
- "DHCP server");
- }
-
- // IfaceMgr does not set broadcast option on the socket. We rely
- // on CommandOptions object to find out if socket has to have
- // broadcast enabled.
- if ((options.getIpVersion() == 4) && options.isBroadcast()) {
- int broadcast_enable = 1;
- int ret = setsockopt(sock, SOL_SOCKET, SO_BROADCAST,
- &broadcast_enable, sizeof(broadcast_enable));
- if (ret < 0) {
- isc_throw(InvalidOperation,
- "unable to set broadcast option on the socket");
- }
- } else if (options.getIpVersion() == 6) {
- // If remote address is multicast we need to enable it on
- // the socket that has been created.
- if (remoteaddr.isV6Multicast()) {
- int hops = 1;
- int ret = setsockopt(sock, IPPROTO_IPV6, IPV6_MULTICAST_HOPS,
- &hops, sizeof(hops));
- // If user specified interface name with '-l' the
- // IPV6_MULTICAST_IF has to be set.
- if ((ret >= 0) && options.isInterface()) {
- IfacePtr iface =
- IfaceMgr::instance().getIface(options.getLocalName());
- if (iface == NULL) {
- isc_throw(Unexpected, "unknown interface "
- << options.getLocalName());
- }
- int idx = iface->getIndex();
- ret = setsockopt(sock, IPPROTO_IPV6, IPV6_MULTICAST_IF,
- &idx, sizeof(idx));
- }
- if (ret < 0) {
- isc_throw(InvalidOperation,
- "unable to enable multicast on socket " << sock
- << ". errno = " << errno);
- }
- }
- }
-
- return (sock);
-}
-
-void
-TestControl::sendPackets(const PerfSocket& socket,
- const uint64_t packets_num,
+TestControl::sendPackets(const uint64_t packets_num,
const bool preload /* = false */) {
CommandOptions& options = CommandOptions::instance();
for (uint64_t i = packets_num; i > 0; --i) {
@@ -867,31 +521,30 @@ TestControl::sendPackets(const PerfSocket& socket,
// No template packets means that no -T option was specified.
// We have to build packets ourselves.
if (template_buffers_.empty()) {
- sendDiscover4(socket, preload);
+ sendDiscover4(preload);
} else {
/// @todo add defines for packet type index that can be
/// used to access template_buffers_.
- sendDiscover4(socket, template_buffers_[0], preload);
+ sendDiscover4(template_buffers_[0], preload);
}
} else {
// No template packets means that no -T option was specified.
// We have to build packets ourselves.
if (template_buffers_.empty()) {
- sendSolicit6(socket, preload);
+ sendSolicit6(preload);
} else {
/// @todo add defines for packet type index that can be
/// used to access template_buffers_.
- sendSolicit6(socket, template_buffers_[0], preload);
+ sendSolicit6(template_buffers_[0], preload);
}
}
}
}
uint64_t
-TestControl::sendMultipleRequests(const PerfSocket& socket,
- const uint64_t msg_num) {
+TestControl::sendMultipleRequests(const uint64_t msg_num) {
for (uint64_t i = 0; i < msg_num; ++i) {
- if (!sendRequestFromAck(socket)) {
+ if (!sendRequestFromAck()) {
return (i);
}
}
@@ -899,11 +552,10 @@ TestControl::sendMultipleRequests(const PerfSocket& socket,
}
uint64_t
-TestControl::sendMultipleMessages6(const PerfSocket& socket,
- const uint32_t msg_type,
+TestControl::sendMultipleMessages6(const uint32_t msg_type,
const uint64_t msg_num) {
for (uint64_t i = 0; i < msg_num; ++i) {
- if (!sendMessageFromReply(msg_type, socket)) {
+ if (!sendMessageFromReply(msg_type)) {
return (i);
}
}
@@ -999,28 +651,26 @@ 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) {
- StatsMgr4::ExchangeType xchg_type =
+ xchg_type =
options.getExchangeMode() == CommandOptions::DO_SA ?
- StatsMgr4::XCHG_DO : StatsMgr4::XCHG_RA;
- if (xchg_type == StatsMgr4::XCHG_DO) {
+ ExchangeType::DO : ExchangeType::RA;
+ if (xchg_type == ExchangeType::DO) {
exchange_name = "DISCOVER-OFFER";
}
- double duration =
- stats_mgr4_->getTestPeriod().length().total_nanoseconds() / 1e9;
- rate = stats_mgr4_->getRcvdPacketsNum(xchg_type) / duration;
} else if (options.getIpVersion() == 6) {
- StatsMgr6::ExchangeType xchg_type =
+ xchg_type =
options.getExchangeMode() == CommandOptions::DO_SA ?
- StatsMgr6::XCHG_SA : StatsMgr6::XCHG_RR;
- if (xchg_type == StatsMgr6::XCHG_SA) {
+ ExchangeType::SA : ExchangeType::RR;
+ if (xchg_type == ExchangeType::SA) {
exchange_name = options.isRapidCommit() ? "Solicit-Reply" :
"Solicit-Advertise";
}
- double duration =
- stats_mgr6_->getTestPeriod().length().total_nanoseconds() / 1e9;
- rate = stats_mgr6_->getRcvdPacketsNum(xchg_type) / duration;
}
+ double duration =
+ stats_mgr_.getTestPeriod().length().total_nanoseconds() / 1e9;
+ rate = stats_mgr_.getRcvdPacketsNum(xchg_type) / duration;
std::ostringstream s;
s << "***Rate statistics***" << std::endl;
s << "Rate: " << rate << " " << exchange_name << "/second";
@@ -1038,11 +688,7 @@ TestControl::printIntermediateStats() {
ptime now = microsec_clock::universal_time();
time_period time_since_report(last_report_, now);
if (time_since_report.length().total_seconds() >= delay) {
- if (options.getIpVersion() == 4) {
- stats_mgr4_->printIntermediateStats();
- } else if (options.getIpVersion() == 6) {
- stats_mgr6_->printIntermediateStats();
- }
+ stats_mgr_.printIntermediateStats();
last_report_ = now;
}
}
@@ -1050,25 +696,9 @@ TestControl::printIntermediateStats() {
void
TestControl::printStats() const {
printRate();
- CommandOptions& options = CommandOptions::instance();
- if (options.getIpVersion() == 4) {
- if (!stats_mgr4_) {
- isc_throw(InvalidOperation, "Statistics Manager for DHCPv4 "
- "hasn't been initialized");
- }
- stats_mgr4_->printStats();
- if (testDiags('i')) {
- stats_mgr4_->printCustomCounters();
- }
- } else if (options.getIpVersion() == 6) {
- if (!stats_mgr6_) {
- isc_throw(InvalidOperation, "Statistics Manager for DHCPv6 "
- "hasn't been initialized");
- }
- stats_mgr6_->printStats();
- if (testDiags('i')) {
- stats_mgr6_->printCustomCounters();
- }
+ stats_mgr_.printStats();
+ if (testDiags('i')) {
+ stats_mgr_.printCustomCounters();
}
}
@@ -1137,32 +767,31 @@ TestControl::readPacketTemplate(const std::string& file_name) {
}
void
-TestControl::processReceivedPacket4(const PerfSocket& socket,
- const Pkt4Ptr& pkt4) {
+TestControl::processReceivedPacket4(const Pkt4Ptr& pkt4) {
if (pkt4->getType() == DHCPOFFER) {
- Pkt4Ptr discover_pkt4(stats_mgr4_->passRcvdPacket(StatsMgr4::XCHG_DO,
- pkt4));
+ PktPtr pkt = stats_mgr_.passRcvdPacket(ExchangeType::DO, pkt4);
+ Pkt4Ptr discover_pkt4(boost::dynamic_pointer_cast<Pkt4>(pkt));
CommandOptions::ExchangeMode xchg_mode =
CommandOptions::instance().getExchangeMode();
if ((xchg_mode == CommandOptions::DORA_SARR) && discover_pkt4) {
if (template_buffers_.size() < 2) {
- sendRequest4(socket, discover_pkt4, pkt4);
+ sendRequest4(discover_pkt4, pkt4);
} else {
/// @todo add defines for packet type index that can be
/// used to access template_buffers_.
- sendRequest4(socket, template_buffers_[1], discover_pkt4, pkt4);
+ sendRequest4(template_buffers_[1], discover_pkt4, pkt4);
}
}
} else if (pkt4->getType() == DHCPACK) {
// If received message is DHCPACK, we have to check if this is
// a response to 4-way exchange. We'll match this packet with
// a DHCPREQUEST sent as part of the 4-way exchanges.
- if (stats_mgr4_->passRcvdPacket(StatsMgr4::XCHG_RA, pkt4)) {
+ if (stats_mgr_.passRcvdPacket(ExchangeType::RA, pkt4)) {
// The DHCPACK belongs to DHCPREQUEST-DHCPACK exchange type.
// So, we may need to keep this DHCPACK in the storage if renews.
// Note that, DHCPACK messages hold the information about
// leases assigned. We use this information to renew.
- if (stats_mgr4_->hasExchangeStats(StatsMgr4::XCHG_RNA)) {
+ if (stats_mgr_.hasExchangeStats(ExchangeType::RNA)) {
// Renew messages are sent, because StatsMgr has the
// specific exchange type specified. Let's append the DHCPACK.
// message to a storage
@@ -1173,19 +802,18 @@ TestControl::processReceivedPacket4(const PerfSocket& socket,
// renewal. In this case we first check if StatsMgr has exchange type
// for renew specified, and if it has, if there is a corresponding
// renew message for the received DHCPACK.
- } else if (stats_mgr4_->hasExchangeStats(StatsMgr4::XCHG_RNA)) {
- stats_mgr4_->passRcvdPacket(StatsMgr4::XCHG_RNA, pkt4);
+ } else if (stats_mgr_.hasExchangeStats(ExchangeType::RNA)) {
+ stats_mgr_.passRcvdPacket(ExchangeType::RNA, pkt4);
}
}
}
void
-TestControl::processReceivedPacket6(const PerfSocket& socket,
- const Pkt6Ptr& pkt6) {
+TestControl::processReceivedPacket6(const Pkt6Ptr& pkt6) {
uint8_t packet_type = pkt6->getType();
if (packet_type == DHCPV6_ADVERTISE) {
- Pkt6Ptr solicit_pkt6(stats_mgr6_->passRcvdPacket(StatsMgr6::XCHG_SA,
- pkt6));
+ PktPtr pkt = stats_mgr_.passRcvdPacket(ExchangeType::SA, pkt6);
+ Pkt6Ptr solicit_pkt6(boost::dynamic_pointer_cast<Pkt6>(pkt));
CommandOptions::ExchangeMode xchg_mode =
CommandOptions::instance().getExchangeMode();
if ((xchg_mode == CommandOptions::DORA_SARR) && solicit_pkt6) {
@@ -1193,11 +821,11 @@ TestControl::processReceivedPacket6(const PerfSocket& socket,
/// We might want to check if STATUS_CODE option is non-zero
/// and if there is IAADR option in IA_NA.
if (template_buffers_.size() < 2) {
- sendRequest6(socket, pkt6);
+ sendRequest6(pkt6);
} else {
/// @todo add defines for packet type index that can be
/// used to access template_buffers_.
- sendRequest6(socket, template_buffers_[1], pkt6);
+ sendRequest6(template_buffers_[1], pkt6);
}
}
} else if (packet_type == DHCPV6_REPLY) {
@@ -1205,14 +833,14 @@ TestControl::processReceivedPacket6(const PerfSocket& socket,
// type the Reply message belongs to. It is doable by matching the Reply
// transaction id with the transaction id of the sent Request, Renew
// or Release. First we start with the Request.
- if (stats_mgr6_->passRcvdPacket(StatsMgr6::XCHG_RR, pkt6)) {
+ if (stats_mgr_.passRcvdPacket(ExchangeType::RR, pkt6)) {
// The Reply belongs to Request-Reply exchange type. So, we may need
// to keep this Reply in the storage if Renews or/and Releases are
// being sent. Note that, Reply messages hold the information about
// leases assigned. We use this information to construct Renew and
// Release messages.
- if (stats_mgr6_->hasExchangeStats(StatsMgr6::XCHG_RN) ||
- stats_mgr6_->hasExchangeStats(StatsMgr6::XCHG_RL)) {
+ if (stats_mgr_.hasExchangeStats(ExchangeType::RN) ||
+ stats_mgr_.hasExchangeStats(ExchangeType::RL)) {
// Renew or Release messages are sent, because StatsMgr has the
// specific exchange type specified. Let's append the Reply
// message to a storage.
@@ -1225,28 +853,28 @@ TestControl::processReceivedPacket6(const PerfSocket& socket,
// a corresponding Renew message for the received Reply. If not,
// we check that StatsMgr has exchange type for Release specified,
// as possibly the Reply has been sent in response to Release.
- } else if (!(stats_mgr6_->hasExchangeStats(StatsMgr6::XCHG_RN) &&
- stats_mgr6_->passRcvdPacket(StatsMgr6::XCHG_RN, pkt6)) &&
- stats_mgr6_->hasExchangeStats(StatsMgr6::XCHG_RL)) {
+ } else if (!(stats_mgr_.hasExchangeStats(ExchangeType::RN) &&
+ stats_mgr_.passRcvdPacket(ExchangeType::RN, pkt6)) &&
+ stats_mgr_.hasExchangeStats(ExchangeType::RL)) {
// At this point, it is only possible that the Reply has been sent
// in response to a Release. Try to match the Reply with Release.
- stats_mgr6_->passRcvdPacket(StatsMgr6::XCHG_RL, pkt6);
+ stats_mgr_.passRcvdPacket(ExchangeType::RL, pkt6);
}
}
}
unsigned int
-TestControl::consumeReceivedPackets(Receiver& receiver, const PerfSocket& socket) {
+TestControl::consumeReceivedPackets() {
unsigned int pkt_count = 0;
PktPtr pkt;
- while ((pkt = receiver.getPkt())) {
+ while ((pkt = receiver_.getPkt())) {
pkt_count += 1;
if (CommandOptions::instance().getIpVersion() == 4) {
Pkt4Ptr pkt4 = boost::dynamic_pointer_cast<Pkt4>(pkt);
- processReceivedPacket4(socket, pkt4);
+ processReceivedPacket4(pkt4);
} else {
Pkt6Ptr pkt6 = boost::dynamic_pointer_cast<Pkt6>(pkt);
- processReceivedPacket6(socket, pkt6);
+ processReceivedPacket6(pkt6);
}
}
return pkt_count;
@@ -1328,11 +956,6 @@ TestControl::registerOptionFactories() const {
void
TestControl::reset() {
- CommandOptions& options = CommandOptions::instance();
- basic_rate_control_.setRate(options.getRate());
- renew_rate_control_.setRate(options.getRenewRate());
- release_rate_control_.setRate(options.getReleaseRate());
-
transid_gen_.reset();
last_report_ = microsec_clock::universal_time();
// Actual generators will have to be set later on because we need to
@@ -1343,8 +966,10 @@ TestControl::reset() {
interrupted_ = false;
}
-int
-TestControl::run() {
+TestControl::TestControl(bool ignore_timestamp_reorder) :
+ number_generator_(0, CommandOptions::instance().getMacsFromFile().size()),
+ stats_mgr_(ignore_timestamp_reorder)
+{
// Reset singleton state before test starts.
reset();
@@ -1374,10 +999,6 @@ TestControl::run() {
printDiagnostics();
// Option factories have to be registered.
registerOptionFactories();
- PerfSocket socket(openSocket());
- if (!socket.valid_) {
- isc_throw(Unexpected, "invalid socket descriptor");
- }
// Initialize packet templates.
initPacketTemplates();
// Initialize randomization seed.
@@ -1392,138 +1013,6 @@ TestControl::run() {
}
// If user interrupts the program we will exit gracefully.
signal(SIGINT, TestControl::handleInterrupt);
-
- // Initialize Statistics Manager. Release previous if any.
- initializeStatsMgr();
-
- Receiver receiver(socket);
-
- // Preload server with the number of packets.
- if (options.getPreload() > 0) {
- sendPackets(socket, options.getPreload(), true);
- }
-
- // Fork and run command specified with -w<wrapped-command>
- if (!options.getWrapped().empty()) {
- runWrapped();
- }
-
- receiver.start();
-
- for (;;) {
- // 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 (options.getIpVersion() == 4) {
- stats_mgr4_->incrementCounter("shortwait");
- } else if (options.getIpVersion() == 6) {
- stats_mgr6_->incrementCounter("shortwait");
- }
- }
-
- // Pull some packets from receiver thread, process them, update some stats
- // and respond to the server if needed.
- auto pkt_count = consumeReceivedPackets(receiver, socket);
-
- // 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) {
- /// @todo: need to implement adaptive time here, so the sleep time
- /// is not fixed, but adjusts to current situation.
- usleep(1);
- }
-
- // If test period finished, maximum number of packet drops
- // has been reached or test has been interrupted we have to
- // finish the test.
- if (checkExitConditions()) {
- break;
- }
-
- // Initiate new DHCP packet exchanges.
- sendPackets(socket, packets_due);
-
- // If -f<renew-rate> 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) {
- uint64_t renew_packets_due =
- renew_rate_control_.getOutboundMessageCount();
-
- // Send multiple renews to satisfy the desired rate.
- if (options.getIpVersion() == 4) {
- sendMultipleRequests(socket, renew_packets_due);
- } else {
- sendMultipleMessages6(socket, DHCPV6_RENEW, renew_packets_due);
- }
- }
-
- // If -F<release-rate> 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)) {
- uint64_t release_packets_due =
- release_rate_control_.getOutboundMessageCount();
- // Send Release messages.
- sendMultipleMessages6(socket, DHCPV6_RELEASE, release_packets_due);
- }
-
- // Report delay means that user requested printing number
- // of sent/received/dropped packets repeatedly.
- if (options.getReportDelay() > 0) {
- printIntermediateStats();
- }
-
- // If we are sending Renews to the server, the Reply packets are cached
- // so as leases for which we send Renews can be identified. The major
- // issue with this approach is that most of the time we are caching
- // more packets than we actually need. This function removes excessive
- // Reply messages to reduce the memory and CPU utilization. Note that
- // searches in the long list of Reply packets increases CPU utilization.
- cleanCachedPackets();
- }
-
- receiver.stop();
-
- printStats();
-
- if (!options.getWrapped().empty()) {
- // true means that we execute wrapped command with 'stop' argument.
- runWrapped(true);
- }
-
- // Print packet timestamps
- if (testDiags('t')) {
- if (options.getIpVersion() == 4) {
- stats_mgr4_->printTimestamps();
- } else if (options.getIpVersion() == 6) {
- stats_mgr6_->printTimestamps();
- }
- }
-
- // Print server id.
- if (testDiags('s') && (first_packet_serverid_.size() > 0)) {
- std::cout << "Server id: " << vector2Hex(first_packet_serverid_) << std::endl;
- }
-
- // Diagnostics flag 'e' means show exit reason.
- if (testDiags('e')) {
- std::cout << "Interrupted" << std::endl;
- }
- // 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')) {
- printTemplates();
- }
-
- int ret_code = 0;
- // Check if any packet drops occurred.
- if (options.getIpVersion() == 4) {
- ret_code = stats_mgr4_->droppedPackets() ? 3 : 0;
- } else if (options.getIpVersion() == 6) {
- ret_code = stats_mgr6_->droppedPackets() ? 3 : 0;
- }
- return (ret_code);
}
void
@@ -1562,8 +1051,7 @@ TestControl::saveFirstPacket(const Pkt6Ptr& pkt) {
}
void
-TestControl::sendDiscover4(const PerfSocket& socket,
- const bool preload /*= false*/) {
+TestControl::sendDiscover4(const bool preload /*= false*/) {
// Generate the MAC address to be passed in the packet.
uint8_t randomized = 0;
std::vector<uint8_t> mac_address = generateMacAddress(randomized);
@@ -1587,7 +1075,7 @@ TestControl::sendDiscover4(const PerfSocket& socket,
// Set client's and server's ports as well as server's address,
// and local (relay) address.
- setDefaults4(socket, pkt4);
+ setDefaults4(pkt4);
// Set hardware address
pkt4->setHWAddr(HTYPE_ETHER, mac_address.size(), mac_address);
@@ -1601,18 +1089,13 @@ TestControl::sendDiscover4(const PerfSocket& socket,
pkt4->pack();
IfaceMgr::instance().send(pkt4);
if (!preload) {
- if (!stats_mgr4_) {
- isc_throw(InvalidOperation, "Statistics Manager for DHCPv4 "
- "hasn't been initialized");
- }
- stats_mgr4_->passSentPacket(StatsMgr4::XCHG_DO, pkt4);
+ stats_mgr_.passSentPacket(ExchangeType::DO, pkt4);
}
saveFirstPacket(pkt4);
}
void
-TestControl::sendDiscover4(const PerfSocket& socket,
- const std::vector<uint8_t>& template_buf,
+TestControl::sendDiscover4(const std::vector<uint8_t>& template_buf,
const bool preload /* = false */) {
// Get the first argument if multiple the same arguments specified
// in the command line. First one refers to DISCOVER packets.
@@ -1644,25 +1127,21 @@ TestControl::sendDiscover4(const PerfSocket& socket,
// Replace MAC address in the template with actual MAC address.
pkt4->writeAt(rand_offset, mac_address.begin(), mac_address.end());
// Create a packet from the temporary buffer.
- setDefaults4(socket, boost::static_pointer_cast<Pkt4>(pkt4));
+ setDefaults4(boost::static_pointer_cast<Pkt4>(pkt4));
// 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>(pkt4));
if (!preload) {
- if (!stats_mgr4_) {
- isc_throw(InvalidOperation, "Statistics Manager for DHCPv4 "
- "hasn't been initialized");
- }
// Update packet stats.
- stats_mgr4_->passSentPacket(StatsMgr4::XCHG_DO,
+ stats_mgr_.passSentPacket(ExchangeType::DO,
boost::static_pointer_cast<Pkt4>(pkt4));
}
saveFirstPacket(pkt4);
}
bool
-TestControl::sendRequestFromAck(const PerfSocket& socket) {
+TestControl::sendRequestFromAck() {
// Get one of the recorded DHCPACK messages.
Pkt4Ptr ack = ack_storage_.getRandom();
if (!ack) {
@@ -1671,7 +1150,7 @@ TestControl::sendRequestFromAck(const PerfSocket& socket) {
// Create message of the specified type.
Pkt4Ptr msg = createRequestFromAck(ack);
- setDefaults4(socket, msg);
+ setDefaults4(msg);
// Add any extra options that user may have specified.
addExtraOpts(msg);
@@ -1679,18 +1158,13 @@ TestControl::sendRequestFromAck(const PerfSocket& socket) {
msg->pack();
// And send it.
IfaceMgr::instance().send(msg);
- if (!stats_mgr4_) {
- isc_throw(Unexpected, "Statistics Manager for DHCPv4 "
- "hasn't been initialized");
- }
- stats_mgr4_->passSentPacket(StatsMgr4::XCHG_RNA, msg);
+ stats_mgr_.passSentPacket(ExchangeType::RNA, msg);
return (true);
}
bool
-TestControl::sendMessageFromReply(const uint16_t msg_type,
- const PerfSocket& socket) {
+TestControl::sendMessageFromReply(const uint16_t msg_type) {
// We only permit Release or Renew messages to be sent using this function.
if (msg_type != DHCPV6_RENEW && msg_type != DHCPV6_RELEASE) {
isc_throw(isc::BadValue, "invalid message type " << msg_type
@@ -1702,7 +1176,7 @@ TestControl::sendMessageFromReply(const uint16_t msg_type,
}
// Prepare the message of the specified type.
Pkt6Ptr msg = createMessageFromReply(msg_type, reply);
- setDefaults6(socket, msg);
+ setDefaults6(msg);
// Add any extra options that user may have specified.
addExtraOpts(msg);
@@ -1710,18 +1184,13 @@ TestControl::sendMessageFromReply(const uint16_t msg_type,
msg->pack();
// And send it.
IfaceMgr::instance().send(msg);
- if (!stats_mgr6_) {
- isc_throw(Unexpected, "Statistics Manager for DHCPv6 "
- "hasn't been initialized");
- }
- stats_mgr6_->passSentPacket((msg_type == DHCPV6_RENEW ? StatsMgr6::XCHG_RN
- : StatsMgr6::XCHG_RL), msg);
+ stats_mgr_.passSentPacket((msg_type == DHCPV6_RENEW ? ExchangeType::RN
+ : ExchangeType::RL), msg);
return (true);
}
void
-TestControl::sendRequest4(const PerfSocket& socket,
- const dhcp::Pkt4Ptr& discover_pkt4,
+TestControl::sendRequest4(const dhcp::Pkt4Ptr& discover_pkt4,
const dhcp::Pkt4Ptr& offer_pkt4) {
// Use the same transaction id as the one used in the discovery packet.
const uint32_t transid = discover_pkt4->getTransid();
@@ -1740,7 +1209,7 @@ TestControl::sendRequest4(const PerfSocket& socket,
isc_throw(BadValue, "there is no SERVER_IDENTIFIER option "
<< "in OFFER message");
}
- if (stats_mgr4_->getRcvdPacketsNum(StatsMgr4::XCHG_DO) == 1) {
+ if (stats_mgr_.getRcvdPacketsNum(ExchangeType::DO) == 1) {
first_packet_serverid_ = opt_serverid->getData();
}
pkt4->addOption(opt_serverid);
@@ -1762,7 +1231,7 @@ TestControl::sendRequest4(const PerfSocket& socket,
pkt4->addOption(opt_parameter_list);
// Set client's and server's ports as well as server's address,
// and local (relay) address.
- setDefaults4(socket, pkt4);
+ setDefaults4(pkt4);
// Add any extra options that user may have specified.
addExtraOpts(pkt4);
@@ -1777,17 +1246,12 @@ TestControl::sendRequest4(const PerfSocket& socket,
// Prepare on wire data to send.
pkt4->pack();
IfaceMgr::instance().send(pkt4);
- if (!stats_mgr4_) {
- isc_throw(InvalidOperation, "Statistics Manager for DHCPv4 "
- "hasn't been initialized");
- }
- stats_mgr4_->passSentPacket(StatsMgr4::XCHG_RA, pkt4);
+ stats_mgr_.passSentPacket(ExchangeType::RA, pkt4);
saveFirstPacket(pkt4);
}
void
-TestControl::sendRequest4(const PerfSocket& socket,
- const std::vector<uint8_t>& template_buf,
+TestControl::sendRequest4(const std::vector<uint8_t>& template_buf,
const dhcp::Pkt4Ptr& discover_pkt4,
const dhcp::Pkt4Ptr& offer_pkt4) {
// Get the second argument if multiple the same arguments specified
@@ -1857,7 +1321,7 @@ TestControl::sendRequest4(const PerfSocket& socket,
opt_serverid_offer->getData(),
sid_offset));
pkt4->addOption(opt_serverid);
- if (stats_mgr4_->getRcvdPacketsNum(StatsMgr4::XCHG_DO) == 1) {
+ if (stats_mgr_.getRcvdPacketsNum(ExchangeType::DO) == 1) {
first_packet_serverid_ = opt_serverid_offer->getData();
}
}
@@ -1881,7 +1345,7 @@ TestControl::sendRequest4(const PerfSocket& socket,
opt_requested_ip->setUint32(yiaddr.toUint32());
pkt4->addOption(opt_requested_ip);
- setDefaults4(socket, boost::static_pointer_cast<Pkt4>(pkt4));
+ setDefaults4(boost::static_pointer_cast<Pkt4>(pkt4));
// Add any extra options that user may have specified.
addExtraOpts(pkt4);
@@ -1889,19 +1353,14 @@ TestControl::sendRequest4(const PerfSocket& socket,
// Prepare on-wire data.
pkt4->rawPack();
IfaceMgr::instance().send(boost::static_pointer_cast<Pkt4>(pkt4));
- if (!stats_mgr4_) {
- isc_throw(InvalidOperation, "Statistics Manager for DHCPv4 "
- "hasn't been initialized");
- }
// Update packet stats.
- stats_mgr4_->passSentPacket(StatsMgr4::XCHG_RA,
- boost::static_pointer_cast<Pkt4>(pkt4));
+ stats_mgr_.passSentPacket(ExchangeType::RA,
+ boost::static_pointer_cast<Pkt4>(pkt4));
saveFirstPacket(pkt4);
}
void
-TestControl::sendRequest6(const PerfSocket& socket,
- const Pkt6Ptr& advertise_pkt6) {
+TestControl::sendRequest6(const Pkt6Ptr& advertise_pkt6) {
const uint32_t transid = generateTransid();
Pkt6Ptr pkt6(new Pkt6(DHCPV6_REQUEST, transid));
// Set elapsed time.
@@ -1926,7 +1385,7 @@ TestControl::sendRequest6(const PerfSocket& socket,
if (!opt_serverid) {
isc_throw(Unexpected, "server id not found in received packet");
}
- if (stats_mgr6_->getRcvdPacketsNum(StatsMgr6::XCHG_SA) == 1) {
+ if (stats_mgr_.getRcvdPacketsNum(ExchangeType::SA) == 1) {
first_packet_serverid_ = opt_serverid->getData();
}
pkt6->addOption(opt_serverid);
@@ -1940,7 +1399,7 @@ TestControl::sendRequest6(const PerfSocket& socket,
copyIaOptions(advertise_pkt6, pkt6);
// Set default packet data.
- setDefaults6(socket, pkt6);
+ setDefaults6(pkt6);
// Add any extra options that user may have specified.
addExtraOpts(pkt6);
@@ -1948,17 +1407,12 @@ TestControl::sendRequest6(const PerfSocket& socket,
// Prepare on-wire data.
pkt6->pack();
IfaceMgr::instance().send(pkt6);
- if (!stats_mgr6_) {
- isc_throw(InvalidOperation, "Statistics Manager for DHCPv6 "
- "hasn't been initialized");
- }
- stats_mgr6_->passSentPacket(StatsMgr6::XCHG_RR, pkt6);
+ stats_mgr_.passSentPacket(ExchangeType::RR, pkt6);
saveFirstPacket(pkt6);
}
void
-TestControl::sendRequest6(const PerfSocket& socket,
- const std::vector<uint8_t>& template_buf,
+TestControl::sendRequest6(const std::vector<uint8_t>& template_buf,
const Pkt6Ptr& advertise_pkt6) {
// Get the second argument if multiple the same arguments specified
// in the command line. Second one refers to REQUEST packets.
@@ -2004,7 +1458,7 @@ TestControl::sendRequest6(const PerfSocket& socket,
opt_serverid_advertise->getData(),
sid_offset));
pkt6->addOption(opt_serverid);
- if (stats_mgr6_->getRcvdPacketsNum(StatsMgr6::XCHG_SA) == 1) {
+ if (stats_mgr_.getRcvdPacketsNum(ExchangeType::SA) == 1) {
first_packet_serverid_ = opt_serverid_advertise->getData();
}
}
@@ -2048,7 +1502,7 @@ TestControl::sendRequest6(const PerfSocket& socket,
rand_offset));
pkt6->addOption(opt_clientid);
// Set default packet data.
- setDefaults6(socket, pkt6);
+ setDefaults6(pkt6);
// Add any extra options that user may have specified.
addExtraOpts(pkt6);
@@ -2057,12 +1511,8 @@ TestControl::sendRequest6(const PerfSocket& socket,
pkt6->rawPack();
// Send packet.
IfaceMgr::instance().send(pkt6);
- if (!stats_mgr6_) {
- isc_throw(InvalidOperation, "Statistics Manager for DHCPv6 "
- "hasn't been initialized");
- }
// Update packet stats.
- stats_mgr6_->passSentPacket(StatsMgr6::XCHG_RR, pkt6);
+ stats_mgr_.passSentPacket(ExchangeType::RR, pkt6);
// When 'T' diagnostics flag is specified it means that user requested
// printing packet contents. It will be just one (first) packet which
@@ -2076,8 +1526,7 @@ TestControl::sendRequest6(const PerfSocket& socket,
}
void
-TestControl::sendSolicit6(const PerfSocket& socket,
- const bool preload /*= false*/) {
+TestControl::sendSolicit6(const bool preload /*= false*/) {
// Generate DUID to be passed to the packet
uint8_t randomized = 0;
std::vector<uint8_t> duid = generateDuid(randomized);
@@ -2108,7 +1557,7 @@ TestControl::sendSolicit6(const PerfSocket& socket,
pkt6->addOption(Option::factory(Option::V6, D6O_IA_PD));
}
- setDefaults6(socket, pkt6);
+ setDefaults6(pkt6);
// Add any extra options that user may have specified.
addExtraOpts(pkt6);
@@ -2116,19 +1565,14 @@ TestControl::sendSolicit6(const PerfSocket& socket,
pkt6->pack();
IfaceMgr::instance().send(pkt6);
if (!preload) {
- if (!stats_mgr6_) {
- isc_throw(InvalidOperation, "Statistics Manager for DHCPv6 "
- "hasn't been initialized");
- }
- stats_mgr6_->passSentPacket(StatsMgr6::XCHG_SA, pkt6);
+ stats_mgr_.passSentPacket(ExchangeType::SA, pkt6);
}
saveFirstPacket(pkt6);
}
void
-TestControl::sendSolicit6(const PerfSocket& socket,
- const std::vector<uint8_t>& template_buf,
+TestControl::sendSolicit6(const std::vector<uint8_t>& template_buf,
const bool preload /*= false*/) {
const int arg_idx = 0;
// Get transaction id offset.
@@ -2156,7 +1600,7 @@ TestControl::sendSolicit6(const PerfSocket& socket,
// Prepare on-wire data.
pkt6->rawPack();
- setDefaults6(socket, pkt6);
+ setDefaults6(pkt6);
// Add any extra options that user may have specified.
addExtraOpts(pkt6);
@@ -2164,29 +1608,24 @@ TestControl::sendSolicit6(const PerfSocket& socket,
// Send solicit packet.
IfaceMgr::instance().send(pkt6);
if (!preload) {
- if (!stats_mgr6_) {
- isc_throw(InvalidOperation, "Statistics Manager for DHCPv6 "
- "hasn't been initialized");
- }
// Update packet stats.
- stats_mgr6_->passSentPacket(StatsMgr6::XCHG_SA, pkt6);
+ stats_mgr_.passSentPacket(ExchangeType::SA, pkt6);
}
saveFirstPacket(pkt6);
}
void
-TestControl::setDefaults4(const PerfSocket& socket,
- const Pkt4Ptr& pkt) {
+TestControl::setDefaults4(const Pkt4Ptr& pkt) {
CommandOptions& options = CommandOptions::instance();
// Interface name.
- IfacePtr iface = IfaceMgr::instance().getIface(socket.ifindex_);
+ IfacePtr iface = IfaceMgr::instance().getIface(socket_.ifindex_);
if (iface == NULL) {
isc_throw(BadValue, "unable to find interface with given index");
}
pkt->setIface(iface->getName());
// Interface index.
- pkt->setIndex(socket.ifindex_);
+ pkt->setIndex(socket_.ifindex_);
// Local client's port (68)
pkt->setLocalPort(DHCP4_CLIENT_PORT);
// Server's port (67)
@@ -2198,25 +1637,24 @@ TestControl::setDefaults4(const PerfSocket& socket,
// The remote server's name or IP.
pkt->setRemoteAddr(IOAddress(options.getServerName()));
// Set local address.
- pkt->setLocalAddr(IOAddress(socket.addr_));
+ pkt->setLocalAddr(IOAddress(socket_.addr_));
// Set relay (GIADDR) address to local address.
- pkt->setGiaddr(IOAddress(socket.addr_));
+ pkt->setGiaddr(IOAddress(socket_.addr_));
// Pretend that we have one relay (which is us).
pkt->setHops(1);
}
void
-TestControl::setDefaults6(const PerfSocket& socket,
- const Pkt6Ptr& pkt) {
+TestControl::setDefaults6(const Pkt6Ptr& pkt) {
CommandOptions& options = CommandOptions::instance();
// Interface name.
- IfacePtr iface = IfaceMgr::instance().getIface(socket.ifindex_);
+ IfacePtr iface = IfaceMgr::instance().getIface(socket_.ifindex_);
if (iface == NULL) {
isc_throw(BadValue, "unable to find interface with given index");
}
pkt->setIface(iface->getName());
// Interface index.
- pkt->setIndex(socket.ifindex_);
+ pkt->setIndex(socket_.ifindex_);
// Local client's port (547)
pkt->setLocalPort(DHCP6_CLIENT_PORT);
// Server's port (548)
@@ -2226,7 +1664,7 @@ TestControl::setDefaults6(const PerfSocket& socket,
pkt->setRemotePort(DHCP6_SERVER_PORT);
}
// Set local address.
- pkt->setLocalAddr(socket.addr_);
+ pkt->setLocalAddr(socket_.addr_);
// The remote server's name or IP.
pkt->setRemoteAddr(IOAddress(options.getServerName()));
@@ -2237,8 +1675,8 @@ TestControl::setDefaults6(const PerfSocket& socket,
Pkt6::RelayInfo relay_info;
relay_info.msg_type_ = DHCPV6_RELAY_FORW;
relay_info.hop_count_ = 1;
- relay_info.linkaddr_ = IOAddress(socket.addr_);
- relay_info.peeraddr_ = IOAddress(socket.addr_);
+ relay_info.linkaddr_ = IOAddress(socket_.addr_);
+ relay_info.peeraddr_ = IOAddress(socket_.addr_);
pkt->addRelayInfo(relay_info);
}
}
diff --git a/src/bin/perfdhcp/test_control.h b/src/bin/perfdhcp/test_control.h
index 0606a21de8..0be0b1b478 100644
--- a/src/bin/perfdhcp/test_control.h
+++ b/src/bin/perfdhcp/test_control.h
@@ -25,6 +25,7 @@
#include <string>
#include <vector>
+#include <unordered_map>
namespace isc {
namespace perfdhcp {
@@ -122,6 +123,8 @@ public:
/// DHCPv4 or DHCPv6 option.
class TestControl : public boost::noncopyable {
public:
+ /// \brief Default constructor.
+ TestControl(bool ignore_timestamp_reorder);
/// Packet template buffer.
typedef std::vector<uint8_t> TemplateBuffer;
@@ -192,24 +195,6 @@ public:
/// address is longer than this (e.g. 20 bytes).
static const uint8_t HW_ETHER_LEN = 6;
- /// TestControl is a singleton class. This method returns reference
- /// to its sole instance.
- ///
- /// \return the only existing instance of test control
- static TestControl& instance();
-
- /// brief\ Run performance test.
- ///
- /// Method runs whole performance test. Command line options must
- /// be parsed prior to running this function. Otherwise function will
- /// throw exception.
- ///
- /// \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();
-
/// \brief Set new transaction id generator.
///
/// \param generator generator object to be used.
@@ -233,28 +218,11 @@ public:
// they have to be accessible for unit-testing. Another, possibly better,
// solution is to make this class friend of test class but this is not
// what's followed in other classes.
-protected:
- /// \brief Default constructor.
- ///
- /// Default constructor is protected as the object can be created
- /// only via \ref instance method.
- TestControl();
-
+//protected:
+public: // TODO clean up what should be and what should not be protected
/// Generate uniformly distributed integers in range of [min, max]
isc::util::random::UniformRandomIntegerGenerator number_generator_;
- /// \brief Check if test exit conditions fulfilled.
- ///
- /// Method checks if the test exit conditions are fulfilled.
- /// Exit conditions are checked periodically from the
- /// main loop. Program should break the main loop when
- /// this method returns true. It is calling function
- /// responsibility to break main loop gracefully and
- /// cleanup after test execution.
- ///
- /// \return true if any of the exit conditions is fulfilled.
- bool checkExitConditions() const;
-
/// \brief Removes cached DHCPv6 Reply packets every second.
///
/// This function wipes cached Reply packets from the storage.
@@ -476,32 +444,6 @@ protected:
/// odd number of hexadecimal digits.
void initPacketTemplates();
- /// \brief Initializes Statistics Manager.
- ///
- /// This function initializes Statistics Manager. If there is
- /// the one initialized already it is released.
- void initializeStatsMgr();
-
- /// \brief Open socket to communicate with DHCP server.
- ///
- /// Method opens socket and binds it to local address. Function will
- /// use either interface name, local address or server address
- /// to create a socket, depending on what is available (specified
- /// from the command line). If socket can't be created for any
- /// reason, exception is thrown.
- /// If destination address is broadcast (for DHCPv4) or multicast
- /// (for DHCPv6) than broadcast or multicast option is set on
- /// the socket. Opened socket is registered and managed by IfaceMgr.
- ///
- /// \throw isc::BadValue if socket can't be created for given
- /// interface, local address or remote address.
- /// \throw isc::InvalidOperation if broadcast option can't be
- /// set for the v4 socket or if multicast option can't be set
- /// for the v6 socket.
- /// \throw isc::Unexpected if internal unexpected error occurred.
- /// \return socket descriptor.
- int openSocket() const;
-
/// \brief Print intermediate statistics.
///
/// Print brief statistics regarding number of sent packets,
@@ -523,7 +465,7 @@ protected:
/// \brief Pull packets from receiver and process them.
/// It runs in a loop until there are no packets in receiver.
- unsigned int consumeReceivedPackets(Receiver& receiver, const PerfSocket& socket);
+ unsigned int consumeReceivedPackets();
/// \brief Process received DHCPv4 packet.
///
@@ -535,12 +477,10 @@ protected:
/// \warning this method does not check if provided socket is
/// valid (specifically if v4 socket for received v4 packet).
///
- /// \param [in] socket socket to be used.
/// \param [in] pkt4 object representing DHCPv4 packet received.
/// \throw isc::BadValue if unknown message type received.
/// \throw isc::Unexpected if unexpected error occurred.
- void processReceivedPacket4(const PerfSocket& socket,
- const dhcp::Pkt4Ptr& pkt4);
+ void processReceivedPacket4(const dhcp::Pkt4Ptr& pkt4);
/// \brief Process received DHCPv6 packet.
///
@@ -549,15 +489,10 @@ protected:
/// e.g. when ADVERTISE packet arrives, this function will initiate
/// REQUEST message to the server.
///
- /// \warning this method does not check if provided socket is
- /// valid (specifically if v4 socket for received v4 packet).
- ///
- /// \param [in] socket socket to be used.
/// \param [in] pkt6 object representing DHCPv6 packet received.
/// \throw isc::BadValue if unknown message type received.
/// \throw isc::Unexpected if unexpected error occurred.
- void processReceivedPacket6(const PerfSocket& socket,
- const dhcp::Pkt6Ptr& pkt6);
+ void processReceivedPacket6(const dhcp::Pkt6Ptr& pkt6);
/// \brief Register option factory functions for DHCPv4
///
@@ -630,17 +565,15 @@ protected:
/// The transaction id and MAC address are randomly generated for
/// the message. Range of unique MAC addresses generated depends
/// on the number of clients specified from the command line.
- /// Copy of sent packet is stored in the stats_mgr4_ object to
+ /// Copy of sent packet is stored in the stats_mgr_ object to
/// update statistics.
///
- /// \param socket socket to be used to send the message.
/// \param preload preload mode, packets not included in statistics.
///
/// \throw isc::Unexpected if failed to create new packet instance.
/// \throw isc::BadValue if MAC address has invalid length.
/// \throw isc::dhcp::SocketWriteError if failed to send the packet.
- void sendDiscover4(const PerfSocket& socket,
- const bool preload = false);
+ void sendDiscover4(const bool preload = false);
/// \brief Send DHCPv4 DISCOVER message from template.
///
@@ -648,17 +581,15 @@ protected:
/// template data is expected to be in binary format. Provided
/// buffer is copied and parts of it are replaced with actual
/// data (e.g. MAC address, transaction id etc.).
- /// Copy of sent packet is stored in the stats_mgr4_ object to
+ /// Copy of sent packet is stored in the stats_mgr_ object to
/// update statistics.
///
- /// \param socket socket to be used to send the message.
/// \param template_buf buffer holding template packet.
/// \param preload preload mode, packets not included in statistics.
///
/// \throw isc::OutOfRange if randomization offset is out of bounds.
/// \throw isc::dhcp::SocketWriteError if failed to send the packet.
- void sendDiscover4(const PerfSocket& socket,
- const std::vector<uint8_t>& template_buf,
+ void sendDiscover4(const std::vector<uint8_t>& template_buf,
const bool preload = false);
/// \brief Send number of packets to initiate new exchanges.
@@ -676,46 +607,37 @@ protected:
///
/// \todo do not count responses in preload mode as orphans.
///
- /// \param socket socket to be used to send packets.
/// \param packets_num number of packets to be sent.
/// \param preload preload mode, packets not included in statistics.
/// \throw isc::Unexpected if thrown by packet sending method.
/// \throw isc::InvalidOperation if thrown by packet sending method.
/// \throw isc::OutOfRange if thrown by packet sending method.
- void sendPackets(const PerfSocket &socket,
- const uint64_t packets_num,
+ void sendPackets(const uint64_t packets_num,
const bool preload = false);
/// \brief Send number of DHCPREQUEST (renew) messages to a server.
///
- /// \param socket An object representing socket to be used to send packets.
/// \param msg_num A number of messages to be sent.
///
/// \return A number of messages actually sent.
- uint64_t sendMultipleRequests(const PerfSocket& socket,
- const uint64_t msg_num);
+ uint64_t sendMultipleRequests(const uint64_t msg_num);
/// \brief Send number of DHCPv6 Renew or Release messages to the server.
///
- /// \param socket An object representing socket to be used to send packets.
/// \param msg_type A type of the messages to be sent (DHCPV6_RENEW or
/// DHCPV6_RELEASE).
/// \param msg_num A number of messages to be sent.
///
/// \return A number of messages actually sent.
- uint64_t sendMultipleMessages6(const PerfSocket& socket,
- const uint32_t msg_type,
+ uint64_t sendMultipleMessages6(const uint32_t msg_type,
const uint64_t msg_num);
- /// \brief Send DHCPv4 renew (DHCPREQUEST) using specified socket.
- ///
- /// \param socket An object encapsulating socket to be used to send
- /// a packet.
+ /// \brief Send DHCPv4 renew (DHCPREQUEST).
///
/// \return true if the message has been sent, false otherwise.
- bool sendRequestFromAck(const PerfSocket& socket);
+ bool sendRequestFromAck();
- /// \brief Send DHCPv6 Renew or Release message using specified socket.
+ /// \brief Send DHCPv6 Renew or Release message.
///
/// This method will select an existing lease from the Reply packet cache
/// If there is no lease that can be renewed or released this method will
@@ -723,20 +645,16 @@ protected:
///
/// \param msg_type A type of the message to be sent (DHCPV6_RENEW or
/// DHCPV6_RELEASE).
- /// \param socket An object encapsulating socket to be used to send
- /// a packet.
///
/// \return true if the message has been sent, false otherwise.
- bool sendMessageFromReply(const uint16_t msg_type,
- const PerfSocket& socket);
+ bool sendMessageFromReply(const uint16_t msg_type);
/// \brief Send DHCPv4 REQUEST message.
///
/// Method creates and sends DHCPv4 REQUEST message to the server.
- /// Copy of sent packet is stored in the stats_mgr4_ object to
+ /// Copy of sent packet is stored in the stats_mgr_ object to
/// update statistics.
///
- /// \param socket socket to be used to send message.
/// \param discover_pkt4 DISCOVER packet sent.
/// \param offer_pkt4 OFFER packet object.
///
@@ -744,24 +662,21 @@ protected:
/// \throw isc::InvalidOperation if Statistics Manager has not been
/// initialized.
/// \throw isc::dhcp::SocketWriteError if failed to send the packet.
- void sendRequest4(const PerfSocket& socket,
- const dhcp::Pkt4Ptr& discover_pkt4,
+ void sendRequest4(const dhcp::Pkt4Ptr& discover_pkt4,
const dhcp::Pkt4Ptr& offer_pkt4);
/// \brief Send DHCPv4 REQUEST message from template.
///
/// Method sends DHCPv4 REQUEST message from template.
- /// Copy of sent packet is stored in the stats_mgr4_ object to
+ /// Copy of sent packet is stored in the stats_mgr_ object to
/// update statistics.
///
- /// \param socket socket to be used to send message.
/// \param template_buf buffer holding template packet.
/// \param discover_pkt4 DISCOVER packet sent.
/// \param offer_pkt4 OFFER packet received.
///
/// \throw isc::dhcp::SocketWriteError if failed to send the packet.
- void sendRequest4(const PerfSocket& socket,
- const std::vector<uint8_t>& template_buf,
+ void sendRequest4(const std::vector<uint8_t>& template_buf,
const dhcp::Pkt4Ptr& discover_pkt4,
const dhcp::Pkt4Ptr& offer_pkt4);
@@ -772,32 +687,28 @@ protected:
/// - D6O_ELAPSED_TIME
/// - D6O_CLIENTID
/// - D6O_SERVERID
- /// Copy of sent packet is stored in the stats_mgr6_ object to
+ /// Copy of sent packet is stored in the stats_mgr_ object to
/// update statistics.
///
- /// \param socket socket to be used to send message.
/// \param advertise_pkt6 ADVERTISE packet object.
/// \throw isc::Unexpected if unexpected error occurred.
/// \throw isc::InvalidOperation if Statistics Manager has not been
/// initialized.
///
/// \throw isc::dhcp::SocketWriteError if failed to send the packet.
- void sendRequest6(const PerfSocket& socket,
- const dhcp::Pkt6Ptr& advertise_pkt6);
+ void sendRequest6(const dhcp::Pkt6Ptr& advertise_pkt6);
/// \brief Send DHCPv6 REQUEST message from template.
///
/// Method sends DHCPv6 REQUEST message from template.
- /// Copy of sent packet is stored in the stats_mgr6_ object to
+ /// Copy of sent packet is stored in the stats_mgr_ object to
/// update statistics.
///
- /// \param socket socket to be used to send message.
/// \param template_buf packet template buffer.
/// \param advertise_pkt6 ADVERTISE packet object.
///
/// \throw isc::dhcp::SocketWriteError if failed to send the packet.
- void sendRequest6(const PerfSocket& socket,
- const std::vector<uint8_t>& template_buf,
+ void sendRequest6(const std::vector<uint8_t>& template_buf,
const dhcp::Pkt6Ptr& advertise_pkt6);
/// \brief Send DHCPv6 SOLICIT message.
@@ -809,30 +720,26 @@ protected:
/// - D6O_CLIENTID,
/// - D6O_ORO (Option Request Option),
/// - D6O_IA_NA.
- /// Copy of sent packet is stored in the stats_mgr6_ object to
+ /// Copy of sent packet is stored in the stats_mgr_ object to
/// update statistics.
///
- /// \param socket socket to be used to send the message.
/// \param preload mode, packets not included in statistics.
///
/// \throw isc::Unexpected if failed to create new packet instance.
/// \throw isc::dhcp::SocketWriteError if failed to send the packet.
- void sendSolicit6(const PerfSocket& socket,
- const bool preload = false);
+ void sendSolicit6(const bool preload = false);
/// \brief Send DHCPv6 SOLICIT message from template.
///
/// Method sends DHCPv6 SOLICIT message from template.
- /// Copy of sent packet is stored in the stats_mgr6_ object to
+ /// Copy of sent packet is stored in the stats_mgr_ object to
/// update statistics.
///
- /// \param socket socket to be used to send the message.
/// \param template_buf packet template buffer.
/// \param preload mode, packets not included in statistics.
///
/// \throw isc::dhcp::SocketWriteError if failed to send the packet.
- void sendSolicit6(const PerfSocket& socket,
- const std::vector<uint8_t>& template_buf,
+ void sendSolicit6(const std::vector<uint8_t>& template_buf,
const bool preload = false);
/// \brief Set default DHCPv4 packet parameters.
@@ -845,10 +752,8 @@ protected:
/// - GIADDR = local address where socket is bound to,
/// - hops = 1 (pretending that we are a relay)
///
- /// \param socket socket used to send the packet.
/// \param pkt reference to packet to be configured.
- void setDefaults4(const PerfSocket& socket,
- const dhcp::Pkt4Ptr& pkt);
+ void setDefaults4(const dhcp::Pkt4Ptr& pkt);
/// \brief Set default DHCPv6 packet parameters.
///
@@ -860,10 +765,8 @@ protected:
/// - local address,
/// - remote address (server).
///
- /// \param socket socket used to send the packet.
/// \param pkt reference to packet to be configured.
- void setDefaults6(const PerfSocket& socket,
- const dhcp::Pkt6Ptr& pkt);
+ void setDefaults6(const dhcp::Pkt6Ptr& pkt);
/// @brief Inserts extra options specified by user.
///
@@ -885,6 +788,27 @@ protected:
/// @param pkt6 options will be added here
void addExtraOpts(const dhcp::Pkt6Ptr& pkt6);
+ StatsMgr& getStatsMgr() { return stats_mgr_; };
+
+ void start() { receiver_.start(); }
+ void stop() { receiver_.stop(); }
+
+ /// \brief Print templates information.
+ ///
+ /// Method prints information about data offsets
+ /// in packet templates and their contents.
+ void printTemplates() const;
+
+ /// \brief Run wrapped command.
+ ///
+ /// \param do_stop execute wrapped command with "stop" argument.
+ void runWrapped(bool do_stop = false) const;
+
+ bool serverIdReceived() const { return first_packet_serverid_.size() > 0; }
+ std::string getServerId() const { return vector2Hex(first_packet_serverid_); }
+
+ bool interrupted() const { return interrupted_; }
+
protected:
/// \brief Copies IA_NA or IA_PD option from one packet to another.
@@ -956,35 +880,14 @@ protected:
/// \return transaction id offset in packet.
int getTransactionIdOffset(const int arg_idx) const;
- /// \brief Get number of received packets.
- ///
- /// Get the number of received packets from the Statistics Manager.
- /// Function may throw if Statistics Manager object is not
- /// initialized.
- ///
- /// \note The method parameter is non-const to suppress the cppcheck
- /// warning about the object being passed by value. However, passing
- /// an enum by reference doesn't make much sense. At the same time,
- /// removing the constness should be pretty safe for this function.
- ///
- /// \param xchg_type packet exchange type.
- /// \return number of received packets.
- uint64_t getRcvdPacketsNum(ExchangeType xchg_type) const;
-
- /// \brief Get number of sent packets.
- ///
- /// Get the number of sent packets from the Statistics Manager.
- /// Function may throw if Statistics Manager object is not
- /// initialized.
+ /// \brief Convert vector in hexadecimal string.
///
- /// \note The method parameter is non-const to suppress the cppcheck
- /// warning about the object being passed by value. However, passing
- /// an enum by reference doesn't make much sense. At the same time,
- /// removing the constness should be pretty safe for this function.
+ /// \todo Consider moving this function to src/lib/util.
///
- /// \param xchg_type packet exchange type.
- /// \return number of sent packets.
- uint64_t getSentPacketsNum(ExchangeType xchg_type) const;
+ /// \param vec vector to be converted.
+ /// \param separator separator.
+ std::string vector2Hex(const std::vector<uint8_t>& vec,
+ const std::string& separator = "") const;
/// \brief Handle child signal.
///
@@ -1012,12 +915,6 @@ protected:
/// \param packet_type packet type.
void printTemplate(const uint8_t packet_type) const;
- /// \brief Print templates information.
- ///
- /// Method prints information about data offsets
- /// in packet templates and their contents.
- void printTemplates() const;
-
/// \brief Read DHCP message template from file.
///
/// Method reads DHCP message template from file and
@@ -1031,31 +928,12 @@ protected:
/// spaces or hexadecimal digits.
void readPacketTemplate(const std::string& file_name);
- /// \brief Run wrapped command.
- ///
- /// \param do_stop execute wrapped command with "stop" argument.
- void runWrapped(bool do_stop = false) const;
-
- /// \brief Convert vector in hexadecimal string.
- ///
- /// \todo Consider moving this function to src/lib/util.
- ///
- /// \param vec vector to be converted.
- /// \param separator separator.
- std::string vector2Hex(const std::vector<uint8_t>& vec,
- const std::string& separator = "") const;
-
- /// \brief A rate control class for Discover and Solicit messages.
- RateControl basic_rate_control_;
- /// \brief A rate control class for Renew messages.
- RateControl renew_rate_control_;
- /// \brief A rate control class for Release messages.
- RateControl release_rate_control_;
+ PerfSocket socket_;
+ Receiver receiver_;
boost::posix_time::ptime last_report_; ///< Last intermediate report time.
- StatsMgr4Ptr stats_mgr4_; ///< Statistics Manager 4.
- StatsMgr6Ptr stats_mgr6_; ///< Statistics Manager 6.
+ StatsMgr stats_mgr_; ///< Statistics Manager.
PacketStorage<dhcp::Pkt4> ack_storage_; ///< A storage for DHCPACK messages.
PacketStorage<dhcp::Pkt6> reply_storage_; ///< A storage for reply messages.
diff --git a/src/bin/perfdhcp/tests/stats_mgr_unittest.cc b/src/bin/perfdhcp/tests/stats_mgr_unittest.cc
index 37f0361a03..41de29527f 100644
--- a/src/bin/perfdhcp/tests/stats_mgr_unittest.cc
+++ b/src/bin/perfdhcp/tests/stats_mgr_unittest.cc
@@ -368,24 +368,6 @@ TEST_F(StatsMgrTest, MultipleExchanges) {
stats_mgr->getRcvdPacketsNum(StatsMgr6::XCHG_RR));
}
-TEST_F(StatsMgrTest, ExchangeToString) {
- // Test DHCPv4 specific exchange names.
- EXPECT_EQ("DISCOVER-OFFER",
- StatsMgr4::exchangeToString(StatsMgr4::XCHG_DO));
- EXPECT_EQ("REQUEST-ACK", StatsMgr4::exchangeToString(StatsMgr4::XCHG_RA));
- EXPECT_EQ("REQUEST-ACK (renewal)",
- StatsMgr4::exchangeToString(StatsMgr4::XCHG_RNA));
-
-
- // Test DHCPv6 specific exchange names.
- EXPECT_EQ("SOLICIT-ADVERTISE",
- StatsMgr6::exchangeToString(StatsMgr6::XCHG_SA));
- EXPECT_EQ("REQUEST-REPLY", StatsMgr6::exchangeToString(StatsMgr6::XCHG_RR));
- EXPECT_EQ("RENEW-REPLY", StatsMgr6::exchangeToString(StatsMgr6::XCHG_RN));
- EXPECT_EQ("RELEASE-REPLY", StatsMgr6::exchangeToString(StatsMgr6::XCHG_RL));
-
-}
-
TEST_F(StatsMgrTest, SendReceiveSimple) {
boost::scoped_ptr<StatsMgr4> stats_mgr(new StatsMgr4());
boost::shared_ptr<Pkt4> sent_packet(createPacket4(DHCPDISCOVER,