BIND 10 master, updated. 66f2939830926f4337623b159210103b5a8e2434 [master] Merge branch 'trac3183'
BIND 10 source code commits
bind10-changes at lists.isc.org
Mon Oct 28 18:38:17 UTC 2013
The branch, master has been updated
via 66f2939830926f4337623b159210103b5a8e2434 (commit)
via 445a1ea248aee12202c7e076f7b85192384f57fd (commit)
via 5bb29d3c8d9f318fd4242e3c5a34a59b70964311 (commit)
via 276e655555df9302ba56ea5520a5713d1915bd7f (commit)
via 2838c0769e219dbd2c8a479efe87f9bfdb4da8e9 (commit)
via f1094390dc096d25df6465d737974a5d6f82e6ea (commit)
via d130980f515cc5cfab65df8dd0944b56d5fbcdea (commit)
via 79569d47bdbece10f1ee4532f33ec23d4c8e00b4 (commit)
via 4386caec66f57272c1239a3134dc8a0add870506 (commit)
via 0d62790c36094e5789cdb7ff713e8e58ba9326fd (commit)
via 235246a1c1aaf52a976896ab1bfc0df551abdb5a (commit)
via 4527401f4afbd8f7a9dafb7296863d01da0d8c09 (commit)
via 2c20227ed4c9f1ccb5184e8c9698dcf7a7e79c4d (commit)
from 64ae9936d165c0e070bdf17809971918c29ee838 (commit)
Those revisions listed above that are new to this repository have
not appeared on any other notification email; so we list those
revisions in full, below.
- Log -----------------------------------------------------------------
commit 66f2939830926f4337623b159210103b5a8e2434
Merge: 64ae993 445a1ea
Author: Marcin Siodelski <marcin at isc.org>
Date: Mon Oct 28 19:10:29 2013 +0100
[master] Merge branch 'trac3183'
-----------------------------------------------------------------------
Summary of changes:
tests/tools/perfdhcp/Makefile.am | 1 +
tests/tools/perfdhcp/command_options.cc | 94 +++++----
tests/tools/perfdhcp/command_options.h | 12 +-
tests/tools/perfdhcp/packet_storage.h | 161 +++++++++++++++
tests/tools/perfdhcp/stats_mgr.h | 14 +-
tests/tools/perfdhcp/test_control.cc | 169 ++++++++++++++--
tests/tools/perfdhcp/test_control.h | 91 +++++++--
tests/tools/perfdhcp/tests/Makefile.am | 1 +
.../tools/perfdhcp/tests/command_options_helper.h | 8 +-
.../perfdhcp/tests/command_options_unittest.cc | 31 +++
.../perfdhcp/tests/packet_storage_unittest.cc | 205 ++++++++++++++++++++
.../tools/perfdhcp/tests/test_control_unittest.cc | 197 +++++++++++++++++--
12 files changed, 901 insertions(+), 83 deletions(-)
create mode 100644 tests/tools/perfdhcp/packet_storage.h
create mode 100644 tests/tools/perfdhcp/tests/packet_storage_unittest.cc
-----------------------------------------------------------------------
diff --git a/tests/tools/perfdhcp/Makefile.am b/tests/tools/perfdhcp/Makefile.am
index c4b82b5..31e5a31 100644
--- a/tests/tools/perfdhcp/Makefile.am
+++ b/tests/tools/perfdhcp/Makefile.am
@@ -23,6 +23,7 @@ perfdhcp_SOURCES += command_options.cc command_options.h
perfdhcp_SOURCES += localized_option.h
perfdhcp_SOURCES += perf_pkt6.cc perf_pkt6.h
perfdhcp_SOURCES += perf_pkt4.cc perf_pkt4.h
+perfdhcp_SOURCES += packet_storage.h
perfdhcp_SOURCES += pkt_transform.cc pkt_transform.h
perfdhcp_SOURCES += stats_mgr.h
perfdhcp_SOURCES += test_control.cc test_control.h
diff --git a/tests/tools/perfdhcp/command_options.cc b/tests/tools/perfdhcp/command_options.cc
index 7df1ea4..7edb84b 100644
--- a/tests/tools/perfdhcp/command_options.cc
+++ b/tests/tools/perfdhcp/command_options.cc
@@ -12,19 +12,21 @@
// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
// PERFORMANCE OF THIS SOFTWARE.
+#include "command_options.h"
+#include <exceptions/exceptions.h>
+#include <dhcp/iface_mgr.h>
+#include <dhcp/duid.h>
+
+#include <boost/lexical_cast.hpp>
+#include <boost/date_time/posix_time/posix_time.hpp>
+
#include <config.h>
+#include <sstream>
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <unistd.h>
-#include <boost/lexical_cast.hpp>
-#include <boost/date_time/posix_time/posix_time.hpp>
-
-#include <exceptions/exceptions.h>
-#include <dhcp/iface_mgr.h>
-#include <dhcp/duid.h>
-#include "command_options.h"
using namespace std;
using namespace isc;
@@ -111,6 +113,7 @@ CommandOptions::reset() {
exchange_mode_ = DORA_SARR;
lease_type_.set(LeaseType::ADDRESS);
rate_ = 0;
+ renew_rate_ = 0;
report_delay_ = 0;
clients_num_ = 0;
mac_template_.assign(mac, mac + 6);
@@ -208,7 +211,7 @@ CommandOptions::initialize(int argc, char** argv, bool print_cmd_line) {
// In this section we collect argument values from command line
// they will be tuned and validated elsewhere
while((opt = getopt(argc, argv, "hv46r:t:R:b:n:p:d:D:l:P:a:L:"
- "s:iBc1T:X:O:E:S:I:x:w:e:")) != -1) {
+ "s:iBc1T:X:O:E:S:I:x:w:e:f:")) != -1) {
stream << " -" << static_cast<char>(opt);
if (optarg) {
stream << " " << optarg;
@@ -299,6 +302,11 @@ CommandOptions::initialize(int argc, char** argv, bool print_cmd_line) {
" must not be a negative integer");
break;
+ case 'f':
+ renew_rate_ = positiveInteger("value of the renew rate: -f<renew-rate>"
+ " must be a positive integer");
+ break;
+
case 'h':
usage();
return (true);
@@ -680,6 +688,8 @@ CommandOptions::validate() const {
"-B is not compatible with IPv6 (-6)");
check((getIpVersion() != 6) && (isRapidCommit() != 0),
"-6 (IPv6) must be set to use -c");
+ check((getIpVersion() != 6) && (getRenewRate() !=0),
+ "-f<renew-rate> may be used with -6 (IPv6) only");
check((getExchangeMode() == DO_SA) && (getNumRequests().size() > 1),
"second -n<num-request> is not compatible with -i");
check((getIpVersion() == 4) && !getLeaseType().is(LeaseType::ADDRESS),
@@ -692,44 +702,52 @@ CommandOptions::validate() const {
"second -d<drop-time> is not compatible with -i");
check((getExchangeMode() == DO_SA) &&
((getMaxDrop().size() > 1) || (getMaxDropPercentage().size() > 1)),
- "second -D<max-drop> is not compatible with -i\n");
+ "second -D<max-drop> is not compatible with -i");
check((getExchangeMode() == DO_SA) && (isUseFirst()),
- "-1 is not compatible with -i\n");
+ "-1 is not compatible with -i");
check((getExchangeMode() == DO_SA) && (getTemplateFiles().size() > 1),
- "second -T<template-file> is not compatible with -i\n");
+ "second -T<template-file> is not compatible with -i");
check((getExchangeMode() == DO_SA) && (getTransactionIdOffset().size() > 1),
- "second -X<xid-offset> is not compatible with -i\n");
+ "second -X<xid-offset> is not compatible with -i");
check((getExchangeMode() == DO_SA) && (getRandomOffset().size() > 1),
- "second -O<random-offset is not compatible with -i\n");
+ "second -O<random-offset is not compatible with -i");
check((getExchangeMode() == DO_SA) && (getElapsedTimeOffset() >= 0),
- "-E<time-offset> is not compatible with -i\n");
+ "-E<time-offset> is not compatible with -i");
check((getExchangeMode() == DO_SA) && (getServerIdOffset() >= 0),
- "-S<srvid-offset> is not compatible with -i\n");
+ "-S<srvid-offset> is not compatible with -i");
check((getExchangeMode() == DO_SA) && (getRequestedIpOffset() >= 0),
- "-I<ip-offset> is not compatible with -i\n");
+ "-I<ip-offset> is not compatible with -i");
+ check((getExchangeMode() == DO_SA) && (getRenewRate() != 0),
+ "-f<renew-rate> is not compatible with -i");
check((getExchangeMode() != DO_SA) && (isRapidCommit() != 0),
- "-i must be set to use -c\n");
+ "-i must be set to use -c");
check((getRate() == 0) && (getReportDelay() != 0),
- "-r<rate> must be set to use -t<report>\n");
+ "-r<rate> must be set to use -t<report>");
check((getRate() == 0) && (getNumRequests().size() > 0),
- "-r<rate> must be set to use -n<num-request>\n");
+ "-r<rate> must be set to use -n<num-request>");
check((getRate() == 0) && (getPeriod() != 0),
- "-r<rate> must be set to use -p<test-period>\n");
+ "-r<rate> must be set to use -p<test-period>");
check((getRate() == 0) &&
((getMaxDrop().size() > 0) || getMaxDropPercentage().size() > 0),
- "-r<rate> must be set to use -D<max-drop>\n");
+ "-r<rate> must be set to use -D<max-drop>");
+ check((getRate() != 0) && (getRenewRate() > getRate()),
+ "Renew rate specified as -f<renew-rate> must not be greater than"
+ " the rate specified as -r<rate>");
+ check((getRate() == 0) && (getRenewRate() != 0),
+ "Renew rate specified as -f<renew-rate> must not be specified"
+ " when -r<rate> parameter is not specified");
check((getTemplateFiles().size() < getTransactionIdOffset().size()),
- "-T<template-file> must be set to use -X<xid-offset>\n");
+ "-T<template-file> must be set to use -X<xid-offset>");
check((getTemplateFiles().size() < getRandomOffset().size()),
- "-T<template-file> must be set to use -O<random-offset>\n");
+ "-T<template-file> must be set to use -O<random-offset>");
check((getTemplateFiles().size() < 2) && (getElapsedTimeOffset() >= 0),
- "second/request -T<template-file> must be set to use -E<time-offset>\n");
+ "second/request -T<template-file> must be set to use -E<time-offset>");
check((getTemplateFiles().size() < 2) && (getServerIdOffset() >= 0),
"second/request -T<template-file> must be set to "
- "use -S<srvid-offset>\n");
+ "use -S<srvid-offset>");
check((getTemplateFiles().size() < 2) && (getRequestedIpOffset() >= 0),
"second/request -T<template-file> must be set to "
- "use -I<ip-offset>\n");
+ "use -I<ip-offset>");
}
@@ -737,6 +755,8 @@ void
CommandOptions::check(bool condition, const std::string& errmsg) const {
// The same could have been done with macro or just if statement but
// we prefer functions to macros here
+ std::ostringstream stream;
+ stream << errmsg << "\n";
if (condition) {
isc_throw(isc::InvalidParameter, errmsg);
}
@@ -793,6 +813,9 @@ CommandOptions::printCommandLine() const {
if (rate_ != 0) {
std::cout << "rate[1/s]=" << rate_ << std::endl;
}
+ if (getRenewRate() != 0) {
+ std::cout << "renew-rate[1/s]=" << getRenewRate() << std::endl;
+ }
if (report_delay_ != 0) {
std::cout << "report[s]=" << report_delay_ << std::endl;
}
@@ -875,13 +898,14 @@ CommandOptions::printCommandLine() const {
void
CommandOptions::usage() const {
std::cout <<
- "perfdhcp [-hv] [-4|-6] [-e<lease-type>] [-r<rate>] [-t<report>]\n"
- " [-R<range>] [-b<base>] [-n<num-request>] [-p<test-period>]\n"
- " [-d<drop-time>] [-D<max-drop>] [-l<local-addr|interface>]\n"
- " [-P<preload>] [-a<aggressivity>] [-L<local-port>] [-s<seed>]\n"
- " [-i] [-B] [-c] [-1] [-T<template-file>] [-X<xid-offset>]\n"
- " [-O<random-offset] [-E<time-offset>] [-S<srvid-offset>]\n"
- " [-I<ip-offset>] [-x<diagnostic-selector>] [-w<wrapped>] [server]\n"
+ "perfdhcp [-hv] [-4|-6] [-e<lease-type>] [-r<rate>] [-f<renew-rate>]\n"
+ " [-t<report>] [-R<range>] [-b<base>] [-n<num-request>]\n"
+ " [-p<test-period>] [-d<drop-time>] [-D<max-drop>]\n"
+ " [-l<local-addr|interface>] [-P<preload>] [-a<aggressivity>]\n"
+ " [-L<local-port>] [-s<seed>] [-i] [-B] [-c] [-1]\n"
+ " [-T<template-file>] [-X<xid-offset>] [-O<random-offset]\n"
+ " [-E<time-offset>] [-S<srvid-offset>] [-I<ip-offset>]\n"
+ " [-x<diagnostic-selector>] [-w<wrapped>] [server]\n"
"\n"
"The [server] argument is the name/address of the DHCP server to\n"
"contact. For DHCPv4 operation, exchanges are initiated by\n"
@@ -924,6 +948,10 @@ CommandOptions::usage() const {
"-E<time-offset>: Offset of the (DHCPv4) secs field / (DHCPv6)\n"
" elapsed-time option in the (second/request) template.\n"
" The value 0 disables it.\n"
+ "-f<renew-rate>: A rate at which IPv6 Renew requests are sent to\n"
+ " a server. This value must not be equal or lower than the rate\n"
+ " specified as -r<rate>. If -r<rate> is not specified, this\n"
+ " parameter must not be specified too.\n"
"-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"
diff --git a/tests/tools/perfdhcp/command_options.h b/tests/tools/perfdhcp/command_options.h
index 7431057..42a31c9 100644
--- a/tests/tools/perfdhcp/command_options.h
+++ b/tests/tools/perfdhcp/command_options.h
@@ -16,11 +16,12 @@
#ifndef COMMAND_OPTIONS_H
#define COMMAND_OPTIONS_H
+#include <boost/noncopyable.hpp>
+
+#include <stdint.h>
#include <string>
#include <vector>
-#include <boost/noncopyable.hpp>
-
namespace isc {
namespace perfdhcp {
@@ -155,6 +156,11 @@ public:
/// \return exchange rate per second.
int getRate() const { return rate_; }
+ /// \brief Returns a rate at which IPv6 Renew messages are sent.
+ ///
+ /// \return A rate at which IPv6 Renew messages are sent.
+ int getRenewRate() const { return (renew_rate_); }
+
/// \brief Returns delay between two performance reports.
///
/// \return delay between two consecutive performance reports.
@@ -461,6 +467,8 @@ private:
LeaseType lease_type_;
/// Rate in exchange per second
int rate_;
+ /// A rate at which DHCPv6 Renew messages are sent.
+ int renew_rate_;
/// Delay between generation of two consecutive
/// performance reports
int report_delay_;
diff --git a/tests/tools/perfdhcp/packet_storage.h b/tests/tools/perfdhcp/packet_storage.h
new file mode 100644
index 0000000..2adb070
--- /dev/null
+++ b/tests/tools/perfdhcp/packet_storage.h
@@ -0,0 +1,161 @@
+// Copyright (C) 2013 Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#ifndef PACKET_STORAGE_H
+#define PACKET_STORAGE_H
+
+#include <boost/noncopyable.hpp>
+#include <boost/shared_ptr.hpp>
+#include <list>
+#include <stdint.h>
+
+namespace isc {
+namespace perfdhcp {
+
+/// \brief Represents a list of packets with a sequential and random access to
+/// list elements.
+///
+/// The main purpose of this class is to support sending Renew and Release
+/// messages from perfdhcp. The Renew and Release messages are sent for existing
+/// leases only. Therefore, the typical use case for this class is that it holds
+/// a list of Reply messages sent by the server in response to Request messages.
+/// The Request messages hold addresses and/or IPv6 prefixes acquired so they
+/// can be used to identify existing leases. When perfdhcp needs to send Renew
+/// or Release message, it will access one of the elements on this list and
+/// will create the Renew or Release message based on its content. Once the
+/// element (packet) is returned it is also deleted from the list, so as it is
+/// not used again. This class provide either sequential access to the packets
+/// or random access. The random access algorithm is much slower but at least
+/// it allows to simulate more real scenario when the renewing or releasing
+/// client is random.
+///
+/// \tparam Pkt4 or Pkt6 class, which represents DHCPv4 or DHCPv6 message
+/// respectively.
+///
+/// \note Although the class is intended to hold Pkt4 and Pkt6 objects, the
+/// current implementation is generic enough to holds any object wrapped in the
+/// boost::shared_ptr.
+template<typename T>
+class PacketStorage : public boost::noncopyable {
+public:
+ /// A type which represents the pointer to a packet.
+ typedef boost::shared_ptr<T> PacketPtr;
+
+private:
+ /// An internal container actually holding packets.
+ typedef typename std::list<PacketPtr> PacketContainer;
+ /// An iterator to the element in the internal container.
+ typedef typename PacketContainer::iterator PacketContainerIterator;
+
+public:
+
+ /// \brief Constructor.
+ PacketStorage() { }
+
+ /// \brief Appends the new packet object to the collection.
+ ///
+ /// \param packet A pointer to an object representing a packet.
+ void append(const PacketPtr& packet) {
+ storage_.push_back(packet);
+ if (storage_.size() == 1) {
+ current_pointer_ = storage_.begin();
+ }
+ }
+
+ /// \brief Removes packets from the storage.
+ ///
+ /// It is possible to specify a number of packets to be removed
+ /// from a storage. Packets are removed from the beginning of the
+ /// storage. If specified number is greater than the size of the
+ /// storage, all packets are removed.
+ ///
+ /// @param num A number of packets to be removed. If omitted,
+ /// all packets will be removed.
+ void clear(const uint64_t num = 0) {
+ if (num != 0) {
+ PacketContainerIterator last = storage_.begin();
+ std::advance(last, num > size() ? size() : num);
+ current_pointer_ = storage_.erase(storage_.begin(), last);
+ } else {
+ storage_.clear();
+ current_pointer_ = storage_.begin();
+ }
+ }
+
+ /// \brief Checks if the storage has no packets.
+ ///
+ /// \return true if storage is empty, false otherwise.
+ bool empty() const {
+ return (storage_.empty());
+ }
+
+ /// \brief Returns next packet from the storage.
+ ///
+ /// This function returns packets sequentially (in the same order
+ /// in which they have been appended). The returned packet is
+ /// instantly removed from the storage.
+ ///
+ /// \return next packet from the storage.
+ PacketPtr getNext() {
+ if (storage_.empty()) {
+ return (PacketPtr());
+ } else if (current_pointer_ == storage_.end()) {
+ current_pointer_ = storage_.begin();
+ }
+ PacketPtr packet = *current_pointer_;
+ current_pointer_ = storage_.erase(current_pointer_);
+ return (packet);
+ }
+
+ /// \brief Returns random packet from the storage.
+ ///
+ /// This function picks random packet from the storage and returns
+ /// it. It is way slower than the @c getNext function because it has to
+ /// iterate over all existing entries from the beginning of the storage
+ /// to the random packet's position. Therefore, care should be taken
+ /// when using this function to access elements when storage is large.
+ ///
+ /// \return random packet from the storage.
+ PacketPtr getRandom() {
+ if (empty()) {
+ return (PacketPtr());
+ }
+ current_pointer_ = storage_.begin();
+ if (size() > 1) {
+ std::advance(current_pointer_, rand() % (size() - 1));
+ }
+ PacketPtr packet = *current_pointer_;
+ current_pointer_ = storage_.erase(current_pointer_);
+ return (packet);
+ }
+
+ /// \brief Returns number of packets in the storage.
+ ///
+ /// \return number of packets in the storage.
+ uint64_t size() const {
+ return (storage_.size());
+ }
+
+private:
+
+ std::list<PacketPtr> storage_; ///< Holds all appended packets.
+ PacketContainerIterator current_pointer_; ///< Holds the iterator to the
+ ///< next element returned.
+
+};
+
+} // namespace perfdhcp
+} // namespace isc
+
+#endif // PACKET_STORAGE_H
diff --git a/tests/tools/perfdhcp/stats_mgr.h b/tests/tools/perfdhcp/stats_mgr.h
index d614ad0..d0af943 100644
--- a/tests/tools/perfdhcp/stats_mgr.h
+++ b/tests/tools/perfdhcp/stats_mgr.h
@@ -15,8 +15,9 @@
#ifndef STATS_MGR_H
#define STATS_MGR_H
-#include <iostream>
-#include <map>
+#include <dhcp/pkt4.h>
+#include <dhcp/pkt6.h>
+#include <exceptions/exceptions.h>
#include <boost/noncopyable.hpp>
#include <boost/shared_ptr.hpp>
@@ -27,7 +28,9 @@
#include <boost/multi_index/mem_fun.hpp>
#include <boost/date_time/posix_time/posix_time.hpp>
-#include <exceptions/exceptions.h>
+#include <iostream>
+#include <map>
+
namespace isc {
namespace perfdhcp {
@@ -120,7 +123,8 @@ public:
XCHG_DO, ///< DHCPv4 DISCOVER-OFFER
XCHG_RA, ///< DHCPv4 REQUEST-ACK
XCHG_SA, ///< DHCPv6 SOLICIT-ADVERTISE
- XCHG_RR ///< DHCPv6 REQUEST-REPLY
+ XCHG_RR, ///< DHCPv6 REQUEST-REPLY
+ XCHG_RN ///< DHCPv6 RENEW-REPLY
};
/// \brief Exchange Statistics.
@@ -1165,6 +1169,8 @@ public:
return("SOLICIT-ADVERTISE");
case XCHG_RR:
return("REQUEST-REPLY");
+ case XCHG_RN:
+ return("RENEW-REPLY");
default:
return("Unknown exchange type");
}
diff --git a/tests/tools/perfdhcp/test_control.cc b/tests/tools/perfdhcp/test_control.cc
index a126d51..d94f032 100644
--- a/tests/tools/perfdhcp/test_control.cc
+++ b/tests/tools/perfdhcp/test_control.cc
@@ -98,6 +98,38 @@ TestControl::TestControl() {
}
void
+TestControl::cleanCachedPackets() {
+ CommandOptions& options = CommandOptions::instance();
+ // When Renews are not sent, Reply packets are not cached so there
+ // is nothing to do.
+ if (options.getRenewRate() == 0) {
+ return;
+ }
+
+ static boost::posix_time::ptime last_clean =
+ microsec_clock::universal_time();
+
+ // Check how much time has passed since last cleanup.
+ time_period time_since_clean(last_clean,
+ microsec_clock::universal_time());
+ // Cleanup every 1 second.
+ if (time_since_clean.length().total_seconds() >= 1) {
+ // Calculate how many cached packets to remove. Actually we could
+ // just leave enough packets to handle Renews for 1 second but
+ // since we want to randomize leases to be renewed so leave 5
+ // times more packets to randomize from.
+ // @todo The cache size might be controlled from the command line.
+ if (reply_storage_.size() > 5 * options.getRenewRate()) {
+ reply_storage_.clear(reply_storage_.size() -
+ 5 * options.getRenewRate());
+ }
+ // Remember when we performed a cleanup for the last time.
+ // We want to do the next cleanup not earlier than in one second.
+ last_clean = microsec_clock::universal_time();
+ }
+}
+
+void
TestControl::copyIaOptions(const Pkt6Ptr& pkt_from, Pkt6Ptr& pkt_to) {
if (!pkt_from || !pkt_to) {
isc_throw(BadValue, "NULL pointers must not be specified as arguments"
@@ -283,6 +315,31 @@ TestControl::checkExitConditions() const {
return (false);
}
+Pkt6Ptr
+TestControl::createRenew(const Pkt6Ptr& reply) {
+ if (!reply) {
+ isc_throw(isc::BadValue,"Unable to create Renew packet from the Reply packet"
+ " because the instance of the Reply is NULL");
+ }
+ Pkt6Ptr renew(new Pkt6(DHCPV6_RENEW, generateTransid()));
+ // Client id.
+ OptionPtr opt_clientid = reply->getOption(D6O_CLIENTID);
+ if (!opt_clientid) {
+ isc_throw(isc::Unexpected, "failed to create Renew packet because client id"
+ " option has not been found in the Reply from the server");
+ }
+ renew->addOption(opt_clientid);
+ // Server id.
+ OptionPtr opt_serverid = reply->getOption(D6O_SERVERID);
+ if (!opt_serverid) {
+ isc_throw(isc::Unexpected, "failed to create Renew packet because server id"
+ " option has not been found in the Reply from the server");
+ }
+ renew->addOption(opt_serverid);
+ copyIaOptions(reply, renew);
+ return (renew);
+}
+
OptionPtr
TestControl::factoryElapsedTime6(Option::Universe, uint16_t,
const OptionBuffer& buf) {
@@ -426,6 +483,24 @@ TestControl::generateDuid(uint8_t& randomized) const {
return (duid);
}
+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 >= send_due_ ||
+ (options.getRenewRate() != 0 && now >= renew_due_)) {
+ return (0);
+ }
+
+ // There is a due time to send Solicit and Renew. We should adjust
+ // the timeout to the due time which occurs sooner.
+ ptime due = send_due_ > renew_due_ ? renew_due_ : send_due_;
+ time_period due_period(now, due);
+ return (due_period.length().total_microseconds());
+}
+
int
TestControl::getElapsedTimeOffset() const {
int elp_offset = CommandOptions::instance().getIpVersion() == 4 ?
@@ -456,17 +531,18 @@ TestControl::getElapsedTime(const T& pkt1, const T& pkt2) {
uint64_t
-TestControl::getNextExchangesNum() const {
+TestControl::getNextExchangesNum(const boost::posix_time::ptime& send_due,
+ const int rate) {
CommandOptions& options = CommandOptions::instance();
// Get current time.
ptime now(microsec_clock::universal_time());
- if (now >= send_due_) {
+ if (now >= send_due) {
// Reset number of exchanges.
uint64_t due_exchanges = 0;
// If rate is specified from the command line we have to
// synchornize with it.
- if (options.getRate() != 0) {
- time_period period(send_due_, now);
+ if (rate != 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.
@@ -475,7 +551,7 @@ TestControl::getNextExchangesNum() const {
due_factor += 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 * options.getRate());
+ due_exchanges = static_cast<uint64_t>(due_factor * rate);
// We want to make sure that at least one packet goes out.
if (due_exchanges == 0) {
due_exchanges = 1;
@@ -616,6 +692,9 @@ TestControl::initializeStatsMgr() {
stats_mgr6_->addExchangeStats(StatsMgr6::XCHG_RR,
options.getDropTime()[1]);
}
+ if (options.getRenewRate() != 0) {
+ stats_mgr6_->addExchangeStats(StatsMgr6::XCHG_RN);
+ }
}
if (testDiags('i')) {
if (options.getIpVersion() == 4) {
@@ -769,6 +848,17 @@ TestControl::sendPackets(const TestControlSocket& socket,
}
}
+uint64_t
+TestControl::sendRenewPackets(const TestControlSocket& socket,
+ const uint64_t packets_num) {
+ for (uint64_t i = 0; i < packets_num; ++i) {
+ if (!sendRenew(socket)) {
+ return (i);
+ }
+ }
+ return (packets_num);
+}
+
void
TestControl::printDiagnostics() const {
CommandOptions& options = CommandOptions::instance();
@@ -1024,20 +1114,27 @@ TestControl::processReceivedPacket6(const TestControlSocket& socket,
}
}
} else if (packet_type == DHCPV6_REPLY) {
- stats_mgr6_->passRcvdPacket(StatsMgr6::XCHG_RR, pkt6);
+ Pkt6Ptr sent_packet = stats_mgr6_->passRcvdPacket(StatsMgr6::XCHG_RR,
+ pkt6);
+ if (sent_packet) {
+ if (CommandOptions::instance().getRenewRate() != 0) {
+ reply_storage_.append(pkt6);
+ }
+ } else {
+ stats_mgr6_->passRcvdPacket(StatsMgr6::XCHG_RN, pkt6);
+ }
}
}
uint64_t
TestControl::receivePackets(const TestControlSocket& socket) {
- int timeout = 0;
bool receiving = true;
uint64_t received = 0;
while (receiving) {
if (CommandOptions::instance().getIpVersion() == 4) {
Pkt4Ptr pkt4;
try {
- pkt4 = IfaceMgr::instance().receive4(timeout);
+ pkt4 = IfaceMgr::instance().receive4(0, getCurrentTimeout());
} catch (const Exception& e) {
std::cerr << "Failed to receive DHCPv4 packet: "
<< e.what() << std::endl;
@@ -1055,7 +1152,7 @@ TestControl::receivePackets(const TestControlSocket& socket) {
} else if (CommandOptions::instance().getIpVersion() == 6) {
Pkt6Ptr pkt6;
try {
- pkt6 = IfaceMgr::instance().receive6(timeout);
+ pkt6 = IfaceMgr::instance().receive6(0, getCurrentTimeout());
} catch (const Exception& e) {
std::cerr << "Failed to receive DHCPv6 packet: "
<< e.what() << std::endl;
@@ -1156,6 +1253,8 @@ TestControl::reset() {
send_due_ = microsec_clock::universal_time();
last_sent_ = send_due_;
last_report_ = send_due_;
+ renew_due_ = send_due_;
+ last_renew_ = send_due_;
transid_gen_.reset();
// Actual generators will have to be set later on because we need to
// get command line parameters first.
@@ -1223,10 +1322,10 @@ TestControl::run() {
initializeStatsMgr();
for (;;) {
// Calculate send due based on when last exchange was initiated.
- updateSendDue();
+ updateSendDue(last_sent_, options.getRate(), send_due_);
// Calculate number of packets to be sent to stay
// catch up with rate.
- uint64_t packets_due = getNextExchangesNum();
+ uint64_t packets_due = getNextExchangesNum(send_due_, options.getRate());
if ((packets_due == 0) && testDiags('i')) {
if (options.getIpVersion() == 4) {
stats_mgr4_->incrementCounter("shortwait");
@@ -1249,11 +1348,29 @@ TestControl::run() {
// 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.getIpVersion() == 6) && (options.getRenewRate() != 0)) {
+ updateSendDue(last_renew_, options.getRenewRate(), renew_due_);
+ uint64_t renew_packets_due =
+ getNextExchangesNum(renew_due_, options.getRenewRate());
+ // Send renew packets.
+ sendRenewPackets(socket, renew_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 idenitfied. 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();
}
printStats();
@@ -1429,6 +1546,25 @@ TestControl::sendDiscover4(const TestControlSocket& socket,
saveFirstPacket(pkt4);
}
+bool
+TestControl::sendRenew(const TestControlSocket& socket) {
+ last_renew_ = microsec_clock::universal_time();
+ Pkt6Ptr reply = reply_storage_.getRandom();
+ if (!reply) {
+ return (false);
+ }
+ Pkt6Ptr renew = createRenew(reply);
+ setDefaults6(socket, renew);
+ renew->pack();
+ IfaceMgr::instance().send(renew);
+ if (!stats_mgr6_) {
+ isc_throw(Unexpected, "Statistics Manager for DHCPv6 "
+ "hasn't been initialized");
+ }
+ stats_mgr6_->passSentPacket(StatsMgr6::XCHG_RN, renew);
+ return (true);
+}
+
void
TestControl::sendRequest4(const TestControlSocket& socket,
const dhcp::Pkt4Ptr& discover_pkt4,
@@ -1916,16 +2052,17 @@ TestControl::testDiags(const char diag) const {
}
void
-TestControl::updateSendDue() {
+TestControl::updateSendDue(const boost::posix_time::ptime& last_sent,
+ const int rate,
+ boost::posix_time::ptime& send_due) {
// If default constructor was called, this should not happen but
// if somebody has changed default constructor it is better to
// keep this check.
- if (last_sent_.is_not_a_date_time()) {
+ if (last_sent.is_not_a_date_time()) {
isc_throw(Unexpected, "time of last sent packet not initialized");
}
// Get the expected exchange rate.
CommandOptions& options = CommandOptions::instance();
- int rate = options.getRate();
// If rate was not specified we will wait just one clock tick to
// send next packet. This simulates best effort conditions.
long duration = 1;
@@ -1937,14 +2074,14 @@ TestControl::updateSendDue() {
duration = time_duration::ticks_per_second() / rate;
}
// Calculate due time to initiate next chunk of exchanges.
- send_due_ = last_sent_ + time_duration(0, 0, 0, duration);
+ send_due = last_sent + time_duration(0, 0, 0, duration);
// Check if it is already due.
ptime now(microsec_clock::universal_time());
// \todo verify if this condition is not too tight. In other words
// verify if this will not produce too many late sends.
// We might want to look at this once we are done implementing
// microsecond timeouts in IfaceMgr.
- if (now > send_due_) {
+ if (now > send_due) {
if (testDiags('i')) {
if (options.getIpVersion() == 4) {
stats_mgr4_->incrementCounter("latesend");
diff --git a/tests/tools/perfdhcp/test_control.h b/tests/tools/perfdhcp/test_control.h
index 93509e6..26adf9f 100644
--- a/tests/tools/perfdhcp/test_control.h
+++ b/tests/tools/perfdhcp/test_control.h
@@ -15,20 +15,21 @@
#ifndef TEST_CONTROL_H
#define TEST_CONTROL_H
-#include <string>
-#include <vector>
-
-#include <boost/noncopyable.hpp>
-#include <boost/shared_ptr.hpp>
-#include <boost/function.hpp>
-#include <boost/date_time/posix_time/posix_time.hpp>
+#include "packet_storage.h"
+#include "stats_mgr.h"
#include <dhcp/iface_mgr.h>
#include <dhcp/dhcp6.h>
#include <dhcp/pkt4.h>
#include <dhcp/pkt6.h>
-#include "stats_mgr.h"
+#include <boost/noncopyable.hpp>
+#include <boost/shared_ptr.hpp>
+#include <boost/function.hpp>
+#include <boost/date_time/posix_time/posix_time.hpp>
+
+#include <string>
+#include <vector>
namespace isc {
namespace perfdhcp {
@@ -299,6 +300,27 @@ protected:
/// \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.
+ /// The number of packets left in the storage after the call
+ /// to this function should guarantee that the Renew packets
+ /// can be sent at the given rate. Note that the Renew packets
+ /// are generated for the existing leases, represented here as
+ /// replies from the server.
+ /// @todo Instead of cleaning packets periodically we could
+ /// just stop adding new packets when the certain threshold
+ /// has been reached.
+ void cleanCachedPackets();
+
+ /// \brief Creates IPv6 packet using options from Reply packet.
+ ///
+ /// \param reply An instance of the Reply packet which contents should
+ /// be used to create an instance of the Renew packet.
+ ///
+ /// \return created Renew packet.
+ dhcp::Pkt6Ptr createRenew(const dhcp::Pkt6Ptr& reply);
+
/// \brief Factory function to create DHCPv6 ELAPSED_TIME option.
///
/// This factory function creates DHCPv6 ELAPSED_TIME option instance.
@@ -450,6 +472,15 @@ protected:
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 Returns number of exchanges to be started.
///
/// Method returns number of new exchanges to be started as soon
@@ -457,8 +488,13 @@ protected:
/// is based on current time, due time calculated with
/// \ref updateSendDue function and expected rate.
///
+ /// \param send_due Due time to initiate next chunk set exchanges.
+ /// \param rate A rate at which exchanges are initiated.
+ ///
/// \return number of exchanges to be started immediately.
- uint64_t getNextExchangesNum() const;
+ static uint64_t
+ getNextExchangesNum(const boost::posix_time::ptime& send_due,
+ const int rate);
/// \brief Return template buffer.
///
@@ -702,6 +738,26 @@ protected:
const uint64_t packets_num,
const bool preload = false);
+ /// \brief Send number of DHCPv6 Renew packets to the server.
+ ///
+ /// \param socket An object representing socket to be used to send packets.
+ /// \param packets_num A number of Renew packets to be send.
+ ///
+ /// \return A number of packets actually sent.
+ uint64_t sendRenewPackets(const TestControlSocket& socket,
+ const uint64_t packets_num);
+
+ /// \brief Send a renew message using provided socket.
+ ///
+ /// This method will select an existing lease from the Reply packet cache
+ /// If there is no lease that can be renewed this method will return false.
+ ///
+ /// \param socket An object encapsulating socket to be used to send
+ /// a packet.
+ ///
+ /// \return true if packet has been sent, false otherwise.
+ bool sendRenew(const TestControlSocket& socket);
+
/// \brief Send DHCPv4 REQUEST message.
///
/// Method creates and sends DHCPv4 REQUEST message to the server.
@@ -848,7 +904,14 @@ protected:
/// Method updates due time to initiate next chunk of exchanges.
/// Function takes current time, last sent packet's time and
/// expected rate in its calculations.
- void updateSendDue();
+ ///
+ /// \param last_sent A time when the last exchange was initiated.
+ /// \param rate A rate at which exchangesa re initiated
+ /// \param [out] send_due A reference to the time object to be updated
+ /// with the next due time.
+ void updateSendDue(const boost::posix_time::ptime& last_sent,
+ const int rate,
+ boost::posix_time::ptime& send_due);
private:
@@ -996,13 +1059,19 @@ private:
boost::posix_time::ptime send_due_; ///< Due time to initiate next chunk
///< of exchanges.
boost::posix_time::ptime last_sent_; ///< Indicates when the last exchange
- /// was initiated.
+ ///< was initiated.
+ boost::posix_time::ptime renew_due_; ///< Due time to send next set of
+ ///< Renew requests.
+ boost::posix_time::ptime last_renew_; ///< Indicates when the last Renew
+ ///< was attempted.
boost::posix_time::ptime last_report_; ///< Last intermediate report time.
StatsMgr4Ptr stats_mgr4_; ///< Statistics Manager 4.
StatsMgr6Ptr stats_mgr6_; ///< Statistics Manager 6.
+ PacketStorage<dhcp::Pkt6> reply_storage_; ///< A storage for reply messages.
+
NumberGeneratorPtr transid_gen_; ///< Transaction id generator.
NumberGeneratorPtr macaddr_gen_; ///< Numbers generator for MAC address.
diff --git a/tests/tools/perfdhcp/tests/Makefile.am b/tests/tools/perfdhcp/tests/Makefile.am
index aa4c0cf..e48757a 100644
--- a/tests/tools/perfdhcp/tests/Makefile.am
+++ b/tests/tools/perfdhcp/tests/Makefile.am
@@ -25,6 +25,7 @@ run_unittests_SOURCES += command_options_unittest.cc
run_unittests_SOURCES += perf_pkt6_unittest.cc
run_unittests_SOURCES += perf_pkt4_unittest.cc
run_unittests_SOURCES += localized_option_unittest.cc
+run_unittests_SOURCES += packet_storage_unittest.cc
run_unittests_SOURCES += stats_mgr_unittest.cc
run_unittests_SOURCES += test_control_unittest.cc
run_unittests_SOURCES += command_options_helper.h
diff --git a/tests/tools/perfdhcp/tests/command_options_helper.h b/tests/tools/perfdhcp/tests/command_options_helper.h
index dbb5c42..afa0dd3 100644
--- a/tests/tools/perfdhcp/tests/command_options_helper.h
+++ b/tests/tools/perfdhcp/tests/command_options_helper.h
@@ -15,11 +15,15 @@
#ifndef COMMAND_OPTIONS_HELPER_H
#define COMMAND_OPTIONS_HELPER_H
+#include "../command_options.h"
+#include <exceptions/exceptions.h>
+
+#include <assert.h>
+#include <iterator>
+#include <cstring>
#include <string>
#include <vector>
-#include <exceptions/exceptions.h>
-#include "../command_options.h"
namespace isc {
namespace perfdhcp {
diff --git a/tests/tools/perfdhcp/tests/command_options_unittest.cc b/tests/tools/perfdhcp/tests/command_options_unittest.cc
index d323782..3431b87 100644
--- a/tests/tools/perfdhcp/tests/command_options_unittest.cc
+++ b/tests/tools/perfdhcp/tests/command_options_unittest.cc
@@ -334,6 +334,37 @@ TEST_F(CommandOptionsTest, Rate) {
isc::InvalidParameter);
}
+TEST_F(CommandOptionsTest, RenewRate) {
+ CommandOptions& opt = CommandOptions::instance();
+ // If -f is specified together with -r the command line should
+ // be accepted and the renew rate should be set.
+ EXPECT_NO_THROW(process("perfdhcp -6 -r 10 -f 10 -l ethx all"));
+ EXPECT_EQ(10, opt.getRenewRate());
+ // Check that the release rate can be set to different value than
+ // rate specified as -r<rate>. Also, swap -f na d-r to make sure
+ // that order doesn't matter.
+ EXPECT_NO_THROW(process("perfdhcp -6 -f 5 -r 10 -l ethx all"));
+ EXPECT_EQ(5, opt.getRenewRate());
+ // The renew-rate of 0 is invalid.
+ EXPECT_THROW(process("perfdhcp -6 -r 10 -f 0 - l ethx all"),
+ isc::InvalidParameter);
+ // If -r<rate> is not specified the -f<renew-rate> should not
+ // be accepted.
+ EXPECT_THROW(process("perfdhcp -6 -f 10 -l ethx all"),
+ isc::InvalidParameter);
+ // Currently the -f<renew-rate> can be specified for IPv6 mode
+ // only.
+ EXPECT_THROW(process("perfdhcp -4 -r 10 -f 10 -l ethx all"),
+ isc::InvalidParameter);
+ // Renew rate should be specified.
+ EXPECT_THROW(process("perfdhcp -6 -r 10 -f -l ethx all"),
+ isc::InvalidParameter);
+
+ // -f and -i are mutually exclusive
+ EXPECT_THROW(process("perfdhcp -6 -r 10 -f 10 -l ethx -i all"),
+ isc::InvalidParameter);
+}
+
TEST_F(CommandOptionsTest, ReportDelay) {
CommandOptions& opt = CommandOptions::instance();
EXPECT_NO_THROW(process("perfdhcp -r 100 -t 17 -l ethx all"));
diff --git a/tests/tools/perfdhcp/tests/packet_storage_unittest.cc b/tests/tools/perfdhcp/tests/packet_storage_unittest.cc
new file mode 100644
index 0000000..b6e415b
--- /dev/null
+++ b/tests/tools/perfdhcp/tests/packet_storage_unittest.cc
@@ -0,0 +1,205 @@
+// Copyright (C) 2013 Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#include "../packet_storage.h"
+#include <dhcp/dhcp6.h>
+#include <dhcp/pkt6.h>
+
+#include <gtest/gtest.h>
+
+namespace {
+
+using namespace isc;
+using namespace isc::dhcp;
+using namespace perfdhcp;
+
+/// @todo Implement the tests which use Pkt4 objects once the support for
+/// DHCPv4 renewals / releases is added.
+
+/// The number of packets in the test storage.
+const unsigned int STORAGE_SIZE = 20;
+
+/// Test fixture class for PacketStorage container testing.
+class PacketStorageTest : public ::testing::Test {
+public:
+ /// \brief Constructor, initializes the storage for each test.
+ PacketStorageTest() {
+ for (uint32_t i = 0; i < STORAGE_SIZE; ++i) {
+ storage_.append(createPacket6(DHCPV6_REPLY, i));
+ }
+ }
+
+ /// \brief Creates an instance of the Pkt6.
+ ///
+ /// \param packet_type A type of the packet.
+ /// \param transid Transaction id.
+ /// \return An instance of the Pkt6.
+ Pkt6Ptr createPacket6(const uint16_t packet_type,
+ const uint32_t transid) {
+ return (Pkt6Ptr(new Pkt6(packet_type, transid)));
+ }
+
+ /// Packet storage under test.
+ PacketStorage<Pkt6> storage_;
+
+};
+
+// This test verifies that the packets in the storage can be accessed
+// sequentially and when a packet is returned, it is removed from the
+// storage. It also verifies the correctness of the behaviour of the
+// empty() and size() functions.
+TEST_F(PacketStorageTest, getNext) {
+ ASSERT_EQ(STORAGE_SIZE, storage_.size());
+ for (int i = 0; i < STORAGE_SIZE; ++i) {
+ Pkt6Ptr packet = storage_.getNext();
+ ASSERT_TRUE(packet) << "NULL packet returned by storage_.getNext() for"
+ << " iteration number " << i;
+ EXPECT_EQ(i, packet->getTransid());
+ EXPECT_EQ(STORAGE_SIZE - i - 1, storage_.size());
+ }
+ EXPECT_TRUE(storage_.empty());
+ // When storage is empty, the attempt to get the next packet should
+ // result in returning NULL pointer.
+ EXPECT_FALSE(storage_.getNext());
+ // Let's try it again to see if the previous call to getNext didn't
+ // put the storage into the state in which the subsequent calls to
+ // getNext would result in incorrect behaviour.
+ EXPECT_FALSE(storage_.getNext());
+
+ // Let's add a new packet to the empty storage to check that storage
+ // "recovers" from being empty, i.e. that the internal indicator
+ // which points to current packet reinitializes correctly.
+ storage_.append(createPacket6(DHCPV6_REPLY, 100));
+ ASSERT_EQ(1, storage_.size());
+ Pkt6Ptr packet = storage_.getNext();
+ EXPECT_EQ(100, packet->getTransid());
+ EXPECT_FALSE(storage_.getNext());
+}
+
+// This test verifies that the packets in the storage can be accessed
+// randomly and when a packet is returned, it is removed from the
+// storage. It also verifies the correctness of the behaviour of the
+// empty() and size() functions.
+TEST_F(PacketStorageTest, getRandom) {
+ ASSERT_EQ(STORAGE_SIZE, storage_.size());
+ int cnt_equals = 0;
+ for (int i = 0; i < STORAGE_SIZE; ++i) {
+ Pkt6Ptr packet = storage_.getRandom();
+ ASSERT_TRUE(packet) << "NULL packet returned by storage_.getRandom()"
+ " for iteration number " << i;
+ EXPECT_EQ(STORAGE_SIZE - i - 1, storage_.size());
+ cnt_equals += (i == packet->getTransid() ? 1 : 0);
+ }
+ // If the number of times id is equal to i, is the same as the number
+ // of elements then they were NOT accessed randomly.
+ // The odds of 20 elements being randomly accessed sequential order
+ // is nil isn't it?
+ EXPECT_NE(cnt_equals, STORAGE_SIZE);
+
+ EXPECT_TRUE(storage_.empty());
+ // When storage is empty, the attempt to get the random packet should
+ // result in returning NULL pointer.
+ EXPECT_FALSE(storage_.getRandom());
+ // Let's try it again to see if the previous call to getRandom didn't
+ // put the storage into the state in which the subsequent calls to
+ // getRandom would result in incorrect behaviour.
+ EXPECT_FALSE(storage_.getRandom());
+
+ // Let's add a new packet to the empty storage to check that storage
+ // "recovers" from being empty, i.e. that the internal indicator
+ // which points to the current packet reinitializes correctly.
+ storage_.append(createPacket6(DHCPV6_REPLY, 100));
+ ASSERT_EQ(1, storage_.size());
+ Pkt6Ptr packet = storage_.getRandom();
+ ASSERT_TRUE(packet);
+ EXPECT_EQ(100, packet->getTransid());
+ EXPECT_FALSE(storage_.getRandom());
+}
+
+// This test verifies that the packets in the storage can be accessed
+// either randomly or sequentially in the same time. It verifies that
+// each returned packet is removed from the storage.
+TEST_F(PacketStorageTest, getNextAndRandom) {
+ ASSERT_EQ(STORAGE_SIZE, storage_.size());
+ for (int i = 0; i < STORAGE_SIZE / 2; ++i) {
+ Pkt6Ptr packet_random = storage_.getRandom();
+ ASSERT_TRUE(packet_random) << "NULL packet returned by"
+ " storage_.getRandom() for iteration number " << i;
+ EXPECT_EQ(STORAGE_SIZE - 2 *i - 1, storage_.size());
+ Pkt6Ptr packet_seq = storage_.getNext();
+ ASSERT_TRUE(packet_seq) << "NULL packet returned by"
+ " storage_.getNext() for iteration number " << i;
+ EXPECT_EQ(STORAGE_SIZE - 2 * i - 2, storage_.size());
+ }
+ EXPECT_TRUE(storage_.empty());
+ EXPECT_FALSE(storage_.getRandom());
+ EXPECT_FALSE(storage_.getNext());
+
+ // Append two packets to the storage to check if it can "recover"
+ // from being empty and that new elements can be accessed.
+ storage_.append(createPacket6(DHCPV6_REPLY, 100));
+ storage_.append(createPacket6(DHCPV6_REPLY, 101));
+ ASSERT_EQ(2, storage_.size());
+ // The newly added elements haven't been accessed yet. So, if we
+ // call getNext the first one should be returned.
+ Pkt6Ptr packet_next = storage_.getNext();
+ ASSERT_TRUE(packet_next);
+ // The first packet has transaction id equal to 100.
+ EXPECT_EQ(100, packet_next->getTransid());
+ // There should be just one packet left in the storage.
+ ASSERT_EQ(1, storage_.size());
+ // The call to getRandom should return the sole packet from the
+ // storage.
+ Pkt6Ptr packet_random = storage_.getRandom();
+ ASSERT_TRUE(packet_random);
+ EXPECT_EQ(101, packet_random->getTransid());
+ // Any further calls to getRandom and getNext should return NULL.
+ EXPECT_FALSE(storage_.getRandom());
+ EXPECT_FALSE(storage_.getNext());
+}
+
+// This test verifies that all packets are removed from the storage when
+// clear() function is invoked.
+TEST_F(PacketStorageTest, clearAll) {
+ ASSERT_EQ(STORAGE_SIZE, storage_.size());
+ ASSERT_NO_THROW(storage_.clear());
+ EXPECT_TRUE(storage_.empty());
+}
+
+// This test verifies that a set of packets can be removed from the
+// storage when a number of packets to be removed is specified. If
+// number of packets to be removed exceeds the storage size, all
+// packets should be removed.
+TEST_F(PacketStorageTest, clear) {
+ // Initially storage should have 20 elements.
+ ASSERT_EQ(STORAGE_SIZE, storage_.size());
+ // Remove 10 of them.
+ ASSERT_NO_THROW(storage_.clear(10));
+ // We should have 10 remaining.
+ ASSERT_EQ(10, storage_.size());
+
+ // Check that the retrieval still works after partial clear.
+ EXPECT_TRUE(storage_.getNext());
+ EXPECT_TRUE(storage_.getRandom());
+ // We should have 10 - 2 = 8 packets in the storage after retrieval.
+ ASSERT_EQ(8, storage_.size());
+
+ // Try to remove more elements that actually is. It
+ // should result in removal of all elements.
+ ASSERT_NO_THROW(storage_.clear(15));
+ EXPECT_TRUE(storage_.empty());
+}
+
+
+} // anonymous namespace
diff --git a/tests/tools/perfdhcp/tests/test_control_unittest.cc b/tests/tools/perfdhcp/tests/test_control_unittest.cc
index c0b7231..0d45c88 100644
--- a/tests/tools/perfdhcp/tests/test_control_unittest.cc
+++ b/tests/tools/perfdhcp/tests/test_control_unittest.cc
@@ -12,21 +12,22 @@
// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
// PERFORMANCE OF THIS SOFTWARE.
+#include "command_options_helper.h"
+#include "../test_control.h"
+
+#include <asiolink/io_address.h>
+#include <exceptions/exceptions.h>
+#include <dhcp/dhcp4.h>
+#include <dhcp/iface_mgr.h>
+
+#include <boost/date_time/posix_time/posix_time.hpp>
+
#include <cstddef>
#include <stdint.h>
#include <string>
#include <fstream>
#include <gtest/gtest.h>
-#include <boost/date_time/posix_time/posix_time.hpp>
-
-#include <exceptions/exceptions.h>
-#include <asiolink/io_address.h>
-#include <dhcp/dhcp4.h>
-#include <dhcp/iface_mgr.h>
-#include "command_options_helper.h"
-#include "../test_control.h"
-
using namespace std;
using namespace boost::posix_time;
using namespace isc;
@@ -63,11 +64,18 @@ public:
virtual uint32_t generate() {
return (++transid_);
}
+
+ /// \brief Return next transaction id value.
+ uint32_t getNext() const {
+ return (transid_ + 1);
+ }
+
private:
uint32_t transid_; ///< Last generated transaction id.
};
using TestControl::checkExitConditions;
+ using TestControl::createRenew;
using TestControl::factoryElapsedTime6;
using TestControl::factoryGeneric;
using TestControl::factoryIana6;
@@ -85,6 +93,9 @@ public:
using TestControl::processReceivedPacket6;
using TestControl::registerOptionFactories;
using TestControl::sendDiscover4;
+ using TestControl::sendPackets;
+ using TestControl::sendRenewPackets;
+ using TestControl::sendRequest6;
using TestControl::sendSolicit6;
using TestControl::setDefaults4;
using TestControl::setDefaults6;
@@ -281,7 +292,7 @@ public:
return (cnt);
}
- /// brief Test generation of mulitple DUIDs
+ /// \brief Test generation of mulitple DUIDs
///
/// This method checks the generation of multiple DUIDs. Number
/// of iterations depends on the number of simulated clients.
@@ -624,7 +635,6 @@ public:
CommandOptionsHelper::process(cmdline);
}
-private:
/// \brief Create DHCPv4 OFFER packet.
///
/// \param transid transaction id.
@@ -645,8 +655,8 @@ private:
///
/// \param transid transaction id.
/// \return instance of the packet.
- boost::shared_ptr<Pkt6>
- createAdvertisePkt6(uint32_t transid) const {
+ Pkt6Ptr
+ createAdvertisePkt6(const uint32_t transid) const {
boost::shared_ptr<Pkt6> advertise(new Pkt6(DHCPV6_ADVERTISE, transid));
// Add IA_NA if requested by the client.
if (CommandOptions::instance().getLeaseType()
@@ -671,6 +681,33 @@ private:
return (advertise);
}
+ Pkt6Ptr
+ createReplyPkt6(const uint32_t transid) const {
+ Pkt6Ptr reply(new Pkt6(DHCPV6_REPLY, transid));
+ // Add IA_NA if requested by the client.
+ if (CommandOptions::instance().getLeaseType()
+ .includes(CommandOptions::LeaseType::ADDRESS)) {
+ OptionPtr opt_ia_na = Option::factory(Option::V6, D6O_IA_NA);
+ reply->addOption(opt_ia_na);
+ }
+ // Add IA_PD if requested by the client.
+ if (CommandOptions::instance().getLeaseType()
+ .includes(CommandOptions::LeaseType::PREFIX)) {
+ OptionPtr opt_ia_pd = Option::factory(Option::V6, D6O_IA_PD);
+ reply->addOption(opt_ia_pd);
+ }
+ OptionPtr opt_serverid(new Option(Option::V6, D6O_SERVERID));
+ NakedTestControl tc;
+ uint8_t randomized = 0;
+ std::vector<uint8_t> duid(tc.generateDuid(randomized));
+ OptionPtr opt_clientid(Option::factory(Option::V6, D6O_CLIENTID, duid));
+ reply->addOption(opt_serverid);
+ reply->addOption(opt_clientid);
+ reply->updateTimestamp();
+ return (reply);
+
+ }
+
};
TEST_F(TestControlTest, GenerateDuid) {
@@ -1197,7 +1234,8 @@ TEST_F(TestControlTest, RateControl) {
CommandOptions& options = CommandOptions::instance();
NakedTestControl tc1;
- uint64_t xchgs_num = tc1.getNextExchangesNum();
+ uint64_t xchgs_num = tc1.getNextExchangesNum(microsec_clock::universal_time(),
+ options.getRate());
EXPECT_EQ(options.getAggressivity(), xchgs_num);
// The exchange rate is now 1 per second. We don't know how many
@@ -1208,8 +1246,137 @@ TEST_F(TestControlTest, RateControl) {
processCmdLine("perfdhcp -l 127.0.0.1 -a 1000000 -r 1 all")
);
NakedTestControl tc2;
- xchgs_num = tc2.getNextExchangesNum();
+ xchgs_num = tc2.getNextExchangesNum(microsec_clock::universal_time(),
+ options.getRate());
EXPECT_GT(xchgs_num, 0);
EXPECT_LT(xchgs_num, options.getAggressivity());
// @todo add more thorough checks for rate values.
}
+
+TEST_F(TestControlTest, processRenew) {
+ std::string loopback_iface(getLocalLoopback());
+ if (loopback_iface.empty()) {
+ std::cout << "Skipping the test because loopback interface could"
+ " not be detected" << std::endl;
+ return;
+ }
+ // This command line specifies that the Renew messages should be sent
+ // with the same rate as the Solicit messages.
+ ASSERT_NO_THROW(processCmdLine("perfdhcp -6 -l " + loopback_iface +
+ " -r 10 -f 10 -R 10 -L 10547 -n 10 ::1"));
+ // Create a test controller class.
+ NakedTestControl tc;
+ tc.initializeStatsMgr();
+ // Set the transaction id generator to sequential to control to guarantee
+ // that transaction ids are predictable.
+ boost::shared_ptr<NakedTestControl::IncrementalGenerator>
+ generator(new NakedTestControl::IncrementalGenerator());
+ tc.setTransidGenerator(generator);
+ // Socket has to be created so as we can actually send packets.
+ int sock_handle = 0;
+ ASSERT_NO_THROW(sock_handle = tc.openSocket());
+ TestControl::TestControlSocket sock(sock_handle);
+
+ // Send a number of Solicit messages. Each generated Solicit will be
+ // assigned a different transaction id, starting from 1 to 10.
+ tc.sendPackets(sock, 10);
+
+ // Simulate Advertise responses from the server. Each advertise is assigned
+ // a transaction id from the range of 1 to 10, so as they match the
+ // transaction ids from the Solicit messages.
+ for (int i = generator->getNext() - 10; i < generator->getNext(); ++i) {
+ Pkt6Ptr advertise(createAdvertisePkt6(i));
+ // If Advertise is matched with the Solicit the call below will
+ // trigger a corresponding Request. They will be assigned
+ // transaction ids from the range from 11 to 20 (the range of
+ // 1 to 10 has been used by Solicit-Advertise).
+ ASSERT_NO_THROW(tc.processReceivedPacket6(sock, advertise));
+ }
+
+ // Requests have been sent, so now let's simulate responses from the server.
+ // Generate corresponding Reply messages with the transaction ids from the
+ // range from 11 to 20.
+ for (int i = generator->getNext() - 10; i < generator->getNext(); ++i) {
+ Pkt6Ptr reply(createReplyPkt6(i));
+ // Each Reply packet corresponds to the new lease acquired. Since
+ // -f<renew-rate> option has been specified, received Reply
+ // messages are held so as Renew messages can be sent for
+ // existing leases.
+ ASSERT_NO_THROW(tc.processReceivedPacket6(sock, reply));
+ }
+
+ uint64_t renew_num;
+ // Try to send 5 Renew packets. It should be successful because
+ // 10 Reply messages has been received. For each of them we should
+ // be able to send Renew.
+ ASSERT_NO_THROW(renew_num = tc.sendRenewPackets(sock, 5));
+ // Make sure that we have sent 5 packets.
+ EXPECT_EQ(5, renew_num);
+
+ // Try to do it again. We should still have 5 Reply packets for
+ // which Renews haven't been sent yet.
+ ASSERT_NO_THROW(renew_num = tc.sendRenewPackets(sock, 5));
+ EXPECT_EQ(5, renew_num);
+
+ // We used all the Reply packets (we sent Renew for each of them
+ // already). Therefore, no further Renew packets should be sent before
+ // We acquire new leases.
+ ASSERT_NO_THROW(renew_num = tc.sendRenewPackets(sock, 5));
+ // Make sure that no Renew has been sent.
+ EXPECT_EQ(0, renew_num);
+}
+
+TEST_F(TestControlTest, createRenew) {
+ // This command line specifies that the Renew messages should be sent
+ // with the same rate as the Solicit messages.
+ ASSERT_NO_THROW(processCmdLine("perfdhcp -6 -l lo -r 10 -f 10 -R 10"
+ " -L 10547 -n 10 -e address-and-prefix"
+ " ::1"));
+ // Create a test controller class.
+ NakedTestControl tc;
+ // Set the transaction id generator because createRenew function requires
+ // it to generate the transaction id for the Renew packet.
+ boost::shared_ptr<NakedTestControl::IncrementalGenerator>
+ generator(new NakedTestControl::IncrementalGenerator());
+ tc.setTransidGenerator(generator);
+
+ // Create a Reply packet. The createRenew function will need Reply
+ // packet to create a corresponding Renew.
+ Pkt6Ptr reply = createReplyPkt6(1);
+ Pkt6Ptr renew;
+ // Check that Renew is created.
+ ASSERT_NO_THROW(renew = tc.createRenew(reply));
+ ASSERT_TRUE(renew);
+ EXPECT_EQ(DHCPV6_RENEW, renew->getType());
+ EXPECT_EQ(1, renew->getTransid());
+
+ // Now check that the Renew packet created, has expected options. The
+ // payload of these options should be the same as the payload of the
+ // options in the Reply.
+
+ // Client Identifier
+ OptionPtr opt_clientid = renew->getOption(D6O_CLIENTID);
+ ASSERT_TRUE(opt_clientid);
+ EXPECT_TRUE(reply->getOption(D6O_CLIENTID)->getData() ==
+ opt_clientid->getData());
+
+ // Server identifier
+ OptionPtr opt_serverid = renew->getOption(D6O_SERVERID);
+ ASSERT_TRUE(opt_serverid);
+ EXPECT_TRUE(reply->getOption(D6O_SERVERID)->getData() ==
+ opt_serverid->getData());
+
+ // IA_NA
+ OptionPtr opt_ia_na = renew->getOption(D6O_IA_NA);
+ ASSERT_TRUE(opt_ia_na);
+ EXPECT_TRUE(reply->getOption(D6O_IA_NA)->getData() ==
+ opt_ia_na->getData());
+
+ // IA_PD
+ OptionPtr opt_ia_pd = renew->getOption(D6O_IA_PD);
+ ASSERT_TRUE(opt_ia_pd);
+ EXPECT_TRUE(reply->getOption(D6O_IA_PD)->getData() ==
+ opt_ia_pd->getData());
+
+}
+
More information about the bind10-changes
mailing list