From: Marcin Siodelski Date: Tue, 21 Aug 2012 13:17:14 +0000 (+0200) Subject: [1959] Send request messages to server and corresponding unit tests. X-Git-Tag: trac2351_base~47^2~11^2~23 X-Git-Url: http://git.ipfire.org/gitweb.cgi?a=commitdiff_plain;h=fbbae39aea715ccca384083611b6faa8250e068d;p=thirdparty%2Fkea.git [1959] Send request messages to server and corresponding unit tests. --- diff --git a/tests/tools/perfdhcp/stats_mgr.h b/tests/tools/perfdhcp/stats_mgr.h index 7ade910192..5a3f11445d 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 +/// \param T class representing DHCPv4 or DHCPv6 packet. +template 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& packet) { + static uint32_t hashTransid(const boost::shared_ptr& packet) { if (!packet) { isc_throw(BadValue, "Packet is null"); } @@ -214,12 +214,12 @@ public: /// } /// \endcode typedef boost::multi_index_container< - boost::shared_ptr, + boost::shared_ptr, 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 boost::shared_ptr&, 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& packet) { + void appendSent(const boost::shared_ptr& 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& packet) { + void appendRcvd(const boost::shared_ptr& 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& sent_packet, - const boost::shared_ptr& rcvd_packet) { + void updateDelays(const boost::shared_ptr& sent_packet, + const boost::shared_ptr& 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 matchPackets(const boost::shared_ptr& rcvd_packet) { + boost::shared_ptr + matchPackets(const boost::shared_ptr& 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()); + return(boost::shared_ptr()); } 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()); + return(boost::shared_ptr()); } // 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 sent_packet(*next_sent_); + boost::shared_ptr 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 rcvd_packet = *it; + boost::shared_ptr rcvd_packet = *it; PktListTransidHashIndex& idx = archived_packets_.template get<1>(); std::pairgetTransid() == rcvd_packet->getTransid()) { - boost::shared_ptr sent_packet = *it_archived; + boost::shared_ptr 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& packet) { + const boost::shared_ptr& 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& packet) { + boost::shared_ptr + passRcvdPacket(const ExchangeType xchg_type, + const boost::shared_ptr& packet) { ExchangeStatsPtr xchg_stats = getExchangeStats(xchg_type); - boost::shared_ptr sent_packet + boost::shared_ptr 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 286fc823e7..87b2814c84 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 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(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(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 mac_address = generateMacAddress(); // Generate trasnaction id to be set for the new exchange. - const uint32_t transid = static_cast(random() % 0x00FFFFFF); - boost::shared_ptr 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"); } @@ -580,6 +687,59 @@ TestControl::sendDiscover4(const TestControlSocket& socket) { stats_mgr4_->passSentPacket(StatsMgr4::XCHG_DO, pkt4); } +void +TestControl::sendRequest6(const TestControlSocket& socket, + const Pkt6Ptr& solicit_pkt6, + const Pkt6Ptr& advertise_pkt6) { + const uint32_t transid = static_cast(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(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_; @@ -589,13 +749,13 @@ TestControl::sendSolicit6(const TestControlSocket& socket) { // Generate DUID to be passed to the packet std::vector duid = generateDuid(); // Generate trasnaction id to be set for the new exchange. - const uint32_t transid = static_cast(random() % 0x00FFFFFF); - boost::shared_ptr 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& 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& 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 c153fac3b6..1a3157b3e3 100644 --- a/tests/tools/perfdhcp/test_control.h +++ b/tests/tools/perfdhcp/test_control.h @@ -20,6 +20,7 @@ #include #include +#include #include #include @@ -55,6 +56,8 @@ public: typedef StatsMgr StatsMgr6; // Pointer to Statistics Manager for DHCPv6. typedef boost::shared_ptr 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(random() % 0x00FFFFFF); + } + }; + + typedef boost::shared_ptr 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 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& 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& 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 aa8983051e..52c55b2f10 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 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 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 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 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 + createOfferPkt4(uint32_t transid) const { + boost::shared_ptr 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 + 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 duid(tc.generateDuid()); + OptionPtr opt_clientid(Option::factory(Option::V6, D6O_CLIENTID, duid)); + boost::shared_ptr 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