BIND 10 trac1959, updated. fbbae39aea715ccca384083611b6faa8250e068d [1959] Send request messages to server and corresponding unit tests.

BIND 10 source code commits bind10-changes at lists.isc.org
Tue Aug 21 13:17:54 UTC 2012


The branch, trac1959 has been updated
       via  fbbae39aea715ccca384083611b6faa8250e068d (commit)
      from  00e8a3ebd6c42824b0bfdd0ca96a6acd179a1194 (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 fbbae39aea715ccca384083611b6faa8250e068d
Author: Marcin Siodelski <marcin at isc.org>
Date:   Tue Aug 21 15:17:14 2012 +0200

    [1959] Send request messages to server and corresponding unit tests.

-----------------------------------------------------------------------

Summary of changes:
 tests/tools/perfdhcp/stats_mgr.h                   |   75 ++++---
 tests/tools/perfdhcp/test_control.cc               |  228 ++++++++++++++++---
 tests/tools/perfdhcp/test_control.h                |  132 ++++++++++-
 .../tools/perfdhcp/tests/test_control_unittest.cc  |  237 +++++++++++++++++++-
 4 files changed, 603 insertions(+), 69 deletions(-)

-----------------------------------------------------------------------
diff --git a/tests/tools/perfdhcp/stats_mgr.h b/tests/tools/perfdhcp/stats_mgr.h
index 7ade910..5a3f114 100644
--- a/tests/tools/perfdhcp/stats_mgr.h
+++ b/tests/tools/perfdhcp/stats_mgr.h
@@ -47,8 +47,8 @@ namespace perfdhcp {
 /// stored on the list of sent packets. When packets are matched the
 /// round trip time can be calculated.
 ///
-/// \tparam T class representing DHCPv4 or DHCPv6 packet.
-template <class T>
+/// \param T class representing DHCPv4 or DHCPv6 packet.
+template <class T = dhcp::Pkt4>
 class StatsMgr : public boost::noncopyable {
 public:
 
@@ -138,7 +138,7 @@ public:
         /// \param packet packet which transaction id is to be hashed.
         /// \throw isc::BadValue if packet is null.
         /// \return transaction id hash.
-        static uint32_t hashTransid(const boost::shared_ptr<const T>& packet) {
+        static uint32_t hashTransid(const boost::shared_ptr<T>& packet) {
             if (!packet) {
                 isc_throw(BadValue, "Packet is null");
             }
@@ -214,12 +214,12 @@ public:
         /// }
         /// \endcode
         typedef boost::multi_index_container<
-            boost::shared_ptr<const T>,
+            boost::shared_ptr<T>,
             boost::multi_index::indexed_by<
                 boost::multi_index::sequenced<>,
                 boost::multi_index::hashed_non_unique<
                         boost::multi_index::global_fun<
-                            const boost::shared_ptr<const T>&,
+                            const boost::shared_ptr<T>&,
                             uint32_t,
                             &ExchangeStats::hashTransid
                         >
@@ -228,7 +228,7 @@ public:
         > PktList;
 
         /// Packet list iterator for sequencial access to elements.
-        typedef typename PktList::const_iterator PktListIterator;
+        typedef typename PktList::iterator PktListIterator;
         /// Packet list index to search packets using transaction id hash.
         typedef typename PktList::template nth_index<1>::type
             PktListTransidHashIndex;
@@ -267,7 +267,7 @@ public:
         ///
         /// \param packet packet object to be added.
         /// \throw isc::BadValue if packet is null.
-        void appendSent(const boost::shared_ptr<const T>& packet) {
+        void appendSent(const boost::shared_ptr<T>& packet) {
             if (!packet) {
                 isc_throw(BadValue, "Packet is null");
             }
@@ -281,7 +281,7 @@ public:
         ///
         /// \param packet packet object to be added.
         /// \throw isc::BadValue if packet is null.
-        void appendRcvd(const boost::shared_ptr<const T>& packet) {
+        void appendRcvd(const boost::shared_ptr<T>& packet) {
             if (!packet) {
                 isc_throw(BadValue, "Packet is null");
             }
@@ -297,8 +297,8 @@ public:
         /// \param rcvd_packet received packet
         /// \throw isc::BadValue if sent or received packet is null.
         /// \throw isc::Unexpected if failed to calculate timestamps
-        void updateDelays(const boost::shared_ptr<const T>& sent_packet,
-                          const boost::shared_ptr<const T>& rcvd_packet) {
+        void updateDelays(const boost::shared_ptr<T>& sent_packet,
+                          const boost::shared_ptr<T>& rcvd_packet) {
             if (!sent_packet) {
                 isc_throw(BadValue, "Sent packet is null");
             }
@@ -356,7 +356,8 @@ public:
         /// \throw isc::BadValue if received packet is null.
         /// \return packet having specified transaction or NULL if packet
         /// not found
-        boost::shared_ptr<const T> matchPackets(const boost::shared_ptr<const T>& rcvd_packet) {
+        boost::shared_ptr<T>
+        matchPackets(const boost::shared_ptr<T>& rcvd_packet) {
             if (!rcvd_packet) {
                 isc_throw(BadValue, "Received packet is null");
             }
@@ -367,7 +368,7 @@ public:
                 // that the received packet we got has no corresponding
                 // sent packet so orphans counter has to be updated.
                 ++orphans_;
-                return(boost::shared_ptr<const T>());
+                return(boost::shared_ptr<T>());
             } else if (next_sent_ == sent_packets_.end()) {
                 // Even if there are still many unmatched packets on the
                 // list we might hit the end of it because of unordered
@@ -426,13 +427,13 @@ public:
                 // If we are here, it means that both ordered lookup and
                 // unordered lookup failed. Searched packet is not on the list.
                 ++orphans_;
-                return(boost::shared_ptr<const T>());
+                return(boost::shared_ptr<T>());
             }
 
             // Packet is matched so we count it. We don't count unmatched packets
             // as they are counted as orphans with a separate counter.
             ++rcvd_packets_num_;
-            boost::shared_ptr<const T> sent_packet(*next_sent_);
+            boost::shared_ptr<T> sent_packet(*next_sent_);
             // If packet was found, we assume it will be never searched
             // again. We want to delete this packet from the list to
             // improve performance of future searches.
@@ -549,6 +550,19 @@ public:
         /// \return number of received packets.
         uint64_t getRcvdPacketsNum() const { return(rcvd_packets_num_); }
 
+        /// \brief Return number of dropped packets.
+        ///
+        /// Method returns number of dropped packets.
+        ///
+        /// \return number of dropped packets.
+        uint64_t getDroppedPacketsNum() const {
+            uint64_t drops = 0;
+            if (getSentPacketsNum() > getRcvdPacketsNum()) {
+                drops = getSentPacketsNum() - getRcvdPacketsNum();
+            }
+            return(drops);
+        }
+
         /// \brief Print main statistics for packet exchange.
         ///
         /// Method prints main statistics for particular exchange.
@@ -556,13 +570,9 @@ public:
         /// number of dropped packets and number of orphans.
         void printMainStats() const {
             using namespace std;
-            uint64_t drops = 0;
-            if (getRcvdPacketsNum() >= getSentPacketsNum()) {
-                drops = getRcvdPacketsNum() - getSentPacketsNum();
-            }
             cout << "sent packets: " << getSentPacketsNum() << endl
                  << "received packets: " << getRcvdPacketsNum() << endl
-                 << "drops: " << drops << endl
+                 << "drops: " << getDroppedPacketsNum() << endl
                  << "orphans: " << getOrphans() << endl;
         }
 
@@ -614,7 +624,7 @@ public:
             for (PktListIterator it = rcvd_packets_.begin();
                  it != rcvd_packets_.end();
                  ++it) {
-                boost::shared_ptr<const T> rcvd_packet = *it;
+                boost::shared_ptr<T> rcvd_packet = *it;
                 PktListTransidHashIndex& idx =
                     archived_packets_.template get<1>();
                 std::pair<PktListTransidHashIterator,
@@ -625,7 +635,7 @@ public:
                      ++it) {
                     if ((*it_archived)->getTransid() ==
                         rcvd_packet->getTransid()) {
-                        boost::shared_ptr<const T> sent_packet = *it_archived;
+                        boost::shared_ptr<T> sent_packet = *it_archived;
                         // Get sent and received packet times.
                         ptime sent_time = sent_packet->getTimestamp();
                         ptime rcvd_time = rcvd_packet->getTimestamp();
@@ -839,7 +849,7 @@ public:
     /// \throw isc::BadValue if invalid exchange type specified or
     /// packet is null.
     void passSentPacket(const ExchangeType xchg_type,
-                        const boost::shared_ptr<const T>& packet) {
+                        const boost::shared_ptr<T>& packet) {
         ExchangeStatsPtr xchg_stats = getExchangeStats(xchg_type);
         xchg_stats->appendSent(packet);
     }
@@ -857,10 +867,11 @@ public:
     /// or packet is null.
     /// \throw isc::Unexpected if corresponding packet was not
     /// found on the list of sent packets.
-    void passRcvdPacket(const ExchangeType xchg_type,
-                        const boost::shared_ptr<const T>& packet) {
+    boost::shared_ptr<T>
+    passRcvdPacket(const ExchangeType xchg_type,
+                   const boost::shared_ptr<T>& packet) {
         ExchangeStatsPtr xchg_stats = getExchangeStats(xchg_type);
-        boost::shared_ptr<const T> sent_packet
+        boost::shared_ptr<T> sent_packet
             = xchg_stats->matchPackets(packet);
 
         if (sent_packet) {
@@ -869,6 +880,7 @@ public:
                 xchg_stats->appendRcvd(packet);
             }
         }
+        return(sent_packet);
     }
 
     /// \brief Return minumum delay between sent and received packet.
@@ -1003,6 +1015,19 @@ public:
         return(xchg_stats->getRcvdPacketsNum());
     }
 
+    /// \brief Return total number of dropped packets.
+    ///
+    /// Method returns total number of dropped packets for specified
+    /// exchange type.
+    ///
+    /// \param xchg_type exchange type.
+    /// \throw isc::BadValue if invalid exchange type specified.
+    /// \return number of dropped packets.
+    uint64_t getDroppedPacketsNum(const ExchangeType xchg_type) const {
+        ExchangeStatsPtr xchg_stats = getExchangeStats(xchg_type);
+        return(xchg_stats->getDroppedPacketsNum());
+    }
+
     /// \brief Return name of the exchange.
     ///
     /// Method returns name of the specified exchange type.
diff --git a/tests/tools/perfdhcp/test_control.cc b/tests/tools/perfdhcp/test_control.cc
index 286fc82..87b2814 100644
--- a/tests/tools/perfdhcp/test_control.cc
+++ b/tests/tools/perfdhcp/test_control.cc
@@ -80,18 +80,104 @@ TestControl::instance() {
 
 TestControl::TestControl() :
     send_due_(microsec_clock::universal_time()),
-    last_sent_(send_due_) {
+    last_sent_(send_due_),
+    transid_gen_(new TransidGenerator()) {
 }
 
 bool
 TestControl::checkExitConditions() const {
     CommandOptions& options = CommandOptions::instance();
-    if ((options.getNumRequests().size() > 0) &&
-        (sent_packets_0_ >= options.getNumRequests()[0])) {
-        return(true);
-    } else if ((options.getNumRequests().size() == 2) &&
-               (sent_packets_1_ >= options.getNumRequests()[1])) {
-        return(true);
+    // Check if we reached maximum number of DISCOVER/SOLICIT sent.
+    if (options.getNumRequests().size() > 0) {
+        if (options.getIpVersion() == 4) {
+            if (getSentPacketsNum(StatsMgr4::XCHG_DO) >=
+                options.getNumRequests()[0]) {
+                return(true);
+            }
+        } else if (options.getIpVersion() == 6) {
+            if (stats_mgr6_->getSentPacketsNum(StatsMgr6::XCHG_SA) >=
+                options.getNumRequests()[0]) {
+                return(true);
+            }
+        }
+    }
+    // Check if we reached maximum number REQUEST packets.
+    if (options.getNumRequests().size() == 2) {
+        if (options.getIpVersion() == 4) {
+            if (stats_mgr4_->getSentPacketsNum(StatsMgr4::XCHG_RA) >=
+                options.getNumRequests()[1]) {
+                return(true);
+            }
+        } else if (options.getIpVersion() == 6) {
+            if (stats_mgr6_->getSentPacketsNum(StatsMgr6::XCHG_RR) >=
+                options.getNumRequests()[1]) {
+                return(true);
+            }
+        }
+    }
+    // Check if we reached maximum number of drops of OFFER/ADVERTISE packets.
+    if (options.getMaxDrop().size() > 0) {
+        if (options.getIpVersion() == 4) {
+            if (stats_mgr4_->getDroppedPacketsNum(StatsMgr4::XCHG_DO) >=
+                options.getMaxDrop()[0]) {
+                return(true);
+            }
+        } else if (options.getIpVersion() == 6) {
+            if (stats_mgr6_->getDroppedPacketsNum(StatsMgr6::XCHG_SA) >=
+                options.getMaxDrop()[0]) {
+                return(true);
+            }
+        }
+    }
+    // Check if we reached maximum number of drops of ACK/REPLY packets.
+    if (options.getMaxDrop().size() == 2) {
+        if (options.getIpVersion() == 4) {
+            if (stats_mgr4_->getDroppedPacketsNum(StatsMgr4::XCHG_RA) >=
+                options.getMaxDrop()[1]) {
+                return(true);
+            }
+        } else if (options.getIpVersion() == 6) {
+            if (stats_mgr6_->getDroppedPacketsNum(StatsMgr6::XCHG_RR) >=
+                options.getMaxDrop()[1]) {
+                return(true);
+            }
+        }
+    }
+    // Check if we reached maximum drops percentage of OFFER/ADVERTISE packets.
+    if (options.getMaxDropPercentage().size() > 0) {
+        if (options.getIpVersion() == 4) {
+            if ((stats_mgr4_->getSentPacketsNum(StatsMgr4::XCHG_DO) > 10) &&
+                ((100. * stats_mgr4_->getDroppedPacketsNum(StatsMgr4::XCHG_DO) /
+                 stats_mgr4_->getSentPacketsNum(StatsMgr4::XCHG_DO)) >=
+                 options.getMaxDropPercentage()[0])) {
+                return(true);
+            }
+        } else if (options.getIpVersion() == 6) {
+            if ((stats_mgr6_->getSentPacketsNum(StatsMgr6::XCHG_SA) > 10) &&
+                ((100. * stats_mgr6_->getDroppedPacketsNum(StatsMgr6::XCHG_SA) /
+                  stats_mgr6_->getSentPacketsNum(StatsMgr6::XCHG_SA)) >=
+                 options.getMaxDropPercentage()[0])) {
+                return(true);
+            }
+        }
+    }
+    // Check if we reached maximum drops percentage of ACK/REPLY packets.
+    if (options.getMaxDropPercentage().size() == 2) {
+        if (options.getIpVersion() == 4) {
+            if ((stats_mgr4_->getSentPacketsNum(StatsMgr4::XCHG_RA) > 10) &&
+                ((100. * stats_mgr4_->getDroppedPacketsNum(StatsMgr4::XCHG_RA) /
+                 stats_mgr4_->getSentPacketsNum(StatsMgr4::XCHG_RA)) >=
+                 options.getMaxDropPercentage()[0])) {
+                return(true);
+            }
+        } else if (options.getIpVersion() == 6) {
+            if ((stats_mgr6_->getSentPacketsNum(StatsMgr6::XCHG_RR) > 10) &&
+                ((100. * stats_mgr6_->getDroppedPacketsNum(StatsMgr6::XCHG_RR) /
+                  stats_mgr6_->getSentPacketsNum(StatsMgr6::XCHG_RR)) >=
+                 options.getMaxDropPercentage()[0])) {
+                return(true);
+            }
+        }
     }
     return(false);
 }
@@ -170,8 +256,6 @@ TestControl::factoryRequestList4(Option::Universe u,
     return opt;
 }
 
-
-
 std::vector<uint8_t>
 TestControl::generateMacAddress() const {
     CommandOptions& options = CommandOptions::instance();
@@ -268,6 +352,26 @@ TestControl::getNextExchangesNum() const {
     return (0);
 }
 
+uint64_t
+TestControl::getRcvdPacketsNum(const ExchangeType xchg_type) const {
+    uint8_t ip_version = CommandOptions::instance().getIpVersion();
+    if (ip_version == 4) {
+        return(stats_mgr4_->getRcvdPacketsNum(xchg_type));
+    }
+    return(stats_mgr6_->
+           getRcvdPacketsNum(static_cast<StatsMgr6::ExchangeType>(xchg_type)));
+}
+
+uint64_t
+TestControl::getSentPacketsNum(const ExchangeType xchg_type) const {
+    uint8_t ip_version = CommandOptions::instance().getIpVersion();
+    if (ip_version == 4) {
+        return(stats_mgr4_->getSentPacketsNum(xchg_type));
+    }
+    return(stats_mgr6_->
+           getSentPacketsNum(static_cast<StatsMgr6::ExchangeType>(xchg_type)));
+}
+
 void
 TestControl::initializeStatsMgr() {
     CommandOptions& options = CommandOptions::instance();
@@ -275,7 +379,7 @@ TestControl::initializeStatsMgr() {
         stats_mgr4_.reset();
         stats_mgr4_ = StatsMgr4Ptr(new StatsMgr4());
         stats_mgr4_->addExchangeStats(StatsMgr4::XCHG_DO);
-        if (options.getExchangeMode() == CommandOptions::DO_SA) {
+        if (options.getExchangeMode() == CommandOptions::DORA_SARR) {
             stats_mgr4_->addExchangeStats(StatsMgr4::XCHG_RA);
         }
 
@@ -283,9 +387,9 @@ TestControl::initializeStatsMgr() {
         stats_mgr6_.reset();
         stats_mgr6_ = StatsMgr6Ptr(new StatsMgr6());
         stats_mgr6_->addExchangeStats(StatsMgr6::XCHG_SA);
-        if (options.getExchangeMode() == CommandOptions::DO_SA) {
+        if (options.getExchangeMode() == CommandOptions::DORA_SARR) {
             stats_mgr6_->addExchangeStats(StatsMgr6::XCHG_RR);
-        } 
+        }
     }
 }
 
@@ -395,7 +499,8 @@ TestControl::printStats() const {
 }
 
 void
-TestControl::receivePacket4(Pkt4Ptr& pkt4) {
+TestControl::receivePacket4(const TestControlSocket&,
+                            const Pkt4Ptr& pkt4) {
     switch(pkt4->getType()) {
     case DHCPOFFER :
         stats_mgr4_->passRcvdPacket(StatsMgr4::XCHG_DO, pkt4);
@@ -410,23 +515,25 @@ TestControl::receivePacket4(Pkt4Ptr& pkt4) {
 }
 
 void
-TestControl::receivePacket6(Pkt6Ptr& pkt6) {
-    switch(pkt6->getType()) {
-    case DHCPV6_ADVERTISE :
-        stats_mgr6_->passRcvdPacket(StatsMgr6::XCHG_SA, pkt6);
-        break;
-    case DHCPV6_REPLY :
+TestControl::receivePacket6(const TestControlSocket& socket,
+                            const Pkt6Ptr& pkt6) {
+    uint8_t packet_type = pkt6->getType();
+    if (packet_type == DHCPV6_ADVERTISE) {
+        Pkt6Ptr solicit_pkt6(stats_mgr6_->passRcvdPacket(StatsMgr6::XCHG_SA,
+                                                         pkt6));
+        if (solicit_pkt6) {
+            sendRequest6(socket, solicit_pkt6, pkt6);
+        }
+    } else if (packet_type == DHCPV6_REPLY) {
         stats_mgr6_->passRcvdPacket(StatsMgr6::XCHG_RR, pkt6);
-        break;
-    default:
+    } else {
         isc_throw(BadValue, "unknown type " << pkt6->getType()
                   << " of received DHCPv6 packet");
     }
-
 }
 
 void
-TestControl::receivePackets() {
+TestControl::receivePackets(const TestControlSocket& socket) {
     int timeout = 0;
     bool receiving = true;
     while (receiving) {
@@ -436,7 +543,7 @@ TestControl::receivePackets() {
                 receiving = false;
             } else {
                 pkt4->unpack();
-                receivePacket4(pkt4);
+                receivePacket4(socket, pkt4);
             }
         } else if (CommandOptions::instance().getIpVersion() == 6) {
             Pkt6Ptr pkt6 = IfaceMgr::instance().receive6(timeout);
@@ -444,7 +551,7 @@ TestControl::receivePackets() {
                 receiving  = false;
             } else {
                 if (pkt6->unpack()) {
-                    receivePacket6(pkt6);
+                    receivePacket6(socket, pkt6);
                 }
             }
         }
@@ -524,7 +631,7 @@ TestControl::run() {
     }
     registerOptionFactories();
     TestControlSocket socket(openSocket());
-    
+
     initializeStatsMgr();
     uint64_t packets_sent = 0;
     for (;;) {
@@ -534,7 +641,7 @@ TestControl::run() {
         }
         uint64_t packets_due = getNextExchangesNum();
 
-        receivePackets();
+        receivePackets(socket);
 
         for (uint64_t i = packets_due; i > 0; --i) {
             if (options.getIpVersion() == 4) {
@@ -555,8 +662,8 @@ TestControl::sendDiscover4(const TestControlSocket& socket) {
     // Generate the MAC address to be passed in the packet.
     std::vector<uint8_t> mac_address = generateMacAddress();
     // Generate trasnaction id to be set for the new exchange.
-    const uint32_t transid = static_cast<uint32_t>(random() % 0x00FFFFFF);
-    boost::shared_ptr<Pkt4> pkt4(new Pkt4(DHCPDISCOVER, transid));
+    const uint32_t transid = generateTransid();
+    Pkt4Ptr pkt4(new Pkt4(DHCPDISCOVER, transid));
     if (!pkt4) {
         isc_throw(Unexpected, "failed to create DISCOVER packet");
     }
@@ -581,6 +688,59 @@ TestControl::sendDiscover4(const TestControlSocket& socket) {
 }
 
 void
+TestControl::sendRequest6(const TestControlSocket& socket,
+                          const Pkt6Ptr& solicit_pkt6,
+                          const Pkt6Ptr& advertise_pkt6) {
+    const uint32_t transid = static_cast<uint32_t>(random() % 0x00FFFFFF);
+    Pkt6Ptr pkt6(new Pkt6(DHCPV6_REQUEST, transid));
+    // Calculate elapsed time
+    ptime solicit_time = solicit_pkt6->getTimestamp();
+    ptime advertise_time = advertise_pkt6->getTimestamp();
+    if (solicit_time.is_not_a_date_time()) {
+        isc_throw(Unexpected, "timestamp was not set for SOLICIT packet");
+    }
+    if (advertise_time.is_not_a_date_time()) {
+        isc_throw(Unexpected, "timestamp was not set for ADVERTISE packet");
+    }
+    time_period period(solicit_time, advertise_time);
+    if (period.is_null()) {
+        pkt6->addOption(Option::factory(Option::V6, D6O_ELAPSED_TIME));
+    } else {
+        OptionBuffer buf();
+        const uint32_t elapsed_time = period.length().total_seconds();
+        OptionPtr opt_elapsed_time =
+            Option::factory(Option::V6, D6O_ELAPSED_TIME);
+        opt_elapsed_time->setUint16(static_cast<uint16_t>(elapsed_time));
+        pkt6->addOption(opt_elapsed_time);
+    }
+    OptionPtr opt_clientid = advertise_pkt6->getOption(D6O_CLIENTID);
+    if (!opt_clientid) {
+        isc_throw(Unexpected, "client id not found in received packet");
+    }
+    pkt6->addOption(opt_clientid);
+    OptionPtr opt_serverid = advertise_pkt6->getOption(D6O_SERVERID);
+    if (!opt_serverid) {
+        isc_throw(Unexpected, "server id not found in received packet");
+    }
+    pkt6->addOption(opt_serverid);
+    OptionPtr opt_ia_na = advertise_pkt6->getOption(D6O_IA_NA);
+    if (!opt_ia_na) {
+        isc_throw(Unexpected, "DHCPv6 IA_NA option not found in received "
+                  "packet");
+    }
+    pkt6->addOption(opt_ia_na);
+    setDefaults6(socket, pkt6);
+
+    pkt6->pack();
+    IfaceMgr::instance().send(pkt6);
+    if (!stats_mgr6_) {
+        isc_throw(InvalidOperation, "Statistics Manager for DHCPv6 "
+                  "hasn't been initialized");
+    }
+    stats_mgr6_->passSentPacket(StatsMgr6::XCHG_RR, pkt6);
+}
+
+void
 TestControl::sendSolicit6(const TestControlSocket& socket) {
     ++sent_packets_0_;
     last_sent_ = microsec_clock::universal_time();
@@ -589,13 +749,13 @@ TestControl::sendSolicit6(const TestControlSocket& socket) {
     // Generate DUID to be passed to the packet
     std::vector<uint8_t> duid = generateDuid();
     // Generate trasnaction id to be set for the new exchange.
-    const uint32_t transid = static_cast<uint32_t>(random() % 0x00FFFFFF);
-    boost::shared_ptr<Pkt6> pkt6(new Pkt6(DHCPV6_SOLICIT, transid));
+    const uint32_t transid = generateTransid();
+    Pkt6Ptr pkt6(new Pkt6(DHCPV6_SOLICIT, transid));
     if (!pkt6) {
         isc_throw(Unexpected, "failed to create SOLICIT packet");
     }
     pkt6->addOption(Option::factory(Option::V6, D6O_ELAPSED_TIME));
-    pkt6->addOption(Option::factory(Option::V6, D6O_RAPID_COMMIT));
+    //    pkt6->addOption(Option::factory(Option::V6, D6O_RAPID_COMMIT));
     pkt6->addOption(Option::factory(Option::V6, D6O_CLIENTID, duid));
     pkt6->addOption(Option::factory(Option::V6, D6O_ORO));
     pkt6->addOption(Option::factory(Option::V6, D6O_IA_NA));
@@ -612,7 +772,7 @@ TestControl::sendSolicit6(const TestControlSocket& socket) {
 
 void
 TestControl::setDefaults4(const TestControlSocket& socket,
-                          const boost::shared_ptr<Pkt4>& pkt) {
+                          const Pkt4Ptr& pkt) {
     CommandOptions& options = CommandOptions::instance();
     // Interface name.
     pkt->setIface(socket.getIface());
@@ -634,7 +794,7 @@ TestControl::setDefaults4(const TestControlSocket& socket,
 
 void
 TestControl::setDefaults6(const TestControlSocket& socket,
-                          const boost::shared_ptr<Pkt6>& pkt) {
+                          const Pkt6Ptr& pkt) {
     CommandOptions& options = CommandOptions::instance();
     // Interface name.
     pkt->setIface(socket.getIface());
diff --git a/tests/tools/perfdhcp/test_control.h b/tests/tools/perfdhcp/test_control.h
index c153fac..1a3157b 100644
--- a/tests/tools/perfdhcp/test_control.h
+++ b/tests/tools/perfdhcp/test_control.h
@@ -20,6 +20,7 @@
 
 #include <boost/noncopyable.hpp>
 #include <boost/shared_ptr.hpp>
+#include <boost/function.hpp>
 #include <boost/date_time/posix_time/posix_time.hpp>
 
 #include <dhcp/dhcp6.h>
@@ -55,6 +56,8 @@ public:
     typedef StatsMgr<dhcp::Pkt6> StatsMgr6;
     // Pointer to Statistics Manager for DHCPv6.
     typedef boost::shared_ptr<StatsMgr6> StatsMgr6Ptr;
+    // Packet exchange type.
+    typedef StatsMgr<>::ExchangeType ExchangeType;
 
     /// \brief Socket wrapper class.
     ///
@@ -120,6 +123,26 @@ public:
         asiolink::IOAddress addr_; ///< Address bound.
     };
 
+    /// \brief Default transaction id generator class.
+    ///
+    /// This is default transaction id generator class. The member
+    /// function is used to generate unique transaction id value.
+    /// Other generator classes should derive from this one to
+    /// override the standard generation algorithm (e.g. unit tests
+    /// override this class wih algorithm that produces more predictable
+    /// transaction id values).
+    class TransidGenerator {
+    public:
+        /// \brief generate transaction id.
+        ///
+        /// \return generated transazction id value.
+        virtual uint32_t generate() {
+            return static_cast<uint32_t>(random() % 0x00FFFFFF);
+        }
+    };
+
+    typedef boost::shared_ptr<TransidGenerator> TransidGeneratorPtr;
+
     /// \brief Length of the Ethernet HW address (MAC) in bytes.
     static const uint8_t HW_ETHER_LEN = 6;
 
@@ -129,7 +152,7 @@ public:
     /// \return the only existing instance of test control
     static TestControl& instance();
 
-    /// Run performance test.
+    /// brief\ Run performance test.
     ///
     /// Method runs whole performance test. Command line options must
     /// be parsed prior to running this function. Othewise function will
@@ -139,6 +162,14 @@ public:
     /// \throw isc::Unexpected if internal Test Controler error occured.
     void run();
 
+    /// \brief Set new transaction id generator.
+    ///
+    /// \param generator generator object to be used.
+    void setTransidGenerator(TransidGeneratorPtr& generator) {
+        transid_gen_.reset();
+        transid_gen_ = generator;
+    }
+
 protected:
 
     // We would really like these methods and members to be private but
@@ -285,6 +316,13 @@ protected:
     /// \return generated MAC address.
     std::vector<uint8_t> generateMacAddress() const;
 
+    /// \brief generate transaction id.
+    ///
+    /// \return generated transaction id.
+    uint32_t generateTransid() {
+        return(transid_gen_->generate());
+    }
+
     /// \brief Returns number of exchanges to be started.
     ///
     /// Method returns number of new exchanges to be started as soon
@@ -329,14 +367,45 @@ protected:
     /// not initialized.
     void printStats() const;
 
-    void receivePacket4(dhcp::Pkt4Ptr& pkt4);
+    /// \brief Receive DHCPv4 packet.
+    ///
+    /// Method performs reception of the DHCPv4 packet, updates
+    /// statistics and responsds to the server if required, e.g.
+    /// when OFFER packet arrives, this function will initiate
+    /// REQUEST message to the server.
+    ///
+    /// \param socket socket to be used.
+    /// \param pkt4 object representing DHCPv4 packet received.
+    /// \throw isc::BadValue if unknown message type received.
+    /// \throw isc::Unexpected if unexpected error occured.
+    void receivePacket4(const TestControlSocket& socket,
+                        const dhcp::Pkt4Ptr& pkt4);
 
-    void receivePacket6(dhcp::Pkt6Ptr& pkt4);
+    /// \brief Receive DHCPv6 packet.
+    ///
+    /// Method performs reception of the DHCPv6 packet, updates
+    /// statistics and responsds to the server if required, e.g.
+    /// when ADVERTISE packet arrives, this function will initiate
+    /// REQUEST message to the server.
+    ///
+    /// \param socket socket to be used.
+    /// \param pkt6 object representing DHCPv6 packet received.
+    /// \throw isc::BadValue if unknown message type received.
+    /// \throw isc::Unexpected if unexpected error occured.
+    void receivePacket6(const TestControlSocket& socket,
+                        const dhcp::Pkt6Ptr& pkt6);
 
     /// \brief Receive DHCPv4 or DHCPv6 packets from the server.
     ///
     /// Method receives DHCPv4 or DHCPv6 packets from the server.
-    void receivePackets();
+    /// This function will call \ref receivePacket4 or
+    /// \ref receivePacket6 depending if DHCPv4 or DHCPv6 packet
+    /// has arrived.
+    ///
+    /// \param socket socket to be used.
+    /// \throw::BadValue if unknown message type received.
+    /// \throw::Unexpected if unexpected error occured.
+    void receivePackets(const TestControlSocket& socket);
 
     /// \brief Register option factory functions for DHCPv4
     ///
@@ -378,6 +447,28 @@ protected:
     /// \throw isc::BadValue if MAC address has invalid length.
     void sendDiscover4(const TestControlSocket& socket);
 
+    /// \brief Sent DHCPv6 REQUEST message.
+    ///
+    /// Method creates and sends DHCPv6 REQUEST message to the server
+    /// with the following options:
+    /// - D6O_ELAPSED_TIME
+    /// - D6O_CLIENTID
+    /// - D6O_SERVERID
+    /// The elapsed time is calculated based on the duration between
+    /// sending a SOLICIT and receiving the ADVERTISE packet prior.
+    /// For this reason both solicit and advertise packet objects have
+    /// to be passed when calling this function.
+    ///
+    /// \param socket socket to be used to send message.
+    /// \param solicit_pkt6 SOLICIT packet object.
+    /// \param advertise_pkt6 ADVERTISE packet object.
+    /// \throw isc::Unexpected if unexpected error occured.
+    /// \throw isc::InvalidOperation if Statistics Manager has not been
+    /// initialized.
+    void sendRequest6(const TestControlSocket& socket,
+                      const dhcp::Pkt6Ptr& solicit_pkt6,
+                      const dhcp::Pkt6Ptr& advertise_pkt6);
+
     /// \brief Send DHCPv6 SOLICIT message.
     ///
     /// Method creates and sends DHCPv6 SOLICIT message to the server
@@ -405,7 +496,7 @@ protected:
     /// \param socket socket used to send the packet.
     /// \param pkt reference to packet to be configured.
     void setDefaults4(const TestControlSocket& socket,
-                      const boost::shared_ptr<dhcp::Pkt4>& pkt);
+                      const dhcp::Pkt4Ptr& pkt);
 
     /// \brief Set default DHCPv6 packet parameters.
     ///
@@ -420,7 +511,7 @@ protected:
     /// \param socket socket used to send the packet.
     /// \param pkt reference to packet to be configured.
     void setDefaults6(const TestControlSocket& socket,
-                      const boost::shared_ptr<dhcp::Pkt6>& pkt);
+                      const dhcp::Pkt6Ptr& pkt);
 
     /// \brief Update due time to initiate next chunk of exchanges.
     ///
@@ -431,13 +522,38 @@ protected:
 
 private:
 
+    /// \brief Generate transaction id using random function.
+    ///
+    /// \return generated transaction id value.
+    static uint32_t generateTransidRandom();
+
+    /// \brief Get number of received packets.
+    ///
+    /// Get the number of received packets from the Statistics Manager.
+    /// Function may throw if Statistics Manager object is not
+    /// initialized.
+    /// \param xchg_type packet exchange type.
+    /// \return number of received packets.
+    uint64_t getRcvdPacketsNum(const ExchangeType xchg_type) const;
+
+    /// \brief Get number of sent packets.
+    ///
+    /// Get the number of sent packets from the Statistics Manager.
+    /// Function may throw if Statistics Manager object is not
+    /// initialized.
+    /// \param xchg_type packet exchange type.
+    /// \return number of sent packets.
+    uint64_t getSentPacketsNum(const ExchangeType xchg_type) const;
+
     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.
+    StatsMgr4Ptr stats_mgr4_;  ///< Statistics Manager 4.
+    StatsMgr6Ptr stats_mgr6_;  ///< Statistics Manager 6.
 
-    StatsMgr4Ptr stats_mgr4_;  /// Statistics Manager 4.
-    StatsMgr6Ptr stats_mgr6_;  /// Statistics Manager 6.
+    // Pointers to functions.
+    TransidGeneratorPtr transid_gen_; ///< Transaction id generator.
 
     uint64_t sent_packets_0_;
     uint64_t sent_packets_1_;
diff --git a/tests/tools/perfdhcp/tests/test_control_unittest.cc b/tests/tools/perfdhcp/tests/test_control_unittest.cc
index aa89830..52c55b2 100644
--- a/tests/tools/perfdhcp/tests/test_control_unittest.cc
+++ b/tests/tools/perfdhcp/tests/test_control_unittest.cc
@@ -38,6 +38,34 @@ using namespace isc::perfdhcp;
 /// to allow unit testing.
 class NakedTestControl: public TestControl {
 public:
+
+    /// \brief Incremental transaction id generaator.
+    ///
+    /// This is incremental transaction id generator. It overrides
+    /// the default transaction id generator that generates transaction
+    /// ids using random function. This generator will generate values
+    /// like: 1,2,3 etc.
+    class IncrementalGenerator : public TestControl::TransidGenerator {
+    public:
+        /// \brief Default constructor.
+        IncrementalGenerator() :
+            TransidGenerator(),
+            transid_(0) {
+        }
+
+        /// \brief Generate unique transaction id.
+        ///
+        /// Generate unique transaction ids incrementally:
+        /// 1,2,3,4 etc.
+        ///
+        /// \return generated transaction id.
+        virtual uint32_t generate() {
+            return ++transid_;
+        }
+    private:
+        uint32_t transid_; ///< Last generated transaction id.
+    };
+
     using TestControl::checkExitConditions;
     using TestControl::factoryElapsedTime6;
     using TestControl::factoryGeneric;
@@ -48,14 +76,15 @@ public:
     using TestControl::generateDuid;
     using TestControl::generateMacAddress;
     using TestControl::getNextExchangesNum;
+    using TestControl::initializeStatsMgr;
     using TestControl::openSocket;
-    using TestControl::receivePackets;
+    using TestControl::receivePacket4;
+    using TestControl::receivePacket6;
     using TestControl::registerOptionFactories;
     using TestControl::sendDiscover4;
     using TestControl::sendSolicit6;
     using TestControl::setDefaults4;
     using TestControl::setDefaults6;
-    using TestControl::updateSendDue;
 
     NakedTestControl() : TestControl() { };
 
@@ -78,6 +107,11 @@ public:
     /// \brief Default Constructor
     TestControlTest() { }
 
+    static uint32_t generateTransidIncremental() {
+        static uint32_t transid(1);
+        return ++transid;
+    }
+
     /// \brief Get local loopback interface name.
     ///
     /// Scan available network interfaces for local loopback
@@ -260,6 +294,107 @@ public:
         }
     }
 
+    /// \brief Test DHCPv4 exchanges.
+    ///
+    /// Function simulates DHCPv4 exchanges. Function caller specifies
+    /// number of exchanges to be simulated and number of simulated
+    /// responses. When number of responses is lower than number of
+    /// iterations than the difference between them is the number
+    /// of simulated packet drops. This is useful to test if program
+    /// exit conditions are handled properly (maximum number of packet
+    /// drops specified as -D<max-drops> is taken into account).
+    ///
+    /// \param iterations_num number of exchanges to simulate.
+    /// \param receive_num number of received OFFER packets.
+    /// \param iterations_performed actual number of iterations.
+    void testPkt4Exchange(int iterations_num,
+                          int receive_num,
+                          int& iterations_performed) const {
+        uint16_t port = 10547;
+        int sock_handle = 0;
+        NakedTestControl tc;
+        tc.initializeStatsMgr();
+        // Incremental transaction id generator will generate
+        // predictable values of transaction id for each iteration.
+        // This is important because we need to simulate reponses
+        // from the server and use the same transaction ids as in
+        // packets sent by client.
+        TestControl::TransidGeneratorPtr
+            generator(new NakedTestControl::IncrementalGenerator());
+        tc.setTransidGenerator(generator);
+        // Socket is needed to send packets through the interface.
+        ASSERT_NO_THROW(sock_handle = tc.openSocket(port));
+        TestControl::TestControlSocket sock(sock_handle);
+        int i = 0;
+        for (; i < iterations_num; ++i) {
+            if (tc.checkExitConditions()) {
+                break;
+            }
+            ASSERT_NO_THROW(tc.sendDiscover4(sock));
+            // Do not simulate responses for packets later
+            // that specified as receive_num. This simulates
+            // packet drops.
+            if (i - 1 < receive_num) {
+                boost::shared_ptr<Pkt4> offer_pkt4(createOfferPkt4(i));
+                // Receive OFFER and send REQUEST.
+                ASSERT_NO_THROW(tc.receivePacket4(sock, offer_pkt4));
+            }
+        }
+        // Return the number of iterations performed.
+        iterations_performed = i;
+    }
+
+    /// \brief Test DHCPv6 exchanges.
+    ///
+    /// Function simulates DHCPv6 exchanges. Function caller specifies
+    /// number of exchanges to be simulated and number of simulated
+    /// responses. When number of responses is lower than number of
+    /// iterations than the difference between them is the number
+    /// of simulated packet drops. This is useful to test if program
+    /// exit conditions are handled properly (maximum number of packet
+    /// drops specified as -D<max-drops> is taken into account).
+    ///
+    /// \param iterations_num number of exchanges to simulate.
+    /// \param receive_num number of received OFFER packets.
+    /// \param iterations_performed actual number of iterations.
+    void testPkt6Exchange(int iterations_num,
+                          int receive_num,
+                          int& iterations_performed) const {
+        uint16_t port = 10547;
+        int sock_handle = 0;
+        NakedTestControl tc;
+        tc.initializeStatsMgr();
+        // Incremental transaction id generator will generate
+        // predictable values of transaction id for each iteration.
+        // This is important because we need to simulate reponses
+        // from the server and use the same transaction ids as in
+        // packets sent by client.
+        TestControl::TransidGeneratorPtr
+            generator(new NakedTestControl::IncrementalGenerator());
+        tc.setTransidGenerator(generator);
+        // Socket is needed to send packets through the interface.
+        ASSERT_NO_THROW(sock_handle = tc.openSocket(port));
+        TestControl::TestControlSocket sock(sock_handle);
+        int i = 0;
+        for (; i < iterations_num; ++i) {
+            if (tc.checkExitConditions()) {
+                break;
+            }
+            // Do not simulate responses for packets later
+            // that specified as receive_num. This simulates
+            // packet drops.
+            ASSERT_NO_THROW(tc.sendSolicit6(sock));
+            if (i - 1 < receive_num) {
+                boost::shared_ptr<Pkt6> advertise_pkt6(createAdvertisePkt6(i));
+                // Receive ADVERTISE and send REQUEST.
+                ASSERT_NO_THROW(tc.receivePacket6(sock, advertise_pkt6));
+            }
+
+        }
+        // Return the number of iterations performed.
+        iterations_performed = i;
+    }
+
     /// \brief Test generation of multiple MAC addresses.
     ///
     /// This method validates generation of multiple MAC addresses.
@@ -322,6 +457,33 @@ public:
         CommandOptionsHelper::process(cmdline);
     }
 
+private:
+    boost::shared_ptr<Pkt4>
+    createOfferPkt4(uint32_t transid) const {
+        boost::shared_ptr<Pkt4> offer(new Pkt4(DHCPOFFER, transid));
+        OptionPtr opt_msg_type = Option::factory(Option::V4, DHO_DHCP_MESSAGE_TYPE,
+                                                 OptionBuffer(DHCPOFFER));
+        offer->setYiaddr(asiolink::IOAddress("127.0.0.1"));
+        offer->addOption(opt_msg_type);
+        offer->updateTimestamp();
+        return(offer);
+    }
+
+    boost::shared_ptr<Pkt6>
+    createAdvertisePkt6(uint32_t transid) const {
+        OptionPtr opt_ia_na = Option::factory(Option::V6, D6O_IA_NA);
+        OptionPtr opt_serverid(new Option(Option::V6, D6O_SERVERID));
+        NakedTestControl tc;
+        std::vector<uint8_t> duid(tc.generateDuid());
+        OptionPtr opt_clientid(Option::factory(Option::V6, D6O_CLIENTID, duid));
+        boost::shared_ptr<Pkt6> advertise(new Pkt6(DHCPV6_ADVERTISE, transid));
+        advertise->addOption(opt_ia_na);
+        advertise->addOption(opt_serverid);
+        advertise->addOption(opt_clientid);
+        advertise->updateTimestamp();
+        return(advertise);
+    }
+
 };
 
 TEST_F(TestControlTest, GenerateDuid) {
@@ -568,6 +730,77 @@ TEST_F(TestControlTest, Packet6) {
     }
 }
 
+TEST_F(TestControlTest, Packet4Exchange) {
+    // Get the local loopback interface to open socket on
+    // it and test packets exchanges. We don't want to fail
+    // the test if interface is not available.
+    std::string loopback_iface(getLocalLoopback());
+    if (loopback_iface.empty()) {
+        std::cout << "Unable to find the loopback interface. Skip test."
+                  << std::endl;
+        return;
+    }
+
+    // Set number of iterations to some high value.
+    const int iterations_num = 100;
+    processCmdLine("perfdhcp -l " + loopback_iface
+                   + " -r 100 -n 10 -R 20 127.0.0.1");
+    // The actual number of iterations will be stored in the
+    // following variable.
+    int iterations_performed = 0;
+    testPkt4Exchange(iterations_num, iterations_num, iterations_performed);
+    // The command line restricts the number of iterations to 10
+    // with -n 10 parameter.
+    EXPECT_EQ(10, iterations_performed);
+
+    // With the following command line we restrict the maximum
+    // number of dropped packets to 20% of all.
+    processCmdLine("perfdhcp -l " + loopback_iface
+                   + " -r 100 -R 20 -n 20 -D 10% 127.0.0.1");
+    // The number iterations is restricted by the percentage of
+    // dropped packets (-D 10%). We also have to bump up the number
+    // of iterations because the percentage limitation checks starts
+    // at packet #10. We expect that at packet #12 the 10% threshold
+    // will be reached.
+    const int received_num = 10;
+    testPkt4Exchange(iterations_num, received_num, iterations_performed);
+    EXPECT_EQ(12, iterations_performed);
+}
+
+TEST_F(TestControlTest, Packet6Exchange) {
+    // Get the local loopback interface to open socket on
+    // it and test packets exchanges. We don't want to fail
+    // the test if interface is not available.
+    std::string loopback_iface(getLocalLoopback());
+    if (loopback_iface.empty()) {
+        std::cout << "Unable to find the loopback interface. Skip test."
+                  << std::endl;
+        return;
+    }
+
+    const int iterations_num = 100;
+    // Set number of iterations to 10.
+    processCmdLine("perfdhcp -l " + loopback_iface
+                   + " -6 -r 100 -n 10 -R 20 ::1");
+    int iterations_performed = 0;
+    // Set number of received packets equal to number of iterations.
+    // This simulates no packet drops.
+    testPkt6Exchange(iterations_num, iterations_num, iterations_performed);
+    // Actual number of iterations should be 10.
+    EXPECT_EQ(10, iterations_performed);
+
+    // The maximum number of dropped packets is 3 (because of -D 3).
+    processCmdLine("perfdhcp -l " + loopback_iface
+                   + " -6 -r 100 -n 10 -R 20 -D 3 ::1");
+    // For the first 3 packets we are simulating responses from server.
+    // For other packets we don't so packet as 4,5,6 will be dropped and
+    // then test should be interrupted and actual number of iterations will
+    // be 6.
+    const int received_num = 3;
+    testPkt6Exchange(iterations_num, received_num, iterations_performed);
+    EXPECT_EQ(6, iterations_performed);
+}
+
 TEST_F(TestControlTest, RateControl) {
     // We don't specify the exchange rate here so the aggressivity
     // value will determine how many packets are to be send each



More information about the bind10-changes mailing list