summaryrefslogtreecommitdiffstats
path: root/src/bin/perfdhcp/tests/basic_scen_unittest.cc
diff options
context:
space:
mode:
Diffstat (limited to 'src/bin/perfdhcp/tests/basic_scen_unittest.cc')
-rw-r--r--src/bin/perfdhcp/tests/basic_scen_unittest.cc364
1 files changed, 364 insertions, 0 deletions
diff --git a/src/bin/perfdhcp/tests/basic_scen_unittest.cc b/src/bin/perfdhcp/tests/basic_scen_unittest.cc
new file mode 100644
index 0000000..5240293
--- /dev/null
+++ b/src/bin/perfdhcp/tests/basic_scen_unittest.cc
@@ -0,0 +1,364 @@
+// Copyright (C) 2012-2020 Internet Systems Consortium, Inc. ("ISC")
+//
+// This Source Code Form is subject to the terms of the Mozilla Public
+// License, v. 2.0. If a copy of the MPL was not distributed with this
+// file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+#include <config.h>
+
+#include "command_options_helper.h"
+#include "../basic_scen.h"
+
+#include <asiolink/io_address.h>
+#include <exceptions/exceptions.h>
+#include <dhcp/dhcp4.h>
+#include <dhcp/pkt4.h>
+#include <dhcp/iface_mgr.h>
+#include <dhcp/option6_iaaddr.h>
+#include <dhcp/option6_iaprefix.h>
+#include <boost/date_time/posix_time/posix_time.hpp>
+#include <boost/foreach.hpp>
+
+#include <algorithm>
+#include <cstddef>
+#include <stdint.h>
+#include <string>
+#include <fstream>
+#include <mutex>
+#include <gtest/gtest.h>
+
+using namespace std;
+using namespace boost::posix_time;
+using namespace isc;
+using namespace isc::dhcp;
+using namespace isc::perfdhcp;
+
+/// \brief FakeScenPerfSocket class that mocks PerfSocket.
+///
+/// It stubs send and receive operations and collects statistics.
+/// Beside that it simulates DHCP server responses for received
+/// packets.
+class FakeScenPerfSocket: public BasePerfSocket {
+public:
+ /// \brief Default constructor for FakeScenPerfSocket.
+ FakeScenPerfSocket(CommandOptions &opt) :
+ opt_(opt),
+ iface_(boost::make_shared<Iface>("fake", 0)),
+ sent_cnt_(0),
+ recv_cnt_(0),
+ start_dropping_after_cnt_(100000) {};
+
+ CommandOptions &opt_;
+
+ IfacePtr iface_; ///< Local fake interface.
+
+ int sent_cnt_; ///< Counter of sent packets.
+ int recv_cnt_; ///< Counter of received packets.
+
+ /// List of pairs <msg_type, trans_id> containing responses
+ /// planned to send to perfdhcp.
+ std::list<std::tuple<uint8_t, uint32_t>> planned_responses_;
+
+ /// Mutex to protect internal state.
+ std::mutex mutex_;
+
+ /// Limit for sent packets. After this limit not more packets
+ /// are sent. This simulate dropping responses.
+ int start_dropping_after_cnt_;
+
+ /// \brief Simulate receiving DHCPv4 packet.
+ virtual dhcp::Pkt4Ptr receive4(uint32_t /*timeout_sec*/, uint32_t /*timeout_usec*/) override {
+ std::lock_guard<std::mutex> lock(mutex_);
+ recv_cnt_++;
+
+ if (planned_responses_.empty() || sent_cnt_ >= start_dropping_after_cnt_) {
+ return Pkt4Ptr();
+ }
+ auto msg = planned_responses_.front();
+ planned_responses_.pop_front();
+ auto msg_type = std::get<0>(msg);
+ Pkt4Ptr pkt(new Pkt4(msg_type, std::get<1>(msg)));
+ OptionPtr opt_serverid = Option::factory(Option::V4,
+ DHO_DHCP_SERVER_IDENTIFIER,
+ OptionBuffer(4, 1));
+ pkt->setYiaddr(asiolink::IOAddress("127.0.0.1"));
+ pkt->addOption(opt_serverid);
+ pkt->updateTimestamp();
+ return (pkt);
+ };
+
+ /// \brief Simulate receiving DHCPv6 packet.
+ virtual dhcp::Pkt6Ptr receive6(uint32_t /*timeout_sec*/, uint32_t /*timeout_usec*/) override {
+ std::lock_guard<std::mutex> lock(mutex_);
+ recv_cnt_++;
+
+ if (planned_responses_.empty() || sent_cnt_ >= start_dropping_after_cnt_) {
+ return Pkt6Ptr();
+ }
+ auto msg = planned_responses_.front();
+ planned_responses_.pop_front();
+ auto msg_type = std::get<0>(msg);
+ Pkt6Ptr pkt(new Pkt6(msg_type, std::get<1>(msg)));
+ // Add IA_NA if requested by the client.
+ if (opt_.getLeaseType().includes(CommandOptions::LeaseType::ADDRESS)) {
+ OptionPtr opt_ia_na = Option::factory(Option::V6, D6O_IA_NA);
+ OptionPtr iaaddr(new Option6IAAddr(D6O_IAADDR,
+ isc::asiolink::IOAddress("fe80::abcd"), 300, 500));
+ opt_ia_na->addOption(iaaddr);
+ pkt->addOption(opt_ia_na);
+ }
+ // Add IA_PD if requested by the client.
+ if (opt_.getLeaseType().includes(CommandOptions::LeaseType::PREFIX)) {
+ OptionPtr opt_ia_pd = Option::factory(Option::V6, D6O_IA_PD);
+ OptionPtr iapref(new Option6IAPrefix(D6O_IAPREFIX,
+ isc::asiolink::IOAddress("fe80::"), 64, 300, 500));
+ opt_ia_pd->addOption(iapref);
+ pkt->addOption(opt_ia_pd);
+ }
+ OptionPtr opt_serverid(new Option(Option::V6, D6O_SERVERID));
+ std::vector<uint8_t> duid({0, 1, 2, 3, 4, 5, 6, 7, 8, 9});
+ OptionPtr opt_clientid(Option::factory(Option::V6, D6O_CLIENTID, duid));
+ pkt->addOption(opt_serverid);
+ pkt->addOption(opt_clientid);
+ pkt->updateTimestamp();
+ return (pkt);
+ };
+
+ /// \brief Simulate sending DHCPv4 packet.
+ virtual bool send(const dhcp::Pkt4Ptr& pkt) override {
+ std::lock_guard<std::mutex> lock(mutex_);
+ sent_cnt_++;
+ pkt->updateTimestamp();
+ if (sent_cnt_ >= start_dropping_after_cnt_) {
+ return true;
+ }
+ if (pkt->getType() == DHCPDISCOVER) {
+ planned_responses_.push_back(std::make_tuple(DHCPOFFER, pkt->getTransid()));
+ } else if (pkt->getType() == DHCPREQUEST) {
+ planned_responses_.push_back(std::make_tuple(DHCPACK, pkt->getTransid()));
+ } else {
+ assert(0);
+ }
+ return true;
+ };
+
+ /// \brief Simulate sending DHCPv6 packet.
+ virtual bool send(const dhcp::Pkt6Ptr& pkt) override {
+ std::lock_guard<std::mutex> lock(mutex_);
+ sent_cnt_++;
+ pkt->updateTimestamp();
+ if (sent_cnt_ >= start_dropping_after_cnt_) {
+ return true;
+ }
+ if (pkt->getType() == DHCPV6_SOLICIT) {
+ planned_responses_.push_back(std::make_tuple(DHCPV6_ADVERTISE, pkt->getTransid()));
+ } else if (pkt->getType() == DHCPV6_REQUEST) {
+ planned_responses_.push_back(std::make_tuple(DHCPV6_REPLY, pkt->getTransid()));
+ } else {
+ assert(0);
+ }
+ return true;
+ };
+
+ /// \brief Override getting interface.
+ virtual IfacePtr getIface() override { return iface_; }
+
+ void reset() {
+ std::lock_guard<std::mutex> lock(mutex_);
+ sent_cnt_ = 0;
+ recv_cnt_ = 0;
+ }
+};
+
+
+/// \brief NakedBasicScen class.
+///
+/// It exposes BasicScen internals for UT.
+class NakedBasicScen: public BasicScen {
+public:
+ using BasicScen::basic_rate_control_;
+ using BasicScen::renew_rate_control_;
+ using BasicScen::release_rate_control_;
+ using BasicScen::tc_;
+
+ FakeScenPerfSocket fake_sock_;
+
+ NakedBasicScen(CommandOptions &opt) : BasicScen(opt, fake_sock_), fake_sock_(opt) {};
+
+};
+
+
+/// \brief Test Fixture Class
+///
+/// This test fixture class is used to perform
+/// unit tests on perfdhcp BasicScenTest class.
+class BasicScenTest : public virtual ::testing::Test
+{
+public:
+ BasicScenTest() { }
+
+ /// \brief Parse command line string with CommandOptions.
+ ///
+ /// \param cmdline command line string to be parsed.
+ /// \throw isc::Unexpected if unexpected error occurred.
+ /// \throw isc::InvalidParameter if command line is invalid.
+ void processCmdLine(CommandOptions &opt, const std::string& cmdline) const {
+ CommandOptionsHelper::process(opt, cmdline);
+ }
+
+ /// \brief Get full path to a file in testdata directory.
+ ///
+ /// \param filename filename being appended to absolute
+ /// path to testdata directory
+ ///
+ /// \return full path to a file in testdata directory.
+ std::string getFullPath(const std::string& filename) const {
+ std::ostringstream stream;
+ stream << TEST_DATA_DIR << "/" << filename;
+ return (stream.str());
+ }
+};
+
+
+// This test verifies that the class members are reset to expected values.
+TEST_F(BasicScenTest, initial_settings) {
+ CommandOptions opt;
+ processCmdLine(opt, "perfdhcp -6 -l ethx -r 50 -f 30 -F 10 all");
+ NakedBasicScen bs(opt);
+
+ EXPECT_EQ(50, bs.basic_rate_control_.getRate());
+ EXPECT_EQ(30, bs.renew_rate_control_.getRate());
+ EXPECT_EQ(10, bs.release_rate_control_.getRate());
+}
+
+
+TEST_F(BasicScenTest, Packet4Exchange) {
+ CommandOptions opt;
+ processCmdLine(opt, "perfdhcp -l fake -r 100 -n 10 -g single 127.0.0.1");
+ NakedBasicScen bs(opt);
+ bs.run();
+ // The command line restricts the number of iterations to 10
+ // with -n 10 parameter.
+ EXPECT_GE(bs.fake_sock_.sent_cnt_, 20); // Discovery + Request
+ EXPECT_GE(bs.tc_.getStatsMgr().getSentPacketsNum(ExchangeType::DO), 10);
+ EXPECT_GE(bs.tc_.getStatsMgr().getRcvdPacketsNum(ExchangeType::DO), 10);
+ EXPECT_GE(bs.tc_.getStatsMgr().getSentPacketsNum(ExchangeType::RA), 10);
+ EXPECT_GE(bs.tc_.getStatsMgr().getRcvdPacketsNum(ExchangeType::RA), 10);
+}
+
+TEST_F(BasicScenTest, Address4Unique) {
+ // send more than 1 discover+request but with the same address
+ // counter of a unique addresses should be 1
+ CommandOptions opt;
+ processCmdLine(opt, "perfdhcp -u -l fake -r 10 -n 10 -g single 127.0.0.1");
+ NakedBasicScen bs(opt);
+ bs.run();
+ EXPECT_GE(bs.fake_sock_.sent_cnt_, 5); // Discovery + Request
+ EXPECT_GE(bs.tc_.getStatsMgr().getRcvdPacketsNum(ExchangeType::DO), 1);
+ EXPECT_GE(bs.tc_.getStatsMgr().getRcvdPacketsNum(ExchangeType::RA), 1);
+ EXPECT_GE(bs.tc_.getAllUniqueAddrReply().size(), 1);
+ EXPECT_GE(bs.tc_.getAllUniqueAddrAdvert().size(), 1);
+ EXPECT_EQ(bs.tc_.getStatsMgr().getNonUniqueAddrNum(ExchangeType::DO), 9);
+ EXPECT_EQ(bs.tc_.getStatsMgr().getNonUniqueAddrNum(ExchangeType::RA), 9);
+}
+
+TEST_F(BasicScenTest, Address6Unique) {
+ // send more than 1 solicit+request but with the same address
+ // counter of a unique addresses should be 1
+ CommandOptions opt;
+ processCmdLine(opt, "perfdhcp -6 -u -l fake -r 10 -n 10 -g single ::1");
+ NakedBasicScen bs(opt);
+ bs.run();
+ EXPECT_GE(bs.fake_sock_.sent_cnt_, 5); // Solicit + Request
+ EXPECT_GE(bs.tc_.getStatsMgr().getRcvdPacketsNum(ExchangeType::SA), 1);
+ EXPECT_GE(bs.tc_.getStatsMgr().getRcvdPacketsNum(ExchangeType::RR), 1);
+ EXPECT_GE(bs.tc_.getAllUniqueAddrReply().size(), 1);
+ EXPECT_GE(bs.tc_.getAllUniqueAddrAdvert().size(), 1);
+ EXPECT_EQ(bs.tc_.getStatsMgr().getNonUniqueAddrNum(ExchangeType::SA), 9);
+ EXPECT_EQ(bs.tc_.getStatsMgr().getNonUniqueAddrNum(ExchangeType::RR), 9);
+}
+
+TEST_F(BasicScenTest, Packet4ExchangeMaxDrop10Proc) {
+ CommandOptions opt;
+
+ // With the following command line we restrict the maximum
+ // number of dropped packets to 10% of all.
+ // Use templates for this test.
+ processCmdLine(opt, "perfdhcp -l fake -r 100 -R 20 -n 100"
+ " -D 10% -L 10547 -g single"
+ // \todo seems to be broken as it crashes building pkt
+ // " -T " + getFullPath("discover-example.hex")
+ // + " -T " + getFullPath("request4-example.hex")
+ " 127.0.0.1");
+ // The number iterations is restricted by the percentage of
+ // dropped packets (-D 10%).
+ NakedBasicScen bs(opt);
+ bs.fake_sock_.start_dropping_after_cnt_ = 10;
+ bs.run();
+ EXPECT_GE(bs.fake_sock_.sent_cnt_, 15); // Discovery + Request
+ EXPECT_LE(bs.fake_sock_.sent_cnt_, 20); // Discovery + Request
+
+ EXPECT_GE(bs.tc_.getStatsMgr().getSentPacketsNum(ExchangeType::DO), 1);
+ EXPECT_LE(bs.tc_.getStatsMgr().getSentPacketsNum(ExchangeType::DO), 15);
+
+ EXPECT_GE(bs.tc_.getStatsMgr().getRcvdPacketsNum(ExchangeType::DO), 1);
+ EXPECT_LE(bs.tc_.getStatsMgr().getRcvdPacketsNum(ExchangeType::DO), 15);
+
+ EXPECT_GE(bs.tc_.getStatsMgr().getSentPacketsNum(ExchangeType::RA), 1);
+ EXPECT_LE(bs.tc_.getStatsMgr().getSentPacketsNum(ExchangeType::RA), 15);
+
+ EXPECT_GE(bs.tc_.getStatsMgr().getRcvdPacketsNum(ExchangeType::RA), 1);
+ EXPECT_LE(bs.tc_.getStatsMgr().getRcvdPacketsNum(ExchangeType::RA), 15);
+}
+
+
+TEST_F(BasicScenTest, Packet6Exchange) {
+ // Set number of iterations to 10.
+ CommandOptions opt;
+ processCmdLine(opt, "perfdhcp -l fake -6 -r 100 -n 10 -g single -R 20 -L 10547 ::1");
+ // Set number of received packets equal to number of iterations.
+ // This simulates no packet drops.
+ NakedBasicScen bs(opt);
+ bs.run();
+ EXPECT_GE(bs.fake_sock_.sent_cnt_, 20); // Solicit + Request
+ EXPECT_GE(bs.tc_.getStatsMgr().getSentPacketsNum(ExchangeType::SA), 10);
+ EXPECT_GE(bs.tc_.getStatsMgr().getRcvdPacketsNum(ExchangeType::SA), 10);
+ EXPECT_GE(bs.tc_.getStatsMgr().getSentPacketsNum(ExchangeType::RR), 10);
+ EXPECT_GE(bs.tc_.getStatsMgr().getRcvdPacketsNum(ExchangeType::RR), 10);
+}
+
+TEST_F(BasicScenTest, Packet6ExchangeMaxDrop3Pkt) {
+ CommandOptions opt;
+ // The maximum number of dropped packets is 3 (because of -D 3).
+ processCmdLine(opt, "perfdhcp -l fake"
+ " -6 -r 100 -n 100 -R 20 -D 3 -L 10547"
+ // \todo seems to be broken as it crashes building pkt
+ // " -T " + getFullPath("solicit-example.hex")
+ // + " -T " + getFullPath("request6-example.hex")
+ " ::1");
+
+ // Simulate the number of Solicit-Advertise-Request-Reply (SARR) exchanges.
+ // The test function generates server's responses and passes it to the
+ // TestControl class methods for processing. The number of exchanges
+ // actually performed is controller by 'start_dropping_after_cnt_'.
+ // All exchanged packets carry the IA_NA option
+ // to simulate the IPv6 address acquisition and to verify that the
+ // IA_NA options returned by the server are processed correctly.
+ NakedBasicScen bs(opt);
+ bs.fake_sock_.start_dropping_after_cnt_ = 10;
+ bs.run();
+ EXPECT_GE(bs.fake_sock_.sent_cnt_, 10);
+ EXPECT_LE(bs.fake_sock_.sent_cnt_, 20);
+
+ EXPECT_GE(bs.tc_.getStatsMgr().getSentPacketsNum(ExchangeType::SA), 1);
+ EXPECT_LE(bs.tc_.getStatsMgr().getSentPacketsNum(ExchangeType::SA), 15);
+
+ EXPECT_GE(bs.tc_.getStatsMgr().getRcvdPacketsNum(ExchangeType::SA), 1);
+ EXPECT_LE(bs.tc_.getStatsMgr().getRcvdPacketsNum(ExchangeType::SA), 15);
+
+ EXPECT_GE(bs.tc_.getStatsMgr().getSentPacketsNum(ExchangeType::RR), 1);
+ EXPECT_LE(bs.tc_.getStatsMgr().getSentPacketsNum(ExchangeType::RR), 15);
+
+ EXPECT_GE(bs.tc_.getStatsMgr().getRcvdPacketsNum(ExchangeType::RR), 1);
+ EXPECT_LE(bs.tc_.getStatsMgr().getRcvdPacketsNum(ExchangeType::RR), 15);
+}