Kea  2.3.3-git
avalanche_scen.cc
Go to the documentation of this file.
1 // Copyright (C) 2012-2021 Internet Systems Consortium, Inc. ("ISC")
2 //
3 // This Source Code Form is subject to the terms of the Mozilla Public
4 // License, v. 2.0. If a copy of the MPL was not distributed with this
5 // file, You can obtain one at http://mozilla.org/MPL/2.0/.
6 
7 #include <config.h>
8 
10 
11 
12 #include <boost/date_time/posix_time/posix_time.hpp>
13 
14 using namespace std;
15 using namespace boost::posix_time;
16 using namespace isc;
17 using namespace isc::dhcp;
18 
19 
20 namespace isc {
21 namespace perfdhcp {
22 
23 int
24 AvalancheScen::resendPackets(ExchangeType xchg_type) {
25  const StatsMgr& stats_mgr(tc_.getStatsMgr());
26 
27  // get list of sent packets that potentially need to be resent
28  auto sent_packets_its = stats_mgr.getSentPackets(xchg_type);
29  auto begin_it = std::get<0>(sent_packets_its);
30  auto end_it = std::get<1>(sent_packets_its);
31 
32  auto& retrans = retransmissions_[xchg_type];
33  auto& start_times = start_times_[xchg_type];
34 
35  int still_left_cnt = 0;
36  int current_cycle_resent_cnt = 0;
37  for (auto it = begin_it; it != end_it; ++it) {
38  still_left_cnt++;
39 
40  dhcp::PktPtr pkt = *it;
41  auto trans_id = pkt->getTransid();
42 
43  // get some things from previous retransmissions
44  auto start_time = pkt->getTimestamp();
45  int current_pkt_resent_cnt = 0;
46  auto r_it = retrans.find(trans_id);
47  if (r_it != retrans.end()) {
48  start_time = (*start_times.find(trans_id)).second;
49  current_pkt_resent_cnt = (*r_it).second;
50  } else {
51  start_times[trans_id] = start_time;
52  }
53 
54  // estimate back off time for resending this packet
55  int delay = (1 << current_pkt_resent_cnt); // in seconds
56  if (delay > 64) {
57  delay = 64;
58  }
59  delay *= 1000; // to miliseconds
60  delay += random() % 2000 - 1000; // adjust by random from -1000..1000 range
61 
62  // if back-off time passed then resend
63  auto now = microsec_clock::universal_time();
64  if (now - start_time > milliseconds(delay)) {
65  current_cycle_resent_cnt++;
66  total_resent_++;
67 
68  // do resend packet
69  if (options_.getIpVersion() == 4) {
70  Pkt4Ptr pkt4 = boost::dynamic_pointer_cast<Pkt4>(pkt);
71  socket_.send(pkt4);
72  } else {
73  Pkt6Ptr pkt6 = boost::dynamic_pointer_cast<Pkt6>(pkt);
74  socket_.send(pkt6);
75  }
76 
77  // restore sending time of original packet
78  pkt->setTimestamp(start_time);
79 
80  current_pkt_resent_cnt++;
81  retrans[trans_id] = current_pkt_resent_cnt;
82  }
83  }
84  if (current_cycle_resent_cnt > 0) {
85  auto now = microsec_clock::universal_time();
86  std::cout << now << " " << xchg_type << ": still waiting for "
87  << still_left_cnt << " answers, resent " << current_cycle_resent_cnt
88  << ", retrying " << retrans.size() << std::endl;
89  }
90  return still_left_cnt;
91 }
92 
93 
94 
95 int
96 AvalancheScen::run() {
97  // First indicated number of DISCOVER packets eg. 4000 are sent.
98  // Then in a loop responses to received packets (this is
99  // consumeReceivedPackets()) are sent and then for every 200ms it is checked
100  // if responses to sent packets were received. If not packets are resent.
101  // This happens in resendPackets() method. For each packet it is checked
102  // how many times it was already resent and then back off time is calculated:
103  // 1, 2, 4, 8, 16, 64 (max) seconds. If estimated time has elapsed
104  // from previous sending then the packet is resent. Some stats are collected
105  // and printed during runtime. The whole procedure is stopped when
106  // all packets got responses.
107 
108  uint32_t clients_num = options_.getClientsNum() == 0 ?
109  1 : options_.getClientsNum();
110 
111  StatsMgr& stats_mgr(tc_.getStatsMgr());
112 
113  tc_.start();
114 
115  auto start = microsec_clock::universal_time();
116 
117  // Initiate new DHCP packet exchanges.
118  tc_.sendPackets(clients_num);
119 
120  auto now = microsec_clock::universal_time();
121  auto prev_cycle_time = now;
122  for (;;) {
123  // Pull some packets from receiver thread, process them, update some stats
124  // and respond to the server if needed.
125  tc_.consumeReceivedPackets();
126 
127  usleep(100);
128 
129  now = microsec_clock::universal_time();
130  // Wait for 200ms between subsequent check for resending.
131  // This time taken based on experiments. For times 10-30ms whole scenario
132  // time significantly grows. The same for times >200ms. The optimal times
133  // are between 50-200ms. \todo more research is needed.
134  if (now - prev_cycle_time > milliseconds(200)) { // check if 0.2s elapsed
135  prev_cycle_time = now;
136  int still_left_cnt = 0;
137  still_left_cnt += resendPackets(stage1_xchg_);
138  if (options_.getExchangeMode() == CommandOptions::DORA_SARR) {
139  still_left_cnt += resendPackets(stage2_xchg_);
140  }
141 
142  if (still_left_cnt == 0) {
143  break;
144  }
145  }
146 
147  if (tc_.interrupted()) {
148  break;
149  }
150  }
151 
152  auto stop = microsec_clock::universal_time();
153  boost::posix_time::time_period duration(start, stop);
154 
155  tc_.stop();
156 
157  tc_.printStats();
158 
159  // Print packet timestamps
160  if (options_.testDiags('t')) {
161  stats_mgr.printTimestamps();
162  }
163 
164  // Print server id.
165  if (options_.testDiags('s') && tc_.serverIdReceived()) {
166  std::cout << "Server id: " << tc_.getServerId() << std::endl;
167  }
168 
169  // Diagnostics flag 'e' means show exit reason.
170  if (options_.testDiags('e')) {
171  std::cout << "Interrupted" << std::endl;
172  }
173 
174  // Print any received leases.
175  if (options_.testDiags('l')) {
176  stats_mgr.printLeases();
177  }
178 
179  // Calculate total stats.
180  int total_sent_pkts = total_resent_; // This holds sent + resent packets counts.
181  int total_rcvd_pkts = 0; // This holds received packets count.
182  // Get sent and received counts for DO/SA (stage1) exchange from StatsMgr.
183  total_sent_pkts += tc_.getStatsMgr().getSentPacketsNum(stage1_xchg_);
184  total_rcvd_pkts += tc_.getStatsMgr().getRcvdPacketsNum(stage1_xchg_);
185  // Get sent and received counts for RA/RR (stage2) exchange from StatsMgr
186  // if RA/RR was not disabled.
187  if (options_.getExchangeMode() == CommandOptions::DORA_SARR) {
188  total_sent_pkts += tc_.getStatsMgr().getSentPacketsNum(stage2_xchg_);
189  total_rcvd_pkts += tc_.getStatsMgr().getRcvdPacketsNum(stage2_xchg_);
190  }
191 
192  std::cout << "It took " << duration.length() << " to provision " << clients_num
193  << " clients. " << std::endl
194  << "Requests sent + resent: " << total_sent_pkts << std::endl
195  << "Requests resent: " << total_resent_ << std::endl
196  << "Responses received: " << total_rcvd_pkts << std::endl;
197 
198  return (0);
199 }
200 
201 } // namespace perfdhcp
202 } // namespace isc
ExchangeType
DHCP packet exchange types.
Represents a DHCPv6 packet.
Definition: pkt6.h:44
STL namespace.
boost::shared_ptr< Pkt6 > Pkt6Ptr
A pointer to Pkt6 packet.
Definition: pkt6.h:28
boost::shared_ptr< Pkt4 > Pkt4Ptr
A pointer to Pkt4 object.
Definition: pkt4.h:544
boost::shared_ptr< isc::dhcp::Pkt > PktPtr
A pointer to either Pkt4 or Pkt6 packet.
Definition: pkt.h:831
void setTimestamp(boost::posix_time::ptime &timestamp)
Set packet timestamp.
Definition: pkt.h:425
Defines the logger used by the top-level component of kea-lfc.
Represents DHCPv4 packet.
Definition: pkt4.h:37
std::tuple< typename ExchangeStats::PktListIterator, typename ExchangeStats::PktListIterator > getSentPackets(const ExchangeType xchg_type) const