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