]> git.ipfire.org Git - thirdparty/kea.git/commitdiff
[283-perfdhcp-sending-thread] Added sender thread
authorFrancis Dupont <fdupont@isc.org>
Fri, 14 Dec 2018 09:28:24 +0000 (10:28 +0100)
committerFrancis Dupont <fdupont@isc.org>
Fri, 14 Dec 2018 09:28:24 +0000 (10:28 +0100)
src/bin/perfdhcp/command_options.cc
src/bin/perfdhcp/command_options.h
src/bin/perfdhcp/perfdhcp.xml
src/bin/perfdhcp/stats_mgr.h
src/bin/perfdhcp/test_control.cc
src/bin/perfdhcp/test_control.h
src/bin/perfdhcp/tests/test_control_unittest.cc

index d264248236629bdd0f324a41fc10cef09c7f17bf..a4ac684b14784040a8f0beba745167925c42e0c1 100644 (file)
@@ -155,6 +155,7 @@ CommandOptions::reset() {
     v6_relay_encapsulation_level_ = 0;
     generateDuidTemplate();
     extra_opts_.clear();
+    single_thread_mode_ = true;
 }
 
 bool
@@ -222,7 +223,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, "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) {
+                        "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;
@@ -522,6 +523,10 @@ CommandOptions::initialize(int argc, char** argv, bool print_cmd_line) {
             xid_offset_.push_back(offset_arg);
             break;
 
+        case 'g':
+            single_thread_mode_ = !single_thread_mode_;
+            break;
+
         default:
             isc_throw(isc::InvalidParameter, "unknown command line option");
         }
@@ -1016,6 +1021,11 @@ CommandOptions::printCommandLine() const {
     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
@@ -1031,7 +1041,7 @@ CommandOptions::usage() const {
         "         [-c] [-1] [-M<mac-list-file>] [-T<template-file>]\n"
         "         [-X<xid-offset>] [-O<random-offset] [-E<time-offset>]\n"
         "         [-S<srvid-offset>] [-I<ip-offset>] [-x<diagnostic-selector>]\n"
-        "         [-w<wrapped>] [server]\n"
+        "         [-w<wrapped>] [-g] [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"
@@ -1079,6 +1089,7 @@ CommandOptions::usage() const {
         "    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: Toggle between single and multi-thread modes (default is single).\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"
index 2ef5533593e1f9c8468b02c7e3cf6058634c2bf0..2ac3a14fa2a72a33b781c1d182604d33e41d6d1a 100644 (file)
@@ -343,6 +343,11 @@ public:
     /// @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.
@@ -646,6 +651,9 @@ private:
 
     /// @brief Extra options to be sent in each packet.
     isc::dhcp::OptionCollection extra_opts_;
+
+    /// @brief Option to switch modes between single and multi-threaded.
+    bool single_thread_mode_;
 };
 
 }  // namespace perfdhcp
index a11f68a4343823817c63793452c1f51394b4127f..b0b255123e7fc5bba7dee9d159257137172b07e5 100644 (file)
@@ -57,6 +57,7 @@
             <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="repeat"><option>-g</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>
             </varlistentry>
 
 
+            <varlistentry>
+                <term><option>-g</option></term>
+                <listitem>
+                    <para>
+                        Toggle between single and multi-thread modes (default
+                        is single-thread mode).
+                    </para>
+                </listitem>
+            </varlistentry>
+
             <varlistentry>
                 <term><option>-h</option></term>
                 <listitem>
index 371e4cc4b2cb4c3ca8d87f59ccd71f680222337a..fe3b790a2b58e41ed64e04c26a3a0ac09bd827fe 100644 (file)
@@ -9,6 +9,7 @@
 
 #include <dhcp/pkt4.h>
 #include <dhcp/pkt6.h>
+#include <util/threads/sync.h>
 #include <exceptions/exceptions.h>
 
 #include <boost/noncopyable.hpp>
@@ -306,6 +307,7 @@ public:
             if (!packet) {
                 isc_throw(BadValue, "Packet is null");
             }
+            isc::util::thread::Mutex::Locker lock(mutex_);
             ++sent_packets_num_;
             sent_packets_.template get<0>().push_back(packet);
         }
@@ -320,6 +322,7 @@ public:
             if (!packet) {
                 isc_throw(BadValue, "Packet is null");
             }
+            isc::util::thread::Mutex::Locker lock(mutex_);
             rcvd_packets_.push_back(packet);
         }
 
@@ -467,6 +470,7 @@ public:
                 ++ordered_lookups_;
                 packet_found = true;
             } else {
+                isc::util::thread::Mutex::Locker lock(mutex_);
                 // If we are here, it means that we were unable to match the
                 // next incoming packet with next sent packet so we need to
                 // take a little more expensive approach to look packets using
@@ -575,6 +579,7 @@ public:
             // If packet was found, we assume it will be never searched
             // again. We want to delete this packet from the list to
             // improve performance of future searches.
+            isc::util::thread::Mutex::Locker lock(mutex_);
             next_sent_ = eraseSent(next_sent_);
             return(sent_packet);
         }
