Delays in sending and receiving (e.g. timeout is select) was causing time slips so sending new packets was always late.
The fix splits receiving into separate thread to not interfere with sending in main thread.
RateController was changed. Now it tracks actual request rate estimating it from the beginning of the test.
In every iteration of sending main loop it is checked if actual request rate is lower than expected.
In such case packets are sent immediatelly. This way actual request rate is always accurate.
The receiver thread receives packets from socket using select as before but do not have to worry about delaying sending.
Read packet are parsed and then passed to main sending thread for further processing.
This processing involves updating stats, matching with sent packets and issueing responses if necessary.
There have been removed some features that do not make sense after introducing these changes.
These includes: aggresivity and some custom statistics.
libperfdhcp_la_SOURCES += rate_control.cc rate_control.h
libperfdhcp_la_SOURCES += stats_mgr.h
libperfdhcp_la_SOURCES += test_control.cc test_control.h
+libperfdhcp_la_SOURCES += receiver.cc receiver.h
+libperfdhcp_la_SOURCES += better_socket.cc better_socket.h
sbin_PROGRAMS = perfdhcp
perfdhcp_SOURCES = main.cc
--- /dev/null
+// Copyright (C) 2012-2018 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 "better_socket.h"
+
+#include <dhcp/iface_mgr.h>
+
+#include <boost/foreach.hpp>
+
+
+using namespace isc::dhcp;
+
+namespace isc {
+namespace perfdhcp {
+
+BetterSocket::BetterSocket(const int socket) :
+SocketInfo(asiolink::IOAddress("127.0.0.1"), 0, socket),
+ ifindex_(0), valid_(true) {
+ try {
+ initSocketData();
+ } catch (const Exception&) {
+ valid_ = false;
+ }
+}
+
+BetterSocket::~BetterSocket() {
+ IfacePtr iface = IfaceMgr::instance().getIface(ifindex_);
+ if (iface) {
+ iface->delSocket(sockfd_);
+ }
+}
+
+void
+BetterSocket::initSocketData() {
+ BOOST_FOREACH(IfacePtr iface, IfaceMgr::instance().getIfaces()) {
+ BOOST_FOREACH(SocketInfo s, iface->getSockets()) {
+ if (s.sockfd_ == sockfd_) {
+ ifindex_ = iface->getIndex();
+ addr_ = s.addr_;
+ return;
+ }
+ }
+ }
+ isc_throw(BadValue, "interface for for specified socket "
+ "descriptor not found");
+}
+
+}
+}
--- /dev/null
+// Copyright (C) 2012-2018 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/.
+
+#pragma once
+
+#include <dhcp/socket_info.h>
+
+namespace isc {
+namespace perfdhcp {
+
+
+/// \brief Socket wrapper structure.
+///
+/// This is the wrapper that holds descriptor of the socket
+/// used to run DHCP test. The wrapped socket is closed in
+/// the destructor. This prevents resource leaks when
+/// function that created the socket ends (normally or
+/// when exception occurs). This structure extends parent
+/// structure with new field ifindex_ that holds interface
+/// index where socket is bound to.
+struct BetterSocket : 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.
+ BetterSocket(const int socket);
+
+ /// \brief Destructor of the socket wrapper class.
+ ///
+ /// Destructor closes wrapped socket.
+ ~BetterSocket();
+
+private:
+ /// \brief Initialize socket data.
+ ///
+ /// This method initializes members of the class that Interface
+ /// Manager holds: interface name, local address.
+ ///
+ /// \throw isc::BadValue if interface for specified socket
+ /// descriptor does not exist.
+ void initSocketData();
+};
+
+}
+}
#include <stdint.h>
#include <unistd.h>
#include <fstream>
+#include <thread>
#ifdef HAVE_OPTRESET
extern int optreset;
localname_.clear();
is_interface_ = false;
preload_ = 0;
- aggressivity_ = 1;
local_port_ = 0;
seeded_ = false;
seed_ = 0;
v6_relay_encapsulation_level_ = 0;
generateDuidTemplate();
extra_opts_.clear();
+ if (std::thread::hardware_concurrency() == 1) {
+ single_thread_mode_ = true;
+ } else {
+ single_thread_mode_ = false;
+ }
}
bool
// 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:M:"
- "s:iBc1T:X:O:o:E:S:I:x:W:w:e:f:F:")) != -1) {
+ while((opt = getopt(argc, argv, "hv46A:r:t:R:b:n:p:d:D:l:P:L:M:"
+ "s:iBc1T:X:O:o:E:S:I:x:W:w:e:f:F:g:")) != -1) {
stream << " -" << static_cast<char>(opt);
if (optarg) {
stream << " " << optarg;
ipversion_ = 6;
break;
- case 'a':
- aggressivity_ = positiveInteger("value of aggressivity: -a<value>"
- " must be a positive integer");
- break;
-
case 'b':
check(base_.size() > 3, "-b<value> already specified,"
" unexpected occurrence of 5th -b<value>");
" positive integer");
break;
+ case 'g': {
+ auto optarg_text = std::string(optarg);
+ if (optarg_text == "single") {
+ single_thread_mode_ = true;
+ } else if (optarg_text == "multi") {
+ single_thread_mode_ = false;
+ } else {
+ isc_throw(InvalidParameter, "value of thread mode (-g) '" << optarg << "' is wrong - should be '-g single' or '-g multi'");
+ }
+ break;
+ }
case 'h':
usage();
return (true);
break;
default:
- isc_throw(isc::InvalidParameter, "unknown command line option");
+ isc_throw(isc::InvalidParameter, "wrong command line option");
}
}
"use -I<ip-offset>");
check((!getMacListFile().empty() && base_.size() > 0),
"Can't use -b with -M option");
+
+ auto nthreads = std::thread::hardware_concurrency();
+ if (nthreads == 1 && isSingleThreaded() == false) {
+ std::cout << "WARNING: Currently system can run only 1 thread in parallel." << std::endl
+ << "WARNING: Better results are achieved when run in single-threaded mode." << std::endl
+ << "WARNING: To switch use -g single option." << std::endl;
+ } else if (nthreads > 1 && isSingleThreaded()) {
+ std::cout << "WARNING: Currently system can run more than 1 thread in parallel." << std::endl
+ << "WARNING: Better results are achieved when run in multi-threaded mode." << std::endl
+ << "WARNING: To switch use -g multi option." << std::endl;
+ }
}
void
if (preload_ != 0) {
std::cout << "preload=" << preload_ << std::endl;
}
- std::cout << "aggressivity=" << aggressivity_ << std::endl;
if (getLocalPort() != 0) {
std::cout << "local-port=" << local_port_ << std::endl;
}
if (!server_name_.empty()) {
std::cout << "server=" << server_name_ << std::endl;
}
+ if (single_thread_mode_) {
+ std::cout << "single-thread-mode" << std::endl;
+ } else {
+ std::cout << "multi-thread-mode" << std::endl;
+ }
}
void
" [-F<release-rate>] [-t<report>] [-R<range>] [-b<base>]\n"
" [-n<num-request>] [-p<test-period>] [-d<drop-time>]\n"
" [-D<max-drop>] [-l<local-addr|interface>] [-P<preload>]\n"
- " [-a<aggressivity>] [-L<local-port>] [-s<seed>] [-i] [-B]\n"
+ " [-L<local-port>] [-s<seed>] [-i] [-B] [-g<single/multi>]\n"
" [-W<late-exit-delay>]\n"
" [-c] [-1] [-M<mac-list-file>] [-T<template-file>]\n"
" [-X<xid-offset>] [-O<random-offset] [-E<time-offset>]\n"
"-1: Take the server-ID option from the first received message.\n"
"-4: DHCPv4 operation (default). This is incompatible with the -6 option.\n"
"-6: DHCPv6 operation. This is incompatible with the -4 option.\n"
- "-a<aggressivity>: When the target sending rate is not yet reached,\n"
- " control how many exchanges are initiated before the next pause.\n"
"-b<base>: The base mac, duid, IP, etc, used to simulate different\n"
" clients. This can be specified multiple times, each instance is\n"
" in the <type>=<value> form, for instance:\n"
" with the exchange rate (given by -r<rate>). Furthermore the sum of\n"
" this value and the release-rate (given by -F<rate) must be equal\n"
" to or less than the exchange rate.\n"
+ "-g Select thread mode: 'single' or 'multi'. In multi-thread mode packets\n"
+ " are received in separate thread. This allows better utilisation of CPUs."
+ " If more than 1 CPU is present then multi-thread mode is default otherwise"
+ " single-thread is default."
"-h: Print this help.\n"
"-i: Do only the initial part of an exchange: DO or SA, depending on\n"
" whether -6 is given.\n"
/// \return number of preload exchanges.
int getPreload() const { return preload_; }
- /// \brief Returns aggressivity value.
- ///
- /// \return aggressivity value.
- int getAggressivity() const { return aggressivity_; }
-
/// \brief Returns local port number.
///
/// \return local port number.
/// @return container with options
const isc::dhcp::OptionCollection& getExtraOpts() const { return extra_opts_; }
+ /// \brief Check if single-threaded mode is enabled.
+ ///
+ /// \return true if single-threaded mode is enabled.
+ bool isSingleThreaded() const { return single_thread_mode_; }
+
/// \brief Returns server name.
///
/// \return server name.
/// measurements.
int preload_;
- /// Number of exchanges sent before next pause.
- int aggressivity_;
-
/// Local port number (host endian)
int local_port_;
/// @brief Extra options to be sent in each packet.
isc::dhcp::OptionCollection extra_opts_;
+
+ /// @brief Option to switch modes between single-threaded and multi-threaded.
+ bool single_thread_mode_;
};
} // namespace perfdhcp
}
} catch(isc::Exception& e) {
ret_code = 1;
+ command_options.usage();
std::cerr << "Error parsing command line options: "
<< e.what() << std::endl;
- command_options.usage();
if (diags.find('e') != std::string::npos) {
std::cerr << "Fatal error" << std::endl;
}
<arg choice="opt" rep="norepeat"><option>-1</option></arg>
<arg choice="opt" rep="norepeat"><option>-4|-6</option></arg>
<arg choice="opt" rep="norepeat"><option>-A <replaceable class="parameter">encapsulation-level</replaceable></option></arg>
- <arg choice="opt" rep="norepeat"><option>-a <replaceable class="parameter">aggressivity</replaceable></option></arg>
<arg choice="opt" rep="norepeat"><option>-b <replaceable class="parameter">base</replaceable></option></arg>
<arg choice="opt" rep="norepeat"><option>-B</option></arg>
<arg choice="opt" rep="norepeat"><option>-c</option></arg>
<arg choice="opt" rep="norepeat"><option>-E <replaceable class="parameter">time-offset</replaceable></option></arg>
<arg choice="opt" rep="norepeat"><option>-f <replaceable class="parameter">renew-rate</replaceable></option></arg>
<arg choice="opt" rep="norepeat"><option>-F <replaceable class="parameter">release-rate</replaceable></option></arg>
+ <arg choice="opt" rep="norepeat"><option>-g <replaceable class="parameter">thread-mode</replaceable></option></arg>
<arg choice="opt" rep="norepeat"><option>-h</option></arg>
<arg choice="opt" rep="norepeat"><option>-i</option></arg>
<arg choice="opt" rep="norepeat"><option>-I <replaceable class="parameter">ip-offset</replaceable></option></arg>
</listitem>
</varlistentry>
- <varlistentry>
- <term><option>-a <replaceable class="parameter">aggressivity</replaceable></option></term>
- <listitem>
- <para>
- When the target sending rate is not yet reached,
- control how many exchanges are initiated before the
- next pause. This is a positive integer and defaults
- to 1.
- </para>
- </listitem>
- </varlistentry>
-
<varlistentry>
<term><option>-b <replaceable class="parameter">basetype=value</replaceable></option></term>
<listitem>
</listitem>
</varlistentry>
+ <varlistentry>
+ <term><option>-g <replaceable class="parameter">thread-mode</replaceable></option></term>
+ <listitem>
+ <para>
+ Thread operation mode can be either 'single' or 'multi'. In multi-thread mode packets
+ are received in separate thread. This allows better utilisation of CPUs.
+ In single CPU system it is better to run in 1 thread to avoid blocking of threads each other.
+ If more than 1 CPU is present in the system then multi-thread mode is default otherwise
+ single-thread is default.
+ </para>
+ </listitem>
+ </varlistentry>
<varlistentry>
<term><option>-h</option></term>
using namespace boost::posix_time;
RateControl::RateControl()
- : send_due_(currentTime()), last_sent_(currentTime()),
- aggressivity_(1), rate_(0), late_sent_(false) {
+ : rate_(0), total_pkts_sent_count_(0) {
}
-RateControl::RateControl(const int rate, const int aggressivity)
- : send_due_(currentTime()), last_sent_(currentTime()),
- aggressivity_(aggressivity), rate_(rate), late_sent_(false) {
- if (aggressivity_ < 1) {
- isc_throw(isc::BadValue, "invalid value of aggressivity "
- << aggressivity << ", expected value is greater than 0");
- }
+RateControl::RateControl(const int rate)
+ : rate_(rate), total_pkts_sent_count_(0) {
if (rate_ < 0) {
isc_throw(isc::BadValue, "invalid value of rate " << rate
<< ", expected non-negative value");
uint64_t
RateControl::getOutboundMessageCount() {
+ if (total_pkts_sent_count_ == 0) {
+ start_time_ = currentTime();
+ total_pkts_sent_count_ = 1;
+ return 1;
+ }
+
+ // If rate is not limited, then each time send 1 packet.
+ if (getRate() == 0) {
+ return 1;
+ }
- // We need calculate the due time for sending next set of messages.
- updateSendDue();
+ // Estimate number of packets to sent. If we are behind of time we will
+ // try to catch up to upkeep request rate by sending more packets in one cycle.
+ auto now = currentTime();
+ time_period period(start_time_, now);
+ time_duration duration = period.length();
+ uint64_t should_sent_pkts_count = static_cast<double>(getRate()) / static_cast<double>(time_duration::ticks_per_second()) * duration.ticks();
+ if (should_sent_pkts_count <= total_pkts_sent_count_) {
+ return 0;
+ }
+ auto pending_pkts_count = should_sent_pkts_count - total_pkts_sent_count_;
- // Get current time. If we are behind due time, we have to calculate
- // how many messages to send to catch up with the rate.
- ptime now = currentTime();
- if (now >= send_due_) {
- // Reset number of exchanges.
- uint64_t due_exchanges = 0;
- // If rate is specified from the command line we have to
- // synchronize with it.
- if (getRate() != 0) {
- time_period period(send_due_, now);
- time_duration duration = period.length();
- // due_factor indicates the number of seconds that
- // sending next chunk of packets will take.
- double due_factor =
- static_cast<double>(duration.fractional_seconds()) /
- static_cast<double>(time_duration::ticks_per_second());
- due_factor += static_cast<double>(duration.total_seconds());
- // Multiplying due_factor by expected rate gives the number
- // of exchanges to be initiated.
- due_exchanges = static_cast<uint64_t>(due_factor * getRate());
- // We want to make sure that at least one packet goes out.
- if (due_exchanges == 0) {
- due_exchanges = 1;
- }
- // We should not exceed aggressivity as it could have been
- // restricted from command line.
- if (due_exchanges > getAggressivity()) {
- due_exchanges = getAggressivity();
- }
- } else {
- // Rate is not specified so we rely on aggressivity
- // which is the number of packets to be sent in
- // one chunk.
- due_exchanges = getAggressivity();
- }
- return (due_exchanges);
+ // Reduce bursts to have more uniform traffic.
+ if (pending_pkts_count > 3) {
+ pending_pkts_count = 3;
}
- return (0);
+ total_pkts_sent_count_ += pending_pkts_count;
+
+ return pending_pkts_count;
}
boost::posix_time::ptime
return (microsec_clock::universal_time());
}
-void
-RateControl::updateSendDue() {
- // There is no sense to update due time if the current due time is in the
- // future. The due time is calculated as a duration between the moment
- // when the last message of the given type was sent and the time when
- // next one is supposed to be sent based on a given rate. The former value
- // will not change until we send the next message, which we don't do
- // until we reach the due time.
- if (send_due_ > currentTime()) {
- return;
- }
- // This is initialized in the class constructor, so if it is not initialized
- // it is a programmatic error.
- if (last_sent_.is_not_a_date_time()) {
- isc_throw(isc::Unexpected, "timestamp of the last sent packet not"
- " initialized");
- }
- // If rate was not specified we will wait just one clock tick to
- // send next packet. This simulates best effort conditions.
- long duration = 1;
- if (getRate() != 0) {
- // We use number of ticks instead of nanoseconds because
- // nanosecond resolution may not be available on some
- // machines. Number of ticks guarantees the highest possible
- // timer resolution.
- duration = time_duration::ticks_per_second() / getRate();
- }
- // Calculate due time to initiate next chunk of exchanges.
- send_due_ = last_sent_ + time_duration(0, 0, 0, duration);
- if (send_due_ > currentTime()) {
- late_sent_ = true;
- } else {
- late_sent_ = false;
- }
-}
-
-void
-RateControl::setAggressivity(const int aggressivity) {
- if (aggressivity < 1) {
- isc_throw(isc::BadValue, "invalid value of aggressivity "
- << aggressivity << ", expected value is greater than 0");
- }
- aggressivity_ = aggressivity;
-}
-
void
RateControl::setRate(const int rate) {
if (rate < 0) {
rate_ = rate;
}
-void
-RateControl::setRelativeDue(const int offset) {
- send_due_ = offset > 0 ?
- currentTime() + seconds(abs(offset)) :
- currentTime() - seconds(abs(offset));
-}
-
-void
-RateControl::updateSendTime() {
- last_sent_ = currentTime();
-}
-
} // namespace perfdhcp
} // namespace isc
/// \brief Default constructor.
RateControl();
- /// \brief Constructor which sets desired rate and aggressivity.
+ /// \brief Constructor which sets desired rate.
///
/// \param rate A desired rate.
- /// \param aggressivity A desired aggressivity.
- RateControl(const int rate, const int aggressivity);
-
- /// \brief Returns the value of aggressivity.
- int getAggressivity() const {
- return (aggressivity_);
- }
-
- /// \brief Returns current due time to send next message.
- boost::posix_time::ptime getDue() const {
- return (send_due_);
- }
+ RateControl(const int rate);
/// \brief Returns number of messages to be sent "now".
///
/// the timer resolution). If the calculated value is equal to 0, it is
/// rounded to 1, so as at least one message is sent.
///
- /// The value of aggressivity limits the maximal number of messages to
- /// be sent one after another. If the number of messages calculated with
- /// the equation above exceeds the aggressivity, this function will return
- /// the value equal to aggressivity.
- ///
- /// If the rate is not specified (equal to 0), the value calculated by
- /// this function is equal to aggressivity.
- ///
/// \return A number of messages to be sent immediately.
uint64_t getOutboundMessageCount();
return (rate_);
}
- /// \brief Returns the value of the late send flag.
- ///
- /// The flag returned by this function indicates whether the new due time
- /// calculated by the \c RateControl::updateSendDue is in the past.
- /// This value is used by the \c TestControl object to increment the counter
- /// of the late sent messages in the \c StatsMgr.
- bool isLateSent() const {
- return (late_sent_);
- }
-
- /// \brief Sets the value of aggressivity.
- ///
- /// \param aggressivity A new value of aggressivity. This value must be
- /// a positive integer.
- /// \throw isc::BadValue if new value is not a positive integer.
- void setAggressivity(const int aggressivity);
-
/// \brief Sets the new rate.
///
/// \param rate A new value of rate. This value must not be negative.
/// \throw isc::BadValue if new rate is negative.
void setRate(const int rate);
- /// \brief Sets the value of the due time.
- ///
- /// This function is intended for unit testing. It manipulates the value of
- /// the due time. The parameter passed to this function specifies the
- /// (positive or negative) number of seconds relative to current time.
- ///
- /// \param offset A number of seconds relative to current time which
- /// constitutes the new due time.
- void setRelativeDue(const int offset);
-
- /// \brief Sets the timestamp of the last sent message to current time.
- void updateSendTime();
-
protected:
/// \brief Convenience function returning current time.
///
/// \return current time.
- static boost::posix_time::ptime currentTime();
-
- /// \brief Calculates the send due.
- ///
- /// This function calculates the send due timestamp using the current time
- /// and desired rate. The due timestamp is calculated as a sum of the
- /// timestamp when the last message was sent and the reciprocal of the rate
- /// in micro or nanoseconds (depending on the timer resolution). If the rate
- /// is not specified, the duration between two consecutive sends is one
- /// timer tick.
- void updateSendDue();
-
- /// \brief Holds a timestamp when the next message should be sent.
- boost::posix_time::ptime send_due_;
-
- /// \brief Holds a timestamp when the last message was sent.
- boost::posix_time::ptime last_sent_;
-
- /// \brief Holds an aggressivity value.
- int aggressivity_;
+ boost::posix_time::ptime currentTime();
/// \brief Holds a desired rate value.
int rate_;
- /// \brief A flag which indicates that the calculated due time is in the
- /// past.
- bool late_sent_;
+ /// \brief Holds number of packets send from the beginning.
+
+ /// It is used to calculate current request rate. Then this is used
+ /// to estimate number of packets to send in current cycle.
+ uint64_t total_pkts_sent_count_;
+
+ /// \brief Holds time of start of testing.
+ /// It is used to calculate current request rate. Then this is used
+ /// to estimate number of packets to send in current cycle.
+ boost::posix_time::ptime start_time_;
};
}
--- /dev/null
+// Copyright (C) 2018 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 "receiver.h"
+#include "command_options.h"
+
+#include <dhcp/iface_mgr.h>
+
+using namespace std;
+
+namespace isc {
+namespace perfdhcp {
+
+
+void
+Receiver::start() {
+ if (single_threaded_) {
+ return;
+ }
+ assert(run_flag_.test_and_set() == false);
+ recv_thread_ = move(unique_ptr<thread>(new thread{&Receiver::run, this}));
+}
+
+void
+Receiver::stop() {
+ if (single_threaded_) {
+ return;
+ }
+ // Clear flags to order the thread to stop its main loop.
+ run_flag_.clear();
+
+ // To be sure first check if thread is running and ready to be joined.
+ if (recv_thread_->joinable()) {
+ recv_thread_->join();
+ }
+}
+
+Receiver::~Receiver() {
+ if (single_threaded_) {
+ return;
+ }
+ stop();
+}
+
+
+PktPtr
+Receiver::getPkt() {
+ if (single_threaded_) {
+ // In single thread mode read packet directly from the socket and return it.
+ auto pkt = readPktFromSocket();
+ return pkt;
+ } else {
+ // In multi thread mode read packet from the queue which is feed by Receiver thread.
+ unique_lock<mutex> lock(pkt_queue_mutex_);
+ if (pkt_queue_.empty()) {
+ if (CommandOptions::instance().getIpVersion() == 4) {
+ return Pkt4Ptr{nullptr};
+ } else {
+ return Pkt6Ptr{nullptr};
+ }
+ }
+ auto pkt = pkt_queue_.front();
+ pkt_queue_.pop();
+ return pkt;
+ }
+}
+
+void
+Receiver::run() {
+ assert(single_threaded_ == false);
+ try {
+ // If the flag is still true receive packets.
+ while (run_flag_.test_and_set()) {
+ receivePackets();
+ }
+ } catch (...) {
+ cout << "SOMETHING WENT WRONG" << endl;
+ }
+}
+
+PktPtr
+Receiver::readPktFromSocket() {
+ PktPtr pkt;
+ uint32_t timeout;
+ if (single_threaded_) {
+ // In case of single thread just check socket and if empty exit immediatelly
+ // to not slow down sending part.
+ timeout = 0;
+ } else {
+ // In case of multi thread wait for packets a little bit (1ms) as it is run
+ // in separate thread and do not interefere with sending thread.
+ timeout = 1000;
+ }
+ try {
+ if (CommandOptions::instance().getIpVersion() == 4) {
+ pkt = IfaceMgr::instance().receive4(0, timeout);
+ } else {
+ pkt = IfaceMgr::instance().receive6(0, timeout);
+ }
+ } catch (const Exception& e) {
+ cerr << "Failed to receive DHCP packet: " << e.what() << endl;
+ }
+ if (!pkt) {
+ return nullptr;
+ }
+
+ /// @todo: Add packet exception handling here. Right now any
+ /// malformed packet will cause perfdhcp to abort.
+ pkt->unpack();
+ return pkt;
+}
+
+void
+Receiver::receivePackets() {
+ while (true) {
+ PktPtr pkt = readPktFromSocket();
+ if (!pkt) {
+ break;
+ }
+
+ // Drop packet if not supported.
+ if (pkt->getType() == DHCPOFFER || pkt->getType() == DHCPACK ||
+ pkt->getType() == DHCPV6_ADVERTISE || pkt->getType() == DHCPV6_REPLY) {
+ // Otherwise push to another thread.
+ unique_lock<mutex> lock(pkt_queue_mutex_);
+ pkt_queue_.push(pkt);
+ }
+ }
+}
+
+}
+}
--- /dev/null
+// Copyright (C) 2018 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/.
+
+#pragma once
+
+#include "better_socket.h"
+#include "command_options.h"
+
+#include <dhcp/pkt4.h>
+#include <dhcp/pkt6.h>
+
+#include <queue>
+#include <thread>
+#include <mutex>
+#include <boost/atomic.hpp>
+
+using namespace std;
+using namespace isc::dhcp;
+
+namespace isc {
+namespace perfdhcp {
+
+typedef boost::shared_ptr<Pkt> PktPtr;
+
+/// \brief A receviving DHCP packets class.
+///
+/// Receiver can be used in two modes: single-thread and multi-thread.
+///
+/// In single-thread mode the class directly reads packets from socket
+/// and returns them to consumer using getPkt method.
+///
+/// In case of multi-thread mode the class starts a thread in backgroud.
+/// The thread reads the packets and pushes them to pkt_queue_. Then
+/// in main thread packets can be consumed from the queue using getPkt
+/// method.
+class Receiver {
+ /// \brief Socket for receiving.
+ const BetterSocket& socket_;
+
+ /// \brief Flag indicating if thread should run (true) or not (false).
+ boost::atomic_flag run_flag_;
+
+ /// \brief Thread for receiving packets.
+ unique_ptr<thread> recv_thread_;
+
+ /// \brief Queue for passing packets from receiver thread to main thread.
+ queue<PktPtr> pkt_queue_;
+
+ /// \brief Mutex for controlling access to the queue.
+ mutex pkt_queue_mutex_;
+
+ /// \brief Single- or thread-mode indicator.
+ bool single_threaded_;
+
+public:
+ /// \brief Receiver constructor.
+ ///
+ /// \param socket A socket for receiving packets.
+ Receiver(const BetterSocket& socket) :
+ socket_(socket),
+ single_threaded_(CommandOptions::instance().isSingleThreaded()) {
+ }
+
+ /// \brief Destructor.
+ ~Receiver();
+
+ /// \brief Start a receiving thread in multi-thread mode.
+ ///
+ /// In single-thread mode it does nothing.
+ void start();
+
+ /// \brief Stop a receiving thread in multi-thread mode.
+ ///
+ /// In single-thread mode it does nothing.
+ void stop();
+
+ /// \brief Get DHCP packet.
+ ///
+ /// In single-thread mode it reads directly from the socket.
+ /// In multi-thread mode it reads packets from the queue.
+ PktPtr getPkt();
+
+private:
+ /// \brief Receiving thread main function.
+ void run();
+
+ /// \brief Receive packets from sockets and pushes them to the queue.
+ ///
+ /// It runs in a loop until socket is empty.
+ void receivePackets();
+
+ /// \brief Read a packet directly from the socket.
+ PktPtr readPktFromSocket();
+};
+
+}
+}
/// perfdhcp.
void printMainStats() const {
using namespace std;
- cout << "sent packets: " << getSentPacketsNum() << endl
+ auto sent = getSentPacketsNum();
+ auto drops = getDroppedPacketsNum();
+ double drops_ratio = 100 * static_cast<double>(drops) / static_cast<double>(sent);
+
+ cout << "sent packets: " << sent << endl
<< "received packets: " << getRcvdPacketsNum() << endl
- << "drops: " << getDroppedPacketsNum() << endl;
+ << "drops: " << drops << endl
+ << "drops ratio: " << drops_ratio << " %" << endl;
// << "orphans: " << getOrphans() << endl;
}
boost::posix_time::ptime boot_time_; ///< Time when test is started.
};
+/// 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;
+
} // namespace perfdhcp
} // namespace isc
#include <dhcp/option6_ia.h>
#include <util/unittests/check_valgrind.h>
#include "test_control.h"
+#include "receiver.h"
#include "command_options.h"
#include "perf_pkt4.h"
#include "perf_pkt6.h"
#include <boost/date_time/posix_time/posix_time.hpp>
-#include <boost/foreach.hpp>
#include <algorithm>
#include <fstream>
bool TestControl::interrupted_ = false;
-ptime late_exit_target_time_ = ptime(not_a_date_time);
+/// \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
-TestControl::hasLateExitCommenced() const {
- return !late_exit_target_time_.is_not_a_date_time();
+testDiags(const char diag) {
+ std::string diags(CommandOptions::instance().getDiags());
+ if (diags.find(diag) != std::string::npos) {
+ return (true);
+ }
+ return (false);
}
bool
return (responses == requests);
}
-TestControl::TestControlSocket::TestControlSocket(const int socket) :
- SocketInfo(asiolink::IOAddress("127.0.0.1"), 0, socket),
- ifindex_(0), valid_(true) {
- try {
- initSocketData();
- } catch (const Exception&) {
- valid_ = false;
- }
-}
-
-TestControl::TestControlSocket::~TestControlSocket() {
- IfacePtr iface = IfaceMgr::instance().getIface(ifindex_);
- if (iface) {
- iface->delSocket(sockfd_);
- }
-}
-
-void
-TestControl::TestControlSocket::initSocketData() {
- BOOST_FOREACH(IfacePtr iface, IfaceMgr::instance().getIfaces()) {
- BOOST_FOREACH(SocketInfo s, iface->getSockets()) {
- if (s.sockfd_ == sockfd_) {
- ifindex_ = iface->getIndex();
- addr_ = s.addr_;
- return;
- }
- }
- }
- isc_throw(BadValue, "interface for for specified socket "
- "descriptor not found");
-}
-
TestControl&
TestControl::instance() {
static TestControl test_control;
reset();
}
-void
-TestControl::checkLateMessages(RateControl& rate_control) {
- // If diagnostics is disabled, there is no need to log late sent messages.
- // If it is enabled and the rate control object indicates that the last
- // sent message was late, bump up the counter in Stats Manager.
- if (rate_control.isLateSent() && testDiags('i')) {
- CommandOptions& options = CommandOptions::instance();
- if (options.getIpVersion() == 4) {
- stats_mgr4_->incrementCounter("latesend");
- } else if (options.getIpVersion() == 6) {
- stats_mgr6_->incrementCounter("latesend");
- }
- }
-}
-
void
TestControl::cleanCachedPackets() {
CommandOptions& options = CommandOptions::instance();
}
pkt_to->addOption(option);
}
-
-
}
std::string
}
}
-uint32_t
-TestControl::getCurrentTimeout() const {
- CommandOptions& options = CommandOptions::instance();
- ptime now(microsec_clock::universal_time());
- // Check that we haven't passed the moment to send the next set of
- // packets.
- if (now >= basic_rate_control_.getDue() ||
- (options.getRenewRate() != 0 && now >= renew_rate_control_.getDue()) ||
- (options.getReleaseRate() != 0 &&
- now >= release_rate_control_.getDue())) {
- return (0);
- }
-
- // Let's assume that the due time for Solicit is the soonest.
- ptime due = basic_rate_control_.getDue();
- // If we are sending Renews and due time for Renew occurs sooner,
- // set the due time to Renew due time.
- if ((options.getRenewRate()) != 0 && (renew_rate_control_.getDue() < due)) {
- due = renew_rate_control_.getDue();
- }
- // If we are sending Releases and the due time for Release occurs
- // sooner than the current due time, let's use the due for Releases.
- if ((options.getReleaseRate() != 0) &&
- (release_rate_control_.getDue() < due)) {
- due = release_rate_control_.getDue();
- }
- // Return the timeout in microseconds.
- return (time_period(now, due).length().total_microseconds());
-}
-
int
TestControl::getElapsedTimeOffset() const {
int elp_offset = CommandOptions::instance().getIpVersion() == 4 ?
}
if (testDiags('i')) {
if (options.getIpVersion() == 4) {
- stats_mgr4_->addCustomCounter("latesend", "Late sent packets");
stats_mgr4_->addCustomCounter("shortwait", "Short waits for packets");
- stats_mgr4_->addCustomCounter("multircvd", "Multiple packets receives");
- stats_mgr4_->addCustomCounter("latercvd", "Late received packets");
} else if (options.getIpVersion() == 6) {
- stats_mgr6_->addCustomCounter("latesend", "Late sent packets");
stats_mgr6_->addCustomCounter("shortwait", "Short waits for packets");
- stats_mgr6_->addCustomCounter("multircvd", "Multiple packets receives");
- stats_mgr6_->addCustomCounter("latercvd", "Late received packets");
}
}
}
}
void
-TestControl::sendPackets(const TestControlSocket& socket,
+TestControl::sendPackets(const BetterSocket& socket,
const uint64_t packets_num,
const bool preload /* = false */) {
CommandOptions& options = CommandOptions::instance();
sendSolicit6(socket, template_buffers_[0], preload);
}
}
- // If we preload server we don't want to receive any packets.
- if (!preload) {
- uint64_t latercvd = receivePackets(socket);
- if (testDiags('i')) {
- if (options.getIpVersion() == 4) {
- stats_mgr4_->incrementCounter("latercvd", latercvd);
- } else if (options.getIpVersion() == 6) {
- stats_mgr6_->incrementCounter("latercvd", latercvd);
- }
- }
- }
}
}
uint64_t
-TestControl::sendMultipleRequests(const TestControlSocket& socket,
+TestControl::sendMultipleRequests(const BetterSocket& socket,
const uint64_t msg_num) {
for (uint64_t i = 0; i < msg_num; ++i) {
if (!sendRequestFromAck(socket)) {
}
uint64_t
-TestControl::sendMultipleMessages6(const TestControlSocket& socket,
+TestControl::sendMultipleMessages6(const BetterSocket& socket,
const uint32_t msg_type,
const uint64_t msg_num) {
for (uint64_t i = 0; i < msg_num; ++i) {
}
void
-TestControl::processReceivedPacket4(const TestControlSocket& socket,
+TestControl::processReceivedPacket4(const BetterSocket& socket,
const Pkt4Ptr& pkt4) {
if (pkt4->getType() == DHCPOFFER) {
Pkt4Ptr discover_pkt4(stats_mgr4_->passRcvdPacket(StatsMgr4::XCHG_DO,
}
void
-TestControl::processReceivedPacket6(const TestControlSocket& socket,
+TestControl::processReceivedPacket6(const BetterSocket& socket,
const Pkt6Ptr& pkt6) {
uint8_t packet_type = pkt6->getType();
if (packet_type == DHCPV6_ADVERTISE) {
}
}
-uint64_t
-TestControl::receivePackets(const TestControlSocket& socket) {
- bool receiving = true;
- uint64_t received = 0;
- while (receiving) {
+void
+TestControl::consumeReceivedPackets(Receiver& receiver, const BetterSocket& socket) {
+ PktPtr pkt;
+ while (pkt = receiver.getPkt()) {
if (CommandOptions::instance().getIpVersion() == 4) {
- Pkt4Ptr pkt4;
- try {
- pkt4 = IfaceMgr::instance().receive4(0, getCurrentTimeout());
- } catch (const Exception& e) {
- std::cerr << "Failed to receive DHCPv4 packet: "
- << e.what() << std::endl;
- }
- if (!pkt4) {
- receiving = false;
- } else {
- ++received;
- if ((received > 1) && testDiags('i')) {
- stats_mgr4_->incrementCounter("multircvd");
- }
-
- /// @todo: Add packet exception handling here. Right now any
- /// malformed packet will cause perfdhcp to abort.
- pkt4->unpack();
- processReceivedPacket4(socket, pkt4);
- }
- } else if (CommandOptions::instance().getIpVersion() == 6) {
- Pkt6Ptr pkt6;
- try {
- pkt6 = IfaceMgr::instance().receive6(0, getCurrentTimeout());
- } catch (const Exception& e) {
- std::cerr << "Failed to receive DHCPv6 packet: "
- << e.what() << std::endl;
- }
- if (!pkt6) {
- receiving = false;
- } else {
- ++received;
- if ((received > 1) && testDiags('i')) {
- stats_mgr6_->incrementCounter("multircvd");
- }
-
- /// @todo: Add packet exception handling here. Right now any
- /// malformed packet will cause perfdhcp to abort.
- pkt6->unpack();
- processReceivedPacket6(socket, pkt6);
- }
+ Pkt4Ptr pkt4 = boost::dynamic_pointer_cast<Pkt4>(pkt);
+ processReceivedPacket4(socket, pkt4);
+ } else {
+ Pkt6Ptr pkt6 = boost::dynamic_pointer_cast<Pkt6>(pkt);
+ processReceivedPacket6(socket, pkt6);
}
}
- return (received);
}
-
void
TestControl::registerOptionFactories4() const {
static bool factories_registered = false;
void
TestControl::reset() {
CommandOptions& options = CommandOptions::instance();
- basic_rate_control_.setAggressivity(options.getAggressivity());
basic_rate_control_.setRate(options.getRate());
- renew_rate_control_.setAggressivity(options.getAggressivity());
renew_rate_control_.setRate(options.getRenewRate());
- release_rate_control_.setAggressivity(options.getAggressivity());
release_rate_control_.setRate(options.getReleaseRate());
transid_gen_.reset();
printDiagnostics();
// Option factories have to be registered.
registerOptionFactories();
- TestControlSocket socket(openSocket());
+ BetterSocket socket(openSocket());
if (!socket.valid_) {
isc_throw(Unexpected, "invalid socket descriptor");
}
// 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.
- sendPackets(socket, options.getPreload(), true);
+ if (options.getPreload() > 0) {
+ sendPackets(socket, options.getPreload(), true);
+ }
// Fork and run command specified with -w<wrapped-command>
if (!options.getWrapped().empty()) {
runWrapped();
}
- // Initialize Statistics Manager. Release previous if any.
- initializeStatsMgr();
+ receiver.start();
+
for (;;) {
// Calculate number of packets to be sent to stay
// catch up with rate.
uint64_t packets_due = basic_rate_control_.getOutboundMessageCount();
- checkLateMessages(basic_rate_control_);
if ((packets_due == 0) && testDiags('i')) {
if (options.getIpVersion() == 4) {
stats_mgr4_->incrementCounter("shortwait");
}
}
- // @todo: set non-zero timeout for packets once we implement
- // microseconds timeout in IfaceMgr.
- receivePackets(socket);
+ // Pull some packets from receiver thread, process them, update some stats
+ // and respond to the server if needed.
+ consumeReceivedPackets(receiver, socket);
// If test period finished, maximum number of packet drops
// has been reached or test has been interrupted we have to
break;
}
- if (!hasLateExitCommenced()) {
- // Initiate new DHCP packet exchanges.
- sendPackets(socket, packets_due);
- }
+ // 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();
- checkLateMessages(renew_rate_control_);
// Send multiple renews to satisfy the desired rate.
if (options.getIpVersion() == 4) {
if ((options.getIpVersion() == 6) && (options.getReleaseRate() != 0)) {
uint64_t release_packets_due =
release_rate_control_.getOutboundMessageCount();
- checkLateMessages(release_rate_control_);
// Send Release messages.
sendMultipleMessages6(socket, DHCPV6_RELEASE, release_packets_due);
}
// searches in the long list of Reply packets increases CPU utilization.
cleanCachedPackets();
}
+
+ receiver.stop();
+
printStats();
if (!options.getWrapped().empty()) {
}
void
-TestControl::sendDiscover4(const TestControlSocket& socket,
+TestControl::sendDiscover4(const BetterSocket& socket,
const bool preload /*= false*/) {
- basic_rate_control_.updateSendTime();
// Generate the MAC address to be passed in the packet.
uint8_t randomized = 0;
std::vector<uint8_t> mac_address = generateMacAddress(randomized);
}
void
-TestControl::sendDiscover4(const TestControlSocket& socket,
+TestControl::sendDiscover4(const BetterSocket& socket,
const std::vector<uint8_t>& template_buf,
const bool preload /* = false */) {
- basic_rate_control_.updateSendTime();
// Get the first argument if multiple the same arguments specified
// in the command line. First one refers to DISCOVER packets.
const uint8_t arg_idx = 0;
}
bool
-TestControl::sendRequestFromAck(const TestControlSocket& socket) {
- // Update timestamp of last sent renewal.
- renew_rate_control_.updateSendTime();
-
+TestControl::sendRequestFromAck(const BetterSocket& socket) {
// Get one of the recorded DHCPACK messages.
Pkt4Ptr ack = ack_storage_.getRandom();
if (!ack) {
bool
TestControl::sendMessageFromReply(const uint16_t msg_type,
- const TestControlSocket& socket) {
+ const BetterSocket& socket) {
// 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
<< " to be sent, expected DHCPV6_RENEW or DHCPV6_RELEASE");
}
- // We track the timestamp of last Release and Renew in different variables.
- if (msg_type == DHCPV6_RENEW) {
- renew_rate_control_.updateSendTime();
- } else {
- release_rate_control_.updateSendTime();
- }
Pkt6Ptr reply = reply_storage_.getRandom();
if (!reply) {
return (false);
}
void
-TestControl::sendRequest4(const TestControlSocket& socket,
+TestControl::sendRequest4(const BetterSocket& socket,
const dhcp::Pkt4Ptr& discover_pkt4,
const dhcp::Pkt4Ptr& offer_pkt4) {
// Use the same transaction id as the one used in the discovery packet.
}
void
-TestControl::sendRequest4(const TestControlSocket& socket,
+TestControl::sendRequest4(const BetterSocket& socket,
const std::vector<uint8_t>& template_buf,
const dhcp::Pkt4Ptr& discover_pkt4,
const dhcp::Pkt4Ptr& offer_pkt4) {
}
void
-TestControl::sendRequest6(const TestControlSocket& socket,
+TestControl::sendRequest6(const BetterSocket& socket,
const Pkt6Ptr& advertise_pkt6) {
const uint32_t transid = generateTransid();
Pkt6Ptr pkt6(new Pkt6(DHCPV6_REQUEST, transid));
}
void
-TestControl::sendRequest6(const TestControlSocket& socket,
+TestControl::sendRequest6(const BetterSocket& socket,
const std::vector<uint8_t>& template_buf,
const Pkt6Ptr& advertise_pkt6) {
// Get the second argument if multiple the same arguments specified
}
void
-TestControl::sendSolicit6(const TestControlSocket& socket,
+TestControl::sendSolicit6(const BetterSocket& socket,
const bool preload /*= false*/) {
- basic_rate_control_.updateSendTime();
// Generate DUID to be passed to the packet
uint8_t randomized = 0;
std::vector<uint8_t> duid = generateDuid(randomized);
}
void
-TestControl::sendSolicit6(const TestControlSocket& socket,
+TestControl::sendSolicit6(const BetterSocket& socket,
const std::vector<uint8_t>& template_buf,
const bool preload /*= false*/) {
- basic_rate_control_.updateSendTime();
const int arg_idx = 0;
// Get transaction id offset.
size_t transid_offset = getTransactionIdOffset(arg_idx);
void
-TestControl::setDefaults4(const TestControlSocket& socket,
+TestControl::setDefaults4(const BetterSocket& socket,
const Pkt4Ptr& pkt) {
CommandOptions& options = CommandOptions::instance();
// Interface name.
}
void
-TestControl::setDefaults6(const TestControlSocket& socket,
+TestControl::setDefaults6(const BetterSocket& socket,
const Pkt6Ptr& pkt) {
CommandOptions& options = CommandOptions::instance();
// Interface name.
}
}
-bool
-TestControl::testDiags(const char diag) const {
- std::string diags(CommandOptions::instance().getDiags());
- if (diags.find(diag) != std::string::npos) {
- return (true);
- }
- return (false);
-}
-
} // namespace perfdhcp
} // namespace isc
#include "packet_storage.h"
#include "rate_control.h"
#include "stats_mgr.h"
+#include "receiver.h"
#include <dhcp/iface_mgr.h>
#include <dhcp/dhcp6.h>
isc::Exception(file, line, what) { };
};
+
/// \brief Test Control class.
///
/// This singleton class is used to run the performance test with
class TestControl : public boost::noncopyable {
public:
- /// 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;
/// Packet template buffer.
typedef std::vector<uint8_t> TemplateBuffer;
/// Packet template buffers list.
/// @return true if need to wait, false = ok to exit now
bool waitToExit() const;
- /// @brief Check if the program is in that period where the program was
- /// bound to exit, but was delayed by lateExit().
- bool hasLateExitCommenced() const;
-
/// @brief Checks if all expected packets were already received
bool haveAllPacketsBeenReceived() const;
- /// \brief Socket wrapper structure.
- ///
- /// This is the wrapper that holds descriptor of the socket
- /// used to run DHCP test. The wrapped socket is closed in
- /// the destructor. This prevents resource leaks when when
- /// function that created the socket ends (normally or
- /// when exception occurs). This structure extends parent
- /// structure with new field ifindex_ that holds interface
- /// index where socket is bound to.
- struct TestControlSocket : public dhcp::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.
- TestControlSocket(const int socket);
-
- /// \brief Destructor of the socket wrapper class.
- ///
- /// Destructor closes wrapped socket.
- ~TestControlSocket();
-
- private:
- /// \brief Initialize socket data.
- ///
- /// This method initializes members of the class that Interface
- /// Manager holds: interface name, local address.
- ///
- /// \throw isc::BadValue if interface for specified socket
- /// descriptor does not exist.
- void initSocketData();
- };
-
/// \brief Number generator class.
///
/// This is default numbers generator class. The member function is
return (transid_gen_->generate());
}
- /// \brief Returns a timeout for packet reception.
- ///
- /// The calculation is based on the value of the timestamp
- /// when the next set of packets is to be sent. If no packet is
- /// received until then, new packets are sent.
- ///
- /// \return A current timeout in microseconds.
- uint32_t getCurrentTimeout() const;
-
/// \brief Return template buffer.
///
/// Method returns template buffer at specified index.
/// not initialized.
void printStats() const;
+ /// \brief Pull packets from receiver and process them.
+
+ /// It runs in a loop until there are no packets in receiver.
+ void consumeReceivedPackets(Receiver& receiver, const BetterSocket& socket);
+
/// \brief Process received DHCPv4 packet.
///
/// Method performs processing of the received DHCPv4 packet,
/// \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 TestControlSocket& socket,
+ void processReceivedPacket4(const BetterSocket& socket,
const dhcp::Pkt4Ptr& pkt4);
/// \brief Process received DHCPv6 packet.
/// \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 TestControlSocket& socket,
+ void processReceivedPacket6(const BetterSocket& socket,
const dhcp::Pkt6Ptr& pkt6);
/// \brief Receive DHCPv4 or DHCPv6 packets from the server.
/// \throw isc::BadValue if unknown message type received.
/// \throw isc::Unexpected if unexpected error occurred.
/// \return number of received packets.
- uint64_t receivePackets(const TestControlSocket& socket);
+ uint64_t receivePackets(const BetterSocket& socket);
/// \brief Register option factory functions for DHCPv4
///
/// \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 TestControlSocket& socket,
+ void sendDiscover4(const BetterSocket& socket,
const bool preload = false);
/// \brief Send DHCPv4 DISCOVER message from template.
///
/// \throw isc::OutOfRange if randomization offset is out of bounds.
/// \throw isc::dhcp::SocketWriteError if failed to send the packet.
- void sendDiscover4(const TestControlSocket& socket,
+ void sendDiscover4(const BetterSocket& socket,
const std::vector<uint8_t>& template_buf,
const bool preload = false);
/// \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 TestControlSocket &socket,
+ void sendPackets(const BetterSocket &socket,
const uint64_t packets_num,
const bool preload = false);
/// \param msg_num A number of messages to be sent.
///
/// \return A number of messages actually sent.
- uint64_t sendMultipleRequests(const TestControlSocket& socket,
+ uint64_t sendMultipleRequests(const BetterSocket& socket,
const uint64_t msg_num);
/// \brief Send number of DHCPv6 Renew or Release messages to the server.
/// \param msg_num A number of messages to be sent.
///
/// \return A number of messages actually sent.
- uint64_t sendMultipleMessages6(const TestControlSocket& socket,
+ uint64_t sendMultipleMessages6(const BetterSocket& socket,
const uint32_t msg_type,
const uint64_t msg_num);
/// a packet.
///
/// \return true if the message has been sent, false otherwise.
- bool sendRequestFromAck(const TestControlSocket& socket);
+ bool sendRequestFromAck(const BetterSocket& socket);
/// \brief Send DHCPv6 Renew or Release message using specified socket.
///
///
/// \return true if the message has been sent, false otherwise.
bool sendMessageFromReply(const uint16_t msg_type,
- const TestControlSocket& socket);
+ const BetterSocket& socket);
/// \brief Send DHCPv4 REQUEST message.
///
/// \throw isc::InvalidOperation if Statistics Manager has not been
/// initialized.
/// \throw isc::dhcp::SocketWriteError if failed to send the packet.
- void sendRequest4(const TestControlSocket& socket,
+ void sendRequest4(const BetterSocket& socket,
const dhcp::Pkt4Ptr& discover_pkt4,
const dhcp::Pkt4Ptr& offer_pkt4);
/// \param offer_pkt4 OFFER packet received.
///
/// \throw isc::dhcp::SocketWriteError if failed to send the packet.
- void sendRequest4(const TestControlSocket& socket,
+ void sendRequest4(const BetterSocket& socket,
const std::vector<uint8_t>& template_buf,
const dhcp::Pkt4Ptr& discover_pkt4,
const dhcp::Pkt4Ptr& offer_pkt4);
/// initialized.
///
/// \throw isc::dhcp::SocketWriteError if failed to send the packet.
- void sendRequest6(const TestControlSocket& socket,
+ void sendRequest6(const BetterSocket& socket,
const dhcp::Pkt6Ptr& advertise_pkt6);
/// \brief Send DHCPv6 REQUEST message from template.
/// \param advertise_pkt6 ADVERTISE packet object.
///
/// \throw isc::dhcp::SocketWriteError if failed to send the packet.
- void sendRequest6(const TestControlSocket& socket,
+ void sendRequest6(const BetterSocket& socket,
const std::vector<uint8_t>& template_buf,
const dhcp::Pkt6Ptr& advertise_pkt6);
///
/// \throw isc::Unexpected if failed to create new packet instance.
/// \throw isc::dhcp::SocketWriteError if failed to send the packet.
- void sendSolicit6(const TestControlSocket& socket,
+ void sendSolicit6(const BetterSocket& socket,
const bool preload = false);
/// \brief Send DHCPv6 SOLICIT message from template.
/// \param preload mode, packets not included in statistics.
///
/// \throw isc::dhcp::SocketWriteError if failed to send the packet.
- void sendSolicit6(const TestControlSocket& socket,
+ void sendSolicit6(const BetterSocket& socket,
const std::vector<uint8_t>& template_buf,
const bool preload = false);
///
/// \param socket socket used to send the packet.
/// \param pkt reference to packet to be configured.
- void setDefaults4(const TestControlSocket& socket,
+ void setDefaults4(const BetterSocket& socket,
const dhcp::Pkt4Ptr& pkt);
/// \brief Set default DHCPv6 packet parameters.
///
/// \param socket socket used to send the packet.
/// \param pkt reference to packet to be configured.
- void setDefaults6(const TestControlSocket& socket,
+ void setDefaults6(const BetterSocket& socket,
const dhcp::Pkt6Ptr& pkt);
/// @brief Inserts extra options specified by user.
/// @param pkt6 options will be added here
void addExtraOpts(const dhcp::Pkt6Ptr& pkt6);
- /// \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) const;
-
protected:
- /// \brief Increments counter of late sent messages if required.
- ///
- /// This function checks if the message or set of messages of a given type,
- /// were sent later than their due time. If they were sent late, it is
- /// an indication that the perfdhcp doesn't catch up with the desired rate
- /// for sending messages.
- ///
- /// \param rate_control An object tracking due times for a particular
- /// type of messages.
- void checkLateMessages(RateControl& rate_control);
-
/// \brief Copies IA_NA or IA_PD option from one packet to another.
///
/// This function checks the lease-type specified in the command line
run_unittests_SOURCES += rate_control_unittest.cc
run_unittests_SOURCES += stats_mgr_unittest.cc
run_unittests_SOURCES += test_control_unittest.cc
+run_unittests_SOURCES += receiver_unittest.cc
run_unittests_SOURCES += command_options_helper.h
run_unittests_CPPFLAGS = $(AM_CPPFLAGS) $(GTEST_INCLUDES)
EXPECT_EQ("", opt.getLocalName());
EXPECT_FALSE(opt.isInterface());
EXPECT_EQ(0, opt.getPreload());
- EXPECT_EQ(1, opt.getAggressivity());
EXPECT_EQ(0, opt.getLocalPort());
EXPECT_FALSE(opt.isSeeded());
EXPECT_EQ(0, opt.getSeed());
isc::InvalidParameter);
}
-TEST_F(CommandOptionsTest, Aggressivity) {
- CommandOptions& opt = CommandOptions::instance();
- process("perfdhcp -a 10 -l 192.168.0.1 all");
- EXPECT_EQ(10, opt.getAggressivity());
-
- // Negative test cases
- // Aggressivity must be non negative integer
- EXPECT_THROW(process("perfdhcp -l ethx -a 0 all"),
- isc::InvalidParameter);
- EXPECT_THROW(process("perfdhcp -l ethx -a all"),
- isc::InvalidParameter);
- EXPECT_THROW(process("perfdhcp -a -2 -l ethx -a 3 all"),
- isc::InvalidParameter);
-}
-
TEST_F(CommandOptionsTest, MaxDrop) {
CommandOptions& opt = CommandOptions::instance();
EXPECT_NO_THROW(process("perfdhcp -D 25 -l ethx all"));
: RateControl() {
}
- /// \brief Constructor which sets up the rate and aggressivity.
+ /// \brief Constructor which sets up the rate.
///
/// \param rate A rate at which messages are sent.
- /// \param aggressivity A value of aggressivity. This value controls the
/// maximal number of messages sent in one chunk.
- NakedRateControl(const int rate, const int aggressivity)
- : RateControl(rate, aggressivity) {
+ NakedRateControl(const int rate)
+ : RateControl(rate) {
}
using RateControl::currentTime;
- using RateControl::updateSendTime;
- using RateControl::updateSendDue;
- using RateControl::send_due_;
- using RateControl::last_sent_;
- using RateControl::late_sent_;
-
+ using RateControl::start_time_;
};
// Test default constructor.
TEST(RateControl, constructorDefault) {
NakedRateControl rc;
- EXPECT_EQ(1, rc.getAggressivity());
EXPECT_EQ(0, rc.getRate());
- EXPECT_FALSE(rc.getDue().is_not_a_date_time());
- EXPECT_FALSE(rc.last_sent_.is_not_a_date_time());
- EXPECT_FALSE(rc.isLateSent());
}
-// Test the constructor which sets the rate and aggressivity.
+// Test the constructor which sets the rate.
TEST(RateControl, constructor) {
// Call the constructor and verify that it sets the appropriate
// values.
- NakedRateControl rc1(3, 2);
- EXPECT_EQ(2, rc1.getAggressivity());
+ NakedRateControl rc1(3);
EXPECT_EQ(3, rc1.getRate());
- EXPECT_FALSE(rc1.getDue().is_not_a_date_time());
- EXPECT_FALSE(rc1.last_sent_.is_not_a_date_time());
- EXPECT_FALSE(rc1.isLateSent());
// Call the constructor again and make sure that different values
// will be set correctly.
- NakedRateControl rc2(5, 6);
- EXPECT_EQ(6, rc2.getAggressivity());
+ NakedRateControl rc2(5);
EXPECT_EQ(5, rc2.getRate());
- EXPECT_FALSE(rc2.getDue().is_not_a_date_time());
- EXPECT_FALSE(rc2.last_sent_.is_not_a_date_time());
- EXPECT_FALSE(rc2.isLateSent());
- // The 0 value of aggressivity < 1 is not acceptable.
- EXPECT_THROW(RateControl(3, 0), isc::BadValue);
// The negative value of rate is not acceptable.
- EXPECT_THROW(RateControl(-1, 3), isc::BadValue);
-}
-
-// Check the aggressivity accessor.
-TEST(RateControl, getAggressivity) {
- RateControl rc;
- ASSERT_EQ(1, rc.getAggressivity());
- rc.setAggressivity(5);
- ASSERT_EQ(5, rc.getAggressivity());
- rc.setAggressivity(10);
- EXPECT_EQ(10, rc.getAggressivity());
-}
-
-// Check the due time accessor.
-TEST(RateControl, getDue) {
- NakedRateControl rc;
- ASSERT_FALSE(rc.getDue().is_not_a_date_time());
- rc.send_due_ = NakedRateControl::currentTime();
- EXPECT_TRUE(NakedRateControl::currentTime() >= rc.getDue());
- rc.send_due_ = NakedRateControl::currentTime() +
- boost::posix_time::seconds(10);
- EXPECT_TRUE(NakedRateControl::currentTime() < rc.getDue());
+ EXPECT_THROW(RateControl(-1), isc::BadValue);
}
// Check the rate accessor.
EXPECT_EQ(10, rc.getRate());
}
-// Check if late send flag accessor.
-TEST(RateControl, isLateSent) {
- NakedRateControl rc;
- ASSERT_FALSE(rc.isLateSent());
- rc.late_sent_ = true;
- EXPECT_TRUE(rc.isLateSent());
-}
-
// Check that the function returns the number of messages to be sent "now"
// correctly.
// @todo Possibly extend this test to cover more complex scenarios. Note that
// depends on time.
TEST(RateControl, getOutboundMessageCount) {
// Test that the number of outbound messages is correctly defined by the
- // rate. The aggressivity is set high enough so that it will not restrict
- // the calculated count.
- NakedRateControl rc1(1000, 1000000);
- // Set the timestamp of the last sent message well to the past. The
- // resulting due time will be in the past too. A fractional number of
- // seconds is used to check the floating point arithmetic performed inside
- // the RateControl class.
- rc1.last_sent_ =
- NakedRateControl::currentTime() - boost::posix_time::seconds(5) -
- boost::posix_time::milliseconds(250);
- // The number of messages to be sent must be roughly equal to the time
- // between the last sent message and the current time multiplied by the
- // rate. ("Roughly", as current time is advancing, so the actual interval
- // when the measurement is made may be different from the interval set.) The
- // margin in this test is reasonably generous, allowing for a timing error
- // of around 10ms.
- uint64_t count = 0;
- ASSERT_NO_THROW(count = rc1.getOutboundMessageCount());
- EXPECT_TRUE((count >= 5240) && (count <= 5260)) <<
- "count is " << count << ", expected range 5240-5260";
-
- // Check that the aggressivity limits the count of messages.
- NakedRateControl rc2(1000, 3500);
- rc2.last_sent_ =
- NakedRateControl::currentTime() - boost::posix_time::seconds(5) -
- boost::posix_time::milliseconds(250);
- ASSERT_NO_THROW(count = rc2.getOutboundMessageCount());
- EXPECT_EQ(3500, count);
-
- // Now, don't specify the rate. In this case the aggressivity dictates
- // how many messages to send.
- NakedRateControl rc3(0, 3);
- rc3.last_sent_ =
- NakedRateControl::currentTime() - boost::posix_time::seconds(5);
- ASSERT_NO_THROW(count = rc3.getOutboundMessageCount());
- EXPECT_EQ(3, count);
-
- // Specify the rate and set the timestamp of the last sent message well
- // to the future. If the resulting due time is well in the future too,
- // the number of messages to be sent must be 0.
- NakedRateControl rc4(10, 3);
- rc4.last_sent_ = NakedRateControl::currentTime() +
- boost::posix_time::seconds(5);
- ASSERT_NO_THROW(count = rc4.getOutboundMessageCount());
- EXPECT_EQ(0, count);
+ // rate.
+ NakedRateControl rc(2);
-}
-
-// Test the aggressivity modifier for valid and invalid values.
-TEST(RateControl, setAggressivity) {
- NakedRateControl rc;
- ASSERT_NO_THROW(rc.setAggressivity(1));
- EXPECT_THROW(rc.setAggressivity(0), isc::BadValue);
- EXPECT_THROW(rc.setAggressivity(-1), isc::BadValue);
+ // The first call to getOutboundMessageCount always returns 0 as there is
+ // no good estimate at the beginning how many packets to send.
+ uint64_t count = 0;
+ ASSERT_NO_THROW(count = rc.getOutboundMessageCount());
+ EXPECT_EQ(count, 1);
+
+ // After a given amount of time number of packets to send should
+ // allow to catch up with requested rate, ie for rate 2pks/s after 1500ms
+ // it should send 2 pkts/s * 1.5s = about 2 pkts.
+ // To simulate 1500ms lets get back start time by 1500ms.
+ rc.start_time_ -= boost::posix_time::milliseconds(1500);
+ ASSERT_NO_THROW(count = rc.getOutboundMessageCount());
+ EXPECT_EQ(count, 2);
+
+ // If elapsed time is big then a big number of packets would need to be sent.
+ // But the pkts number is capped to 3. Check it.
+ rc.start_time_ -= boost::posix_time::milliseconds(10000);
+ ASSERT_NO_THROW(count = rc.getOutboundMessageCount());
+ EXPECT_EQ(count, 3);
}
// Test the rate modifier for valid and invalid rate values.
EXPECT_NO_THROW(rc.setRate(0));
EXPECT_THROW(rc.setRate(-1), isc::BadValue);
}
-
-// Test the function which calculates the due time to send next set of
-// messages.
-TEST(RateControl, updateSendDue) {
- NakedRateControl rc;
- // Set the send due timestamp to the value which is well in the future.
- // If we don't hit the due time, the function should not modify the
- // due time.
- rc.send_due_ =
- NakedRateControl::currentTime() + boost::posix_time::seconds(10);
- boost::posix_time::ptime last_send_due = rc.send_due_;
- ASSERT_NO_THROW(rc.updateSendDue());
- EXPECT_TRUE(rc.send_due_ == last_send_due);
- // Set the due time to the value which is already behind.
- rc.send_due_ =
- NakedRateControl::currentTime() - boost::posix_time::seconds(10);
- last_send_due = rc.send_due_;
- ASSERT_NO_THROW(rc.updateSendDue());
- // The value should be modified to the new value.
- EXPECT_TRUE(rc.send_due_ > last_send_due);
-
-}
-
-// Test that the message send time is updated to the current time.
-TEST(RateControl, updateSendTime) {
- NakedRateControl rc;
- // Set the timestamp to the future.
- rc.last_sent_ =
- NakedRateControl::currentTime() + boost::posix_time::seconds(5);
- rc.updateSendTime();
- // Updated timestamp should be set to now or to the past.
- EXPECT_TRUE(rc.last_sent_ <= NakedRateControl::currentTime());
-
-}
--- /dev/null
+// Copyright (C) 2018 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 <config.h>
+
+#include "command_options_helper.h"
+
+#include <exceptions/exceptions.h>
+#include "receiver.h"
+#include <gtest/gtest.h>
+
+
+using namespace isc;
+using namespace isc::perfdhcp;
+
+TEST(Receiver, singleThreaded) {
+ CommandOptionsHelper::process("perfdhcp -g single -l 127.0.0.1 all");
+ ASSERT_TRUE(CommandOptions::instance().isSingleThreaded());
+
+ BetterSocket sock(123);
+
+ Receiver receiver(sock);
+
+ ASSERT_NO_THROW(receiver.start());
+
+ auto pkt = receiver.getPkt();
+ EXPECT_EQ(pkt, nullptr);
+
+ ASSERT_NO_THROW(receiver.stop());
+}
+
+
+TEST(Receiver, multiThreaded) {
+ CommandOptionsHelper::process("perfdhcp -g multi -l 127.0.0.1 all");
+ ASSERT_FALSE(CommandOptions::instance().isSingleThreaded());
+
+ BetterSocket sock(123);
+
+ Receiver receiver(sock);
+
+ ASSERT_NO_THROW(receiver.start());
+
+ auto pkt = receiver.getPkt();
+ EXPECT_EQ(pkt, nullptr);
+
+ ASSERT_NO_THROW(receiver.stop());
+}
/// \brief Pointer to incremental generator.
typedef boost::shared_ptr<IncrementalGenerator> IncrementalGeneratorPtr;
- /// \brief Sets the due times for sending Solicit, Renew and Release.
- ///
- /// There are three class members that hold the due time for sending DHCP
- /// messages:
- /// - send_due_ - due time to send Solicit,
- /// - renew_due_ - due time to send Renew,
- /// - release_due_ - due time to send Release.
- /// Some tests in this test suite need to modify these values relative to
- /// the current time. This function modifies this values using time
- /// offset values (positive or negative) specified as a difference in
- /// seconds between current time and the due time.
- ///
- /// \param send_secs An offset of the due time for Solicit.
- /// \param renew_secs An offset of the due time for Renew.
- /// \param release_secs An offset of the due time for Release.
- void setRelativeDueTimes(const int send_secs, const int renew_secs = 0,
- const int release_secs = 0) {
- ptime now = microsec_clock::universal_time();
- // Use now to avoid unused but set warning
- ASSERT_FALSE(now.is_special());
- basic_rate_control_.setRelativeDue(send_secs);
- renew_rate_control_.setRelativeDue(renew_secs);
- release_rate_control_.setRelativeDue(release_secs);
-
- }
-
using TestControl::checkExitConditions;
using TestControl::createMessageFromReply;
using TestControl::createRequestFromAck;
using TestControl::generateClientId;
using TestControl::generateDuid;
using TestControl::generateMacAddress;
- using TestControl::getCurrentTimeout;
using TestControl::getTemplateBuffer;
using TestControl::initPacketTemplates;
using TestControl::initializeStatsMgr;
using TestControl::macaddr_gen_;
using TestControl::first_packet_serverid_;
using TestControl::interrupted_;
- using TestControl::testDiags;
using TestControl::template_packets_v4_;
using TestControl::template_packets_v6_;
using TestControl::ack_storage_;
tc.setTransidGenerator(generator);
// Socket is needed to send packets through the interface.
ASSERT_NO_THROW(sock_handle = tc.openSocket());
- TestControl::TestControlSocket sock(sock_handle);
+ BetterSocket sock(sock_handle);
for (int i = 0; i < iterations_num; ++i) {
// Get next transaction id, without actually using it. The same
// id wll be used by the TestControl class for DHCPDISCOVER.
tc.setTransidGenerator(generator);
// Socket is needed to send packets through the interface.
ASSERT_NO_THROW(sock_handle = tc.openSocket());
- TestControl::TestControlSocket sock(sock_handle);
+ BetterSocket sock(sock_handle);
uint32_t transid = 0;
for (int i = 0; i < iterations_num; ++i) {
// Do not simulate responses for packets later
// Socket has to be created so as we can actually send packets.
int sock_handle = 0;
ASSERT_NO_THROW(sock_handle = tc.openSocket());
- TestControl::TestControlSocket sock(sock_handle);
+ BetterSocket sock(sock_handle);
// Send a number of DHCPDISCOVER messages. Each generated message will
// be assigned a different transaction id, starting from 1 to 10.
// Socket has to be created so as we can actually send packets.
int sock_handle = 0;
ASSERT_NO_THROW(sock_handle = tc.openSocket());
- TestControl::TestControlSocket sock(sock_handle);
+ BetterSocket sock(sock_handle);
// Send a number of Solicit messages. Each generated Solicit will be
// assigned a different transaction id, starting from 1 to 10.
// This test verifies that the class members are reset to expected values.
TEST_F(TestControlTest, reset) {
- ASSERT_NO_THROW(processCmdLine("perfdhcp -6 -l ethx -r 50 -f 30 -F 10 -a 3 all"));
+ ASSERT_NO_THROW(processCmdLine("perfdhcp -6 -l ethx -r 50 -f 30 -F 10 all"));
NakedTestControl tc;
tc.reset();
- EXPECT_EQ(3, tc.basic_rate_control_.getAggressivity());
- EXPECT_EQ(3, tc.renew_rate_control_.getAggressivity());
- EXPECT_EQ(3, tc.release_rate_control_.getAggressivity());
EXPECT_EQ(50, tc.basic_rate_control_.getRate());
EXPECT_EQ(30, tc.renew_rate_control_.getRate());
EXPECT_EQ(10, tc.release_rate_control_.getRate());
// We have to create the socket to setup some parameters of
// outgoing packet.
ASSERT_NO_THROW(sock_handle = tc.openSocket());
- TestControl::TestControlSocket sock(sock_handle);
+ BetterSocket sock(sock_handle);
uint32_t transid = 123;
boost::shared_ptr<Pkt4> pkt4(new Pkt4(DHCPDISCOVER, transid));
// Set parameters on outgoing packet.
// Create the socket. It will be needed to set packet's
// parameters.
ASSERT_NO_THROW(sock_handle = tc.openSocket());
- TestControl::TestControlSocket sock(sock_handle);
+ BetterSocket sock(sock_handle);
uint32_t transid = 123;
boost::shared_ptr<Pkt6> pkt6(new Pkt6(DHCPV6_SOLICIT, transid));
// Set packet's parameters.
// Create the socket. It will be needed to set packet's
// parameters.
ASSERT_NO_THROW(sock_handle = tc.openSocket());
- TestControl::TestControlSocket sock(sock_handle);
+ BetterSocket sock(sock_handle);
uint32_t transid = 123;
boost::shared_ptr<Pkt6> pkt6(new Pkt6(DHCPV6_SOLICIT, transid));
// Set packet's parameters.
testCreateRenewRelease(DHCPV6_RELEASE);
}
-// This test verifies that the current timeout value for waiting for
-// the server's responses is valid. The timeout value corresponds to the
-// time period between now and the next message to be sent from the
-// perfdhcp to a server.
-TEST_F(TestControlTest, getCurrentTimeout) {
- // Process the command line: set the rate for Discovers to 10,
- // and set Renew rate to 0 (-f flag absent).
- ASSERT_NO_THROW(processCmdLine("perfdhcp -4 -l lo -r 10 ::1"));
- NakedTestControl tc;
- // Make sure that the renew rate is 0.
- ASSERT_EQ(0, CommandOptions::instance().getRenewRate());
- // Simulate the case when we are already behind the due time for
- // the next Discover to be sent.
- tc.setRelativeDueTimes(-3);
- // Expected timeout value is 0, which means that perfdhcp should
- // not wait for server's response but rather send the next
- // message to a server immediately.
- EXPECT_EQ(0, tc.getCurrentTimeout());
- // Now, let's do set the due time to a value in the future. The returned
- // timeout value should be somewhere between now and this time in the
- // future. The value of ten seconds ahead should be safe and guarantee
- // that the returned timeout value is non-zero, even though there is a
- // delay between setting the send_due_ value and invoking the function.
- tc.setRelativeDueTimes(10);
- uint32_t timeout = tc.getCurrentTimeout();
- EXPECT_GT(timeout, 0);
- EXPECT_LE(timeout, 10000000);
-}
-
-// This test verifies that the current timeout value for waiting for the
-// server's responses is valid. In this case, we are simulating that perfdhcp
-// sends Renew requests to the server, apart from the regular 4-way exchanges.
-// The timeout value depends on both the due time to send next Solicit and the
-// due time to send Renew - the timeout should be adjusted to the due time
-// that occurs sooner.
-TEST_F(TestControlTest, getCurrentTimeoutRenew) {
- // Set the Solicit rate to 10 and the Renew rate 5.
- ASSERT_NO_THROW(processCmdLine("perfdhcp -6 -l lo -r 10 -f 5 ::1"));
- NakedTestControl tc;
-
- // Make sure, that the Renew rate has been set to 5.
- ASSERT_EQ(5, CommandOptions::instance().getRenewRate());
- // The send_due_ is in the past, the renew_due_ is in the future.
- tc.setRelativeDueTimes(-3, 3);
- EXPECT_EQ(0, tc.getCurrentTimeout());
-
- // Swap the due times from the previous check. The effect should be the
- // same.
- tc.setRelativeDueTimes(3, -3);
- EXPECT_EQ(0, tc.getCurrentTimeout());
-
- // Set both due times to the future. The renew due time is to occur
- // sooner. The timeout should be a value between now and the
- // renew due time.
- tc.setRelativeDueTimes(10, 5);
- EXPECT_GT(tc.getCurrentTimeout(), 0);
- EXPECT_LE(tc.getCurrentTimeout(), 5000000);
-
- // Repeat the same check, but swap the due times.
- tc.setRelativeDueTimes(5, 10);
- EXPECT_GT(tc.getCurrentTimeout(), 0);
- EXPECT_LE(tc.getCurrentTimeout(), 5000000);
-
-}
-
-// This test verifies that the current timeout value for waiting for the
-// server's responses is valid. In this case, we are simulating that perfdhcp
-// sends Release requests to the server, apart from the regular 4-way exchanges.
-TEST_F(TestControlTest, getCurrentTimeoutRelease) {
- // Set the Solicit rate to 10 and the Release rate 5.
- ASSERT_NO_THROW(processCmdLine("perfdhcp -6 -l lo -r 10 -F 5 ::1"));
- NakedTestControl tc;
-
- // Make sure, that the Release rate has been set to 5.
- ASSERT_EQ(5, CommandOptions::instance().getReleaseRate());
- // The send_due_ is in the past, the renew_due_ is in the future.
- tc.setRelativeDueTimes(-3, 0, 3);
- EXPECT_EQ(0, tc.getCurrentTimeout());
-
- // Swap the due times from the previous check. The effect should be the
- // same.
- tc.setRelativeDueTimes(3, 0, -3);
- EXPECT_EQ(0, tc.getCurrentTimeout());
-
- // Set both due times to the future. The renew due time is to occur
- // sooner. The timeout should be a value between now and the
- // release due time.
- tc.setRelativeDueTimes(10, 0, 5);
- EXPECT_GT(tc.getCurrentTimeout(), 0);
- EXPECT_LE(tc.getCurrentTimeout(), 5000000);
-
- // Repeat the same check, but swap the due times.
- tc.setRelativeDueTimes(5, 0, 10);
- EXPECT_GT(tc.getCurrentTimeout(), 0);
- EXPECT_LE(tc.getCurrentTimeout(), 5000000);
-
-}
-
-// This test verifies that the current timeout value for waiting for the
-// server's responses is valid. In this case, we are simulating that perfdhcp
-// sends both Renew and Release requests to the server, apart from the regular
-// 4-way exchanges.
-TEST_F(TestControlTest, getCurrentTimeoutRenewRelease) {
- // Set the Solicit rate to 10 and, Renew rate to 5, Release rate to 3.
- ASSERT_NO_THROW(processCmdLine("perfdhcp -6 -l lo -r 10 -f 5 -F 3 ::1"));
- NakedTestControl tc;
-
- // Make sure the Renew and Release rates has been set to a non-zero value.
- ASSERT_EQ(5, CommandOptions::instance().getRenewRate());
- ASSERT_EQ(3, CommandOptions::instance().getReleaseRate());
-
- // If any of the due times is in the past, the timeout value should be 0,
- // to indicate that the next message should be sent immediately.
- tc.setRelativeDueTimes(-3, 3, 5);
- EXPECT_EQ(0, tc.getCurrentTimeout());
-
- tc.setRelativeDueTimes(-3, 5, 3);
- EXPECT_EQ(0, tc.getCurrentTimeout());
-
- tc.setRelativeDueTimes(3, -3, 5);
- EXPECT_EQ(0, tc.getCurrentTimeout());
-
- tc.setRelativeDueTimes(3, 2, -5);
- EXPECT_EQ(0, tc.getCurrentTimeout());
-
- tc.setRelativeDueTimes(-3, -2, -5);
- EXPECT_EQ(0, tc.getCurrentTimeout());
-
- // If due times are in the future, the timeout value should be aligned to
- // the due time which occurs the soonest.
- tc.setRelativeDueTimes(10, 9, 8);
- EXPECT_GT(tc.getCurrentTimeout(), 0);
- EXPECT_LE(tc.getCurrentTimeout(), 8000000);
-
- tc.setRelativeDueTimes(10, 8, 9);
- EXPECT_GT(tc.getCurrentTimeout(), 0);
- EXPECT_LE(tc.getCurrentTimeout(), 8000000);
-
- tc.setRelativeDueTimes(5, 8, 9);
- EXPECT_GT(tc.getCurrentTimeout(), 0);
- EXPECT_LE(tc.getCurrentTimeout(), 5000000);
-
-}
-
// Test checks if sendDiscover really includes custom options
TEST_F(TestControlTest, sendDiscoverExtraOpts) {
// Socket is needed to send packets through the interface.
int sock_handle = 0;
ASSERT_NO_THROW(sock_handle = tc.openSocket());
- TestControl::TestControlSocket sock(sock_handle);
+ BetterSocket sock(sock_handle);
// Make tc send the packet. The first packet of each type is saved in templates.
ASSERT_NO_THROW(tc.sendDiscover4(sock));
-// Copyright (C) 2013-2018 Internet Systems Consortium, Inc. ("ISC")
+// Copyright (C) 2013-2017 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