Kea: test_control.cc Source File

| Kea 1.5.0

test_control.cc
Go to the documentation of this file.
1 // Copyright (C) 2012-2018 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
9 #include <exceptions/exceptions.h>
10 #include <asiolink/io_address.h>
11 #include <dhcp/libdhcp++.h>
12 #include <dhcp/iface_mgr.h>
13 #include <dhcp/dhcp4.h>
14 #include <dhcp/option6_ia.h>
15 #include <util/unittests/check_valgrind.h>
16 #include "test_control.h"
17 #include "command_options.h"
18 #include "perf_pkt4.h"
19 #include "perf_pkt6.h"
20
21 #include <boost/date_time/posix_time/posix_time.hpp>
22 #include <boost/foreach.hpp>
23
24 #include <algorithm>
25 #include <fstream>
26 #include <stdio.h>
27 #include <stdlib.h>
28 #include <stdint.h>
29 #include <unistd.h>
30 #include <signal.h>
31 #include <sstream>
32 #include <sys/wait.h>
33
34 using namespace std;
35 using namespace boost::posix_time;
36 using namespace isc;
37 using namespace isc::dhcp;
38 using namespace isc::asiolink;
39
40 namespace isc {
41 namespace perfdhcp {
42
43 bool TestControl::interrupted_ = false;
44
45 ptime late_exit_target_time_ = ptime(not_a_date_time);
46
47 bool
48 TestControl::hasLateExitCommenced() const {
49 return !late_exit_target_time_.is_not_a_date_time();
50 }
51
52 bool
53 TestControl::waitToExit() const {
54 static ptime exit_time = ptime(not_a_date_time);
55 CommandOptions& options = CommandOptions::instance();
56 uint32_t wait_time = options.getExitWaitTime();
57
58 // If we care and not all packets are in yet
59 if (wait_time && !haveAllPacketsBeenReceived()) {
60 const ptime now = microsec_clock::universal_time();
61
62 // Init the end time if it hasn't started yet
63 if (exit_time.is_not_a_date_time()) {
64 exit_time = now + time_duration(microseconds(wait_time));
65 }
66
67 // If we're not at end time yet, return true
68 return (now < exit_time);
69 }
70
71 // No need to wait, return false;
72 return (false);
73 }
74
75 bool
76 TestControl::haveAllPacketsBeenReceived() const {
77 const CommandOptions& options = CommandOptions::instance();
78 const uint8_t& ipversion = options.getIpVersion();
79 const std::vector<int>& num_request = options.getNumRequests();
80 const size_t& num_request_size = num_request.size();
81
82 if (num_request_size == 0) {
83 return false;
84 }
85
86 uint32_t responses = 0;
87 uint32_t requests = num_request[0];
88 if (num_request_size >= 2) {
89 requests += num_request[1];
90 }
91
92 if (ipversion == 4) {
93 responses = stats_mgr4_->getRcvdPacketsNum(StatsMgr4::XCHG_DO) +
94 stats_mgr4_->getRcvdPacketsNum(StatsMgr4::XCHG_RA);
95 } else {
96 responses = stats_mgr6_->getRcvdPacketsNum(StatsMgr6::XCHG_SA) +
97 stats_mgr6_->getRcvdPacketsNum(StatsMgr6::XCHG_RR);
98 }
99
100 return (responses == requests);
101 }
102
103 TestControl::TestControlSocket::TestControlSocket(const int socket) :
104 SocketInfo(asiolink::IOAddress("127.0.0.1"), 0, socket),
105 ifindex_(0), valid_(true) {
106 try {
107 initSocketData();
108 } catch (const Exception&) {
109 valid_ = false;
110 }
111 }
112
113 TestControl::TestControlSocket::~TestControlSocket() {
114 IfacePtr iface = IfaceMgr::instance().getIface(ifindex_);
115 if (iface) {
116 iface->delSocket(sockfd_);
117 }
118 }
119
120 void
121 TestControl::TestControlSocket::initSocketData() {
122 BOOST_FOREACH(IfacePtr iface, IfaceMgr::instance().getIfaces()) {
123 BOOST_FOREACH(SocketInfo s, iface->getSockets()) {
124 if (s.sockfd_ == sockfd_) {
125 ifindex_ = iface->getIndex();
126 addr_ = s.addr_;
127 return;
128 }
129 }
130 }
131 isc_throw(BadValue, "interface for for specified socket "
132 "descriptor not found");
133 }
134
135 TestControl&
136 TestControl::instance() {
137 static TestControl test_control;
138 return (test_control);
139 }
140
141 TestControl::TestControl()
142 : number_generator_(0, CommandOptions::instance().getMacsFromFile().size()) {
143 reset();
144 }
145
146 void
147 TestControl::checkLateMessages(RateControl& rate_control) {
148 // If diagnostics is disabled, there is no need to log late sent messages.
149 // If it is enabled and the rate control object indicates that the last
150 // sent message was late, bump up the counter in Stats Manager.
151 if (rate_control.isLateSent() && testDiags('i')) {
152 CommandOptions& options = CommandOptions::instance();
153 if (options.getIpVersion() == 4) {
154 stats_mgr4_->incrementCounter("latesend");
155 } else if (options.getIpVersion() == 6) {
156 stats_mgr6_->incrementCounter("latesend");
157 }
158 }
159 }
160
161 void
162 TestControl::cleanCachedPackets() {
163 CommandOptions& options = CommandOptions::instance();
164 // When Renews are not sent, Reply packets are not cached so there
165 // is nothing to do.
166 if (options.getRenewRate() == 0) {
167 return;
168 }
169
170 static boost::posix_time::ptime last_clean =
171 microsec_clock::universal_time();
172
173 // Check how much time has passed since last cleanup.
174 time_period time_since_clean(last_clean,
175 microsec_clock::universal_time());
176 // Cleanup every 1 second.
177 if (time_since_clean.length().total_seconds() >= 1) {
178 // Calculate how many cached packets to remove. Actually we could
179 // just leave enough packets to handle Renews for 1 second but
180 // since we want to randomize leases to be renewed so leave 5
181 // times more packets to randomize from.
182 // @todo The cache size might be controlled from the command line.
183 if (reply_storage_.size() > 5 * options.getRenewRate()) {
184 reply_storage_.clear(reply_storage_.size() -
185 5 * options.getRenewRate());
186 }
187 // Remember when we performed a cleanup for the last time.
188 // We want to do the next cleanup not earlier than in one second.
189 last_clean = microsec_clock::universal_time();
190 }
191 }
192
193 void
194 TestControl::copyIaOptions(const Pkt6Ptr& pkt_from, Pkt6Ptr& pkt_to) {
195 if (!pkt_from || !pkt_to) {
196 isc_throw(BadValue, "NULL pointers must not be specified as arguments"
197 " for the copyIaOptions function");
198 }
199 // IA_NA
200 if (CommandOptions::instance().getLeaseType()
201 .includes(CommandOptions::LeaseType::ADDRESS)) {
202 OptionPtr option = pkt_from->getOption(D6O_IA_NA);
203 if (!option) {
204 isc_throw(OptionNotFound, "IA_NA option not found in the"
205 " server's response");
206 }
207 pkt_to->addOption(option);
208 }
209 // IA_PD
210 if (CommandOptions::instance().getLeaseType()
211 .includes(CommandOptions::LeaseType::PREFIX)) {
212 OptionPtr option = pkt_from->getOption(D6O_IA_PD);
213 if (!option) {
214 isc_throw(OptionNotFound, "IA_PD option not found in the"
215 " server's response");
216 }
217 pkt_to->addOption(option);
218 }
219
220
221 }
222
223 std::string
224 TestControl::byte2Hex(const uint8_t b) const {
225 const int b1 = b / 16;
226 const int b0 = b % 16;
227 ostringstream stream;
228 stream << std::hex << b1 << b0 << std::dec;
229 return (stream.str());
230 }
231
232 bool
233 TestControl::checkExitConditions() const {
234 if (interrupted_) {
235 return (true);
236 }
237 CommandOptions& options = CommandOptions::instance();
238 bool test_period_reached = false;
239 // Check if test period passed.
240 if (options.getPeriod() != 0) {
241 if (options.getIpVersion() == 4) {
242 time_period period(stats_mgr4_->getTestPeriod());
243 if (period.length().total_seconds() >= options.getPeriod()) {
244 test_period_reached = true;
245 }
246 } else if (options.getIpVersion() == 6) {
247 time_period period = stats_mgr6_->getTestPeriod();
248 if (period.length().total_seconds() >= options.getPeriod()) {
249 test_period_reached = true;
250 }
251 }
252 }
253 if (test_period_reached) {
254 if (testDiags('e')) {
255 std::cout << "reached test-period." << std::endl;
256 }
257 if (!waitToExit()) {
258 return true;
259 }
260 }
261
262 bool max_requests = false;
263 // Check if we reached maximum number of DISCOVER/SOLICIT sent.
264 if (options.getNumRequests().size() > 0) {
265 if (options.getIpVersion() == 4) {
266 if (getSentPacketsNum(StatsMgr4::XCHG_DO) >=
267 options.getNumRequests()[0]) {
268 max_requests = true;
269 }
270 } else if (options.getIpVersion() == 6) {
271 if (stats_mgr6_->getSentPacketsNum(StatsMgr6::XCHG_SA) >=
272 options.getNumRequests()[0]) {
273 max_requests = true;
274 }
275 }
276 }
277 // Check if we reached maximum number REQUEST packets.
278 if (options.getNumRequests().size() > 1) {
279 if (options.getIpVersion() == 4) {
280 if (stats_mgr4_->getSentPacketsNum(StatsMgr4::XCHG_RA) >=
281 options.getNumRequests()[1]) {
282 max_requests = true;
283 }
284 } else if (options.getIpVersion() == 6) {
285 if (stats_mgr6_->getSentPacketsNum(StatsMgr6::XCHG_RR) >=
286 options.getNumRequests()[1]) {
287 max_requests = true;
288 }
289 }
290 }
291 if (max_requests) {
292 if (testDiags('e')) {
293 std::cout << "Reached max requests limit." << std::endl;
294 }
295 if (!waitToExit()) {
296 return true;
297 }
298 }
299
300 // Check if we reached maximum number of drops of OFFER/ADVERTISE packets.
301 bool max_drops = false;
302 if (options.getMaxDrop().size() > 0) {
303 if (options.getIpVersion() == 4) {
304 if (stats_mgr4_->getDroppedPacketsNum(StatsMgr4::XCHG_DO) >=
305 options.getMaxDrop()[0]) {
306 max_drops = true;
307 }
308 } else if (options.getIpVersion() == 6) {
309 if (stats_mgr6_->getDroppedPacketsNum(StatsMgr6::XCHG_SA) >=
310 options.getMaxDrop()[0]) {
311 max_drops = true;
312 }
313 }
314 }
315 // Check if we reached maximum number of drops of ACK/REPLY packets.
316 if (options.getMaxDrop().size() > 1) {
317 if (options.getIpVersion() == 4) {
318 if (stats_mgr4_->getDroppedPacketsNum(StatsMgr4::XCHG_RA) >=
319 options.getMaxDrop()[1]) {
320 max_drops = true;
321 }
322 } else if (options.getIpVersion() == 6) {
323 if (stats_mgr6_->getDroppedPacketsNum(StatsMgr6::XCHG_RR) >=
324 options.getMaxDrop()[1]) {
325 max_drops = true;
326 }
327 }
328 }
329 if (max_drops) {
330 if (testDiags('e')) {
331 std::cout << "Reached maximum drops number." << std::endl;
332 }
333 if (!waitToExit()) {
334 return true;
335 }
336 }
337
338 // Check if we reached maximum drops percentage of OFFER/ADVERTISE packets.
339 bool max_pdrops = false;
340 if (options.getMaxDropPercentage().size() > 0) {
341 if (options.getIpVersion() == 4) {
342 if ((stats_mgr4_->getSentPacketsNum(StatsMgr4::XCHG_DO) > 10) &&
343 ((100. * stats_mgr4_->getDroppedPacketsNum(StatsMgr4::XCHG_DO) /
344 stats_mgr4_->getSentPacketsNum(StatsMgr4::XCHG_DO)) >=
345 options.getMaxDropPercentage()[0])) {
346 max_pdrops = true;
347
348 }
349 } else if (options.getIpVersion() == 6) {
350 if ((stats_mgr6_->getSentPacketsNum(StatsMgr6::XCHG_SA) > 10) &&
351 ((100. * stats_mgr6_->getDroppedPacketsNum(StatsMgr6::XCHG_SA) /
352 stats_mgr6_->getSentPacketsNum(StatsMgr6::XCHG_SA)) >=
353 options.getMaxDropPercentage()[0])) {
354 max_pdrops = true;
355 }
356 }
357 }
358 // Check if we reached maximum drops percentage of ACK/REPLY packets.
359 if (options.getMaxDropPercentage().size() > 1) {
360 if (options.getIpVersion() == 4) {
361 if ((stats_mgr4_->getSentPacketsNum(StatsMgr4::XCHG_RA) > 10) &&
362 ((100. * stats_mgr4_->getDroppedPacketsNum(StatsMgr4::XCHG_RA) /
363 stats_mgr4_->getSentPacketsNum(StatsMgr4::XCHG_RA)) >=
364 options.getMaxDropPercentage()[1])) {
365 max_pdrops = true;
366 }
367 } else if (options.getIpVersion() == 6) {
368 if ((stats_mgr6_->getSentPacketsNum(StatsMgr6::XCHG_RR) > 10) &&
369 ((100. * stats_mgr6_->getDroppedPacketsNum(StatsMgr6::XCHG_RR) /
370 stats_mgr6_->getSentPacketsNum(StatsMgr6::XCHG_RR)) >=
371 options.getMaxDropPercentage()[1])) {
372 max_pdrops = true;
373 }
374 }
375 }
376 if (max_pdrops) {
377 if (testDiags('e')) {
378 std::cout << "Reached maximum percentage of drops." << std::endl;
379 }
380 if (!waitToExit()) {
381 return true;
382 }
383 }
384 return (false);
385 }
386
387 Pkt4Ptr
388 TestControl::createRequestFromAck(const dhcp::Pkt4Ptr& ack) {
389 if (!ack) {
390 isc_throw(isc::BadValue, "Unable to create DHCPREQUEST from a"
391 " null DHCPACK message");
392 } else if (ack->getYiaddr().isV4Zero()) {
393 isc_throw(isc::BadValue, "Unable to create DHCPREQUEST from a"
394 " DHCPACK message containing yiaddr of 0");
395 }
396 Pkt4Ptr msg(new Pkt4(DHCPREQUEST, generateTransid()));
397 msg->setCiaddr(ack->getYiaddr());
398 msg->setHWAddr(ack->getHWAddr());
399 msg->addOption(generateClientId(msg->getHWAddr()));
400 return (msg);
401 }
402
403 Pkt6Ptr
404 TestControl::createMessageFromReply(const uint16_t msg_type,
405 const dhcp::Pkt6Ptr& reply) {
406 // Restrict messages to Release and Renew.
407 if (msg_type != DHCPV6_RENEW && msg_type != DHCPV6_RELEASE) {
408 isc_throw(isc::BadValue, "invalid message type " << msg_type
409 << " to be created from Reply, expected DHCPV6_RENEW or"
410 " DHCPV6_RELEASE");
411 }
412 // Get the string representation of the message - to be used for error
413 // logging purposes.
414 const char* msg_type_str = (msg_type == DHCPV6_RENEW ? "Renew" : "Release");
415 // Reply message must be specified.
416 if (!reply) {
417 isc_throw(isc::BadValue, "Unable to create " << msg_type_str
418 << " message from the Reply message because the instance of"
419 " the Reply message is NULL");
420 }
421
422 Pkt6Ptr msg(new Pkt6(msg_type, generateTransid()));
423 // Client id.
424 OptionPtr opt_clientid = reply->getOption(D6O_CLIENTID);
425 if (!opt_clientid) {
426 isc_throw(isc::Unexpected, "failed to create " << msg_type_str
427 << " message because client id option has not been found"
428 " in the Reply message");
429 }
430 msg->addOption(opt_clientid);
431 // Server id.
432 OptionPtr opt_serverid = reply->getOption(D6O_SERVERID);
433 if (!opt_serverid) {
434 isc_throw(isc::Unexpected, "failed to create " << msg_type_str
435 << " because server id option has not been found in the"
436 " Reply message");
437 }
438 msg->addOption(opt_serverid);
439 copyIaOptions(reply, msg);
440 return (msg);
441 }
442
443 OptionPtr
444 TestControl::factoryElapsedTime6(Option::Universe, uint16_t,
445 const OptionBuffer& buf) {
446 if (buf.size() == 2) {
447 return (OptionPtr(new Option(Option::V6, D6O_ELAPSED_TIME, buf)));
448 } else if (buf.size() == 0) {
449 return (OptionPtr(new Option(Option::V6, D6O_ELAPSED_TIME,
450 OptionBuffer(2, 0))));
451 }
452 isc_throw(isc::BadValue,
453 "elapsed time option buffer size has to be 0 or 2");
454 }
455
456 OptionPtr
457 TestControl::factoryGeneric(Option::Universe u, uint16_t type,
458 const OptionBuffer& buf) {
459 OptionPtr opt(new Option(u, type, buf));
460 return (opt);
461 }
462
463 OptionPtr
464 TestControl::factoryIana6(Option::Universe, uint16_t,
465 const OptionBuffer& buf) {
466 // @todo allow different values of T1, T2 and IAID.
467 const uint8_t buf_array[] = {
468 0, 0, 0, 1, // IAID = 1
469 0, 0, 3600 >> 8, 3600 & 0xff, // T1 = 3600
470 0, 0, 5400 >> 8, 5400 & 0xff, // T2 = 5400
471 };
472 OptionBuffer buf_ia_na(buf_array, buf_array + sizeof(buf_array));
473 for (size_t i = 0; i < buf.size(); ++i) {
474 buf_ia_na.push_back(buf[i]);
475 }
476 return (OptionPtr(new Option(Option::V6, D6O_IA_NA, buf_ia_na)));
477 }
478
479 OptionPtr
480 TestControl::factoryIapd6(Option::Universe, uint16_t,
481 const OptionBuffer& buf) {
482 // @todo allow different values of T1, T2 and IAID.
483 static const uint8_t buf_array[] = {
484 0, 0, 0, 1, // IAID = 1
485 0, 0, 3600 >> 8, 3600 & 0xff, // T1 = 3600
486 0, 0, 5400 >> 8, 5400 & 0xff, // T2 = 5400
487 };
488 OptionBuffer buf_ia_pd(buf_array, buf_array + sizeof(buf_array));
489 // Append sub-options to IA_PD.
490 buf_ia_pd.insert(buf_ia_pd.end(), buf.begin(), buf.end());
491 return (OptionPtr(new Option(Option::V6, D6O_IA_PD, buf_ia_pd)));
492 }
493
494
495 OptionPtr
496 TestControl::factoryRapidCommit6(Option::Universe, uint16_t,
497 const OptionBuffer&) {
498 return (OptionPtr(new Option(Option::V6, D6O_RAPID_COMMIT, OptionBuffer())));
499 }
500
501 OptionPtr
502 TestControl::factoryOptionRequestOption6(Option::Universe,
503 uint16_t,
504 const OptionBuffer&) {
505 const uint8_t buf_array[] = {
506 0, D6O_NAME_SERVERS,
507 0, D6O_DOMAIN_SEARCH,
508 };
509 OptionBuffer buf_with_options(buf_array, buf_array + sizeof(buf_array));
510 return (OptionPtr(new Option(Option::V6, D6O_ORO, buf_with_options)));
511 }
512
513
514 OptionPtr
515 TestControl::factoryRequestList4(Option::Universe u,
516 uint16_t type,
517 const OptionBuffer& buf) {
518 const uint8_t buf_array[] = {
519 DHO_SUBNET_MASK,
520 DHO_BROADCAST_ADDRESS,
521 DHO_TIME_OFFSET,
522 DHO_ROUTERS,
523 DHO_DOMAIN_NAME,
524 DHO_DOMAIN_NAME_SERVERS,
525 DHO_HOST_NAME
526 };
527
528 OptionBuffer buf_with_options(buf_array, buf_array + sizeof(buf_array));
529 OptionPtr opt(new Option(u, type, buf));
530 opt->setData(buf_with_options.begin(), buf_with_options.end());
531 return (opt);
532 }
533
534 std::vector<uint8_t>
535 TestControl::generateMacAddress(uint8_t& randomized) {
536 CommandOptions& options = CommandOptions::instance();
537
538 const CommandOptions::MacAddrsVector& macs = options.getMacsFromFile();
539 // if we are using the -M option return a random one from the list...
540 if (macs.size() > 0) {
541 uint16_t r = number_generator_();
542 if (r >= macs.size()) {
543 r = 0;
544 }
545 return macs[r];
546
547 } else {
548 // ... otherwise use the standard behavior
549 uint32_t clients_num = options.getClientsNum();
550 if (clients_num < 2) {
551 return (options.getMacTemplate());
552 }
553 // Get the base MAC address. We are going to randomize part of it.
554 std::vector<uint8_t> mac_addr(options.getMacTemplate());
555 if (mac_addr.size() != HW_ETHER_LEN) {
556 isc_throw(BadValue, "invalid MAC address template specified");
557 }
558 uint32_t r = macaddr_gen_->generate();
559 randomized = 0;
560 // Randomize MAC address octets.
561 for (std::vector<uint8_t>::iterator it = mac_addr.end() - 1;
562 it >= mac_addr.begin();
563 --it) {
564 // Add the random value to the current octet.
565 (*it) += r;
566 ++randomized;
567 if (r < 256) {
568 // If we are here it means that there is no sense
569 // to randomize the remaining octets of MAC address
570 // because the following bytes of random value
571 // are zero and it will have no effect.
572 break;
573 }
574 // Randomize the next octet with the following
575 // byte of random value.
576 r >>= 8;
577 }
578 return (mac_addr);
579 }
580 }
581
582 OptionPtr
583 TestControl::generateClientId(const dhcp::HWAddrPtr& hwaddr) const {
584 std::vector<uint8_t> client_id(1, static_cast<uint8_t>(hwaddr->htype_));
585 client_id.insert(client_id.end(), hwaddr->hwaddr_.begin(),
586 hwaddr->hwaddr_.end());
587 return (OptionPtr(new Option(Option::V4, DHO_DHCP_CLIENT_IDENTIFIER,
588 client_id)));
589 }
590
591 std::vector<uint8_t>
592 TestControl::generateDuid(uint8_t& randomized) {
593 CommandOptions& options = CommandOptions::instance();
594 std::vector<uint8_t> mac_addr(generateMacAddress(randomized));
595 const CommandOptions::MacAddrsVector& macs = options.getMacsFromFile();
596 // pick a random mac address if we are using option -M..
597 if (macs.size() > 0) {
598 uint16_t r = number_generator_();
599 if (r >= macs.size()) {
600 r = 0;
601 }
602 std::vector<uint8_t> mac = macs[r];
603 // DUID_LL is in this format
604 // 0 1 2 3
605 // 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
606 // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
607 // | 3 | hardware type (16 bits) |
608 // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
609 // . .
610 // . link-layer address (variable length) .
611 // . .
612 // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
613
614 // No C++11 so initializer list support, building a vector<uint8_t> is a
615 // pain...
616 uint8_t duid_ll[] = {0, 3, 0, 1, 0, 0, 0, 0, 0, 0};
617 // copy duid_ll array into the vector
618 std::vector<uint8_t> duid(duid_ll,
619 duid_ll + sizeof(duid_ll) / sizeof(duid_ll[0]));
620 // put the mac address bytes at the end
621 std::copy(mac.begin(), mac.end(), duid.begin() + 4);
622 return (duid);
623 } else {
624 uint32_t clients_num = options.getClientsNum();
625 if ((clients_num == 0) || (clients_num == 1)) {
626 return (options.getDuidTemplate());
627 }
628 // Get the base DUID. We are going to randomize part of it.
629 std::vector<uint8_t> duid(options.getDuidTemplate());
630 // @todo: add support for DUIDs of different sizes.
631 duid.resize(duid.size());
632 std::copy(mac_addr.begin(), mac_addr.end(),
633 duid.begin() + duid.size() - mac_addr.size());
634 return (duid);
635 }
636 }
637
638 uint32_t
639 TestControl::getCurrentTimeout() const {
640 CommandOptions& options = CommandOptions::instance();
641 ptime now(microsec_clock::universal_time());
642 // Check that we haven't passed the moment to send the next set of
643 // packets.
644 if (now >= basic_rate_control_.getDue() ||
645 (options.getRenewRate() != 0 && now >= renew_rate_control_.getDue()) ||
646 (options.getReleaseRate() != 0 &&
647 now >= release_rate_control_.getDue())) {
648 return (0);
649 }
650
651 // Let's assume that the due time for Solicit is the soonest.
652 ptime due = basic_rate_control_.getDue();
653 // If we are sending Renews and due time for Renew occurs sooner,
654 // set the due time to Renew due time.
655 if ((options.getRenewRate()) != 0 && (renew_rate_control_.getDue() < due)) {
656 due = renew_rate_control_.getDue();
657 }
658 // If we are sending Releases and the due time for Release occurs
659 // sooner than the current due time, let's use the due for Releases.
660 if ((options.getReleaseRate() != 0) &&
661 (release_rate_control_.getDue() < due)) {
662 due = release_rate_control_.getDue();
663 }
664 // Return the timeout in microseconds.
665 return (time_period(now, due).length().total_microseconds());
666 }
667
668 int
669 TestControl::getElapsedTimeOffset() const {
670 int elp_offset = CommandOptions::instance().getIpVersion() == 4 ?
671 DHCPV4_ELAPSED_TIME_OFFSET : DHCPV6_ELAPSED_TIME_OFFSET;
672 if (CommandOptions::instance().getElapsedTimeOffset() > 0) {
673 elp_offset = CommandOptions::instance().getElapsedTimeOffset();
674 }
675 return (elp_offset);
676 }
677
678 template<class T>
679 uint32_t
680 TestControl::getElapsedTime(const T& pkt1, const T& pkt2) {
681 using namespace boost::posix_time;
682 ptime pkt1_time = pkt1->getTimestamp();
683 ptime pkt2_time = pkt2->getTimestamp();
684 if (pkt1_time.is_not_a_date_time() ||
685 pkt2_time.is_not_a_date_time()) {
686 isc_throw(InvalidOperation, "packet timestamp not set");;
687 }
688 time_period elapsed_period(pkt1_time, pkt2_time);
689 return (elapsed_period.is_null() ? 0 :
690 elapsed_period.length().total_milliseconds());
691 }
692
693 int
694 TestControl::getRandomOffset(const int arg_idx) const {
695 int rand_offset = CommandOptions::instance().getIpVersion() == 4 ?
696 DHCPV4_RANDOMIZATION_OFFSET : DHCPV6_RANDOMIZATION_OFFSET;
697 if (CommandOptions::instance().getRandomOffset().size() > arg_idx) {
698 rand_offset = CommandOptions::instance().getRandomOffset()[arg_idx];
699 }
700 return (rand_offset);
701 }
702
703 int
704 TestControl::getRequestedIpOffset() const {
705 int rip_offset = CommandOptions::instance().getIpVersion() == 4 ?
706 DHCPV4_REQUESTED_IP_OFFSET : DHCPV6_IA_NA_OFFSET;
707 if (CommandOptions::instance().getRequestedIpOffset() > 0) {
708 rip_offset = CommandOptions::instance().getRequestedIpOffset();
709 }
710 return (rip_offset);
711 }
712
713 uint64_t
714 TestControl::getRcvdPacketsNum(ExchangeType xchg_type) const {
715 uint8_t ip_version = CommandOptions::instance().getIpVersion();
716 if (ip_version == 4) {
717 return (stats_mgr4_->getRcvdPacketsNum(xchg_type));
718 }
719 return (stats_mgr6_->
720 getRcvdPacketsNum(static_cast<StatsMgr6::ExchangeType>(xchg_type)));
721 }
722
723 uint64_t
724 TestControl::getSentPacketsNum(ExchangeType xchg_type) const {
725 uint8_t ip_version = CommandOptions::instance().getIpVersion();
726 if (ip_version == 4) {
727 return (stats_mgr4_->getSentPacketsNum(xchg_type));
728 }
729 return (stats_mgr6_->
730 getSentPacketsNum(static_cast<StatsMgr6::ExchangeType>(xchg_type)));
731 }
732
733 int
734 TestControl::getServerIdOffset() const {
735 int srvid_offset = CommandOptions::instance().getIpVersion() == 4 ?
736 DHCPV4_SERVERID_OFFSET : DHCPV6_SERVERID_OFFSET;
737 if (CommandOptions::instance().getServerIdOffset() > 0) {
738 srvid_offset = CommandOptions::instance().getServerIdOffset();
739 }
740 return (srvid_offset);
741 }
742
743 TestControl::TemplateBuffer
744 TestControl::getTemplateBuffer(const size_t idx) const {
745 if (template_buffers_.size() > idx) {
746 return (template_buffers_[idx]);
747 }
748 isc_throw(OutOfRange, "invalid buffer index");
749 }
750
751 int
752 TestControl::getTransactionIdOffset(const int arg_idx) const {
753 int xid_offset = CommandOptions::instance().getIpVersion() == 4 ?
754 DHCPV4_TRANSID_OFFSET : DHCPV6_TRANSID_OFFSET;
755 if (CommandOptions::instance().getTransactionIdOffset().size() > arg_idx) {
756 xid_offset = CommandOptions::instance().getTransactionIdOffset()[arg_idx];
757 }
758 return (xid_offset);
759 }
760
761 void
762 TestControl::handleChild(int) {
763 int status = 0;
764 while (wait3(&status, WNOHANG, NULL) > 0) {
765 // continue
766 }
767 }
768
769 void
770 TestControl::handleInterrupt(int) {
771 interrupted_ = true;
772 }
773
774 void
775 TestControl::initPacketTemplates() {
776 template_packets_v4_.clear();
777 template_packets_v6_.clear();
778 template_buffers_.clear();
779 CommandOptions& options = CommandOptions::instance();
780 std::vector<std::string> template_files = options.getTemplateFiles();
781 for (std::vector<std::string>::const_iterator it = template_files.begin();
782 it != template_files.end(); ++it) {
783 readPacketTemplate(*it);
784 }
785 }
786
787 void
788 TestControl::initializeStatsMgr() {
789 CommandOptions& options = CommandOptions::instance();
790 // Check if packet archive mode is required. If user
791 // requested diagnostics option -x t we have to enable
792 // it so as StatsMgr preserves all packets.
793 const bool archive_mode = testDiags('t') ? true : false;
794 if (options.getIpVersion() == 4) {
795 stats_mgr4_.reset();
796 stats_mgr4_ = StatsMgr4Ptr(new StatsMgr4(archive_mode));
797 stats_mgr4_->addExchangeStats(StatsMgr4::XCHG_DO,
798 options.getDropTime()[0]);
799 if (options.getExchangeMode() == CommandOptions::DORA_SARR) {
800 stats_mgr4_->addExchangeStats(StatsMgr4::XCHG_RA,
801 options.getDropTime()[1]);
802 }
803 if (options.getRenewRate() != 0) {
804 stats_mgr4_->addExchangeStats(StatsMgr4::XCHG_RNA);
805 }
806
807 } else if (options.getIpVersion() == 6) {
808 stats_mgr6_.reset();
809 stats_mgr6_ = StatsMgr6Ptr(new StatsMgr6(archive_mode));
810 stats_mgr6_->addExchangeStats(StatsMgr6::XCHG_SA,
811 options.getDropTime()[0]);
812 if (options.getExchangeMode() == CommandOptions::DORA_SARR) {
813 stats_mgr6_->addExchangeStats(StatsMgr6::XCHG_RR,
814 options.getDropTime()[1]);
815 }
816 if (options.getRenewRate() != 0) {
817 stats_mgr6_->addExchangeStats(StatsMgr6::XCHG_RN);
818 }
819 if (options.getReleaseRate() != 0) {
820 stats_mgr6_->addExchangeStats(StatsMgr6::XCHG_RL);
821 }
822 }
823 if (testDiags('i')) {
824 if (options.getIpVersion() == 4) {
825 stats_mgr4_->addCustomCounter("latesend", "Late sent packets");
826 stats_mgr4_->addCustomCounter("shortwait", "Short waits for packets");
827 stats_mgr4_->addCustomCounter("multircvd", "Multiple packets receives");
828 stats_mgr4_->addCustomCounter("latercvd", "Late received packets");
829 } else if (options.getIpVersion() == 6) {
830 stats_mgr6_->addCustomCounter("latesend", "Late sent packets");
831 stats_mgr6_->addCustomCounter("shortwait", "Short waits for packets");
832 stats_mgr6_->addCustomCounter("multircvd", "Multiple packets receives");
833 stats_mgr6_->addCustomCounter("latercvd", "Late received packets");
834 }
835 }
836 }
837
838 int
839 TestControl::openSocket() const {
840 CommandOptions& options = CommandOptions::instance();
841 std::string localname = options.getLocalName();
842 std::string servername = options.getServerName();
843 uint16_t port = options.getLocalPort();
844 int sock = 0;
845
846 uint8_t family = (options.getIpVersion() == 6) ? AF_INET6 : AF_INET;
847 IOAddress remoteaddr(servername);
848
849 // Check for mismatch between IP option and server address
850 if (family != remoteaddr.getFamily()) {
851 isc_throw(InvalidParameter,
852 "Values for IP version: " <<
853 static_cast<unsigned int>(options.getIpVersion()) <<
854 " and server address: " << servername << " are mismatched.");
855 }
856
857 if (port == 0) {
858 if (family == AF_INET6) {
859 // need server port (547) because the server is acting as a relay agent
860 port = DHCP6_CLIENT_PORT;
861 // if acting as a relay agent change port.
862 if (options.isUseRelayedV6()) {
863 port = DHCP6_SERVER_PORT;
864 }
865 } else if (options.getIpVersion() == 4) {
866 port = 67; // TODO: find out why port 68 is wrong here.
867 }
868 }
869
870 // Local name is specified along with '-l' option.
871 // It may point to interface name or local address.
872 if (!localname.empty()) {
873 // CommandOptions should be already aware whether local name
874 // is interface name or address because it uses IfaceMgr to
875 // scan interfaces and get's their names.
876 if (options.isInterface()) {
877 sock = IfaceMgr::instance().openSocketFromIface(localname,
878 port,
879 family);
880 } else {
881 IOAddress localaddr(localname);
882 sock = IfaceMgr::instance().openSocketFromAddress(localaddr,
883 port);
884 }
885 } else if (!servername.empty()) {
886 // If only server name is given we will need to try to resolve
887 // the local address to bind socket to based on remote address.
888 sock = IfaceMgr::instance().openSocketFromRemoteAddress(remoteaddr,
889 port);
890 }
891 if (sock <= 0) {
892 isc_throw(BadValue, "unable to open socket to communicate with "
893 "DHCP server");
894 }
895
896 // IfaceMgr does not set broadcast option on the socket. We rely
897 // on CommandOptions object to find out if socket has to have
898 // broadcast enabled.
899 if ((options.getIpVersion() == 4) && options.isBroadcast()) {
900 int broadcast_enable = 1;
901 int ret = setsockopt(sock, SOL_SOCKET, SO_BROADCAST,
902 &broadcast_enable, sizeof(broadcast_enable));
903 if (ret < 0) {
904 isc_throw(InvalidOperation,
905 "unable to set broadcast option on the socket");
906 }
907 } else if (options.getIpVersion() == 6) {
908 // If remote address is multicast we need to enable it on
909 // the socket that has been created.
910 if (remoteaddr.isV6Multicast()) {
911 int hops = 1;
912 int ret = setsockopt(sock, IPPROTO_IPV6, IPV6_MULTICAST_HOPS,
913 &hops, sizeof(hops));
914 // If user specified interface name with '-l' the
915 // IPV6_MULTICAST_IF has to be set.
916 if ((ret >= 0) && options.isInterface()) {
917 IfacePtr iface =
918 IfaceMgr::instance().getIface(options.getLocalName());
919 if (iface == NULL) {
920 isc_throw(Unexpected, "unknown interface "
921 << options.getLocalName());
922 }
923 int idx = iface->getIndex();
924 ret = setsockopt(sock, IPPROTO_IPV6, IPV6_MULTICAST_IF,
925 &idx, sizeof(idx));
926 }
927 if (ret < 0) {
928 isc_throw(InvalidOperation,
929 "unable to enable multicast on socket " << sock
930 << ". errno = " << errno);
931 }
932 }
933 }
934
935 return (sock);
936 }
937
938 void
939 TestControl::sendPackets(const TestControlSocket& socket,
940 const uint64_t packets_num,
941 const bool preload /* = false */) {
942 CommandOptions& options = CommandOptions::instance();
943 for (uint64_t i = packets_num; i > 0; --i) {
944 if (options.getIpVersion() == 4) {
945 // No template packets means that no -T option was specified.
946 // We have to build packets ourselves.
947 if (template_buffers_.empty()) {
948 sendDiscover4(socket, preload);
949 } else {
950 // @todo add defines for packet type index that can be
951 // used to access template_buffers_.
952 sendDiscover4(socket, template_buffers_[0], preload);
953 }
954 } else {
955 // No template packets means that no -T option was specified.
956 // We have to build packets ourselves.
957 if (template_buffers_.empty()) {
958 sendSolicit6(socket, preload);
959 } else {
960 // @todo add defines for packet type index that can be
961 // used to access template_buffers_.
962 sendSolicit6(socket, template_buffers_[0], preload);
963 }
964 }
965 // If we preload server we don't want to receive any packets.
966 if (!preload) {
967 uint64_t latercvd = receivePackets(socket);
968 if (testDiags('i')) {
969 if (options.getIpVersion() == 4) {
970 stats_mgr4_->incrementCounter("latercvd", latercvd);
971 } else if (options.getIpVersion() == 6) {
972 stats_mgr6_->incrementCounter("latercvd", latercvd);
973 }
974 }
975 }
976 }
977 }
978
979 uint64_t
980 TestControl::sendMultipleRequests(const TestControlSocket& socket,
981 const uint64_t msg_num) {
982 for (uint64_t i = 0; i < msg_num; ++i) {
983 if (!sendRequestFromAck(socket)) {
984 return (i);
985 }
986 }
987 return (msg_num);
988 }
989
990 uint64_t
991 TestControl::sendMultipleMessages6(const TestControlSocket& socket,
992 const uint32_t msg_type,
993 const uint64_t msg_num) {
994 for (uint64_t i = 0; i < msg_num; ++i) {
995 if (!sendMessageFromReply(msg_type, socket)) {
996 return (i);
997 }
998 }
999 return (msg_num);
1000 }
1001
1002 void
1003 TestControl::printDiagnostics() const {
1004 CommandOptions& options = CommandOptions::instance();
1005 if (testDiags('a')) {
1006 // Print all command line parameters.
1007 options.printCommandLine();
1008 // Print MAC and DUID.
1009 std::cout << "Set MAC to " << vector2Hex(options.getMacTemplate(), "::")
1010 << std::endl;
1011 if (options.getDuidTemplate().size() > 0) {
1012 std::cout << "Set DUID to " << vector2Hex(options.getDuidTemplate()) << std::endl;
1013 }
1014 }
1015 }
1016
1017 void
1018 TestControl::printTemplate(const uint8_t packet_type) const {
1019 std::string hex_buf;
1020 int arg_idx = 0;
1021 if (CommandOptions::instance().getIpVersion() == 4) {
1022 if (packet_type == DHCPREQUEST) {
1023 arg_idx = 1;
1024 }
1025 std::map<uint8_t, dhcp::Pkt4Ptr>::const_iterator pkt_it =
1026 template_packets_v4_.find(packet_type);
1027 if ((pkt_it != template_packets_v4_.end()) &&
1028 pkt_it->second) {
1029 const util::OutputBuffer& out_buf(pkt_it->second->getBuffer());
1030 const char* out_buf_data =
1031 static_cast<const char*>(out_buf.getData());
1032 std::vector<uint8_t> buf(out_buf_data, out_buf_data + out_buf.getLength());
1033 hex_buf = vector2Hex(buf);
1034 }
1035 } else if (CommandOptions::instance().getIpVersion() == 6) {
1036 if (packet_type == DHCPV6_REQUEST) {
1037 arg_idx = 1;
1038 }
1039 std::map<uint8_t, dhcp::Pkt6Ptr>::const_iterator pkt_it =
1040 template_packets_v6_.find(packet_type);
1041 if (pkt_it != template_packets_v6_.end() &&
1042 pkt_it->second) {
1043 const util::OutputBuffer& out_buf(pkt_it->second->getBuffer());
1044 const char* out_buf_data =
1045 static_cast<const char*>(out_buf.getData());
1046 std::vector<uint8_t> buf(out_buf_data, out_buf_data + out_buf.getLength());
1047 hex_buf = vector2Hex(buf);
1048 }
1049 }
1050 std::cout << "xid-offset=" << getTransactionIdOffset(arg_idx) << std::endl;
1051 std::cout << "random-offset=" << getRandomOffset(arg_idx) << std::endl;
1052 if (arg_idx > 0) {
1053 std::cout << "srvid-offset=" << getServerIdOffset() << std::endl;
1054 std::cout << "time-offset=" << getElapsedTimeOffset() << std::endl;
1055 std::cout << "ip-offset=" << getRequestedIpOffset() << std::endl;
1056 }
1057
1058 std::cout << "contents: " << std::endl;
1059 int line_len = 32;
1060 int i = 0;
1061 while (line_len == 32) {
1062 if (hex_buf.length() - i < 32) {
1063 line_len = hex_buf.length() - i;
1064 };
1065 if (line_len > 0) {
1066 std::cout << setfill('0') << setw(4) << std::hex << i << std::dec
1067 << " " << hex_buf.substr(i, line_len) << std::endl;
1068 }
1069 i += 32;
1070 }
1071 std::cout << std::endl;
1072 }
1073
1074 void
1075 TestControl::printTemplates() const {
1076 CommandOptions& options = CommandOptions::instance();
1077 if (options.getIpVersion() == 4) {
1078 printTemplate(DHCPDISCOVER);
1079 printTemplate(DHCPREQUEST);
1080 } else if (options.getIpVersion() == 6) {
1081 printTemplate(DHCPV6_SOLICIT);
1082 printTemplate(DHCPV6_REQUEST);
1083 }
1084 }
1085
1086 void
1087 TestControl::printRate() const {
1088 double rate = 0;
1089 CommandOptions& options = CommandOptions::instance();
1090 std::string exchange_name = "4-way exchanges";
1091 if (options.getIpVersion() == 4) {
1092 StatsMgr4::ExchangeType xchg_type =
1093 options.getExchangeMode() == CommandOptions::DO_SA ?
1094 StatsMgr4::XCHG_DO : StatsMgr4::XCHG_RA;
1095 if (xchg_type == StatsMgr4::XCHG_DO) {
1096 exchange_name = "DISCOVER-OFFER";
1097 }
1098 double duration =
1099 stats_mgr4_->getTestPeriod().length().total_nanoseconds() / 1e9;
1100 rate = stats_mgr4_->getRcvdPacketsNum(xchg_type) / duration;
1101 } else if (options.getIpVersion() == 6) {
1102 StatsMgr6::ExchangeType xchg_type =
1103 options.getExchangeMode() == CommandOptions::DO_SA ?
1104 StatsMgr6::XCHG_SA : StatsMgr6::XCHG_RR;
1105 if (xchg_type == StatsMgr6::XCHG_SA) {
1106 exchange_name = options.isRapidCommit() ? "Solicit-Reply" :
1107 "Solicit-Advertise";
1108 }
1109 double duration =
1110 stats_mgr6_->getTestPeriod().length().total_nanoseconds() / 1e9;
1111 rate = stats_mgr6_->getRcvdPacketsNum(xchg_type) / duration;
1112 }
1113 std::ostringstream s;
1114 s << "***Rate statistics***" << std::endl;
1115 s << "Rate: " << rate << " " << exchange_name << "/second";
1116 if (options.getRate() > 0) {
1117 s << ", expected rate: " << options.getRate() << std::endl;
1118 }
1119
1120 std::cout << s.str() << std::endl;
1121 }
1122
1123 void
1124 TestControl::printIntermediateStats() {
1125 CommandOptions& options = CommandOptions::instance();
1126 int delay = options.getReportDelay();
1127 ptime now = microsec_clock::universal_time();
1128 time_period time_since_report(last_report_, now);
1129 if (time_since_report.length().total_seconds() >= delay) {
1130 if (options.getIpVersion() == 4) {
1131 stats_mgr4_->printIntermediateStats();
1132 } else if (options.getIpVersion() == 6) {
1133 stats_mgr6_->printIntermediateStats();
1134 }
1135 last_report_ = now;
1136 }
1137 }
1138
1139 void
1140 TestControl::printStats() const {
1141 printRate();
1142 CommandOptions& options = CommandOptions::instance();
1143 if (options.getIpVersion() == 4) {
1144 if (!stats_mgr4_) {
1145 isc_throw(InvalidOperation, "Statistics Manager for DHCPv4 "
1146 "hasn't been initialized");
1147 }
1148 stats_mgr4_->printStats();
1149 if (testDiags('i')) {
1150 stats_mgr4_->printCustomCounters();
1151 }
1152 } else if (options.getIpVersion() == 6) {
1153 if (!stats_mgr6_) {
1154 isc_throw(InvalidOperation, "Statistics Manager for DHCPv6 "
1155 "hasn't been initialized");
1156 }
1157 stats_mgr6_->printStats();
1158 if (testDiags('i')) {
1159 stats_mgr6_->printCustomCounters();
1160 }
1161 }
1162 }
1163
1164 std::string
1165 TestControl::vector2Hex(const std::vector<uint8_t>& vec,
1166 const std::string& separator /* ="" */) const {
1167 std::ostringstream stream;
1168 for (std::vector<uint8_t>::const_iterator it = vec.begin();
1169 it != vec.end();
1170 ++it) {
1171 if (it == vec.begin()) {
1172 stream << byte2Hex(*it);
1173 } else {
1174 stream << separator << byte2Hex(*it);
1175 }
1176 }
1177 return (stream.str());
1178 }
1179
1180 void
1181 TestControl::readPacketTemplate(const std::string& file_name) {
1182 std::ifstream temp_file;
1183 temp_file.open(file_name.c_str(), ios::in | ios::binary | ios::ate);
1184 if (!temp_file.is_open()) {
1185 isc_throw(BadValue, "unable to open template file " << file_name);
1186 }
1187 // Read template file contents.
1188 std::streampos temp_size = temp_file.tellg();
1189 if (temp_size == std::streampos(0)) {
1190 temp_file.close();
1191 isc_throw(OutOfRange, "the template file " << file_name << " is empty");
1192 }
1193 temp_file.seekg(0, ios::beg);
1194 std::vector<char> file_contents(temp_size);
1195 temp_file.read(&file_contents[0], temp_size);
1196 temp_file.close();
1197 // Spaces are allowed so we have to strip the contents
1198 // from them. In the same time we want to make sure that
1199 // apart from spaces the file contains hexadecimal digits
1200 // only.
1201 std::vector<char> hex_digits;
1202 for (size_t i = 0; i < file_contents.size(); ++i) {
1203 if (isxdigit(file_contents[i])) {
1204 hex_digits.push_back(file_contents[i]);
1205 } else if (!isxdigit(file_contents[i]) &&
1206 !isspace(file_contents[i])) {
1207 isc_throw(BadValue, "'" << file_contents[i] << "' is not a"
1208 " hexadecimal digit");
1209 }
1210 }
1211 // Expect even number of digits.
1212 if (hex_digits.size() % 2 != 0) {
1213 isc_throw(OutOfRange, "odd number of digits in template file");
1214 } else if (hex_digits.empty()) {
1215 isc_throw(OutOfRange, "template file " << file_name << " is empty");
1216 }
1217 std::vector<uint8_t> binary_stream;
1218 for (size_t i = 0; i < hex_digits.size(); i += 2) {
1219 stringstream s;
1220 s << "0x" << hex_digits[i] << hex_digits[i+1];
1221 int b;
1222 s >> std::hex >> b;
1223 binary_stream.push_back(static_cast<uint8_t>(b));
1224 }
1225 template_buffers_.push_back(binary_stream);
1226 }
1227
1228 void
1229 TestControl::processReceivedPacket4(const TestControlSocket& socket,
1230 const Pkt4Ptr& pkt4) {
1231 if (pkt4->getType() == DHCPOFFER) {
1232 Pkt4Ptr discover_pkt4(stats_mgr4_->passRcvdPacket(StatsMgr4::XCHG_DO,
1233 pkt4));
1234 CommandOptions::ExchangeMode xchg_mode =
1235 CommandOptions::instance().getExchangeMode();
1236 if ((xchg_mode == CommandOptions::DORA_SARR) && discover_pkt4) {
1237 if (template_buffers_.size() < 2) {
1238 sendRequest4(socket, discover_pkt4, pkt4);
1239 } else {
1240 // @todo add defines for packet type index that can be
1241 // used to access template_buffers_.
1242 sendRequest4(socket, template_buffers_[1], discover_pkt4, pkt4);
1243 }
1244 }
1245 } else if (pkt4->getType() == DHCPACK) {
1246 // If received message is DHCPACK, we have to check if this is
1247 // a response to 4-way exchange. We'll match this packet with
1248 // a DHCPREQUEST sent as part of the 4-way exchanges.
1249 if (stats_mgr4_->passRcvdPacket(StatsMgr4::XCHG_RA, pkt4)) {
1250 // The DHCPACK belongs to DHCPREQUEST-DHCPACK exchange type.
1251 // So, we may need to keep this DHCPACK in the storage if renews.
1252 // Note that, DHCPACK messages hold the information about
1253 // leases assigned. We use this information to renew.
1254 if (stats_mgr4_->hasExchangeStats(StatsMgr4::XCHG_RNA)) {
1255 // Renew messages are sent, because StatsMgr has the
1256 // specific exchange type specified. Let's append the DHCPACK.
1257 // message to a storage
1258 ack_storage_.append(pkt4);
1259 }
1260 // The DHCPACK message is not a server's response to the DHCPREQUEST
1261 // message sent within the 4-way exchange. It may be a response to a
1262 // renewal. In this case we first check if StatsMgr has exchange type
1263 // for renew specified, and if it has, if there is a corresponding
1264 // renew message for the received DHCPACK.
1265 } else if (stats_mgr4_->hasExchangeStats(StatsMgr4::XCHG_RNA)) {
1266 stats_mgr4_->passRcvdPacket(StatsMgr4::XCHG_RNA, pkt4);
1267 }
1268 }
1269 }
1270
1271 void
1272 TestControl::processReceivedPacket6(const TestControlSocket& socket,
1273 const Pkt6Ptr& pkt6) {
1274 uint8_t packet_type = pkt6->getType();
1275 if (packet_type == DHCPV6_ADVERTISE) {
1276 Pkt6Ptr solicit_pkt6(stats_mgr6_->passRcvdPacket(StatsMgr6::XCHG_SA,
1277 pkt6));
1278 CommandOptions::ExchangeMode xchg_mode =
1279 CommandOptions::instance().getExchangeMode();
1280 if ((xchg_mode == CommandOptions::DORA_SARR) && solicit_pkt6) {
1281 // \todo check whether received ADVERTISE packet is sane.
1282 // We might want to check if STATUS_CODE option is non-zero
1283 // and if there is IAADR option in IA_NA.
1284 if (template_buffers_.size() < 2) {
1285 sendRequest6(socket, pkt6);
1286 } else {
1287 // @todo add defines for packet type index that can be
1288 // used to access template_buffers_.
1289 sendRequest6(socket, template_buffers_[1], pkt6);
1290 }
1291 }
1292 } else if (packet_type == DHCPV6_REPLY) {
1293 // If the received message is Reply, we have to find out which exchange
1294 // type the Reply message belongs to. It is doable by matching the Reply
1295 // transaction id with the transaction id of the sent Request, Renew
1296 // or Release. First we start with the Request.
1297 if (stats_mgr6_->passRcvdPacket(StatsMgr6::XCHG_RR, pkt6)) {
1298 // The Reply belongs to Request-Reply exchange type. So, we may need
1299 // to keep this Reply in the storage if Renews or/and Releases are
1300 // being sent. Note that, Reply messages hold the information about
1301 // leases assigned. We use this information to construct Renew and
1302 // Release messages.
1303 if (stats_mgr6_->hasExchangeStats(StatsMgr6::XCHG_RN) ||
1304 stats_mgr6_->hasExchangeStats(StatsMgr6::XCHG_RL)) {
1305 // Renew or Release messages are sent, because StatsMgr has the
1306 // specific exchange type specified. Let's append the Reply
1307 // message to a storage.
1308 reply_storage_.append(pkt6);
1309 }
1310 // The Reply message is not a server's response to the Request message
1311 // sent within the 4-way exchange. It may be a response to the Renew
1312 // or Release message. In the if clause we first check if StatsMgr
1313 // has exchange type for Renew specified, and if it has, if there is
1314 // a corresponding Renew message for the received Reply. If not,
1315 // we check that StatsMgr has exchange type for Release specified,
1316 // as possibly the Reply has been sent in response to Release.
1317 } else if (!(stats_mgr6_->hasExchangeStats(StatsMgr6::XCHG_RN) &&
1318 stats_mgr6_->passRcvdPacket(StatsMgr6::XCHG_RN, pkt6)) &&
1319 stats_mgr6_->hasExchangeStats(StatsMgr6::XCHG_RL)) {
1320 // At this point, it is only possible that the Reply has been sent
1321 // in response to a Release. Try to match the Reply with Release.
1322 stats_mgr6_->passRcvdPacket(StatsMgr6::XCHG_RL, pkt6);
1323 }
1324 }
1325 }
1326
1327 uint64_t
1328 TestControl::receivePackets(const TestControlSocket& socket) {
1329 bool receiving = true;
1330 uint64_t received = 0;
1331 while (receiving) {
1332 if (CommandOptions::instance().getIpVersion() == 4) {
1333 Pkt4Ptr pkt4;
1334 try {
1335 pkt4 = IfaceMgr::instance().receive4(0, getCurrentTimeout());
1336 } catch (const Exception& e) {
1337 std::cerr << "Failed to receive DHCPv4 packet: "
1338 << e.what() << std::endl;
1339 }
1340 if (!pkt4) {
1341 receiving = false;
1342 } else {
1343 ++received;
1344 if ((received > 1) && testDiags('i')) {
1345 stats_mgr4_->incrementCounter("multircvd");
1346 }
1347
1350 pkt4->unpack();
1351 processReceivedPacket4(socket, pkt4);
1352 }
1353 } else if (CommandOptions::instance().getIpVersion() == 6) {
1354 Pkt6Ptr pkt6;
1355 try {
1356 pkt6 = IfaceMgr::instance().receive6(0, getCurrentTimeout());
1357 } catch (const Exception& e) {
1358 std::cerr << "Failed to receive DHCPv6 packet: "
1359 << e.what() << std::endl;
1360 }
1361 if (!pkt6) {
1362 receiving = false;
1363 } else {
1364 ++received;
1365 if ((received > 1) && testDiags('i')) {
1366 stats_mgr6_->incrementCounter("multircvd");
1367 }
1368
1371 pkt6->unpack();
1372 processReceivedPacket6(socket, pkt6);
1373 }
1374 }
1375 }
1376 return (received);
1377 }
1378
1379 void
1380 TestControl::registerOptionFactories4() const {
1381 static bool factories_registered = false;
1382 if (!factories_registered) {
1383 // DHCP_MESSAGE_TYPE option factory.
1384 LibDHCP::OptionFactoryRegister(Option::V4,
1385 DHO_DHCP_MESSAGE_TYPE,
1386 &TestControl::factoryGeneric);
1387 // DHCP_SERVER_IDENTIFIER option factory.
1388 LibDHCP::OptionFactoryRegister(Option::V4,
1389 DHO_DHCP_SERVER_IDENTIFIER,
1390 &TestControl::factoryGeneric);
1391 // DHCP_PARAMETER_REQUEST_LIST option factory.
1392 LibDHCP::OptionFactoryRegister(Option::V4,
1393 DHO_DHCP_PARAMETER_REQUEST_LIST,
1394 &TestControl::factoryRequestList4);
1395 }
1396 factories_registered = true;
1397 }
1398
1399 void
1400 TestControl::registerOptionFactories6() const {
1401 static bool factories_registered = false;
1402 if (!factories_registered) {
1403 // D60_ELAPSED_TIME
1404 LibDHCP::OptionFactoryRegister(Option::V6,
1405 D6O_ELAPSED_TIME,
1406 &TestControl::factoryElapsedTime6);
1407 // D6O_RAPID_COMMIT
1408 LibDHCP::OptionFactoryRegister(Option::V6,
1409 D6O_RAPID_COMMIT,
1410 &TestControl::factoryRapidCommit6);
1411 // D6O_ORO (option request option) factory.
1412 LibDHCP::OptionFactoryRegister(Option::V6,
1413 D6O_ORO,
1414 &TestControl::factoryOptionRequestOption6);
1415 // D6O_CLIENTID option factory.
1416 LibDHCP::OptionFactoryRegister(Option::V6,
1417 D6O_CLIENTID,
1418 &TestControl::factoryGeneric);
1419 // D6O_SERVERID option factory.
1420 LibDHCP::OptionFactoryRegister(Option::V6,
1421 D6O_SERVERID,
1422 &TestControl::factoryGeneric);
1423 // D6O_IA_NA option factory.
1424 LibDHCP::OptionFactoryRegister(Option::V6,
1425 D6O_IA_NA,
1426 &TestControl::factoryIana6);
1427
1428 // D6O_IA_PD option factory.
1429 LibDHCP::OptionFactoryRegister(Option::V6,
1430 D6O_IA_PD,
1431 &TestControl::factoryIapd6);
1432
1433
1434 }
1435 factories_registered = true;
1436 }
1437
1438 void
1439 TestControl::registerOptionFactories() const {
1440 CommandOptions& options = CommandOptions::instance();
1441 switch(options.getIpVersion()) {
1442 case 4:
1443 registerOptionFactories4();
1444 break;
1445 case 6:
1446 registerOptionFactories6();
1447 break;
1448 default:
1449 isc_throw(InvalidOperation, "command line options have to be parsed "
1450 "before DHCP option factories can be registered");
1451 }
1452 }
1453
1454 void
1455 TestControl::reset() {
1456 CommandOptions& options = CommandOptions::instance();
1457 basic_rate_control_.setAggressivity(options.getAggressivity());
1458 basic_rate_control_.setRate(options.getRate());
1459 renew_rate_control_.setAggressivity(options.getAggressivity());
1460 renew_rate_control_.setRate(options.getRenewRate());
1461 release_rate_control_.setAggressivity(options.getAggressivity());
1462 release_rate_control_.setRate(options.getReleaseRate());
1463
1464 transid_gen_.reset();
1465 last_report_ = microsec_clock::universal_time();
1466 // Actual generators will have to be set later on because we need to
1467 // get command line parameters first.
1468 setTransidGenerator(NumberGeneratorPtr());
1469 setMacAddrGenerator(NumberGeneratorPtr());
1470 first_packet_serverid_.clear();
1471 interrupted_ = false;
1472 }
1473
1474 int
1475 TestControl::run() {
1476 // Reset singleton state before test starts.
1477 reset();
1478
1479 CommandOptions& options = CommandOptions::instance();
1480 // Ip version is not set ONLY in case the command options
1481 // were not parsed. This surely means that parse() function
1482 // was not called prior to starting the test. This is fatal
1483 // error.
1484 if (options.getIpVersion() == 0) {
1485 isc_throw(InvalidOperation,
1486 "command options must be parsed before running a test");
1487 } else if (options.getIpVersion() == 4) {
1488 // Turn off packet queueing.
1489 IfaceMgr::instance().configureDHCPPacketQueue(AF_INET, data::ElementPtr());
1490 setTransidGenerator(NumberGeneratorPtr(new SequentialGenerator()));
1491 } else {
1492 // Turn off packet queueing.
1493 IfaceMgr::instance().configureDHCPPacketQueue(AF_INET6, data::ElementPtr());
1494 setTransidGenerator(NumberGeneratorPtr(new SequentialGenerator(0x00FFFFFF)));
1495 }
1496
1497 uint32_t clients_num = options.getClientsNum() == 0 ?
1498 1 : options.getClientsNum();
1499 setMacAddrGenerator(NumberGeneratorPtr(new SequentialGenerator(clients_num)));
1500
1501 // Diagnostics are command line options mainly.
1502 printDiagnostics();
1503 // Option factories have to be registered.
1504 registerOptionFactories();
1505 TestControlSocket socket(openSocket());
1506 if (!socket.valid_) {
1507 isc_throw(Unexpected, "invalid socket descriptor");
1508 }
1509 // Initialize packet templates.
1510 initPacketTemplates();
1511 // Initialize randomization seed.
1512 if (options.isSeeded()) {
1513 srandom(options.getSeed());
1514 } else {
1515 // Seed with current time.
1516 time_period duration(from_iso_string("20111231T235959"),
1517 microsec_clock::universal_time());
1518 srandom(duration.length().total_seconds()
1519 + duration.length().fractional_seconds());
1520 }
1521 // If user interrupts the program we will exit gracefully.
1522 signal(SIGINT, TestControl::handleInterrupt);
1523
1524 // Preload server with the number of packets.
1525 sendPackets(socket, options.getPreload(), true);
1526
1527 // Fork and run command specified with -w<wrapped-command>
1528 if (!options.getWrapped().empty()) {
1529 runWrapped();
1530 }
1531
1532 // Initialize Statistics Manager. Release previous if any.
1533 initializeStatsMgr();
1534 for (;;) {
1535 // Calculate number of packets to be sent to stay
1536 // catch up with rate.
1537 uint64_t packets_due = basic_rate_control_.getOutboundMessageCount();
1538 checkLateMessages(basic_rate_control_);
1539 if ((packets_due == 0) && testDiags('i')) {
1540 if (options.getIpVersion() == 4) {
1541 stats_mgr4_->incrementCounter("shortwait");
1542 } else if (options.getIpVersion() == 6) {
1543 stats_mgr6_->incrementCounter("shortwait");
1544 }
1545 }
1546
1547 // @todo: set non-zero timeout for packets once we implement
1548 // microseconds timeout in IfaceMgr.
1549 receivePackets(socket);
1550
1551 // If test period finished, maximum number of packet drops
1552 // has been reached or test has been interrupted we have to
1553 // finish the test.
1554 if (checkExitConditions()) {
1555 break;
1556 }
1557
1558 if (!hasLateExitCommenced()) {
1559 // Initiate new DHCP packet exchanges.
1560 sendPackets(socket, packets_due);
1561 }
1562
1563 // If -f<renew-rate> option was specified we have to check how many
1564 // Renew packets should be sent to catch up with a desired rate.
1565 if (options.getRenewRate() != 0) {
1566 uint64_t renew_packets_due =
1567 renew_rate_control_.getOutboundMessageCount();
1568 checkLateMessages(renew_rate_control_);
1569
1570 // Send multiple renews to satisfy the desired rate.
1571 if (options.getIpVersion() == 4) {
1572 sendMultipleRequests(socket, renew_packets_due);
1573 } else {
1574 sendMultipleMessages6(socket, DHCPV6_RENEW, renew_packets_due);
1575 }
1576 }
1577
1578 // If -F<release-rate> option was specified we have to check how many
1579 // Release messages should be sent to catch up with a desired rate.
1580 if ((options.getIpVersion() == 6) && (options.getReleaseRate() != 0)) {
1581 uint64_t release_packets_due =
1582 release_rate_control_.getOutboundMessageCount();
1583 checkLateMessages(release_rate_control_);
1584 // Send Release messages.
1585 sendMultipleMessages6(socket, DHCPV6_RELEASE, release_packets_due);
1586 }
1587
1588 // Report delay means that user requested printing number
1589 // of sent/received/dropped packets repeatedly.
1590 if (options.getReportDelay() > 0) {
1591 printIntermediateStats();
1592 }
1593
1594 // If we are sending Renews to the server, the Reply packets are cached
1595 // so as leases for which we send Renews can be identified. The major
1596 // issue with this approach is that most of the time we are caching
1597 // more packets than we actually need. This function removes excessive
1598 // Reply messages to reduce the memory and CPU utilization. Note that
1599 // searches in the long list of Reply packets increases CPU utilization.
1600 cleanCachedPackets();
1601 }
1602 printStats();
1603
1604 if (!options.getWrapped().empty()) {
1605 // true means that we execute wrapped command with 'stop' argument.
1606 runWrapped(true);
1607 }
1608
1609 // Print packet timestamps
1610 if (testDiags('t')) {
1611 if (options.getIpVersion() == 4) {
1612 stats_mgr4_->printTimestamps();
1613 } else if (options.getIpVersion() == 6) {
1614 stats_mgr6_->printTimestamps();
1615 }
1616 }
1617
1618 // Print server id.
1619 if (testDiags('s') && (first_packet_serverid_.size() > 0)) {
1620 std::cout << "Server id: " << vector2Hex(first_packet_serverid_) << std::endl;
1621 }
1622
1623 // Diagnostics flag 'e' means show exit reason.
1624 if (testDiags('e')) {
1625 std::cout << "Interrupted" << std::endl;
1626 }
1627 // Print packet templates. Even if -T options have not been specified the
1628 // dynamically build packet will be printed if at least one has been sent.
1629 if (testDiags('T')) {
1630 printTemplates();
1631 }
1632
1633 int ret_code = 0;
1634 // Check if any packet drops occurred.
1635 if (options.getIpVersion() == 4) {
1636 ret_code = stats_mgr4_->droppedPackets() ? 3 : 0;
1637 } else if (options.getIpVersion() == 6) {
1638 ret_code = stats_mgr6_->droppedPackets() ? 3 : 0;
1639 }
1640 return (ret_code);
1641 }
1642
1643 void
1644 TestControl::runWrapped(bool do_stop /*= false */) const {
1645 CommandOptions& options = CommandOptions::instance();
1646 if (!options.getWrapped().empty()) {
1647 pid_t pid = 0;
1648 signal(SIGCHLD, handleChild);
1649 pid = fork();
1650 if (pid < 0) {
1651 isc_throw(Unexpected, "unable to fork");
1652 } else if (pid == 0) {
1653 execlp(options.getWrapped().c_str(),
1654 do_stop ? "stop" : "start",
1655 NULL);
1656 }
1657 }
1658 }
1659
1660 void
1661 TestControl::saveFirstPacket(const Pkt4Ptr& pkt) {
1662 if (testDiags('T')) {
1663 if (template_packets_v4_.find(pkt->getType()) == template_packets_v4_.end()) {
1664 template_packets_v4_[pkt->getType()] = pkt;
1665 }
1666 }
1667 }
1668
1669 void
1670 TestControl::saveFirstPacket(const Pkt6Ptr& pkt) {
1671 if (testDiags('T')) {
1672 if (template_packets_v6_.find(pkt->getType()) == template_packets_v6_.end()) {
1673 template_packets_v6_[pkt->getType()] = pkt;
1674 }
1675 }
1676 }
1677
1678 void
1679 TestControl::sendDiscover4(const TestControlSocket& socket,
1680 const bool preload /*= false*/) {
1681 basic_rate_control_.updateSendTime();
1682 // Generate the MAC address to be passed in the packet.
1683 uint8_t randomized = 0;
1684 std::vector<uint8_t> mac_address = generateMacAddress(randomized);
1685 // Generate transaction id to be set for the new exchange.
1686 const uint32_t transid = generateTransid();
1687 Pkt4Ptr pkt4(new Pkt4(DHCPDISCOVER, transid));
1688 if (!pkt4) {
1689 isc_throw(Unexpected, "failed to create DISCOVER packet");
1690 }
1691
1692 // Delete the default Message Type option set by Pkt4
1693 pkt4->delOption(DHO_DHCP_MESSAGE_TYPE);
1694
1695 // Set options: DHCP_MESSAGE_TYPE and DHCP_PARAMETER_REQUEST_LIST
1696 OptionBuffer buf_msg_type;
1697 buf_msg_type.push_back(DHCPDISCOVER);
1698 pkt4->addOption(Option::factory(Option::V4, DHO_DHCP_MESSAGE_TYPE,
1699 buf_msg_type));
1700 pkt4->addOption(Option::factory(Option::V4,
1701 DHO_DHCP_PARAMETER_REQUEST_LIST));
1702
1703 // Set client's and server's ports as well as server's address,
1704 // and local (relay) address.
1705 setDefaults4(socket, pkt4);
1706
1707 // Set hardware address
1708 pkt4->setHWAddr(HTYPE_ETHER, mac_address.size(), mac_address);
1709
1710 // Set client identifier
1711 pkt4->addOption(generateClientId(pkt4->getHWAddr()));
1712
1713 // Add any extra options that user may have specified.
1714 addExtraOpts(pkt4);
1715
1716 pkt4->pack();
1717 IfaceMgr::instance().send(pkt4);
1718 if (!preload) {
1719 if (!stats_mgr4_) {
1720 isc_throw(InvalidOperation, "Statistics Manager for DHCPv4 "
1721 "hasn't been initialized");
1722 }
1723 stats_mgr4_->passSentPacket(StatsMgr4::XCHG_DO, pkt4);
1724 }
1725 saveFirstPacket(pkt4);
1726 }
1727
1728 void
1729 TestControl::sendDiscover4(const TestControlSocket& socket,
1730 const std::vector<uint8_t>& template_buf,
1731 const bool preload /* = false */) {
1732 basic_rate_control_.updateSendTime();
1733 // Get the first argument if multiple the same arguments specified
1734 // in the command line. First one refers to DISCOVER packets.
1735 const uint8_t arg_idx = 0;
1736 // Generate the MAC address to be passed in the packet.
1737 uint8_t randomized = 0;
1738 std::vector<uint8_t> mac_address = generateMacAddress(randomized);
1739 // Generate transaction id to be set for the new exchange.
1740 const uint32_t transid = generateTransid();
1741 // Get transaction id offset.
1742 size_t transid_offset = getTransactionIdOffset(arg_idx);
1743 // Get randomization offset.
1744 // We need to go back by HW_ETHER_LEN (MAC address length)
1745 // because this offset points to last octet of MAC address.
1746 size_t rand_offset = getRandomOffset(arg_idx) - HW_ETHER_LEN + 1;
1747 // Create temporary buffer with template contents. We will
1748 // modify this temporary buffer but we don't want to modify
1749 // the original template.
1750 std::vector<uint8_t> in_buf(template_buf.begin(),
1751 template_buf.end());
1752 // Check if we are not going out of bounds.
1753 if (rand_offset + HW_ETHER_LEN > in_buf.size()) {
1754 isc_throw(OutOfRange, "randomization offset is out of bounds");
1755 }
1756 PerfPkt4Ptr pkt4(new PerfPkt4(&in_buf[0], in_buf.size(),
1757 transid_offset,
1758 transid));
1759
1760 // Replace MAC address in the template with actual MAC address.
1761 pkt4->writeAt(rand_offset, mac_address.begin(), mac_address.end());
1762 // Create a packet from the temporary buffer.
1763 setDefaults4(socket, boost::static_pointer_cast<Pkt4>(pkt4));
1764 // Pack the input packet buffer to output buffer so as it can
1765 // be sent to server.
1766 pkt4->rawPack();
1767 IfaceMgr::instance().send(boost::static_pointer_cast<Pkt4>(pkt4));
1768 if (!preload) {
1769 if (!stats_mgr4_) {
1770 isc_throw(InvalidOperation, "Statistics Manager for DHCPv4 "
1771 "hasn't been initialized");
1772 }
1773 // Update packet stats.
1774 stats_mgr4_->passSentPacket(StatsMgr4::XCHG_DO,
1775 boost::static_pointer_cast<Pkt4>(pkt4));
1776 }
1777 saveFirstPacket(pkt4);
1778 }
1779
1780 bool
1781 TestControl::sendRequestFromAck(const TestControlSocket& socket) {
1782 // Update timestamp of last sent renewal.
1783 renew_rate_control_.updateSendTime();
1784
1785 // Get one of the recorded DHCPACK messages.
1786 Pkt4Ptr ack = ack_storage_.getRandom();
1787 if (!ack) {
1788 return (false);
1789 }
1790
1791 // Create message of the specified type.
1792 Pkt4Ptr msg = createRequestFromAck(ack);
1793 setDefaults4(socket, msg);
1794
1795 // Add any extra options that user may have specified.
1796 addExtraOpts(msg);
1797
1798 msg->pack();
1799 // And send it.
1800 IfaceMgr::instance().send(msg);
1801 if (!stats_mgr4_) {
1802 isc_throw(Unexpected, "Statistics Manager for DHCPv4 "
1803 "hasn't been initialized");
1804 }
1805 stats_mgr4_->passSentPacket(StatsMgr4::XCHG_RNA, msg);
1806 return (true);
1807 }
1808
1809
1810 bool
1811 TestControl::sendMessageFromReply(const uint16_t msg_type,
1812 const TestControlSocket& socket) {
1813 // We only permit Release or Renew messages to be sent using this function.
1814 if (msg_type != DHCPV6_RENEW && msg_type != DHCPV6_RELEASE) {
1815 isc_throw(isc::BadValue, "invalid message type " << msg_type
1816 << " to be sent, expected DHCPV6_RENEW or DHCPV6_RELEASE");
1817 }
1818 // We track the timestamp of last Release and Renew in different variables.
1819 if (msg_type == DHCPV6_RENEW) {
1820 renew_rate_control_.updateSendTime();
1821 } else {
1822 release_rate_control_.updateSendTime();
1823 }
1824 Pkt6Ptr reply = reply_storage_.getRandom();
1825 if (!reply) {
1826 return (false);
1827 }
1828 // Prepare the message of the specified type.
1829 Pkt6Ptr msg = createMessageFromReply(msg_type, reply);
1830 setDefaults6(socket, msg);
1831
1832 // Add any extra options that user may have specified.
1833 addExtraOpts(msg);
1834
1835 msg->pack();
1836 // And send it.
1837 IfaceMgr::instance().send(msg);
1838 if (!stats_mgr6_) {
1839 isc_throw(Unexpected, "Statistics Manager for DHCPv6 "
1840 "hasn't been initialized");
1841 }
1842 stats_mgr6_->passSentPacket((msg_type == DHCPV6_RENEW ? StatsMgr6::XCHG_RN
1843 : StatsMgr6::XCHG_RL), msg);
1844 return (true);
1845 }
1846
1847 void
1848 TestControl::sendRequest4(const TestControlSocket& socket,
1849 const dhcp::Pkt4Ptr& discover_pkt4,
1850 const dhcp::Pkt4Ptr& offer_pkt4) {
1851 // Use the same transaction id as the one used in the discovery packet.
1852 const uint32_t transid = discover_pkt4->getTransid();
1853 Pkt4Ptr pkt4(new Pkt4(DHCPREQUEST, transid));
1854
1855 // Use first flags indicates that we want to use the server
1856 // id captured in first packet.
1857 if (CommandOptions::instance().isUseFirst() &&
1858 (first_packet_serverid_.size() > 0)) {
1859 pkt4->addOption(Option::factory(Option::V4, DHO_DHCP_SERVER_IDENTIFIER,
1860 first_packet_serverid_));
1861 } else {
1862 OptionPtr opt_serverid =
1863 offer_pkt4->getOption(DHO_DHCP_SERVER_IDENTIFIER);
1864 if (!opt_serverid) {
1865 isc_throw(BadValue, "there is no SERVER_IDENTIFIER option "
1866 << "in OFFER message");
1867 }
1868 if (stats_mgr4_->getRcvdPacketsNum(StatsMgr4::XCHG_DO) == 1) {
1869 first_packet_serverid_ = opt_serverid->getData();
1870 }
1871 pkt4->addOption(opt_serverid);
1872 }
1873
1875 asiolink::IOAddress yiaddr = offer_pkt4->getYiaddr();
1876 if (!yiaddr.isV4()) {
1877 isc_throw(BadValue, "the YIADDR returned in OFFER packet is not "
1878 " IPv4 address");
1879 }
1880 OptionPtr opt_requested_address =
1881 OptionPtr(new Option(Option::V4, DHO_DHCP_REQUESTED_ADDRESS,
1882 OptionBuffer()));
1883 opt_requested_address->setUint32(yiaddr.toUint32());
1884 pkt4->addOption(opt_requested_address);
1885 OptionPtr opt_parameter_list =
1886 Option::factory(Option::V4, DHO_DHCP_PARAMETER_REQUEST_LIST);
1887 pkt4->addOption(opt_parameter_list);
1888 // Set client's and server's ports as well as server's address,
1889 // and local (relay) address.
1890 setDefaults4(socket, pkt4);
1891
1892 // Add any extra options that user may have specified.
1893 addExtraOpts(pkt4);
1894
1895 // Set hardware address
1896 pkt4->setHWAddr(offer_pkt4->getHWAddr());
1897 // Set client id.
1898 pkt4->addOption(generateClientId(pkt4->getHWAddr()));
1899 // Set elapsed time.
1900 uint32_t elapsed_time = getElapsedTime<Pkt4Ptr>(discover_pkt4, offer_pkt4);
1901 pkt4->setSecs(static_cast<uint16_t>(elapsed_time / 1000));
1902 // Prepare on wire data to send.
1903 pkt4->pack();
1904 IfaceMgr::instance().send(pkt4);
1905 if (!stats_mgr4_) {
1906 isc_throw(InvalidOperation, "Statistics Manager for DHCPv4 "
1907 "hasn't been initialized");
1908 }
1909 stats_mgr4_->passSentPacket(StatsMgr4::XCHG_RA, pkt4);
1910 saveFirstPacket(pkt4);
1911 }
1912
1913 void
1914 TestControl::sendRequest4(const TestControlSocket& socket,
1915 const std::vector<uint8_t>& template_buf,
1916 const dhcp::Pkt4Ptr& discover_pkt4,
1917 const dhcp::Pkt4Ptr& offer_pkt4) {
1918 // Get the second argument if multiple the same arguments specified
1919 // in the command line. Second one refers to REQUEST packets.
1920 const uint8_t arg_idx = 1;
1921 // Use the same transaction id as the one used in the discovery packet.
1922 const uint32_t transid = discover_pkt4->getTransid();
1923 // Get transaction id offset.
1924 size_t transid_offset = getTransactionIdOffset(arg_idx);
1925 // Get the offset of MAC's last octet.
1926 // We need to go back by HW_ETHER_LEN (MAC address length)
1927 // because this offset points to last octet of MAC address.
1928 size_t rand_offset = getRandomOffset(arg_idx) - HW_ETHER_LEN + 1;
1929 // Create temporary buffer from the template.
1930 std::vector<uint8_t> in_buf(template_buf.begin(),
1931 template_buf.end());
1932 // Check if given randomization offset is not out of bounds.
1933 if (rand_offset + HW_ETHER_LEN > in_buf.size()) {
1934 isc_throw(OutOfRange, "randomization offset is out of bounds");
1935 }
1936
1937 // Create packet from the temporary buffer.
1938 PerfPkt4Ptr pkt4(new PerfPkt4(&in_buf[0], in_buf.size(),
1939 transid_offset,
1940 transid));
1941
1942 // Set hardware address from OFFER packet received.
1943 HWAddrPtr hwaddr = offer_pkt4->getHWAddr();
1944 std::vector<uint8_t> mac_address(HW_ETHER_LEN, 0);
1945 uint8_t hw_len = hwaddr->hwaddr_.size();
1946 if (hw_len != 0) {
1947 memcpy(&mac_address[0], &hwaddr->hwaddr_[0],
1948 hw_len > HW_ETHER_LEN ? HW_ETHER_LEN : hw_len);
1949 }
1950 pkt4->writeAt(rand_offset, mac_address.begin(), mac_address.end());
1951
1952 // Set elapsed time.
1953 size_t elp_offset = getElapsedTimeOffset();
1954 uint32_t elapsed_time = getElapsedTime<Pkt4Ptr>(discover_pkt4, offer_pkt4);
1955 pkt4->writeValueAt<uint16_t>(elp_offset,
1956 static_cast<uint16_t>(elapsed_time / 1000));
1957
1958 // Get the actual server id offset.
1959 size_t sid_offset = getServerIdOffset();
1960 // Use first flags indicates that we want to use the server
1961 // id captured in first packet.
1962 if (CommandOptions::instance().isUseFirst() &&
1963 (first_packet_serverid_.size() > 0)) {
1964 boost::shared_ptr<LocalizedOption>
1965 opt_serverid(new LocalizedOption(Option::V4,
1966 DHO_DHCP_SERVER_IDENTIFIER,
1967 first_packet_serverid_,
1968 sid_offset));
1969 pkt4->addOption(opt_serverid);
1970 } else {
1971 // Copy the contents of server identifier received in
1972 // OFFER packet to put this into REQUEST.
1973 OptionPtr opt_serverid_offer =
1974 offer_pkt4->getOption(DHO_DHCP_SERVER_IDENTIFIER);
1975 if (!opt_serverid_offer) {
1976 isc_throw(BadValue, "there is no SERVER_IDENTIFIER option "
1977 << "in OFFER message");
1978 }
1979 boost::shared_ptr<LocalizedOption>
1980 opt_serverid(new LocalizedOption(Option::V4,
1981 DHO_DHCP_SERVER_IDENTIFIER,
1982 opt_serverid_offer->getData(),
1983 sid_offset));
1984 pkt4->addOption(opt_serverid);
1985 if (stats_mgr4_->getRcvdPacketsNum(StatsMgr4::XCHG_DO) == 1) {
1986 first_packet_serverid_ = opt_serverid_offer->getData();
1987 }
1988 }
1989
1991 asiolink::IOAddress yiaddr = offer_pkt4->getYiaddr();
1992 if (!yiaddr.isV4()) {
1993 isc_throw(BadValue, "the YIADDR returned in OFFER packet is not "
1994 " IPv4 address");
1995 }
1996
1997 // Get the actual offset of requested ip.
1998 size_t rip_offset = getRequestedIpOffset();
1999 // Place requested IP option at specified position (rip_offset).
2000 boost::shared_ptr<LocalizedOption>
2001 opt_requested_ip(new LocalizedOption(Option::V4,
2002 DHO_DHCP_REQUESTED_ADDRESS,
2003 OptionBuffer(),
2004 rip_offset));
2005 // The IOAddress is convertible to uint32_t and returns exactly what we need.
2006 opt_requested_ip->setUint32(yiaddr.toUint32());
2007 pkt4->addOption(opt_requested_ip);
2008
2009 setDefaults4(socket, boost::static_pointer_cast<Pkt4>(pkt4));
2010
2011 // Add any extra options that user may have specified.
2012 addExtraOpts(pkt4);
2013
2014 // Prepare on-wire data.
2015 pkt4->rawPack();
2016 IfaceMgr::instance().send(boost::static_pointer_cast<Pkt4>(pkt4));
2017 if (!stats_mgr4_) {
2018 isc_throw(InvalidOperation, "Statistics Manager for DHCPv4 "
2019 "hasn't been initialized");
2020 }
2021 // Update packet stats.
2022 stats_mgr4_->passSentPacket(StatsMgr4::XCHG_RA,
2023 boost::static_pointer_cast<Pkt4>(pkt4));
2024 saveFirstPacket(pkt4);
2025 }
2026
2027 void
2028 TestControl::sendRequest6(const TestControlSocket& socket,
2029 const Pkt6Ptr& advertise_pkt6) {
2030 const uint32_t transid = generateTransid();
2031 Pkt6Ptr pkt6(new Pkt6(DHCPV6_REQUEST, transid));
2032 // Set elapsed time.
2033 OptionPtr opt_elapsed_time =
2034 Option::factory(Option::V6, D6O_ELAPSED_TIME);
2035 pkt6->addOption(opt_elapsed_time);
2036 // Set client id.
2037 OptionPtr opt_clientid = advertise_pkt6->getOption(D6O_CLIENTID);
2038 if (!opt_clientid) {
2039 isc_throw(Unexpected, "client id not found in received packet");
2040 }
2041 pkt6->addOption(opt_clientid);
2042
2043 // Use first flags indicates that we want to use the server
2044 // id captured in first packet.
2045 if (CommandOptions::instance().isUseFirst() &&
2046 (first_packet_serverid_.size() > 0)) {
2047 pkt6->addOption(Option::factory(Option::V6, D6O_SERVERID,
2048 first_packet_serverid_));
2049 } else {
2050 OptionPtr opt_serverid = advertise_pkt6->getOption(D6O_SERVERID);
2051 if (!opt_serverid) {
2052 isc_throw(Unexpected, "server id not found in received packet");
2053 }
2054 if (stats_mgr6_->getRcvdPacketsNum(StatsMgr6::XCHG_SA) == 1) {
2055 first_packet_serverid_ = opt_serverid->getData();
2056 }
2057 pkt6->addOption(opt_serverid);
2058 }
2059
2060 // Copy IA_NA or IA_PD option from the Advertise message to the Request
2061 // message being sent to the server. This will throw exception if the
2062 // option to be copied is not found. Note that this function will copy
2063 // one of IA_NA or IA_PD options, depending on the lease-type value
2064 // specified in the command line.
2065 copyIaOptions(advertise_pkt6, pkt6);
2066
2067 // Set default packet data.
2068 setDefaults6(socket, pkt6);
2069
2070 // Add any extra options that user may have specified.
2071 addExtraOpts(pkt6);
2072
2073 // Prepare on-wire data.
2074 pkt6->pack();
2075 IfaceMgr::instance().send(pkt6);
2076 if (!stats_mgr6_) {
2077 isc_throw(InvalidOperation, "Statistics Manager for DHCPv6 "
2078 "hasn't been initialized");
2079 }
2080 stats_mgr6_->passSentPacket(StatsMgr6::XCHG_RR, pkt6);
2081 saveFirstPacket(pkt6);
2082 }
2083
2084 void
2085 TestControl::sendRequest6(const TestControlSocket& socket,
2086 const std::vector<uint8_t>& template_buf,
2087 const Pkt6Ptr& advertise_pkt6) {
2088 // Get the second argument if multiple the same arguments specified
2089 // in the command line. Second one refers to REQUEST packets.
2090 const uint8_t arg_idx = 1;
2091 // Generate transaction id.
2092 const uint32_t transid = generateTransid();
2093 // Get transaction id offset.
2094 size_t transid_offset = getTransactionIdOffset(arg_idx);
2095 PerfPkt6Ptr pkt6(new PerfPkt6(&template_buf[0], template_buf.size(),
2096 transid_offset, transid));
2097 // Set elapsed time.
2098 size_t elp_offset = getElapsedTimeOffset();
2099 boost::shared_ptr<LocalizedOption>
2100 opt_elapsed_time(new LocalizedOption(Option::V6, D6O_ELAPSED_TIME,
2101 OptionBuffer(), elp_offset));
2102 pkt6->addOption(opt_elapsed_time);
2103
2104 // Get the actual server id offset.
2105 size_t sid_offset = getServerIdOffset();
2106 // Use first flags indicates that we want to use the server
2107 // id captured in first packet.
2108 if (CommandOptions::instance().isUseFirst() &&
2109 (first_packet_serverid_.size() > 0)) {
2110 boost::shared_ptr<LocalizedOption>
2111 opt_serverid(new LocalizedOption(Option::V6,
2112 D6O_SERVERID,
2113 first_packet_serverid_,
2114 sid_offset));
2115 pkt6->addOption(opt_serverid);
2116
2117 } else {
2118 // Copy the contents of server identifier received in
2119 // ADVERTISE packet to put this into REQUEST.
2120 OptionPtr opt_serverid_advertise =
2121 advertise_pkt6->getOption(D6O_SERVERID);
2122 if (!opt_serverid_advertise) {
2123 isc_throw(BadValue, "there is no SERVERID option "
2124 << "in ADVERTISE message");
2125 }
2126 boost::shared_ptr<LocalizedOption>
2127 opt_serverid(new LocalizedOption(Option::V6,
2128 D6O_SERVERID,
2129 opt_serverid_advertise->getData(),
2130 sid_offset));
2131 pkt6->addOption(opt_serverid);
2132 if (stats_mgr6_->getRcvdPacketsNum(StatsMgr6::XCHG_SA) == 1) {
2133 first_packet_serverid_ = opt_serverid_advertise->getData();
2134 }
2135 }
2136 // Set IA_NA
2137 boost::shared_ptr<Option6IA> opt_ia_na_advertise =
2138 boost::static_pointer_cast<Option6IA>(advertise_pkt6->getOption(D6O_IA_NA));
2139 if (!opt_ia_na_advertise) {
2140 isc_throw(Unexpected, "DHCPv6 IA_NA option not found in received "
2141 "packet");
2142 }
2143 size_t addr_offset = getRequestedIpOffset();
2144 boost::shared_ptr<LocalizedOption>
2145 opt_ia_na(new LocalizedOption(opt_ia_na_advertise, addr_offset));
2146 if (!opt_ia_na->valid()) {
2147 isc_throw(BadValue, "Option IA_NA in advertise packet is invalid");
2148 }
2149 pkt6->addOption(opt_ia_na);
2150 // Set server id.
2151 OptionPtr opt_serverid_advertise = advertise_pkt6->getOption(D6O_SERVERID);
2152 if (!opt_serverid_advertise) {
2153 isc_throw(Unexpected, "DHCPV6 SERVERID option not found in received "
2154 "packet");
2155 }
2156 size_t srvid_offset = getServerIdOffset();
2157 boost::shared_ptr<LocalizedOption>
2158 opt_serverid(new LocalizedOption(Option::V6, D6O_SERVERID,
2159 opt_serverid_advertise->getData(),
2160 srvid_offset));
2161 pkt6->addOption(opt_serverid);
2162 // Get randomization offset.
2163 size_t rand_offset = getRandomOffset(arg_idx);
2164 OptionPtr opt_clientid_advertise = advertise_pkt6->getOption(D6O_CLIENTID);
2165 if (!opt_clientid_advertise) {
2166 isc_throw(Unexpected, "DHCPV6 CLIENTID option not found in received packet");
2167 }
2168 rand_offset -= (opt_clientid_advertise->len() - 1);
2169 // Set client id.
2170 boost::shared_ptr<LocalizedOption>
2171 opt_clientid(new LocalizedOption(Option::V6, D6O_CLIENTID,
2172 opt_clientid_advertise->getData(),
2173 rand_offset));
2174 pkt6->addOption(opt_clientid);
2175 // Set default packet data.
2176 setDefaults6(socket, pkt6);
2177
2178 // Add any extra options that user may have specified.
2179 addExtraOpts(pkt6);
2180
2181 // Prepare on wire data.
2182 pkt6->rawPack();
2183 // Send packet.
2184 IfaceMgr::instance().send(pkt6);
2185 if (!stats_mgr6_) {
2186 isc_throw(InvalidOperation, "Statistics Manager for DHCPv6 "
2187 "hasn't been initialized");
2188 }
2189 // Update packet stats.
2190 stats_mgr6_->passSentPacket(StatsMgr6::XCHG_RR, pkt6);
2191
2192 // When 'T' diagnostics flag is specified it means that user requested
2193 // printing packet contents. It will be just one (first) packet which
2194 // contents will be printed. Here we check if this packet has been already
2195 // collected. If it hasn't we save this packet so as we can print its
2196 // contents when test is finished.
2197 if (testDiags('T') &&
2198 (template_packets_v6_.find(DHCPV6_REQUEST) == template_packets_v6_.end())) {
2199 template_packets_v6_[DHCPV6_REQUEST] = pkt6;
2200 }
2201 }
2202
2203 void
2204 TestControl::sendSolicit6(const TestControlSocket& socket,
2205 const bool preload /*= false*/) {
2206 basic_rate_control_.updateSendTime();
2207 // Generate DUID to be passed to the packet
2208 uint8_t randomized = 0;
2209 std::vector<uint8_t> duid = generateDuid(randomized);
2210 // Generate transaction id to be set for the new exchange.
2211 const uint32_t transid = generateTransid();
2212 Pkt6Ptr pkt6(new Pkt6(DHCPV6_SOLICIT, transid));
2213 if (!pkt6) {
2214 isc_throw(Unexpected, "failed to create SOLICIT packet");
2215 }
2216 pkt6->addOption(Option::factory(Option::V6, D6O_ELAPSED_TIME));
2217 if (CommandOptions::instance().isRapidCommit()) {
2218 pkt6->addOption(Option::factory(Option::V6, D6O_RAPID_COMMIT));
2219 }
2220 pkt6->addOption(Option::factory(Option::V6, D6O_CLIENTID, duid));
2221 pkt6->addOption(Option::factory(Option::V6, D6O_ORO));
2222
2223 // Depending on the lease-type option specified, we should request
2224 // IPv6 address (with IA_NA) or IPv6 prefix (IA_PD) or both.
2225
2226 // IA_NA
2227 if (CommandOptions::instance().getLeaseType()
2228 .includes(CommandOptions::LeaseType::ADDRESS)) {
2229 pkt6->addOption(Option::factory(Option::V6, D6O_IA_NA));
2230 }
2231 // IA_PD
2232 if (CommandOptions::instance().getLeaseType()
2233 .includes(CommandOptions::LeaseType::PREFIX)) {
2234 pkt6->addOption(Option::factory(Option::V6, D6O_IA_PD));
2235 }
2236
2237 setDefaults6(socket, pkt6);
2238
2239 // Add any extra options that user may have specified.
2240 addExtraOpts(pkt6);
2241
2242 pkt6->pack();
2243 IfaceMgr::instance().send(pkt6);
2244 if (!preload) {
2245 if (!stats_mgr6_) {
2246 isc_throw(InvalidOperation, "Statistics Manager for DHCPv6 "
2247 "hasn't been initialized");
2248 }
2249 stats_mgr6_->passSentPacket(StatsMgr6::XCHG_SA, pkt6);
2250 }
2251
2252 saveFirstPacket(pkt6);
2253 }
2254
2255 void
2256 TestControl::sendSolicit6(const TestControlSocket& socket,
2257 const std::vector<uint8_t>& template_buf,
2258 const bool preload /*= false*/) {
2259 basic_rate_control_.updateSendTime();
2260 const int arg_idx = 0;
2261 // Get transaction id offset.
2262 size_t transid_offset = getTransactionIdOffset(arg_idx);
2263 // Generate transaction id to be set for the new exchange.
2264 const uint32_t transid = generateTransid();
2265 // Create packet.
2266 PerfPkt6Ptr pkt6(new PerfPkt6(&template_buf[0], template_buf.size(),
2267 transid_offset, transid));
2268 if (!pkt6) {
2269 isc_throw(Unexpected, "failed to create SOLICIT packet");
2270 }
2271 size_t rand_offset = getRandomOffset(arg_idx);
2272 // randomized will pick number of bytes randomized so we can
2273 // just use part of the generated duid and substitute a few bytes
2275 uint8_t randomized = 0;
2276 std::vector<uint8_t> duid = generateDuid(randomized);
2277 if (rand_offset > template_buf.size()) {
2278 isc_throw(OutOfRange, "randomization offset is out of bounds");
2279 }
2280 // Store random part of the DUID into the packet.
2281 pkt6->writeAt(rand_offset - randomized + 1,
2282 duid.end() - randomized, duid.end());
2283
2284 // Prepare on-wire data.
2285 pkt6->rawPack();
2286 setDefaults6(socket, pkt6);
2287
2288 // Add any extra options that user may have specified.
2289 addExtraOpts(pkt6);
2290
2291 // Send solicit packet.
2292 IfaceMgr::instance().send(pkt6);
2293 if (!preload) {
2294 if (!stats_mgr6_) {
2295 isc_throw(InvalidOperation, "Statistics Manager for DHCPv6 "
2296 "hasn't been initialized");
2297 }
2298 // Update packet stats.
2299 stats_mgr6_->passSentPacket(StatsMgr6::XCHG_SA, pkt6);
2300 }
2301 saveFirstPacket(pkt6);
2302 }
2303
2304
2305 void
2306 TestControl::setDefaults4(const TestControlSocket& socket,
2307 const Pkt4Ptr& pkt) {
2308 CommandOptions& options = CommandOptions::instance();
2309 // Interface name.
2310 IfacePtr iface = IfaceMgr::instance().getIface(socket.ifindex_);
2311 if (iface == NULL) {
2312 isc_throw(BadValue, "unable to find interface with given index");
2313 }
2314 pkt->setIface(iface->getName());
2315 // Interface index.
2316 pkt->setIndex(socket.ifindex_);
2317 // Local client's port (68)
2318 pkt->setLocalPort(DHCP4_CLIENT_PORT);
2319 // Server's port (67)
2320 pkt->setRemotePort(DHCP4_SERVER_PORT);
2321 // The remote server's name or IP.
2322 pkt->setRemoteAddr(IOAddress(options.getServerName()));
2323 // Set local address.
2324 pkt->setLocalAddr(IOAddress(socket.addr_));
2325 // Set relay (GIADDR) address to local address.
2326 pkt->setGiaddr(IOAddress(socket.addr_));
2327 // Pretend that we have one relay (which is us).
2328 pkt->setHops(1);
2329 }
2330
2331 void
2332 TestControl::setDefaults6(const TestControlSocket& socket,
2333 const Pkt6Ptr& pkt) {
2334 CommandOptions& options = CommandOptions::instance();
2335 // Interface name.
2336 IfacePtr iface = IfaceMgr::instance().getIface(socket.ifindex_);
2337 if (iface == NULL) {
2338 isc_throw(BadValue, "unable to find interface with given index");
2339 }
2340 pkt->setIface(iface->getName());
2341 // Interface index.
2342 pkt->setIndex(socket.ifindex_);
2343 // Local client's port (547)
2344 pkt->setLocalPort(DHCP6_CLIENT_PORT);
2345 // Server's port (548)
2346 pkt->setRemotePort(DHCP6_SERVER_PORT);
2347 // Set local address.
2348 pkt->setLocalAddr(socket.addr_);
2349 // The remote server's name or IP.
2350 pkt->setRemoteAddr(IOAddress(options.getServerName()));
2351
2352 // only act as a relay agent when told so.
2353 // TODO: support more level of encapsulation, at the moment we only support
2354 // one, via -A1 option.
2355 if (options.isUseRelayedV6()) {
2356 Pkt6::RelayInfo relay_info;
2357 relay_info.msg_type_ = DHCPV6_RELAY_FORW;
2358 relay_info.hop_count_ = 1;
2359 relay_info.linkaddr_ = IOAddress(socket.addr_);
2360 relay_info.peeraddr_ = IOAddress(socket.addr_);
2361 pkt->addRelayInfo(relay_info);
2362 }
2363 }
2364
2365 void
2366 TestControl::addExtraOpts(const Pkt4Ptr& pkt) {
2367 // All all extra options that the user may have specified
2368 CommandOptions& options = CommandOptions::instance();
2369 const dhcp::OptionCollection& extra_opts = options.getExtraOpts();
2370 for (auto entry : extra_opts) {
2371 pkt->addOption(entry.second);
2372 }
2373 }
2374
2375 void
2376 TestControl::addExtraOpts(const Pkt6Ptr& pkt) {
2377 // All all extra options that the user may have specified
2378 CommandOptions& options = CommandOptions::instance();
2379 const dhcp::OptionCollection& extra_opts = options.getExtraOpts();
2380 for (auto entry : extra_opts) {
2381 pkt->addOption(entry.second);
2382 }
2383 }
2384
2385 bool
2386 TestControl::testDiags(const char diag) const {
2387 std::string diags(CommandOptions::instance().getDiags());
2388 if (diags.find(diag) != std::string::npos) {
2389 return (true);
2390 }
2391 return (false);
2392 }
2393
2394 } // namespace perfdhcp
2395 } // namespace isc
isc::perfdhcp::TestControl::run
int run()
brief\ Run performance test.
Definition: test_control.cc:1475
isc::dhcp::Pkt6
Represents a DHCPv6 packet.
Definition: pkt6.h:44
isc::perfdhcp::TestControl::TestControlSocket
Socket wrapper structure.
Definition: test_control.h:160
isc::perfdhcp::PerfPkt6Ptr
boost::shared_ptr< PerfPkt6 > PerfPkt6Ptr
Definition: perf_pkt6.h:126
isc::perfdhcp::TestControl::byte2Hex
std::string byte2Hex(const uint8_t b) const
Convert binary value to hex string.
Definition: test_control.cc:224
isc::dhcp::OptionBuffer
std::vector< uint8_t > OptionBuffer
buffer types used in DHCP code.
Definition: option.h:25
isc::perfdhcp::TestControl::getTransactionIdOffset
int getTransactionIdOffset(const int arg_idx) const
Return transaction id offset in a packet.
Definition: test_control.cc:752
isc::dhcp::Pkt6::RelayInfo::hop_count_
uint8_t hop_count_
number of traversed relays (up to 32)
Definition: pkt6.h:95
isc::perfdhcp::TestControl::initializeStatsMgr
void initializeStatsMgr()
Initializes Statistics Manager.
Definition: test_control.cc:788
isc::data::copy
ElementPtr copy(ConstElementPtr from, int level)
Copy the data up to a nesting level.
Definition: data.cc:1114
isc::perfdhcp::CommandOptions::printCommandLine
void printCommandLine() const
Print command line arguments.
Definition: command_options.cc:920
isc::Unexpected
A generic exception that is thrown when an unexpected error condition occurs.
Definition: exceptions/exceptions.h:153
isc::perfdhcp::StatsMgr::XCHG_RN
@ XCHG_RN
DHCPv6 RENEW-REPLY.
Definition: bin/perfdhcp/stats_mgr.h:121
isc::perfdhcp::CommandOptions::DO_SA
@ DO_SA
Definition: command_options.h:105
isc::perfdhcp::CommandOptions::getServerIdOffset
int getServerIdOffset() const
Returns template offset for server-ID.
Definition: command_options.h:324
isc::perfdhcp::CommandOptions::instance
static CommandOptions & instance()
CommandOptions is a singleton class.
Definition: command_options.cc:99
isc::perfdhcp::TestControl::StatsMgr4
StatsMgr< dhcp::Pkt4 > StatsMgr4
Statistics Manager for DHCPv4.
Definition: test_control.h:125
isc::perfdhcp::TestControl::StatsMgr6Ptr
boost::shared_ptr< StatsMgr6 > StatsMgr6Ptr
Pointer to Statistics Manager for DHCPv6.
Definition: test_control.h:131
isc::perfdhcp::StatsMgr::XCHG_RNA
@ XCHG_RNA
DHCPv4 REQUEST-ACK (renewal)
Definition: bin/perfdhcp/stats_mgr.h:118
isc::perfdhcp::RateControl
A message sending rate control class for perfdhcp.
Definition: rate_control.h:38
isc::dhcp::SocketInfo::addr_
isc::asiolink::IOAddress addr_
Definition: socket_info.h:21
iface_mgr.h
isc::perfdhcp::TestControl::sendMultipleMessages6
uint64_t sendMultipleMessages6(const TestControlSocket &socket, const uint32_t msg_type, const uint64_t msg_num)
Send number of DHCPv6 Renew or Release messages to the server.
Definition: test_control.cc:991
isc::perfdhcp::LocalizedOption
DHCP option at specific offset.
Definition: localized_option.h:37
isc::perfdhcp::TestControl::setMacAddrGenerator
void setMacAddrGenerator(const NumberGeneratorPtr &generator)
Set new MAC address generator.
Definition: test_control.h:281
isc::perfdhcp::TestControl::TestControl
TestControl()
Default constructor.
Definition: test_control.cc:141
isc::perfdhcp::TestControl::stats_mgr4_
StatsMgr4Ptr stats_mgr4_
Statistics Manager 4.
Definition: test_control.h:1148
isc::perfdhcp::CommandOptions::getExchangeMode
ExchangeMode getExchangeMode() const
Returns packet exchange mode.
Definition: command_options.h:142
isc::perfdhcp::TestControl::sendRequestFromAck
bool sendRequestFromAck(const TestControlSocket &socket)
Send DHCPv4 renew (DHCPREQUEST) using specified socket.
Definition: test_control.cc:1781
isc::perfdhcp::TestControl::TestControlSocket::ifindex_
uint16_t ifindex_
Interface index.
Definition: test_control.h:162
isc::perfdhcp::late_exit_target_time_
ptime late_exit_target_time_
Definition: test_control.cc:45
isc::perfdhcp::TestControl::copyIaOptions
void copyIaOptions(const dhcp::Pkt6Ptr &pkt_from, dhcp::Pkt6Ptr &pkt_to)
Copies IA_NA or IA_PD option from one packet to another.
Definition: test_control.cc:194
isc::perfdhcp::TestControl::printDiagnostics
void printDiagnostics() const
Print main diagnostics data.
Definition: test_control.cc:1003
isc::perfdhcp::TestControl::checkExitConditions
bool checkExitConditions() const
Check if test exit conditions fulfilled.
Definition: test_control.cc:233
isc::asiolink::IOAddress
The IOAddress class represents an IP addresses (version agnostic)
Definition: io_address.h:45
libdhcp++.h
io_address.h
isc::perfdhcp::TestControl::receivePackets
uint64_t receivePackets(const TestControlSocket &socket)
Receive DHCPv4 or DHCPv6 packets from the server.
Definition: test_control.cc:1328
option6_ia.h
D6O_IA_PD
@ D6O_IA_PD
Definition: dhcp6.h:45
D6O_ELAPSED_TIME
@ D6O_ELAPSED_TIME
Definition: dhcp6.h:28
isc::dhcp::DHCPDISCOVER
@ DHCPDISCOVER
Definition: dhcp4.h:228
isc::perfdhcp::TestControl::macaddr_gen_
NumberGeneratorPtr macaddr_gen_
Numbers generator for MAC address.
Definition: test_control.h:1155
isc::perfdhcp::TestControl::vector2Hex
std::string vector2Hex(const std::vector< uint8_t > &vec, const std::string &separator="") const
Convert vector in hexadecimal string.
Definition: test_control.cc:1165
isc::perfdhcp::CommandOptions::MacAddrsVector
std::vector< std::vector< uint8_t > > MacAddrsVector
A vector holding MAC addresses.
Definition: command_options.h:29
isc::dhcp::Pkt6::RelayInfo::peeraddr_
isc::asiolink::IOAddress peeraddr_
fixed field in relay-forw/relay-reply
Definition: pkt6.h:97
isc::dhcp::DHO_SUBNET_MASK
@ DHO_SUBNET_MASK
Definition: dhcp4.h:70
isc::perfdhcp::CommandOptions::getRandomOffset
std::vector< int > getRandomOffset() const
Returns template offsets for rnd.
Definition: command_options.h:314
DHCPV6_RENEW
@ DHCPV6_RENEW
Definition: dhcp6.h:217
isc::dhcp::DHO_DOMAIN_NAME_SERVERS
@ DHO_DOMAIN_NAME_SERVERS
Definition: dhcp4.h:75
isc::perfdhcp::CommandOptions::isUseRelayedV6
bool isUseRelayedV6() const
Check if generated DHCPv6 messages should appear as relayed.
Definition: command_options.h:284
isc::perfdhcp::TestControl::template_packets_v6_
std::map< uint8_t, dhcp::Pkt6Ptr > template_packets_v6_
Definition: test_control.h:1166
isc::perfdhcp::CommandOptions::getTemplateFiles
std::vector< std::string > getTemplateFiles() const
Returns template file names.
Definition: command_options.h:289
isc::perfdhcp::TestControl::generateDuid
std::vector< uint8_t > generateDuid(uint8_t &randomized)
Generate DUID.
Definition: test_control.cc:592
isc::perfdhcp::TestControl::setDefaults6
void setDefaults6(const TestControlSocket &socket, const dhcp::Pkt6Ptr &pkt)
Set default DHCPv6 packet parameters.
Definition: test_control.cc:2332
isc::perfdhcp::StatsMgr::ExchangeType
ExchangeType
DHCP packet exchange types.
Definition: bin/perfdhcp/stats_mgr.h:115
isc::perfdhcp::CommandOptions::isInterface
bool isInterface() const
Checks if interface name was used.
Definition: command_options.h:234
isc::perfdhcp::TestControl::registerOptionFactories6
void registerOptionFactories6() const
Register option factory functions for DHCPv6.
Definition: test_control.cc:1400
isc::asiolink
Definition: dummy_io_cb.h:17
isc::perfdhcp::TestControl::printRate
void printRate() const
Print rate statistics.
Definition: test_control.cc:1087
isc::perfdhcp::TestControl::testDiags
bool testDiags(const char diag) const
Find if diagnostic flag has been set.
Definition: test_control.cc:2386
isc::perfdhcp::TestControl::getCurrentTimeout
uint32_t getCurrentTimeout() const
Returns a timeout for packet reception.
Definition: test_control.cc:639
isc::perfdhcp::TestControl::getElapsedTime
uint32_t getElapsedTime(const T &pkt1, const T &pkt2)
Calculate elapsed time between two packets.
Definition: test_control.cc:680
isc::perfdhcp::TestControl::registerOptionFactories
void registerOptionFactories() const
Register option factory functions for DHCPv4 or DHCPv6.
Definition: test_control.cc:1439
isc::perfdhcp::TestControl::runWrapped
void runWrapped(bool do_stop=false) const
Run wrapped command.
Definition: test_control.cc:1644
isc::perfdhcp::CommandOptions::getRequestedIpOffset
int getRequestedIpOffset() const
Returns template offset for requested IP.
Definition: command_options.h:329
isc::perfdhcp::CommandOptions::getRenewRate
int getRenewRate() const
Returns a rate at which DHCPv6 Renew messages are sent.
Definition: command_options.h:157
isc::perfdhcp::CommandOptions::DORA_SARR
@ DORA_SARR
Definition: command_options.h:106
isc::perfdhcp::CommandOptions::getMaxDropPercentage
std::vector< double > getMaxDropPercentage() const
Returns maximal percentage of drops.
Definition: command_options.h:221
isc::perfdhcp::RateControl::setAggressivity
void setAggressivity(const int aggressivity)
Sets the value of aggressivity.
Definition: rate_control.cc:123
DHCPV6_REQUEST
@ DHCPV6_REQUEST
Definition: dhcp6.h:215
isc::dhcp::OptionCollection
std::multimap< unsigned int, OptionPtr > OptionCollection
A collection of DHCP (v4 or v6) options.
Definition: option.h:41
isc::perfdhcp::TestControl::handleInterrupt
static void handleInterrupt(int sig)
Handle interrupt signal.
Definition: test_control.cc:770
perf_pkt6.h
D6O_NAME_SERVERS
@ D6O_NAME_SERVERS
Definition: dhcp6.h:43
isc::perfdhcp::CommandOptions::ExchangeMode
ExchangeMode
2-way (cmd line param -i) or 4-way exchanges
Definition: command_options.h:104
isc::perfdhcp::TestControl::TemplateBuffer
std::vector< uint8_t > TemplateBuffer
Packet template buffer.
Definition: test_control.h:135
isc::perfdhcp::TestControl::hasLateExitCommenced
bool hasLateExitCommenced() const
Check if the program is in that period where the program was bound to exit, but was delayed by lateEx...
Definition: test_control.cc:48
isc::perfdhcp::TestControl::StatsMgr4Ptr
boost::shared_ptr< StatsMgr4 > StatsMgr4Ptr
Pointer to Statistics Manager for DHCPv4;.
Definition: test_control.h:127
isc::perfdhcp::CommandOptions::getAggressivity
int getAggressivity() const
Returns aggressivity value.
Definition: command_options.h:244
isc::perfdhcp::TestControl::checkLateMessages
void checkLateMessages(RateControl &rate_control)
Increments counter of late sent messages if required.
Definition: test_control.cc:147
isc::perfdhcp::CommandOptions
Command Options.
Definition: command_options.h:25
isc::Exception
This is a base class for exceptions thrown from the DNS library module.
Definition: exceptions/exceptions.h:23
isc::perfdhcp::PerfPkt6
PerfPkt6 (DHCPv6 packet)
Definition: perf_pkt6.h:40
dhcp4.h
isc::perfdhcp::TestControl::release_rate_control_
RateControl release_rate_control_
A rate control class for Release messages.
Definition: test_control.h:1144
isc::perfdhcp::TestControl::basic_rate_control_
RateControl basic_rate_control_
A rate control class for Discover and Solicit messages.
Definition: test_control.h:1140
isc::perfdhcp::TestControl::registerOptionFactories4
void registerOptionFactories4() const
Register option factory functions for DHCPv4.
Definition: test_control.cc:1380
isc::perfdhcp::TestControl::openSocket
int openSocket() const
Open socket to communicate with DHCP server.
Definition: test_control.cc:839
DHCPV6_REPLY
@ DHCPV6_REPLY
Definition: dhcp6.h:219
isc
Defines the logger used by the top-level component of kea-dhcp-ddns.
Definition: agent_parser.cc:144
isc::perfdhcp::StatsMgr::XCHG_RA
@ XCHG_RA
DHCPv4 REQUEST-ACK.
Definition: bin/perfdhcp/stats_mgr.h:117
isc::perfdhcp::TestControl::getTemplateBuffer
TemplateBuffer getTemplateBuffer(const size_t idx) const
Return template buffer.
Definition: test_control.cc:744
isc::dhcp::HWAddrPtr
boost::shared_ptr< HWAddr > HWAddrPtr
Shared pointer to a hardware address structure.
Definition: hwaddr.h:154
DHCPV6_RELEASE
@ DHCPV6_RELEASE
Definition: dhcp6.h:220
isc::Exception::what
virtual const char * what() const
Returns a C-style character string of the cause of the exception.
Definition: exceptions/exceptions.cc:32
isc::dhcp::Pkt4Ptr
boost::shared_ptr< Pkt4 > Pkt4Ptr
A pointer to Pkt4 object.
Definition: pkt4.h:546
isc::dhcp::DHO_DHCP_REQUESTED_ADDRESS
@ DHO_DHCP_REQUESTED_ADDRESS
Definition: dhcp4.h:119
isc::perfdhcp::TestControl::stats_mgr6_
StatsMgr6Ptr stats_mgr6_
Statistics Manager 6.
Definition: test_control.h:1149
isc::dhcp::DHO_DHCP_SERVER_IDENTIFIER
@ DHO_DHCP_SERVER_IDENTIFIER
Definition: dhcp4.h:123
isc::perfdhcp::CommandOptions::getIpVersion
uint8_t getIpVersion() const
Returns IP version.
Definition: command_options.h:137
perfdhcp
isc_throw
#define isc_throw(type, stream)
A shortcut macro to insert known values into exception arguments.
Definition: exceptions/exceptions.h:192
isc::perfdhcp::CommandOptions::getServerName
std::string getServerName() const
Returns server name.
Definition: command_options.h:349
isc::InvalidOperation
A generic exception that is thrown if a function is called in a prohibited way.
Definition: exceptions/exceptions.h:143
isc::dhcp::DHO_DHCP_MESSAGE_TYPE
@ DHO_DHCP_MESSAGE_TYPE
Definition: dhcp4.h:122
isc::InvalidParameter
A generic exception that is thrown if a parameter given to a method or function is considered invalid...
Definition: exceptions/exceptions.h:124
isc::perfdhcp::TestControl::factoryRapidCommit6
static dhcp::OptionPtr factoryRapidCommit6(dhcp::Option::Universe u, uint16_t type, const dhcp::OptionBuffer &buf)
Factory function to create DHCPv6 RAPID_COMMIT option instance.
Definition: test_control.cc:496
isc::perfdhcp::RateControl::isLateSent
bool isLateSent() const
Returns the value of the late send flag.
Definition: rate_control.h:104
isc::BadValue
A generic exception that is thrown if a parameter given to a method is considered invalid in that con...
Definition: exceptions/exceptions.h:132
isc::perfdhcp::TestControl::getServerIdOffset
int getServerIdOffset() const
Return server id offset in a packet.
Definition: test_control.cc:734
isc::perfdhcp::TestControl::setTransidGenerator
void setTransidGenerator(const NumberGeneratorPtr &generator)
Set new transaction id generator.
Definition: test_control.h:270
isc::perfdhcp::TestControl::generateMacAddress
std::vector< uint8_t > generateMacAddress(uint8_t &randomized)
Generate MAC address.
Definition: test_control.cc:535
isc::perfdhcp::CommandOptions::getPeriod
int getPeriod() const
Returns test period.
Definition: command_options.h:197
isc::perfdhcp::TestControl::reset
void reset()
Resets internal state of the object.
Definition: test_control.cc:1455
isc::perfdhcp::TestControl
Test Control class.
Definition: test_control.h:121
isc::perfdhcp::TestControl::sendMessageFromReply
bool sendMessageFromReply(const uint16_t msg_type, const TestControlSocket &socket)
Send DHCPv6 Renew or Release message using specified socket.
Definition: test_control.cc:1811
isc::perfdhcp::CommandOptions::isBroadcast
bool isBroadcast() const
Checks if broadcast address is to be used.
Definition: command_options.h:269
isc::perfdhcp::CommandOptions::getElapsedTimeOffset
int getElapsedTimeOffset() const
Returns template offset for elapsed time.
Definition: command_options.h:319
D6O_RAPID_COMMIT
@ D6O_RAPID_COMMIT
Definition: dhcp6.h:34
isc::perfdhcp::CommandOptions::getTransactionIdOffset
std::vector< int > getTransactionIdOffset() const
brief Returns template offsets for xid.
Definition: command_options.h:309
isc::perfdhcp::TestControl::last_report_
boost::posix_time::ptime last_report_
Last intermediate report time.
Definition: test_control.h:1146
isc::perfdhcp::TestControl::printIntermediateStats
void printIntermediateStats()
Print intermediate statistics.
Definition: test_control.cc:1124
isc::dhcp::DHO_ROUTERS
@ DHO_ROUTERS
Definition: dhcp4.h:72
isc::perfdhcp::TestControl::factoryElapsedTime6
static dhcp::OptionPtr factoryElapsedTime6(dhcp::Option::Universe u, uint16_t type, const dhcp::OptionBuffer &buf)
Factory function to create DHCPv6 ELAPSED_TIME option.
Definition: test_control.cc:444
isc::perfdhcp::TestControl::initPacketTemplates
void initPacketTemplates()
Reads packet templates from files.
Definition: test_control.cc:775
isc::perfdhcp::TestControl::processReceivedPacket6
void processReceivedPacket6(const TestControlSocket &socket, const dhcp::Pkt6Ptr &pkt6)
Process received DHCPv6 packet.
Definition: test_control.cc:1272
isc::perfdhcp::TestControl::factoryGeneric
static dhcp::OptionPtr factoryGeneric(dhcp::Option::Universe u, uint16_t type, const dhcp::OptionBuffer &buf)
Factory function to create generic option.
Definition: test_control.cc:457
isc::perfdhcp::TestControl::sendPackets
void sendPackets(const TestControlSocket &socket, const uint64_t packets_num, const bool preload=false)
Send number of packets to initiate new exchanges.
Definition: test_control.cc:939
isc::perfdhcp::CommandOptions::getDropTime
std::vector< double > getDropTime() const
Returns drop time.
Definition: command_options.h:205
test_control.h
isc::util::OutputBuffer::getLength
size_t getLength() const
Return the length of data written in the buffer.
Definition: buffer.h:403
D6O_SERVERID
@ D6O_SERVERID
Definition: dhcp6.h:22
isc::dhcp
Definition: ctrl_dhcp4_srv.cc:75
isc::perfdhcp::TestControl::addExtraOpts
void addExtraOpts(const dhcp::Pkt4Ptr &pkt4)
Inserts extra options specified by user.
Definition: test_control.cc:2366
isc::perfdhcp::TestControl::cleanCachedPackets
void cleanCachedPackets()
Removes cached DHCPv6 Reply packets every second.
Definition: test_control.cc:162
DHCPV6_RELAY_FORW
@ DHCPV6_RELAY_FORW
Definition: dhcp6.h:224
isc::perfdhcp::TestControl::createMessageFromReply
dhcp::Pkt6Ptr createMessageFromReply(const uint16_t msg_type, const dhcp::Pkt6Ptr &reply)
Creates DHCPv6 message from the Reply packet.
Definition: test_control.cc:404
isc::util::OutputBuffer
The OutputBuffer class is a buffer abstraction for manipulating mutable data.
Definition: buffer.h:294
isc::perfdhcp::TestControl::getRequestedIpOffset
int getRequestedIpOffset() const
Return requested ip offset in a packet.
Definition: test_control.cc:704
isc::perfdhcp::TestControl::StatsMgr6
StatsMgr< dhcp::Pkt6 > StatsMgr6
Statistics Manager for DHCPv6.
Definition: test_control.h:129
isc::perfdhcp::TestControl::sendSolicit6
void sendSolicit6(const TestControlSocket &socket, const bool preload=false)
Send DHCPv6 SOLICIT message.
Definition: test_control.cc:2204
isc::asiolink::IOAddress::isV6Multicast
bool isV6Multicast() const
checks whether and address is IPv6 and is multicast
Definition: io_address.cc:103
isc::perfdhcp::TestControl::getRandomOffset
int getRandomOffset(const int arg_idx) const
Return randomization offset in a packet.
Definition: test_control.cc:694
isc::perfdhcp::TestControl::template_buffers_
TemplateBufferCollection template_buffers_
Packet template buffers.
Definition: test_control.h:1161
isc::perfdhcp::CommandOptions::getRate
int getRate() const
Returns exchange rate.
Definition: command_options.h:152
isc::perfdhcp::TestControl::number_generator_
isc::util::random::UniformRandomIntegerGenerator number_generator_
Generate uniformly distributed integers in range of [min, max].
Definition: test_control.h:298
isc::perfdhcp::CommandOptions::getMacsFromFile
const MacAddrsVector & getMacsFromFile() const
Returns reference to a vector of MAC addresses read from a file.
Definition: command_options.h:304
isc::perfdhcp::CommandOptions::getExtraOpts
const isc::dhcp::OptionCollection & getExtraOpts() const
Returns extra options to be inserted.
Definition: command_options.h:344
isc::dhcp::Pkt6::RelayInfo::msg_type_
uint8_t msg_type_
message type (RELAY-FORW oro RELAY-REPL)
Definition: pkt6.h:94
perf_pkt4.h
isc::perfdhcp::TestControl::TestControlSocket::valid_
bool valid_
Is socket valid.
Definition: test_control.h:165
isc::perfdhcp::TestControl::ack_storage_
PacketStorage< dhcp::Pkt4 > ack_storage_
A storage for DHCPACK messages.
Definition: test_control.h:1151
isc::perfdhcp::CommandOptions::isRapidCommit
bool isRapidCommit() const
Check if rapid commit option used.
Definition: command_options.h:274
isc::perfdhcp::TestControl::setDefaults4
void setDefaults4(const TestControlSocket &socket, const dhcp::Pkt4Ptr &pkt)
Set default DHCPv4 packet parameters.
Definition: test_control.cc:2306
isc::dhcp::Pkt4
Represents DHCPv4 packet.
Definition: pkt4.h:38
isc::perfdhcp::CommandOptions::getSeed
uint32_t getSeed() const
Returns random seed.
Definition: command_options.h:264
isc::dhcp::Pkt6::RelayInfo
structure that describes a single relay information
Definition: pkt6.h:85
isc::perfdhcp::TestControl::getSentPacketsNum
uint64_t getSentPacketsNum(ExchangeType xchg_type) const
Get number of sent packets.
Definition: test_control.cc:724
isc::perfdhcp::TestControl::SequentialGenerator
Sequential numbers generator class.
Definition: test_control.h:215
isc::perfdhcp::TestControl::sendDiscover4
void sendDiscover4(const TestControlSocket &socket, const bool preload=false)
Send DHCPv4 DISCOVER message.
Definition: test_control.cc:1679
isc::perfdhcp::TestControl::printTemplate
void printTemplate(const uint8_t packet_type) const
Print template information.
Definition: test_control.cc:1018
D6O_IA_NA
@ D6O_IA_NA
Definition: dhcp6.h:23
isc::perfdhcp::TestControl::readPacketTemplate
void readPacketTemplate(const std::string &file_name)
Read DHCP message template from file.
Definition: test_control.cc:1181
isc::perfdhcp::TestControl::HW_ETHER_LEN
static const uint8_t HW_ETHER_LEN
Length of the Ethernet HW address (MAC) in bytes.
Definition: test_control.h:247
isc::perfdhcp::TestControl::saveFirstPacket
void saveFirstPacket(const dhcp::Pkt4Ptr &pkt)
Save the first DHCPv4 sent packet of the specified type.
Definition: test_control.cc:1661
isc::perfdhcp::TestControl::sendMultipleRequests
uint64_t sendMultipleRequests(const TestControlSocket &socket, const uint64_t msg_num)
Send number of DHCPREQUEST (renew) messages to a server.
Definition: test_control.cc:980
isc::perfdhcp::TestControl::template_packets_v4_
std::map< uint8_t, dhcp::Pkt4Ptr > template_packets_v4_
First packets send.
Definition: test_control.h:1165
isc::perfdhcp::TestControl::printTemplates
void printTemplates() const
Print templates information.
Definition: test_control.cc:1075
DHCPV6_ADVERTISE
@ DHCPV6_ADVERTISE
Definition: dhcp6.h:214
isc::perfdhcp::CommandOptions::getNumRequests
std::vector< int > getNumRequests() const
Returns maximum number of exchanges.
Definition: command_options.h:192
isc::perfdhcp::RateControl::setRate
void setRate(const int rate)
Sets the new rate.
Definition: rate_control.cc:132
isc::dhcp::SocketInfo
Holds information about socket.
Definition: socket_info.h:19
isc::perfdhcp::TestControl::factoryRequestList4
static dhcp::OptionPtr factoryRequestList4(dhcp::Option::Universe u, uint16_t type, const dhcp::OptionBuffer &buf)
Factory function to create DHCPv4 Request List option.
Definition: test_control.cc:515
isc::perfdhcp::RateControl::getDue
boost::posix_time::ptime getDue() const
Returns current due time to send next message.
Definition: rate_control.h:56
isc::perfdhcp::TestControl::NumberGeneratorPtr
boost::shared_ptr< NumberGenerator > NumberGeneratorPtr
The default generator pointer.
Definition: test_control.h:212
asiolink
A wrapper interface for the ASIO library.
isc::perfdhcp::TestControl::createRequestFromAck
dhcp::Pkt4Ptr createRequestFromAck(const dhcp::Pkt4Ptr &ack)
Creates DHCPREQUEST from a DHCPACK message.
Definition: test_control.cc:388
isc::perfdhcp::StatsMgr::XCHG_SA
@ XCHG_SA
DHCPv6 SOLICIT-ADVERTISE.
Definition: bin/perfdhcp/stats_mgr.h:119
isc::perfdhcp::CommandOptions::getPreload
int getPreload() const
Returns number of preload exchanges.
Definition: command_options.h:239
isc::perfdhcp::RateControl::updateSendTime
void updateSendTime()
Sets the timestamp of the last sent message to current time.
Definition: rate_control.cc:148
isc::dhcp::DHO_HOST_NAME
@ DHO_HOST_NAME
Definition: dhcp4.h:81
isc::asiolink::IOAddress::toUint32
uint32_t toUint32() const
Converts IPv4 address to uint32_t.
Definition: io_address.cc:111
isc::perfdhcp::TestControl::processReceivedPacket4
void processReceivedPacket4(const TestControlSocket &socket, const dhcp::Pkt4Ptr &pkt4)
Process received DHCPv4 packet.
Definition: test_control.cc:1229
isc::perfdhcp::CommandOptions::getReleaseRate
int getReleaseRate() const
Returns a rate at which DHCPv6 Release messages are sent.
Definition: command_options.h:162
isc::perfdhcp::TestControl::instance
static TestControl & instance()
TestControl is a singleton class.
Definition: test_control.cc:136
isc::perfdhcp::CommandOptions::LeaseType::ADDRESS
@ ADDRESS
Definition: command_options.h:42
isc::dhcp::DHCPOFFER
@ DHCPOFFER
Definition: dhcp4.h:229
isc::perfdhcp::StatsMgr::XCHG_RR
@ XCHG_RR
DHCPv6 REQUEST-REPLY.
Definition: bin/perfdhcp/stats_mgr.h:120
check_valgrind.h
isc::perfdhcp::CommandOptions::getExitWaitTime
int getExitWaitTime() const
Returns the time in microseconds to delay the program by.
Definition: command_options.h:254
isc::OutOfRange
A generic exception that is thrown if a parameter given to a method would refer to or modify out-of-r...
Definition: exceptions/exceptions.h:115
isc::dhcp::SocketInfo::sockfd_
int sockfd_
IPv4 or IPv6.
Definition: socket_info.h:26
isc::perfdhcp::CommandOptions::LeaseType::PREFIX
@ PREFIX
Definition: command_options.h:43
isc::perfdhcp::TestControl::factoryIana6
static dhcp::OptionPtr factoryIana6(dhcp::Option::Universe u, uint16_t type, const dhcp::OptionBuffer &buf)
Factory function to create IA_NA option.
Definition: test_control.cc:464
isc::perfdhcp::TestControl::sendRequest6
void sendRequest6(const TestControlSocket &socket, const dhcp::Pkt6Ptr &advertise_pkt6)
Send DHCPv6 REQUEST message.
Definition: test_control.cc:2028
isc::perfdhcp::CommandOptions::getWrapped
std::string getWrapped() const
Returns wrapped command.
Definition: command_options.h:339
isc::perfdhcp::TestControl::handleChild
static void handleChild(int sig)
Handle child signal.
Definition: test_control.cc:762
D6O_ORO
@ D6O_ORO
Definition: dhcp6.h:26
isc::asiolink::IOAddress::getFamily
short getFamily() const
Returns the address family.
Definition: io_address.cc:86
isc::perfdhcp::CommandOptions::getClientsNum
uint32_t getClientsNum() const
Returns number of simulated clients.
Definition: command_options.h:172
isc::dhcp::OptionPtr
boost::shared_ptr< Option > OptionPtr
Definition: option.h:37
isc::perfdhcp::PerfPkt4Ptr
boost::shared_ptr< PerfPkt4 > PerfPkt4Ptr
Definition: perf_pkt4.h:126
isc::dhcp::HTYPE_ETHER
@ HTYPE_ETHER
Ethernet 10Mbps.
Definition: dhcp4.h:56
isc::dhcp::DHO_DOMAIN_NAME
@ DHO_DOMAIN_NAME
Definition: dhcp4.h:84
exceptions.h
isc::perfdhcp::TestControl::waitToExit
bool waitToExit() const
Delay the exit by a fixed given time to catch up to all exchanges that were already started.
Definition: test_control.cc:53
isc::perfdhcp::CommandOptions::getMaxDrop
std::vector< int > getMaxDrop() const
Returns maximum drops number.
Definition: command_options.h:213
isc::perfdhcp::CommandOptions::getDuidTemplate
std::vector< uint8_t > getDuidTemplate() const
Returns DUID template.
Definition: command_options.h:182
isc::perfdhcp::CommandOptions::getLocalPort
int getLocalPort() const
Returns local port number.
Definition: command_options.h:249
D6O_CLIENTID
@ D6O_CLIENTID
Definition: dhcp6.h:21
isc::perfdhcp::TestControl::factoryOptionRequestOption6
static dhcp::OptionPtr factoryOptionRequestOption6(dhcp::Option::Universe u, uint16_t type, const dhcp::OptionBuffer &buf)
Factory function to create DHCPv6 ORO option.
Definition: test_control.cc:502
isc::util::OutputBuffer::getData
const void * getData() const
Return a pointer to the head of the data stored in the buffer.
Definition: buffer.h:401
isc::perfdhcp::OptionNotFound
Exception thrown when the required option is not found in a packet.
Definition: test_control.h:54
isc::perfdhcp::StatsMgr::XCHG_RL
@ XCHG_RL
DHCPv6 RELEASE-REPLY.
Definition: bin/perfdhcp/stats_mgr.h:122
isc::dhcp::DHCPACK
@ DHCPACK
Definition: dhcp4.h:232
isc::dhcp::Option::Universe
Universe
defines option universe DHCPv4 or DHCPv6
Definition: option.h:67
isc::data::ElementPtr
boost::shared_ptr< Element > ElementPtr
Definition: data.h:20
isc::perfdhcp::CommandOptions::isSeeded
bool isSeeded() const
Checks if seed provided.
Definition: command_options.h:259
isc::perfdhcp::TestControl::generateClientId
dhcp::OptionPtr generateClientId(const dhcp::HWAddrPtr &hwaddr) const
Generate DHCPv4 client identifier from HW address.
Definition: test_control.cc:583
isc::perfdhcp::TestControl::renew_rate_control_
RateControl renew_rate_control_
A rate control class for Renew messages.
Definition: test_control.h:1142
isc::perfdhcp::TestControl::getRcvdPacketsNum
uint64_t getRcvdPacketsNum(ExchangeType xchg_type) const
Get number of received packets.
Definition: test_control.cc:714
isc::dhcp::DHO_DHCP_CLIENT_IDENTIFIER
@ DHO_DHCP_CLIENT_IDENTIFIER
Definition: dhcp4.h:130
D6O_DOMAIN_SEARCH
@ D6O_DOMAIN_SEARCH
Definition: dhcp6.h:44
isc::perfdhcp::TestControl::printStats
void printStats() const
Print performance statistics.
Definition: test_control.cc:1140
isc::dhcp::DHO_TIME_OFFSET
@ DHO_TIME_OFFSET
Definition: dhcp4.h:71
isc::dhcp::DHO_DHCP_PARAMETER_REQUEST_LIST
@ DHO_DHCP_PARAMETER_REQUEST_LIST
Definition: dhcp4.h:124
isc::perfdhcp::CommandOptions::getMacTemplate
std::vector< uint8_t > getMacTemplate() const
Returns MAC address template.
Definition: command_options.h:177
isc::perfdhcp::StatsMgr::XCHG_DO
@ XCHG_DO
DHCPv4 DISCOVER-OFFER.
Definition: bin/perfdhcp/stats_mgr.h:116
isc::perfdhcp::CommandOptions::getReportDelay
int getReportDelay() const
Returns delay between two performance reports.
Definition: command_options.h:167
isc::dhcp::Option
Definition: option.h:58
isc::perfdhcp::TestControl::sendRequest4
void sendRequest4(const TestControlSocket &socket, const dhcp::Pkt4Ptr &discover_pkt4, const dhcp::Pkt4Ptr &offer_pkt4)
Send DHCPv4 REQUEST message.
Definition: test_control.cc:1848
isc::dhcp::Pkt6::RelayInfo::linkaddr_
isc::asiolink::IOAddress linkaddr_
fixed field in relay-forw/relay-reply
Definition: pkt6.h:96
command_options.h
isc::asiolink::IOAddress::isV4
bool isV4() const
Convenience function to check for an IPv4 address.
Definition: io_address.h:103
isc::perfdhcp::CommandOptions::getLocalName
std::string getLocalName() const
Returns local address or interface name.
Definition: command_options.h:226
isc::perfdhcp::PerfPkt4
PerfPkt4 (DHCPv4 packet)
Definition: perf_pkt4.h:40
isc::perfdhcp::TestControl::factoryIapd6
static dhcp::OptionPtr factoryIapd6(dhcp::Option::Universe u, uint16_t type, const dhcp::OptionBuffer &buf)
Factory function to create IA_PD option.
Definition: test_control.cc:480
isc::perfdhcp::RateControl::getOutboundMessageCount
uint64_t getOutboundMessageCount()
Returns number of messages to be sent "now".
Definition: rate_control.cc:36
isc::dhcp::IfacePtr
boost::shared_ptr< Iface > IfacePtr
Definition: iface_mgr.h:457
isc::dhcp::Pkt6Ptr
boost::shared_ptr< Pkt6 > Pkt6Ptr
A pointer to Pkt6 packet.
Definition: pkt6.h:28
isc::dhcp::DHCPREQUEST
@ DHCPREQUEST
Definition: dhcp4.h:230
isc::perfdhcp::TestControl::ExchangeType
StatsMgr ::ExchangeType ExchangeType
Packet exchange type.
Definition: test_control.h:133
isc::perfdhcp::TestControl::first_packet_serverid_
dhcp::OptionBuffer first_packet_serverid_
Buffer holding server id received in first packet.
Definition: test_control.h:1158
DHCPV6_SOLICIT
@ DHCPV6_SOLICIT
Definition: dhcp6.h:213
isc::perfdhcp::TestControl::reply_storage_
PacketStorage< dhcp::Pkt6 > reply_storage_
A storage for reply messages.
Definition: test_control.h:1152
isc::perfdhcp::TestControl::interrupted_
static bool interrupted_
Is program interrupted.
Definition: test_control.h:1168
isc::dhcp::DHO_BROADCAST_ADDRESS
@ DHO_BROADCAST_ADDRESS
Definition: dhcp4.h:97
isc::perfdhcp::TestControl::getElapsedTimeOffset
int getElapsedTimeOffset() const
Return elapsed time offset in a packet.
Definition: test_control.cc:669
isc::perfdhcp::TestControl::generateTransid
uint32_t generateTransid()
generate transaction id.
Definition: test_control.h:508
isc::perfdhcp::TestControl::transid_gen_
NumberGeneratorPtr transid_gen_
Transaction id generator.
Definition: test_control.h:1154
isc::perfdhcp::TestControl::TestControlSocket::~TestControlSocket
~TestControlSocket()
Destructor of the socket wrapper class.
Definition: test_control.cc:113