@@ -790,6 +795,13 @@ public:
         /// sending queries.
         void printSentStats() const {
             using namespace std;
+            try {
+                if (getAvgSentDelay() == 0) {
+                    return;
+                }
+            } catch (const Exception&) {
+                return;
+            }
             try {
                 cout << fixed << setprecision(3)
                      << "min sending delay: " << getMinSentDelay() * 1e3
@@ -907,23 +919,24 @@ public:
         /// \brief Erase packet from the list of sent packets.
         ///
         /// Method erases packet from the list of sent packets.
+        /// Lock must be held by caller.
         ///
         /// \param it iterator pointing to packet to be erased.
         /// \return iterator pointing to packet following erased
         /// packet or sent_packets_.end() if packet not found.
         PktListIterator eraseSent(const PktListIterator it) {
-             if (archive_enabled_) {
-                 // We don't want to keep list of all sent packets
-                 // because it will affect packet lookup performance.
-                 // If packet is matched with received packet we
-                 // move it to list of archived packets. List of
-                 // archived packets may be used for diagnostics
-                 // when test is completed.
-                 archived_packets_.push_back(*it);
-             }
-             // get<0>() template returns sequential index to
-             // container.
-             return(sent_packets_.template get<0>().erase(it));
+            if (archive_enabled_) {
+                // We don't want to keep list of all sent packets
+                // because it will affect packet lookup performance.
+                // If packet is matched with received packet we
+                // move it to list of archived packets. List of
+                // archived packets may be used for diagnostics
+                // when test is completed.
+                archived_packets_.push_back(*it);
+            }
+            // get<0>() template returns sequential index to
+            // container.
+            return(sent_packets_.template get<0>().erase(it));
         }
 
         ExchangeType xchg_type_;             ///< Packet exchange type.
@@ -991,6 +1004,7 @@ public:
         uint64_t sent_packets_num_;    ///< Total number of sent packets.
         uint64_t rcvd_packets_num_;    ///< Total number of received packets.
         boost::posix_time::ptime boot_time_; ///< Time when test is started.
+        isc::util::thread::Mutex mutex_; ///< Mutex for concurrent access.
     };
 
     /// Pointer to ExchangeStats.
index 972eaec5b81584e2cf488d90c46a3d16b06d1ed2..6acf235a4dc793081e2b8cc802d67e3caedf6bd4 100644 (file)
@@ -36,6 +36,7 @@ using namespace boost::posix_time;
 using namespace isc;
 using namespace isc::dhcp;
 using namespace isc::asiolink;
+using namespace isc::util::thread;
 
 namespace isc {
 namespace perfdhcp {
@@ -143,6 +144,10 @@ TestControl::TestControl()
   reset();
 }
 
+TestControl::~TestControl() {
+    reset();
+}
+
 void
 TestControl::checkLateMessages(RateControl& rate_control) {
     // If diagnostics is disabled, there is no need to log late sent messages.
@@ -641,15 +646,18 @@ TestControl::getCurrentTimeout() const {
     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() ||
+    if ((!sender_thread_ && now >= basic_rate_control_.getDue()) ||
         (options.getRenewRate() != 0 && now >= renew_rate_control_.getDue()) ||
         (options.getReleaseRate() != 0 &&
          now >= release_rate_control_.getDue())) {
         return (0);
     }
 
+    ptime due = now + microseconds(100);
     // Let's assume that the due time for Solicit is the soonest.
-    ptime due = basic_rate_control_.getDue();
+    if (!sender_thread_) {
+        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)) {
@@ -916,7 +924,7 @@ TestControl::openSocket() const {
             if ((ret >= 0)  && options.isInterface()) {
                 IfacePtr iface =
                     IfaceMgr::instance().getIface(options.getLocalName());
-                if (iface == NULL) {
+                if (!iface) {
                     isc_throw(Unexpected, "unknown interface "
                               << options.getLocalName());
                 }
@@ -936,8 +944,7 @@ TestControl::openSocket() const {
 }
 
 void
-TestControl::sendPackets(const TestControlSocket& socket,
-                         const uint64_t packets_num,
+TestControl::sendPackets(const uint64_t packets_num,
                          const bool preload /* = false */) {
     CommandOptions& options = CommandOptions::instance();
     for (uint64_t i = packets_num; i > 0; --i) {
@@ -949,26 +956,26 @@ TestControl::sendPackets(const TestControlSocket& socket,
             // No template packets means that no -T option was specified.
             // We have to build packets ourselves.
             if (template_buffers_.empty()) {
-                sendDiscover4(socket, preload);
+                sendDiscover4(preload);
             } else {
                 // @todo add defines for packet type index that can be
                 // used to access template_buffers_.
-                sendDiscover4(socket, template_buffers_[0], preload);
+                sendDiscover4(template_buffers_[0], preload);
             }
         } else {
             // No template packets means that no -T option was specified.
             // We have to build packets ourselves.
             if (template_buffers_.empty()) {
-                sendSolicit6(socket, preload);
+                sendSolicit6(preload);
             } else {
                 // @todo add defines for packet type index that can be
                 // used to access template_buffers_.
-                sendSolicit6(socket, template_buffers_[0], preload);
+                sendSolicit6(template_buffers_[0], preload);
             }
         }
         // If we preload server we don't want to receive any packets.
-        if (!preload) {
-            uint64_t latercvd = receivePackets(socket);
+        if (!preload && !sender_thread_) {
+            uint64_t latercvd = receivePackets();
             if (testDiags('i')) {
                 if (options.getIpVersion() == 4) {
                     stats_mgr4_->incrementCounter("latercvd", latercvd);
@@ -981,10 +988,9 @@ TestControl::sendPackets(const TestControlSocket& socket,
 }
 
 uint64_t
-TestControl::sendMultipleRequests(const TestControlSocket& socket,
-                                  const uint64_t msg_num) {
+TestControl::sendMultipleRequests(const uint64_t msg_num) {
     for (uint64_t i = 0; i < msg_num; ++i) {
-        if (!sendRequestFromAck(socket)) {
+        if (!sendRequestFromAck()) {
             return (i);
         }
     }
@@ -992,11 +998,10 @@ TestControl::sendMultipleRequests(const TestControlSocket& socket,
 }
 
 uint64_t
-TestControl::sendMultipleMessages6(const TestControlSocket& socket,
-                                   const uint32_t msg_type,
+TestControl::sendMultipleMessages6(const uint32_t msg_type,
                                    const uint64_t msg_num) {
     for (uint64_t i = 0; i < msg_num; ++i) {
-        if (!sendMessageFromReply(msg_type, socket)) {
+        if (!sendMessageFromReply(msg_type)) {
             return (i);
         }
     }
@@ -1230,8 +1235,7 @@ TestControl::readPacketTemplate(const std::string& file_name) {
 }
 
 void
-TestControl::processReceivedPacket4(const TestControlSocket& socket,
-                            const Pkt4Ptr& pkt4) {
+TestControl::processReceivedPacket4(const Pkt4Ptr& pkt4) {
     if (pkt4->getType() == DHCPOFFER) {
         Pkt4Ptr discover_pkt4(stats_mgr4_->passRcvdPacket(StatsMgr4::XCHG_DO,
                                                           pkt4));
@@ -1239,11 +1243,11 @@ TestControl::processReceivedPacket4(const TestControlSocket& socket,
             CommandOptions::instance().getExchangeMode();
         if ((xchg_mode == CommandOptions::DORA_SARR) && discover_pkt4) {
             if (template_buffers_.size() < 2) {
-                sendRequest4(socket, discover_pkt4, pkt4);
+                sendRequest4(discover_pkt4, pkt4);
             } else {
                 // @todo add defines for packet type index that can be
                 // used to access template_buffers_.
-                sendRequest4(socket, template_buffers_[1], discover_pkt4, pkt4);
+                sendRequest4(template_buffers_[1], discover_pkt4, pkt4);
             }
         }
     } else if (pkt4->getType() == DHCPACK) {
@@ -1273,8 +1277,7 @@ TestControl::processReceivedPacket4(const TestControlSocket& socket,
 }
 
 void
-TestControl::processReceivedPacket6(const TestControlSocket& socket,
-                            const Pkt6Ptr& pkt6) {
+TestControl::processReceivedPacket6(const Pkt6Ptr& pkt6) {
     uint8_t packet_type = pkt6->getType();
     if (packet_type == DHCPV6_ADVERTISE) {
         Pkt6Ptr solicit_pkt6(stats_mgr6_->passRcvdPacket(StatsMgr6::XCHG_SA,
@@ -1286,11 +1289,11 @@ TestControl::processReceivedPacket6(const TestControlSocket& socket,
             // We might want to check if STATUS_CODE option is non-zero
             // and if there is IAADR option in IA_NA.
             if (template_buffers_.size() < 2) {
-                sendRequest6(socket, pkt6);
+                sendRequest6(pkt6);
             } else {
                 // @todo add defines for packet type index that can be
                 // used to access template_buffers_.
-                sendRequest6(socket, template_buffers_[1], pkt6);
+                sendRequest6(template_buffers_[1], pkt6);
             }
         }
     } else if (packet_type == DHCPV6_REPLY) {
@@ -1329,7 +1332,7 @@ TestControl::processReceivedPacket6(const TestControlSocket& socket,
 }
 
 uint64_t
-TestControl::receivePackets(const TestControlSocket& socket) {
+TestControl::receivePackets() {
     bool receiving = true;
     uint64_t received = 0;
     while (receiving) {
@@ -1352,7 +1355,7 @@ TestControl::receivePackets(const TestControlSocket& socket) {
                 /// @todo: Add packet exception handling here. Right now any
                 /// malformed packet will cause perfdhcp to abort.
                 pkt4->unpack();
-                processReceivedPacket4(socket, pkt4);
+                processReceivedPacket4(pkt4);
             }
         } else if (CommandOptions::instance().getIpVersion() == 6) {
             Pkt6Ptr pkt6;
@@ -1373,7 +1376,7 @@ TestControl::receivePackets(const TestControlSocket& socket) {
                 /// @todo: Add packet exception handling here. Right now any
                 /// malformed packet will cause perfdhcp to abort.
                 pkt6->unpack();
-                processReceivedPacket6(socket, pkt6);
+                processReceivedPacket6(pkt6);
             }
         }
     }
@@ -1473,6 +1476,14 @@ TestControl::reset() {
     setMacAddrGenerator(NumberGeneratorPtr());
     first_packet_serverid_.clear();
     interrupted_ = false;
+
+    if (sender_thread_) {
+        if (sender_thread_->isRunning()) {
+            sender_thread_->stop();
+        }
+        sender_thread_.reset();
+    }
+    socket_.reset();
 }
 
 int
@@ -1506,8 +1517,8 @@ TestControl::run() {
     printDiagnostics();
     // Option factories have to be registered.
     registerOptionFactories();
-    TestControlSocket socket(openSocket());
-    if (!socket.valid_) {
+    socket_.reset(new TestControlSocket(openSocket()));
+    if (!socket_ || !socket_->valid_) {
         isc_throw(Unexpected, "invalid socket descriptor");
     }
     // Initialize packet templates.
@@ -1526,7 +1537,7 @@ TestControl::run() {
     signal(SIGINT, TestControl::handleInterrupt);
 
     // Preload server with the number of packets.
-    sendPackets(socket, options.getPreload(), true);
+    sendPackets(options.getPreload(), true);
 
     // Fork and run command specified with -w<wrapped-command>
     if (!options.getWrapped().empty()) {
@@ -1535,35 +1546,48 @@ TestControl::run() {
 
     // Initialize Statistics Manager. Release previous if any.
     initializeStatsMgr();
+
+    // Start sender thread
+    if (!options.isSingleThreaded()) {
+        sender_thread_.reset(new WatchedThread());
+        sender_thread_->start(boost::bind(&TestControl::runSender, this));
+    }
+
     for (;;) {
         // Calculate number of packets to be sent to stay
         // catch up with rate.
-        uint64_t packets_due = basic_rate_control_.getOutboundMessageCount();
-        if (packets_due > 0) {
-            checkLateMessages(basic_rate_control_);
-        }
-        if ((packets_due == 0) && testDiags('i')) {
-            if (options.getIpVersion() == 4) {
-                stats_mgr4_->incrementCounter("shortwait");
-            } else if (options.getIpVersion() == 6) {
-                stats_mgr6_->incrementCounter("shortwait");
+        uint64_t packets_due = 0;
+        if (!sender_thread_) {
+            packets_due = basic_rate_control_.getOutboundMessageCount();
+            if (packets_due > 0) {
+                checkLateMessages(basic_rate_control_);
+            }
+            if ((packets_due == 0) && testDiags('i')) {
+                if (options.getIpVersion() == 4) {
+                    stats_mgr4_->incrementCounter("shortwait");
+                } else if (options.getIpVersion() == 6) {
+                    stats_mgr6_->incrementCounter("shortwait");
+                }
             }
         }
 
         // @todo: set non-zero timeout for packets once we implement
         // microseconds timeout in IfaceMgr.
-        receivePackets(socket);
+        receivePackets();
 
         // If test period finished, maximum number of packet drops
         // has been reached or test has been interrupted we have to
         // finish the test.
         if (checkExitConditions()) {
+            if (sender_thread_ && sender_thread_->isRunning()) {
+                sender_thread_->stop();
+            }
             break;
         }
 
         if ((packets_due > 0) && !hasLateExitCommenced()) {
             // Initiate new DHCP packet exchanges.
-            sendPackets(socket, packets_due);
+            sendPackets(packets_due);
         }
 
         // If -f<renew-rate> option was specified we have to check how many
@@ -1577,9 +1601,9 @@ TestControl::run() {
 
             // Send multiple renews to satisfy the desired rate.
             if (options.getIpVersion() == 4) {
-                sendMultipleRequests(socket, renew_packets_due);
+                sendMultipleRequests(renew_packets_due);
             } else {
-                sendMultipleMessages6(socket, DHCPV6_RENEW, renew_packets_due);
+                sendMultipleMessages6(DHCPV6_RENEW, renew_packets_due);
             }
         }
 
@@ -1592,7 +1616,7 @@ TestControl::run() {
                 checkLateMessages(release_rate_control_);
             }
             // Send Release messages.
-            sendMultipleMessages6(socket, DHCPV6_RELEASE, release_packets_due);
+            sendMultipleMessages6(DHCPV6_RELEASE, release_packets_due);
         }
 
         // Report delay means that user requested printing number
@@ -1647,9 +1671,51 @@ TestControl::run() {
     } else if (options.getIpVersion() == 6)  {
         ret_code = stats_mgr6_->droppedPackets() ? 3 : 0;
     }
+
+    // Do not wait object destruction.
+    reset();
     return (ret_code);
 }
 
+void
+TestControl::runSender() {
+    // Add terminate watch socket.
+    int fd = sender_thread_->getWatchFd(WatchedThread::READY);
+
+    for (;;) {
+        if (sender_thread_->shouldTerminate()) {
+            return;
+        }
+        uint64_t packets_due = basic_rate_control_.getOutboundMessageCount();
+        if (packets_due > 1) {
+            checkLateMessages(basic_rate_control_);
+        }
+        if (packets_due > 0) {
+            try {
+            sendPackets(packets_due);
+            } catch (const std::exception& ex) {
+                cerr << "sendPackets failed with " << ex.what() << endl;
+            }
+            continue;
+        }
+
+        // Wait for next due or terminate.
+        ptime now(microsec_clock::universal_time());
+        ptime due = basic_rate_control_.getDue();
+        if (now >= due) {
+            continue;
+        }
+        fd_set rdset;
+        FD_ZERO(&rdset);
+        FD_SET(fd, &rdset);
+        struct timeval select_timeout;
+        select_timeout.tv_sec = 0;
+        select_timeout.tv_usec =
+            time_period(now, due).length().total_microseconds();
+        (void) select(fd + 1, &rdset, NULL, NULL, &select_timeout);
+    }
+}
+
 void
 TestControl::runWrapped(bool do_stop /*= false */) const {
     CommandOptions& options = CommandOptions::instance();
@@ -1686,8 +1752,7 @@ TestControl::saveFirstPacket(const Pkt6Ptr& pkt) {
 }
 
 void
-TestControl::sendDiscover4(const TestControlSocket& socket,
-                           const bool preload /*= false*/) {
+TestControl::sendDiscover4(const bool preload /*= false*/) {
     if (!preload) {
         basic_rate_control_.updateSendTime();
     }
@@ -1714,7 +1779,7 @@ TestControl::sendDiscover4(const TestControlSocket& socket,
 
     // Set client's and server's ports as well as server's address,
     // and local (relay) address.
-    setDefaults4(socket, pkt4);
+    setDefaults4(pkt4);
 
     // Set hardware address
     pkt4->setHWAddr(HTYPE_ETHER, mac_address.size(), mac_address);
@@ -1741,8 +1806,7 @@ TestControl::sendDiscover4(const TestControlSocket& socket,
 }
 
 void
-TestControl::sendDiscover4(const TestControlSocket& socket,
-                           const std::vector<uint8_t>& template_buf,
+TestControl::sendDiscover4(const std::vector<uint8_t>& template_buf,
                            const bool preload /* = false */) {
     if (!preload) {
         basic_rate_control_.updateSendTime();
@@ -1777,7 +1841,7 @@ TestControl::sendDiscover4(const TestControlSocket& socket,
     // Replace MAC address in the template with actual MAC address.
     pkt4->writeAt(rand_offset, mac_address.begin(), mac_address.end());
     // Create a packet from the temporary buffer.
-    setDefaults4(socket, boost::static_pointer_cast<Pkt4>(pkt4));
+    setDefaults4(boost::static_pointer_cast<Pkt4>(pkt4));
     // Pack the input packet buffer to output buffer so as it can
     // be sent to server.
     pkt4->rawPack();
@@ -1798,8 +1862,9 @@ TestControl::sendDiscover4(const TestControlSocket& socket,
 }
 
 bool
-TestControl::sendRequestFromAck(const TestControlSocket& socket) {
-    // Update timestamp of last sent renewal.
+TestControl::sendRequestFromAck() {
+    // Update timestamps of last sent renewal.
+    renew_rate_control_.updateSendDue();
     renew_rate_control_.updateSendTime();
 
     // Get one of the recorded DHCPACK messages.
@@ -1810,7 +1875,7 @@ TestControl::sendRequestFromAck(const TestControlSocket& socket) {
 
     // Create message of the specified type.
     Pkt4Ptr msg = createRequestFromAck(ack);
-    setDefaults4(socket, msg);
+    setDefaults4(msg);
 
     // Add any extra options that user may have specified.
     addExtraOpts(msg);
@@ -1823,13 +1888,15 @@ TestControl::sendRequestFromAck(const TestControlSocket& socket) {
                   "hasn't been initialized");
     }
     stats_mgr4_->passSentPacket(StatsMgr4::XCHG_RNA, msg);
+    stats_mgr4_->passSentTimes(StatsMgr4::XCHG_RNA,
+                               renew_rate_control_.getDue(),
+                               renew_rate_control_.getLast());
     return (true);
 }
 
 
 bool
-TestControl::sendMessageFromReply(const uint16_t msg_type,
-                                  const TestControlSocket& socket) {
+TestControl::sendMessageFromReply(const uint16_t msg_type) {
     // We only permit Release or Renew messages to be sent using this function.
     if (msg_type != DHCPV6_RENEW && msg_type != DHCPV6_RELEASE) {
         isc_throw(isc::BadValue, "invalid message type " << msg_type
@@ -1837,8 +1904,10 @@ TestControl::sendMessageFromReply(const uint16_t msg_type,
     }
     // We track the timestamp of last Release and Renew in different variables.
     if (msg_type == DHCPV6_RENEW) {
+        renew_rate_control_.updateSendDue();
         renew_rate_control_.updateSendTime();
     } else {
+        release_rate_control_.updateSendDue();
         release_rate_control_.updateSendTime();
     }
     Pkt6Ptr reply = reply_storage_.getRandom();
@@ -1847,7 +1916,7 @@ TestControl::sendMessageFromReply(const uint16_t msg_type,
     }
     // Prepare the message of the specified type.
     Pkt6Ptr msg = createMessageFromReply(msg_type, reply);
-    setDefaults6(socket, msg);
+    setDefaults6(msg);
 
     // Add any extra options that user may have specified.
     addExtraOpts(msg);
@@ -1859,14 +1928,22 @@ TestControl::sendMessageFromReply(const uint16_t msg_type,
         isc_throw(Unexpected, "Statistics Manager for DHCPv6 "
                   "hasn't been initialized");
     }
-    stats_mgr6_->passSentPacket((msg_type == DHCPV6_RENEW ? StatsMgr6::XCHG_RN
-                                 : StatsMgr6::XCHG_RL), msg);
+    if (msg_type == DHCPV6_RENEW) {
+        stats_mgr6_->passSentPacket(StatsMgr6::XCHG_RN, msg);
+        stats_mgr6_->passSentTimes(StatsMgr6::XCHG_RN,
+                                   renew_rate_control_.getDue(),
+                                   renew_rate_control_.getLast());
+    } else {
+        stats_mgr6_->passSentPacket(StatsMgr6::XCHG_RL, msg);
+        stats_mgr6_->passSentTimes(StatsMgr6::XCHG_RL,
+                                   release_rate_control_.getDue(),
+                                   release_rate_control_.getLast());
+    }
     return (true);
 }
 
 void
-TestControl::sendRequest4(const TestControlSocket& socket,
-                          const dhcp::Pkt4Ptr& discover_pkt4,
+TestControl::sendRequest4(const dhcp::Pkt4Ptr& discover_pkt4,
                           const dhcp::Pkt4Ptr& offer_pkt4) {
     // Use the same transaction id as the one used in the discovery packet.
     const uint32_t transid = discover_pkt4->getTransid();
@@ -1907,7 +1984,7 @@ TestControl::sendRequest4(const TestControlSocket& socket,
     pkt4->addOption(opt_parameter_list);
     // Set client's and server's ports as well as server's address,
     // and local (relay) address.
-    setDefaults4(socket, pkt4);
+    setDefaults4(pkt4);
 
     // Add any extra options that user may have specified.
     addExtraOpts(pkt4);
@@ -1931,8 +2008,7 @@ TestControl::sendRequest4(const TestControlSocket& socket,
 }
 
 void
-TestControl::sendRequest4(const TestControlSocket& socket,
-                          const std::vector<uint8_t>& template_buf,
+TestControl::sendRequest4(const std::vector<uint8_t>& template_buf,
                           const dhcp::Pkt4Ptr& discover_pkt4,
                           const dhcp::Pkt4Ptr& offer_pkt4) {
     // Get the second argument if multiple the same arguments specified
@@ -2026,7 +2102,7 @@ TestControl::sendRequest4(const TestControlSocket& socket,
     opt_requested_ip->setUint32(yiaddr.toUint32());
     pkt4->addOption(opt_requested_ip);
 
-    setDefaults4(socket, boost::static_pointer_cast<Pkt4>(pkt4));
+    setDefaults4(boost::static_pointer_cast<Pkt4>(pkt4));
 
     // Add any extra options that user may have specified.
     addExtraOpts(pkt4);
@@ -2045,8 +2121,7 @@ TestControl::sendRequest4(const TestControlSocket& socket,
 }
 
 void
-TestControl::sendRequest6(const TestControlSocket& socket,
-                          const Pkt6Ptr& advertise_pkt6) {
+TestControl::sendRequest6(const Pkt6Ptr& advertise_pkt6) {
     const uint32_t transid = generateTransid();
     Pkt6Ptr pkt6(new Pkt6(DHCPV6_REQUEST, transid));
     // Set elapsed time.
@@ -2085,7 +2160,7 @@ TestControl::sendRequest6(const TestControlSocket& socket,
     copyIaOptions(advertise_pkt6, pkt6);
 
     // Set default packet data.
-    setDefaults6(socket, pkt6);
+    setDefaults6(pkt6);
 
     // Add any extra options that user may have specified.
     addExtraOpts(pkt6);
@@ -2102,8 +2177,7 @@ TestControl::sendRequest6(const TestControlSocket& socket,
 }
 
 void
-TestControl::sendRequest6(const TestControlSocket& socket,
-                          const std::vector<uint8_t>& template_buf,
+TestControl::sendRequest6(const std::vector<uint8_t>& template_buf,
                           const Pkt6Ptr& advertise_pkt6) {
     // Get the second argument if multiple the same arguments specified
     // in the command line. Second one refers to REQUEST packets.
@@ -2193,7 +2267,7 @@ TestControl::sendRequest6(const TestControlSocket& socket,
                                          rand_offset));
     pkt6->addOption(opt_clientid);
     // Set default packet data.
-    setDefaults6(socket, pkt6);
+    setDefaults6(pkt6);
 
     // Add any extra options that user may have specified.
     addExtraOpts(pkt6);
@@ -2221,8 +2295,7 @@ TestControl::sendRequest6(const TestControlSocket& socket,
 }
 
 void
-TestControl::sendSolicit6(const TestControlSocket& socket,
-                          const bool preload /*= false*/) {
+TestControl::sendSolicit6(const bool preload /*= false*/) {
     if (!preload) {
         basic_rate_control_.updateSendTime();
     }
@@ -2256,7 +2329,7 @@ TestControl::sendSolicit6(const TestControlSocket& socket,
         pkt6->addOption(Option::factory(Option::V6, D6O_IA_PD));
     }
 
-    setDefaults6(socket, pkt6);
+    setDefaults6(pkt6);
 
     // Add any extra options that user may have specified.
     addExtraOpts(pkt6);
@@ -2278,8 +2351,7 @@ TestControl::sendSolicit6(const TestControlSocket& socket,
 }
 
 void
-TestControl::sendSolicit6(const TestControlSocket& socket,
-                          const std::vector<uint8_t>& template_buf,
+TestControl::sendSolicit6(const std::vector<uint8_t>& template_buf,
                           const bool preload /*= false*/) {
     if (!preload) {
         basic_rate_control_.updateSendTime();
@@ -2310,7 +2382,7 @@ TestControl::sendSolicit6(const TestControlSocket& socket,
 
     // Prepare on-wire data.
     pkt6->rawPack();
-    setDefaults6(socket, pkt6);
+    setDefaults6(pkt6);
 
     // Add any extra options that user may have specified.
     addExtraOpts(pkt6);
@@ -2333,17 +2405,19 @@ TestControl::sendSolicit6(const TestControlSocket& socket,
 
 
 void
-TestControl::setDefaults4(const TestControlSocket& socket,
-                          const Pkt4Ptr& pkt) {
+TestControl::setDefaults4(const Pkt4Ptr& pkt) {
     CommandOptions& options = CommandOptions::instance();
+    if (!socket_) {
+        isc_throw(BadValue, "NULL socket");
+    }
     // Interface name.
-    IfacePtr iface = IfaceMgr::instance().getIface(socket.ifindex_);
-    if (iface == NULL) {
+    IfacePtr iface = IfaceMgr::instance().getIface(socket_->ifindex_);
+    if (!iface) {
         isc_throw(BadValue, "unable to find interface with given index");
     }
     pkt->setIface(iface->getName());
     // Interface index.
-    pkt->setIndex(socket.ifindex_);
+    pkt->setIndex(socket_->ifindex_);
     // Local client's port (68)
     pkt->setLocalPort(DHCP4_CLIENT_PORT);
     // Server's port (67)
@@ -2351,31 +2425,33 @@ TestControl::setDefaults4(const TestControlSocket& socket,
     // The remote server's name or IP.
     pkt->setRemoteAddr(IOAddress(options.getServerName()));
     // Set local address.
-    pkt->setLocalAddr(IOAddress(socket.addr_));
+    pkt->setLocalAddr(IOAddress(socket_->addr_));
     // Set relay (GIADDR) address to local address.
-    pkt->setGiaddr(IOAddress(socket.addr_));
+    pkt->setGiaddr(IOAddress(socket_->addr_));
     // Pretend that we have one relay (which is us).
     pkt->setHops(1);
 }
 
 void
-TestControl::setDefaults6(const TestControlSocket& socket,
-                          const Pkt6Ptr& pkt) {
+TestControl::setDefaults6(const Pkt6Ptr& pkt) {
     CommandOptions& options = CommandOptions::instance();
+    if (!socket_) {
+        isc_throw(BadValue, "NULL socket");
+    }
     // Interface name.
-    IfacePtr iface = IfaceMgr::instance().getIface(socket.ifindex_);
-    if (iface == NULL) {
+    IfacePtr iface = IfaceMgr::instance().getIface(socket_->ifindex_);
+    if (!iface) {
         isc_throw(BadValue, "unable to find interface with given index");
     }
     pkt->setIface(iface->getName());
     // Interface index.
-    pkt->setIndex(socket.ifindex_);
+    pkt->setIndex(socket_->ifindex_);
     // Local client's port (547)
     pkt->setLocalPort(DHCP6_CLIENT_PORT);
     // Server's port (548)
     pkt->setRemotePort(DHCP6_SERVER_PORT);
     // Set local address.
-    pkt->setLocalAddr(socket.addr_);
+    pkt->setLocalAddr(socket_->addr_);
     // The remote server's name or IP.
     pkt->setRemoteAddr(IOAddress(options.getServerName()));
 
@@ -2386,8 +2462,8 @@ TestControl::setDefaults6(const TestControlSocket& socket,
       Pkt6::RelayInfo relay_info;
       relay_info.msg_type_ = DHCPV6_RELAY_FORW;
       relay_info.hop_count_ = 1;
-      relay_info.linkaddr_ = IOAddress(socket.addr_);
-      relay_info.peeraddr_ = IOAddress(socket.addr_);
+      relay_info.linkaddr_ = IOAddress(socket_->addr_);
+      relay_info.peeraddr_ = IOAddress(socket_->addr_);
       pkt->addRelayInfo(relay_info);
     }
 }
index e38fee17a882b78e8c4c91f541a42ccd60296b93..507428da583e4e45798b91a9924e1ea85a6a1030 100644 (file)
@@ -16,6 +16,7 @@
 #include <dhcp/pkt4.h>
 #include <dhcp/pkt6.h>
 #include <util/random/random_number_generator.h>
+#include <util/threads/watched_thread.h>
 
 #include <boost/noncopyable.hpp>
 #include <boost/shared_ptr.hpp>
@@ -252,7 +253,7 @@ public:
     /// \return the only existing instance of test control
     static TestControl& instance();
 
-    /// brief\ Run performance test.
+    /// \brief Run performance test.
     ///
     /// Method runs whole performance test. Command line options must
     /// be parsed prior to running this function. Otherwise function will
@@ -264,6 +265,12 @@ public:
     /// to number of sent packets, 0 if everything is ok.
     int run();
 
+    /// \brief Run sender thread.
+    ///
+    /// The sender thread runs this method which sends at the wanted rate
+    /// solicits or advertises.
+    void runSender();
+
     /// \brief Set new transaction id generator.
     ///
     /// \param generator generator object to be used.
@@ -294,6 +301,11 @@ protected:
     /// only via \ref instance method.
     TestControl();
 
+    /// \brief Destructor.
+    ///
+    /// Call reset() for cleanup.
+    virtual ~TestControl();
+
     /// Generate uniformly distributed integers in range of [min, max]
     isc::util::random::UniformRandomIntegerGenerator number_generator_;
 
@@ -452,7 +464,7 @@ protected:
     /// \param type option-type (ignored).
     /// \param buf option-buffer (ignored).
     /// \return instance o the generic option.
-     static dhcp::OptionPtr factoryRequestList4(dhcp::Option::Universe u,
+    static dhcp::OptionPtr factoryRequestList4(dhcp::Option::Universe u,
                                                uint16_t type,
                                                const dhcp::OptionBuffer& buf);
 
@@ -593,12 +605,10 @@ protected:
     /// \warning this method does not check if provided socket is
     /// valid (specifically if v4 socket for received v4 packet).
     ///
-    /// \param [in] socket socket to be used.
     /// \param [in] pkt4 object representing DHCPv4 packet received.
     /// \throw isc::BadValue if unknown message type received.
     /// \throw isc::Unexpected if unexpected error occurred.
-    void processReceivedPacket4(const TestControlSocket& socket,
-                                const dhcp::Pkt4Ptr& pkt4);
+    void processReceivedPacket4(const dhcp::Pkt4Ptr& pkt4);
 
     /// \brief Process received DHCPv6 packet.
     ///
@@ -610,12 +620,10 @@ protected:
     /// \warning this method does not check if provided socket is
     /// valid (specifically if v4 socket for received v4 packet).
     ///
-    /// \param [in] socket socket to be used.
     /// \param [in] 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,
-                                const dhcp::Pkt6Ptr& pkt6);
+    void processReceivedPacket6(const dhcp::Pkt6Ptr& pkt6);
 
     /// \brief Receive DHCPv4 or DHCPv6 packets from the server.
     ///
@@ -627,11 +635,10 @@ protected:
     /// \warning this method does not check if provided socket is
     /// valid. Ensure that it is valid prior to calling it.
     ///
-    /// \param socket socket to be used.
     /// \throw 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();
 
     /// \brief Register option factory functions for DHCPv4
     ///
@@ -707,14 +714,12 @@ protected:
     /// Copy of sent packet is stored in the stats_mgr4_ object to
     /// update statistics.
     ///
-    /// \param socket socket to be used to send the message.
     /// \param preload preload mode, packets not included in statistics.
     ///
     /// \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,
-                       const bool preload = false);
+    void sendDiscover4(const bool preload = false);
 
     /// \brief Send DHCPv4 DISCOVER message from template.
     ///
@@ -725,14 +730,12 @@ protected:
     /// Copy of sent packet is stored in the stats_mgr4_ object to
     /// update statistics.
     ///
-    /// \param socket socket to be used to send the message.
     /// \param template_buf buffer holding template packet.
     /// \param preload preload mode, packets not included in statistics.
     ///
     /// \throw isc::OutOfRange if randomization offset is out of bounds.
     /// \throw isc::dhcp::SocketWriteError if failed to send the packet.
-    void sendDiscover4(const TestControlSocket& socket,
-                       const std::vector<uint8_t>& template_buf,
+    void sendDiscover4(const std::vector<uint8_t>& template_buf,
                        const bool preload = false);
 
     /// \brief Send number of packets to initiate new exchanges.
@@ -750,44 +753,34 @@ protected:
     ///
     /// \todo do not count responses in preload mode as orphans.
     ///
-    /// \param socket socket to be used to send packets.
     /// \param packets_num number of packets to be sent.
     /// \param preload preload mode, packets not included in statistics.
     /// \throw isc::Unexpected if thrown by packet sending method.
     /// \throw isc::InvalidOperation if thrown by packet sending method.
     /// \throw isc::OutOfRange if thrown by packet sending method.
-    void sendPackets(const TestControlSocket &socket,
-                     const uint64_t packets_num,
-                     const bool preload = false);
+    void sendPackets(const uint64_t packets_num, const bool preload = false);
 
     /// \brief Send number of DHCPREQUEST (renew) messages to a server.
     ///
-    /// \param socket An object representing socket to be used to send packets.
     /// \param msg_num A number of messages to be sent.
     ///
     /// \return A number of messages actually sent.
-    uint64_t sendMultipleRequests(const TestControlSocket& socket,
-                                  const uint64_t msg_num);
+    uint64_t sendMultipleRequests(const uint64_t msg_num);
 
     /// \brief Send number of DHCPv6 Renew or Release messages to the server.
     ///
-    /// \param socket An object representing socket to be used to send packets.
     /// \param msg_type A type of the messages to be sent (DHCPV6_RENEW or
     /// DHCPV6_RELEASE).
     /// \param msg_num A number of messages to be sent.
     ///
     /// \return A number of messages actually sent.
-    uint64_t sendMultipleMessages6(const TestControlSocket& socket,
-                                   const uint32_t msg_type,
+    uint64_t sendMultipleMessages6(const uint32_t msg_type,
                                    const uint64_t msg_num);
 
     /// \brief Send DHCPv4 renew (DHCPREQUEST) using specified socket.
     ///
-    /// \param socket An object encapsulating socket to be used to send
-    /// a packet.
-    ///
     /// \return true if the message has been sent, false otherwise.
-    bool sendRequestFromAck(const TestControlSocket& socket);
+    bool sendRequestFromAck();
 
     /// \brief Send DHCPv6 Renew or Release message using specified socket.
     ///
@@ -797,12 +790,9 @@ protected:
     ///
     /// \param msg_type A type of the message to be sent (DHCPV6_RENEW or
     /// DHCPV6_RELEASE).
-    /// \param socket An object encapsulating socket to be used to send
-    /// a packet.
     ///
     /// \return true if the message has been sent, false otherwise.
-    bool sendMessageFromReply(const uint16_t msg_type,
-                              const TestControlSocket& socket);
+    bool sendMessageFromReply(const uint16_t msg_type);
 
     /// \brief Send DHCPv4 REQUEST message.
     ///
@@ -810,7 +800,6 @@ protected:
     /// Copy of sent packet is stored in the stats_mgr4_ object to
     /// update statistics.
     ///
-    /// \param socket socket to be used to send message.
     /// \param discover_pkt4 DISCOVER packet sent.
     /// \param offer_pkt4 OFFER packet object.
     ///
@@ -818,8 +807,7 @@ protected:
     /// \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,
-                      const dhcp::Pkt4Ptr& discover_pkt4,
+    void sendRequest4(const dhcp::Pkt4Ptr& discover_pkt4,
                       const dhcp::Pkt4Ptr& offer_pkt4);
 
     /// \brief Send DHCPv4 REQUEST message from template.
@@ -828,14 +816,12 @@ protected:
     /// Copy of sent packet is stored in the stats_mgr4_ object to
     /// update statistics.
     ///
-    /// \param socket socket to be used to send message.
     /// \param template_buf buffer holding template packet.
     /// \param discover_pkt4 DISCOVER packet sent.
     /// \param offer_pkt4 OFFER packet received.
     ///
     /// \throw isc::dhcp::SocketWriteError if failed to send the packet.
-    void sendRequest4(const TestControlSocket& socket,
-                      const std::vector<uint8_t>& template_buf,
+    void sendRequest4(const std::vector<uint8_t>& template_buf,
                       const dhcp::Pkt4Ptr& discover_pkt4,
                       const dhcp::Pkt4Ptr& offer_pkt4);
 
@@ -849,15 +835,13 @@ protected:
     /// Copy of sent packet is stored in the stats_mgr6_ object to
     /// update statistics.
     ///
-    /// \param socket socket to be used to send message.
     /// \param advertise_pkt6 ADVERTISE packet object.
     /// \throw isc::Unexpected if unexpected error occurred.
     /// \throw isc::InvalidOperation if Statistics Manager has not been
     /// initialized.
     ///
     /// \throw isc::dhcp::SocketWriteError if failed to send the packet.
-    void sendRequest6(const TestControlSocket& socket,
-                      const dhcp::Pkt6Ptr& advertise_pkt6);
+    void sendRequest6(const dhcp::Pkt6Ptr& advertise_pkt6);
 
     /// \brief Send DHCPv6 REQUEST message from template.
     ///
@@ -865,13 +849,11 @@ protected:
     /// Copy of sent packet is stored in the stats_mgr6_ object to
     /// update statistics.
     ///
-    /// \param socket socket to be used to send message.
     /// \param template_buf packet template buffer.
     /// \param advertise_pkt6 ADVERTISE packet object.
     ///
     /// \throw isc::dhcp::SocketWriteError if failed to send the packet.
-    void sendRequest6(const TestControlSocket& socket,
-                      const std::vector<uint8_t>& template_buf,
+    void sendRequest6(const std::vector<uint8_t>& template_buf,
                       const dhcp::Pkt6Ptr& advertise_pkt6);
 
     /// \brief Send DHCPv6 SOLICIT message.
@@ -886,13 +868,11 @@ protected:
     /// Copy of sent packet is stored in the stats_mgr6_ object to
     /// update statistics.
     ///
-    /// \param socket socket to be used to send the message.
     /// \param preload mode, packets not included in statistics.
     ///
     /// \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,
-                      const bool preload = false);
+    void sendSolicit6(const bool preload = false);
 
     /// \brief Send DHCPv6 SOLICIT message from template.
     ///
@@ -900,13 +880,11 @@ protected:
     /// Copy of sent packet is stored in the stats_mgr6_ object to
     /// update statistics.
     ///
-    /// \param socket socket to be used to send the message.
     /// \param template_buf packet template buffer.
     /// \param preload mode, packets not included in statistics.
     ///
     /// \throw isc::dhcp::SocketWriteError if failed to send the packet.
-    void sendSolicit6(const TestControlSocket& socket,
-                      const std::vector<uint8_t>& template_buf,
+    void sendSolicit6(const std::vector<uint8_t>& template_buf,
                       const bool preload = false);
 
     /// \brief Set default DHCPv4 packet parameters.
@@ -919,10 +897,8 @@ protected:
     /// - GIADDR = local address where socket is bound to,
     /// - hops = 1 (pretending that we are a relay)
     ///
-    /// \param socket socket used to send the packet.
     /// \param pkt reference to packet to be configured.
-    void setDefaults4(const TestControlSocket& socket,
-                      const dhcp::Pkt4Ptr& pkt);
+    void setDefaults4(const dhcp::Pkt4Ptr& pkt);
 
     /// \brief Set default DHCPv6 packet parameters.
     ///
@@ -934,10 +910,8 @@ protected:
     /// - local address,
     /// - remote address (server).
     ///
-    /// \param socket socket used to send the packet.
     /// \param pkt reference to packet to be configured.
-    void setDefaults6(const TestControlSocket& socket,
-                      const dhcp::Pkt6Ptr& pkt);
+    void setDefaults6(const dhcp::Pkt6Ptr& pkt);
 
     /// @brief Inserts extra options specified by user.
     ///
@@ -1166,6 +1140,10 @@ protected:
     std::map<uint8_t, dhcp::Pkt6Ptr> template_packets_v6_;
 
     static bool interrupted_;  ///< Is program interrupted.
+
+    boost::shared_ptr<TestControlSocket> socket_; ///< Socket object.
+
+    util::thread::WatchedThreadPtr sender_thread_; ///< Sender watched thread.
 };
 
 }  // namespace perfdhcp
index 75a9848cd66a5d9af022875b17dd5e575ae3c9a3..343b26a6dfe2ee6a1b98bedd2b9056e602b51f6d 100644 (file)
@@ -143,6 +143,7 @@ public:
     using TestControl::template_packets_v6_;
     using TestControl::ack_storage_;
     using TestControl::sendRequestFromAck;
+    using TestControl::socket_;
 
     NakedTestControl() : TestControl() {
         uint32_t clients_num = CommandOptions::instance().getClientsNum() == 0 ?
@@ -493,16 +494,17 @@ public:
             generator(new NakedTestControl::IncrementalGenerator());
         tc.setTransidGenerator(generator);
         // Socket is needed to send packets through the interface.
+        ASSERT_NO_THROW(tc.socket_.reset());
         ASSERT_NO_THROW(sock_handle = tc.openSocket());
-        TestControl::TestControlSocket sock(sock_handle);
+        tc.socket_.reset(new TestControl::TestControlSocket(sock_handle));
         for (int i = 0; i < iterations_num; ++i) {
             // Get next transaction id, without actually using it. The same
             // id wll be used by the TestControl class for DHCPDISCOVER.
             uint32_t transid = generator->getNext();
             if (use_templates) {
-                ASSERT_NO_THROW(tc.sendDiscover4(sock, tc.getTemplateBuffer(0)));
+                ASSERT_NO_THROW(tc.sendDiscover4(tc.getTemplateBuffer(0)));
             } else {
-                ASSERT_NO_THROW(tc.sendDiscover4(sock));
+                ASSERT_NO_THROW(tc.sendDiscover4());
             }
 
             // Do not simulate responses for packets later
@@ -510,7 +512,7 @@ public:
             // packet drops.
             if (i < receive_num) {
                 boost::shared_ptr<Pkt4> offer_pkt4(createOfferPkt4(transid));
-                ASSERT_NO_THROW(tc.processReceivedPacket4(sock, offer_pkt4));
+                ASSERT_NO_THROW(tc.processReceivedPacket4(offer_pkt4));
             }
             if (tc.checkExitConditions()) {
                 iterations_performed = i + 1;
@@ -558,25 +560,25 @@ public:
             generator(new NakedTestControl::IncrementalGenerator());
         tc.setTransidGenerator(generator);
         // Socket is needed to send packets through the interface.
+        ASSERT_NO_THROW(tc.socket_.reset());
         ASSERT_NO_THROW(sock_handle = tc.openSocket());
-        TestControl::TestControlSocket sock(sock_handle);
+        tc.socket_.reset(new TestControl::TestControlSocket(sock_handle));
         uint32_t transid = 0;
         for (int i = 0; i < iterations_num; ++i) {
             // Do not simulate responses for packets later
             // that specified as receive_num. This simulates
             // packet drops.
             if (use_templates) {
-                ASSERT_NO_THROW(tc.sendSolicit6(sock, tc.getTemplateBuffer(0)));
+                ASSERT_NO_THROW(tc.sendSolicit6(tc.getTemplateBuffer(0)));
             } else {
-                ASSERT_NO_THROW(tc.sendSolicit6(sock));
+                ASSERT_NO_THROW(tc.sendSolicit6());
             }
             ++transid;
             if (i < receive_num) {
                 boost::shared_ptr<Pkt6>
                     advertise_pkt6(createAdvertisePkt6(transid));
                 // Receive ADVERTISE and send REQUEST.
-                ASSERT_NO_THROW(tc.processReceivedPacket6(sock,
-                                                          advertise_pkt6));
+                ASSERT_NO_THROW(tc.processReceivedPacket6(advertise_pkt6));
                 ++transid;
             }
             if (tc.checkExitConditions()) {
@@ -701,11 +703,11 @@ public:
         // 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);
+        tc.socket_.reset(new TestControl::TestControlSocket(sock_handle));
 
         // Send a number of DHCPDISCOVER messages. Each generated message will
         // be assigned a different transaction id, starting from 1 to 10.
-        tc.sendPackets(sock, 10);
+        tc.sendPackets(10);
 
         // Simulate DHCPOFFER responses from the server. Each DHCPOFFER is
         // assigned a transaction id from the range of 1 to 10, so as they
@@ -717,7 +719,7 @@ public:
             // will trigger a corresponding DHCPREQUEST. They will be assigned
             // transaction ids from the range from 11 to 20 (the range of
             // 1 to 10 has been used by DHCPDISCOVER-DHCPOFFER).
-            ASSERT_NO_THROW(tc.processReceivedPacket4(sock, offer));
+            ASSERT_NO_THROW(tc.processReceivedPacket4(offer));
     }
 
         // Requests have been sent, so now let's simulate responses from the
@@ -730,7 +732,7 @@ public:
             // -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.processReceivedPacket4(sock, ack));
+            ASSERT_NO_THROW(tc.processReceivedPacket4(ack));
         }
 
         uint64_t msg_num;
@@ -738,7 +740,7 @@ public:
         // DHCPREQUEST messages has been received. For each of them we
         // should be able to send renewal.
         ASSERT_NO_THROW(
-            msg_num = tc.sendMultipleRequests(sock, 5)
+            msg_num = tc.sendMultipleRequests(5)
         );
         // Make sure that we have sent 5 messages.
         EXPECT_EQ(5, msg_num);
@@ -746,7 +748,7 @@ public:
         // Try to do it again. We should still have 5 Reply packets for
         // which renews haven't been sent yet.
         ASSERT_NO_THROW(
-            msg_num = tc.sendMultipleRequests(sock, 5)
+            msg_num = tc.sendMultipleRequests(5)
         );
         EXPECT_EQ(5, msg_num);
 
@@ -754,7 +756,7 @@ public:
         // them already). Therefore, no further renew messages should be sent
         // before we acquire new leases.
         ASSERT_NO_THROW(
-            msg_num = tc.sendMultipleRequests(sock, 5)
+            msg_num = tc.sendMultipleRequests(5)
         );
         // Make sure that no message has been sent.
         EXPECT_EQ(0, msg_num);
@@ -908,11 +910,11 @@ public:
         // 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);
+        tc.socket_.reset(new TestControl::TestControlSocket(sock_handle));
 
         // Send a number of Solicit messages. Each generated Solicit will be
         // assigned a different transaction id, starting from 1 to 10.
-        tc.sendPackets(sock, 10);
+        tc.sendPackets(10);
 
         // Simulate Advertise responses from the server. Each advertise is
         // assigned a transaction id from the range of 1 to 10, so as they
@@ -924,7 +926,7 @@ public:
             // 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));
+            ASSERT_NO_THROW(tc.processReceivedPacket6(advertise));
     }
 
         // Requests have been sent, so now let's simulate responses from the
@@ -937,7 +939,7 @@ public:
             // -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));
+            ASSERT_NO_THROW(tc.processReceivedPacket6(reply));
         }
 
         uint64_t msg_num;
@@ -945,7 +947,7 @@ public:
         // messages has been received. For each of them we should be able to
         // send Renew or Release.
         ASSERT_NO_THROW(
-            msg_num = tc.sendMultipleMessages6(sock, msg_type, 5)
+            msg_num = tc.sendMultipleMessages6(msg_type, 5)
         );
         // Make sure that we have sent 5 messages.
         EXPECT_EQ(5, msg_num);
@@ -953,7 +955,7 @@ public:
         // Try to do it again. We should still have 5 Reply packets for
         // which Renews or Releases haven't been sent yet.
         ASSERT_NO_THROW(
-            msg_num = tc.sendMultipleMessages6(sock, msg_type, 5)
+            msg_num = tc.sendMultipleMessages6(msg_type, 5)
         );
         EXPECT_EQ(5, msg_num);
 
@@ -961,7 +963,7 @@ public:
         // them already). Therefore, no further Renew or Release messages should
         // be sent before we acquire new leases.
         ASSERT_NO_THROW(
-            msg_num = tc.sendMultipleMessages6(sock, msg_type, 5)
+            msg_num = tc.sendMultipleMessages6(msg_type, 5)
         );
         // Make sure that no message has been sent.
         EXPECT_EQ(0, msg_num);
@@ -1404,21 +1406,22 @@ TEST_F(TestControlTest, Packet4) {
         // 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);
+        tc.socket_.reset(new TestControl::TestControlSocket(sock_handle));
         uint32_t transid = 123;
         boost::shared_ptr<Pkt4> pkt4(new Pkt4(DHCPDISCOVER, transid));
         // Set parameters on outgoing packet.
-        ASSERT_NO_THROW(tc.setDefaults4(sock, pkt4));
+        ASSERT_NO_THROW(tc.setDefaults4(pkt4));
         // Validate that packet has been setup correctly.
         EXPECT_EQ(loopback_iface, pkt4->getIface());
-        EXPECT_EQ(sock.ifindex_, pkt4->getIndex());
+        EXPECT_EQ(tc.socket_->ifindex_, pkt4->getIndex());
         EXPECT_EQ(DHCP4_CLIENT_PORT, pkt4->getLocalPort());
         EXPECT_EQ(DHCP4_SERVER_PORT, pkt4->getRemotePort());
         EXPECT_EQ(1, pkt4->getHops());
         EXPECT_EQ(asiolink::IOAddress("255.255.255.255"),
                   pkt4->getRemoteAddr());
-        EXPECT_EQ(asiolink::IOAddress(sock.addr_), pkt4->getLocalAddr());
-        EXPECT_EQ(asiolink::IOAddress(sock.addr_), pkt4->getGiaddr());
+        EXPECT_EQ(asiolink::IOAddress(tc.socket_->addr_),
+                  pkt4->getLocalAddr());
+        EXPECT_EQ(asiolink::IOAddress(tc.socket_->addr_), pkt4->getGiaddr());
     } else {
         std::cout << "Unable to find the loopback interface. Skip test. "
                   << std::endl;
@@ -1437,17 +1440,17 @@ TEST_F(TestControlTest, Packet6) {
         // Create the socket. It will be needed to set packet's
         // parameters.
         ASSERT_NO_THROW(sock_handle = tc.openSocket());
-        TestControl::TestControlSocket sock(sock_handle);
+        tc.socket_.reset(new TestControl::TestControlSocket(sock_handle));
         uint32_t transid = 123;
         boost::shared_ptr<Pkt6> pkt6(new Pkt6(DHCPV6_SOLICIT, transid));
         // Set packet's parameters.
-        ASSERT_NO_THROW(tc.setDefaults6(sock, pkt6));
+        ASSERT_NO_THROW(tc.setDefaults6(pkt6));
         // Validate if parameters have been set correctly.
         EXPECT_EQ(loopback_iface, pkt6->getIface());
-        EXPECT_EQ(sock.ifindex_, pkt6->getIndex());
+        EXPECT_EQ(tc.socket_->ifindex_, pkt6->getIndex());
         EXPECT_EQ(DHCP6_CLIENT_PORT, pkt6->getLocalPort());
         EXPECT_EQ(DHCP6_SERVER_PORT, pkt6->getRemotePort());
-        EXPECT_EQ(sock.addr_, pkt6->getLocalAddr());
+        EXPECT_EQ(tc.socket_->addr_, pkt6->getLocalAddr());
         EXPECT_EQ(asiolink::IOAddress("FF05::1:3"), pkt6->getRemoteAddr());
         // Packet must not be relayed.
         EXPECT_TRUE(pkt6->relay_info_.empty());
@@ -1470,24 +1473,24 @@ TEST_F(TestControlTest, Packet6Relayed) {
         // Create the socket. It will be needed to set packet's
         // parameters.
         ASSERT_NO_THROW(sock_handle = tc.openSocket());
-        TestControl::TestControlSocket sock(sock_handle);
+        tc.socket_.reset(new TestControl::TestControlSocket(sock_handle));
         uint32_t transid = 123;
         boost::shared_ptr<Pkt6> pkt6(new Pkt6(DHCPV6_SOLICIT, transid));
         // Set packet's parameters.
-        ASSERT_NO_THROW(tc.setDefaults6(sock, pkt6));
+        ASSERT_NO_THROW(tc.setDefaults6(pkt6));
         // Validate if parameters have been set correctly.
         EXPECT_EQ(loopback_iface, pkt6->getIface());
-        EXPECT_EQ(sock.ifindex_, pkt6->getIndex());
+        EXPECT_EQ(tc.socket_->ifindex_, pkt6->getIndex());
         EXPECT_EQ(DHCP6_CLIENT_PORT, pkt6->getLocalPort());
         EXPECT_EQ(DHCP6_SERVER_PORT, pkt6->getRemotePort());
-        EXPECT_EQ(sock.addr_, pkt6->getLocalAddr());
+        EXPECT_EQ(tc.socket_->addr_, pkt6->getLocalAddr());
         EXPECT_EQ(asiolink::IOAddress("FF05::1:3"), pkt6->getRemoteAddr());
         // Packet should be relayed.
         EXPECT_EQ(pkt6->relay_info_.size(), 1);
         EXPECT_EQ(pkt6->relay_info_[0].hop_count_, 1);
         EXPECT_EQ(pkt6->relay_info_[0].msg_type_, DHCPV6_RELAY_FORW);
-        EXPECT_EQ(pkt6->relay_info_[0].linkaddr_, sock.addr_);
-        EXPECT_EQ(pkt6->relay_info_[0].peeraddr_, sock.addr_);
+        EXPECT_EQ(pkt6->relay_info_[0].linkaddr_, tc.socket_->addr_);
+        EXPECT_EQ(pkt6->relay_info_[0].peeraddr_, tc.socket_->addr_);
     } else {
         std::cout << "Unable to find the loopback interface. Skip test. "
                   << std::endl;
@@ -1958,10 +1961,10 @@ 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);
+    tc.socket_.reset(new TestControl::TestControlSocket(sock_handle));
 
     // Make tc send the packet. The first packet of each type is saved in templates.
-    ASSERT_NO_THROW(tc.sendDiscover4(sock));
+    ASSERT_NO_THROW(tc.sendDiscover4());
 
     // Let's find the packet and see if it includes the right option.
     auto pkt_it = tc.template_packets_v4_.find(DHCPDISCOVER);