diff options
Diffstat (limited to 'src/bin/perfdhcp/avalanche_scen.cc')
-rw-r--r-- | src/bin/perfdhcp/avalanche_scen.cc | 202 |
1 files changed, 202 insertions, 0 deletions
diff --git a/src/bin/perfdhcp/avalanche_scen.cc b/src/bin/perfdhcp/avalanche_scen.cc new file mode 100644 index 0000000..a4b1878 --- /dev/null +++ b/src/bin/perfdhcp/avalanche_scen.cc @@ -0,0 +1,202 @@ +// Copyright (C) 2012-2021 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 <perfdhcp/avalanche_scen.h> + + +#include <boost/date_time/posix_time/posix_time.hpp> + +using namespace std; +using namespace boost::posix_time; +using namespace isc; +using namespace isc::dhcp; + + +namespace isc { +namespace perfdhcp { + +int +AvalancheScen::resendPackets(ExchangeType xchg_type) { + const StatsMgr& stats_mgr(tc_.getStatsMgr()); + + // get list of sent packets that potentially need to be resent + auto sent_packets_its = stats_mgr.getSentPackets(xchg_type); + auto begin_it = std::get<0>(sent_packets_its); + auto end_it = std::get<1>(sent_packets_its); + + auto& retrans = retransmissions_[xchg_type]; + auto& start_times = start_times_[xchg_type]; + + int still_left_cnt = 0; + int current_cycle_resent_cnt = 0; + for (auto it = begin_it; it != end_it; ++it) { + still_left_cnt++; + + dhcp::PktPtr pkt = *it; + auto trans_id = pkt->getTransid(); + + // get some things from previous retransmissions + auto start_time = pkt->getTimestamp(); + int current_pkt_resent_cnt = 0; + auto r_it = retrans.find(trans_id); + if (r_it != retrans.end()) { + start_time = (*start_times.find(trans_id)).second; + current_pkt_resent_cnt = (*r_it).second; + } else { + start_times[trans_id] = start_time; + } + + // estimate back off time for resending this packet + int delay = (1 << current_pkt_resent_cnt); // in seconds + if (delay > 64) { + delay = 64; + } + delay *= 1000; // to miliseconds + delay += random() % 2000 - 1000; // adjust by random from -1000..1000 range + + // if back-off time passed then resend + auto now = microsec_clock::universal_time(); + if (now - start_time > milliseconds(delay)) { + current_cycle_resent_cnt++; + total_resent_++; + + // do resend packet + if (options_.getIpVersion() == 4) { + Pkt4Ptr pkt4 = boost::dynamic_pointer_cast<Pkt4>(pkt); + socket_.send(pkt4); + } else { + Pkt6Ptr pkt6 = boost::dynamic_pointer_cast<Pkt6>(pkt); + socket_.send(pkt6); + } + + // restore sending time of original packet + pkt->setTimestamp(start_time); + + current_pkt_resent_cnt++; + retrans[trans_id] = current_pkt_resent_cnt; + } + } + if (current_cycle_resent_cnt > 0) { + auto now = microsec_clock::universal_time(); + std::cout << now << " " << xchg_type << ": still waiting for " + << still_left_cnt << " answers, resent " << current_cycle_resent_cnt + << ", retrying " << retrans.size() << std::endl; + } + return still_left_cnt; +} + + + +int +AvalancheScen::run() { + // First indicated number of DISCOVER packets eg. 4000 are sent. + // Then in a loop responses to received packets (this is + // consumeReceivedPackets()) are sent and then for every 200ms it is checked + // if responses to sent packets were received. If not packets are resent. + // This happens in resendPackets() method. For each packet it is checked + // how many times it was already resent and then back off time is calculated: + // 1, 2, 4, 8, 16, 64 (max) seconds. If estimated time has elapsed + // from previous sending then the packet is resent. Some stats are collected + // and printed during runtime. The whole procedure is stopped when + // all packets got responses. + + uint32_t clients_num = options_.getClientsNum() == 0 ? + 1 : options_.getClientsNum(); + + StatsMgr& stats_mgr(tc_.getStatsMgr()); + + tc_.start(); + + auto start = microsec_clock::universal_time(); + + // Initiate new DHCP packet exchanges. + tc_.sendPackets(clients_num); + + auto now = microsec_clock::universal_time(); + auto prev_cycle_time = now; + for (;;) { + // Pull some packets from receiver thread, process them, update some stats + // and respond to the server if needed. + tc_.consumeReceivedPackets(); + + usleep(100); + + now = microsec_clock::universal_time(); + // Wait for 200ms between subsequent check for resending. + // This time taken based on experiments. For times 10-30ms whole scenario + // time significantly grows. The same for times >200ms. The optimal times + // are between 50-200ms. \todo more research is needed. + if (now - prev_cycle_time > milliseconds(200)) { // check if 0.2s elapsed + prev_cycle_time = now; + int still_left_cnt = 0; + still_left_cnt += resendPackets(stage1_xchg_); + if (options_.getExchangeMode() == CommandOptions::DORA_SARR) { + still_left_cnt += resendPackets(stage2_xchg_); + } + + if (still_left_cnt == 0) { + break; + } + } + + if (tc_.interrupted()) { + break; + } + } + + auto stop = microsec_clock::universal_time(); + boost::posix_time::time_period duration(start, stop); + + tc_.stop(); + + tc_.printStats(); + + // Print packet timestamps + if (options_.testDiags('t')) { + stats_mgr.printTimestamps(); + } + + // Print server id. + if (options_.testDiags('s') && tc_.serverIdReceived()) { + std::cout << "Server id: " << tc_.getServerId() << std::endl; + } + + // Diagnostics flag 'e' means show exit reason. + if (options_.testDiags('e')) { + std::cout << "Interrupted" << std::endl; + } + + // Print any received leases. + if (options_.testDiags('l')) { + stats_mgr.printLeases(); + } + + // Calculate total stats. + int total_sent_pkts = total_resent_; // This holds sent + resent packets counts. + int total_rcvd_pkts = 0; // This holds received packets count. + // Get sent and received counts for DO/SA (stage1) exchange from StatsMgr. + total_sent_pkts += tc_.getStatsMgr().getSentPacketsNum(stage1_xchg_); + total_rcvd_pkts += tc_.getStatsMgr().getRcvdPacketsNum(stage1_xchg_); + // Get sent and received counts for RA/RR (stage2) exchange from StatsMgr + // if RA/RR was not disabled. + if (options_.getExchangeMode() == CommandOptions::DORA_SARR) { + total_sent_pkts += tc_.getStatsMgr().getSentPacketsNum(stage2_xchg_); + total_rcvd_pkts += tc_.getStatsMgr().getRcvdPacketsNum(stage2_xchg_); + } + + std::cout << "It took " << duration.length() << " to provision " << clients_num + << " clients. " << std::endl + << "Requests sent + resent: " << total_sent_pkts << std::endl + << "Requests resent: " << total_resent_ << std::endl + << "Responses received: " << total_rcvd_pkts << std::endl; + + return (0); +} + +} // namespace perfdhcp +} // namespace isc |