diff options
Diffstat (limited to 'src/bin/perfdhcp/tests/avalanche_scen_unittest.cc')
-rw-r--r-- | src/bin/perfdhcp/tests/avalanche_scen_unittest.cc | 322 |
1 files changed, 322 insertions, 0 deletions
diff --git a/src/bin/perfdhcp/tests/avalanche_scen_unittest.cc b/src/bin/perfdhcp/tests/avalanche_scen_unittest.cc new file mode 100644 index 0000000..3631848 --- /dev/null +++ b/src/bin/perfdhcp/tests/avalanche_scen_unittest.cc @@ -0,0 +1,322 @@ +// Copyright (C) 2012-2024 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 "../avalanche_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 <algorithm> +#include <cstddef> +#include <stdint.h> +#include <string> +#include <fstream> +#include <gtest/gtest.h> + +using namespace std; +using namespace boost::posix_time; +using namespace isc; +using namespace isc::dhcp; +using namespace isc::perfdhcp; + +/// \brief FakeAvalancheScenPerfSocket class that mocks PerfSocket. +/// +/// It stubs send and receive operations and collects statistics. +/// Beside that it simulates DHCP server responses for received +/// packets. +class FakeAvalancheScenPerfSocket: public BasePerfSocket { +public: + /// \brief Default constructor for FakeAvalancheScenPerfSocket. + FakeAvalancheScenPerfSocket(CommandOptions &opt) : + opt_(opt), + iface_(boost::make_shared<Iface>("fake", 0)), + sent_cnt_(0), + recv_cnt_(0), + initial_drops_cnt_(0) {}; + + CommandOptions &opt_; + + IfacePtr iface_; ///< Local fake interface. + + int sent_cnt_; ///< Counter of sent packets. + int recv_cnt_; ///< Counter of received packets. + int initial_drops_cnt_; + + /// List of pairs <msg_type, trans_id> containing responses + /// planned to send to perfdhcp. + std::list<std::tuple<uint8_t, uint32_t>> planned_responses_; + + /// \brief Simulate receiving DHCPv4 packet. + virtual dhcp::Pkt4Ptr receive4(uint32_t timeout_sec, uint32_t timeout_usec) override { + (void)timeout_sec; // silence compile 'unused parameter' warning; + (void)timeout_usec; // silence compile 'unused parameter' warning; + recv_cnt_++; + + if (planned_responses_.empty()) { + return Pkt4Ptr(); + } + + // simulate initial drops + if (initial_drops_cnt_ > 0) { + planned_responses_.pop_front(); + initial_drops_cnt_--; + return(Pkt4Ptr()); + } + + // prepare received packet + 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 { + (void)timeout_sec; // silence compile 'unused parameter' warning; + (void)timeout_usec; // silence compile 'unused parameter' warning; + recv_cnt_++; + + if (planned_responses_.empty()) { + return Pkt6Ptr(); + } + + // simulate initial drops + if (initial_drops_cnt_ > 0) { + planned_responses_.pop_front(); + initial_drops_cnt_--; + return(Pkt6Ptr()); + } + + // prepare received packet + 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 { + sent_cnt_++; + pkt->updateTimestamp(); + 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 { + sent_cnt_++; + pkt->updateTimestamp(); + 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() { + sent_cnt_ = 0; + recv_cnt_ = 0; + } +}; + + +/// \brief NakedAvalancheScen class. +/// +/// It exposes AvalancheScen internals for UT. +class NakedAvalancheScen: public AvalancheScen { +public: + using AvalancheScen::tc_; + using AvalancheScen::total_resent_; + + FakeAvalancheScenPerfSocket fake_sock_; + + NakedAvalancheScen(CommandOptions &opt) : AvalancheScen(opt, fake_sock_), fake_sock_(opt) {}; + +}; + + +/// \brief Test Fixture Class +/// +/// This test fixture class is used to perform +/// unit tests on perfdhcp AvalancheScenTest class. +class AvalancheScenTest : public virtual ::testing::Test +{ +public: + AvalancheScenTest() { } + + /// \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()); + } +}; + + +TEST_F(AvalancheScenTest, Packet4Exchange) { + CommandOptions opt; + processCmdLine(opt, "perfdhcp -l fake -4 -R 10 --scenario avalanche -g single 127.0.0.1"); + NakedAvalancheScen as(opt); + + as.run(); + + // Check if basic exchange of packets happened. No retransmissions expected. + EXPECT_EQ(as.total_resent_, 0); + EXPECT_EQ(as.fake_sock_.sent_cnt_, 20); // Discovery + Request + EXPECT_EQ(as.tc_.getStatsMgr().getSentPacketsNum(ExchangeType::DO), 10); + EXPECT_EQ(as.tc_.getStatsMgr().getRcvdPacketsNum(ExchangeType::DO), 10); + EXPECT_EQ(as.tc_.getStatsMgr().getSentPacketsNum(ExchangeType::RA), 10); + EXPECT_EQ(as.tc_.getStatsMgr().getRcvdPacketsNum(ExchangeType::RA), 10); +} + + +TEST_F(AvalancheScenTest, Packet4ExchangeOnlyDO) { + CommandOptions opt; + processCmdLine(opt, "perfdhcp -l fake -4 -R 10 -i --scenario avalanche -g single 127.0.0.1"); + NakedAvalancheScen as(opt); + + as.run(); + + // Check if DO exchange of packets happened only. No retransmissions expected. + EXPECT_EQ(as.total_resent_, 0); + EXPECT_EQ(as.fake_sock_.sent_cnt_, 10); // Discovery + Request + EXPECT_EQ(as.tc_.getStatsMgr().getSentPacketsNum(ExchangeType::DO), 10); + EXPECT_EQ(as.tc_.getStatsMgr().getRcvdPacketsNum(ExchangeType::DO), 10); + EXPECT_THROW(as.tc_.getStatsMgr().getSentPacketsNum(ExchangeType::RA), isc::BadValue); +} + + +TEST_F(AvalancheScenTest, Packet4ExchangeWithRetransmissions) { + CommandOptions opt; + processCmdLine(opt, "perfdhcp -l fake -4 -R 10 --scenario avalanche -g single 127.0.0.1"); + NakedAvalancheScen as(opt); + + as.fake_sock_.initial_drops_cnt_ = 2; + as.run(); + + // Check if basic exchange of packets happened. No retransmissions expected. + EXPECT_EQ(as.total_resent_, 2); + EXPECT_EQ(as.fake_sock_.sent_cnt_, 22); // Discovery + Request + EXPECT_EQ(as.tc_.getStatsMgr().getSentPacketsNum(ExchangeType::DO), 10); + EXPECT_EQ(as.tc_.getStatsMgr().getRcvdPacketsNum(ExchangeType::DO), 10); + EXPECT_EQ(as.tc_.getStatsMgr().getSentPacketsNum(ExchangeType::RA), 10); + EXPECT_EQ(as.tc_.getStatsMgr().getRcvdPacketsNum(ExchangeType::RA), 10); +} + + +TEST_F(AvalancheScenTest, Packet6Exchange) { + CommandOptions opt; + processCmdLine(opt, "perfdhcp -l fake -6 -R 10 --scenario avalanche -g single ::1"); + NakedAvalancheScen as(opt); + + as.run(); + + // Check if basic exchange of packets happened. No retransmissions expected. + EXPECT_EQ(as.total_resent_, 0); + EXPECT_GE(as.fake_sock_.sent_cnt_, 20); // Solicit + Request + EXPECT_GE(as.tc_.getStatsMgr().getSentPacketsNum(ExchangeType::SA), 10); + EXPECT_GE(as.tc_.getStatsMgr().getRcvdPacketsNum(ExchangeType::SA), 10); + EXPECT_GE(as.tc_.getStatsMgr().getSentPacketsNum(ExchangeType::RR), 10); + EXPECT_GE(as.tc_.getStatsMgr().getRcvdPacketsNum(ExchangeType::RR), 10); +} + + +TEST_F(AvalancheScenTest, Packet6ExchangeOnlySA) { + CommandOptions opt; + processCmdLine(opt, "perfdhcp -l fake -6 -R 10 -i --scenario avalanche -g single ::1"); + NakedAvalancheScen as(opt); + + as.run(); + + // Check if SA exchange of packets happened only. No retransmissions expected. + EXPECT_EQ(as.total_resent_, 0); + EXPECT_GE(as.fake_sock_.sent_cnt_, 10); // Solicit + Request + EXPECT_GE(as.tc_.getStatsMgr().getSentPacketsNum(ExchangeType::SA), 10); + EXPECT_GE(as.tc_.getStatsMgr().getRcvdPacketsNum(ExchangeType::SA), 10); + EXPECT_THROW(as.tc_.getStatsMgr().getSentPacketsNum(ExchangeType::RR), isc::BadValue); +} + + +TEST_F(AvalancheScenTest, Packet6ExchangeWithRetransmissions) { + CommandOptions opt; + processCmdLine(opt, "perfdhcp -l fake -6 -R 10 --scenario avalanche -g single ::1"); + NakedAvalancheScen as(opt); + + as.fake_sock_.initial_drops_cnt_ = 2; + as.run(); + + // Check if basic exchange of packets happened. No retransmissions expected. + EXPECT_EQ(as.total_resent_, 2); + EXPECT_EQ(as.fake_sock_.sent_cnt_, 22); // Discovery + Request + EXPECT_EQ(as.tc_.getStatsMgr().getSentPacketsNum(ExchangeType::SA), 10); + EXPECT_EQ(as.tc_.getStatsMgr().getRcvdPacketsNum(ExchangeType::SA), 10); + EXPECT_EQ(as.tc_.getStatsMgr().getSentPacketsNum(ExchangeType::RR), 10); + EXPECT_EQ(as.tc_.getStatsMgr().getRcvdPacketsNum(ExchangeType::RR), 10); +} |