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
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>
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
34using namespace std;
35using namespace boost::posix_time;
36using namespace isc;
37using namespace isc::dhcp;
38using namespace isc::asiolink;
39
40namespace isc {
41namespace perfdhcp {
42
43bool TestControl::interrupted_ = false;
44
45ptime late_exit_target_time_ = ptime(not_a_date_time);
46
47bool
49 return !late_exit_target_time_.is_not_a_date_time();
50}
51
52bool
54 static ptime exit_time = ptime(not_a_date_time);
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
75bool
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
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
114 IfacePtr iface = IfaceMgr::instance().getIface(ifindex_);
115 if (iface) {
116 iface->delSocket(sockfd_);
117 }
118}
119
120void
121TestControl::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
137 static TestControl test_control;
138 return (test_control);
139}
140
142 : number_generator_(0, CommandOptions::instance().getMacsFromFile().size()) {
143 reset();
144}
145
146void
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')) {
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
161void
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
193void
194TestControl::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()
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()
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
223std::string
224TestControl::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
232bool
234 if (interrupted_) {
235 return (true);
236 }
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) {
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
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 }
397 msg->setCiaddr(ack->getYiaddr());
398 msg->setHWAddr(ack->getHWAddr());
399 msg->addOption(generateClientId(msg->getHWAddr()));
400 return (msg);
401}
402
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
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) {
450 OptionBuffer(2, 0))));
451 }
453 "elapsed time option buffer size has to be 0 or 2");
454}
455
458 const OptionBuffer& buf) {
459 OptionPtr opt(new Option(u, type, buf));
460 return (opt);
461}
462
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
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
497 const OptionBuffer&) {
499}
500
503 uint16_t,
504 const OptionBuffer&) {
505 const uint8_t buf_array[] = {
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
516 uint16_t type,
517 const OptionBuffer& buf) {
518 const uint8_t buf_array[] = {
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
534std::vector<uint8_t>
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
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());
588 client_id)));
589}
590
591std::vector<uint8_t>
592TestControl::generateDuid(uint8_t& randomized) {
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
638uint32_t
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)) {
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)) {
663 }
664 // Return the timeout in microseconds.
665 return (time_period(now, due).length().total_microseconds());
666}
667
668int
670 int elp_offset = CommandOptions::instance().getIpVersion() == 4 ?
671 DHCPV4_ELAPSED_TIME_OFFSET : DHCPV6_ELAPSED_TIME_OFFSET;
674 }
675 return (elp_offset);
676}
677
678template<class T>
679uint32_t
680TestControl::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
693int
694TestControl::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
703int
705 int rip_offset = CommandOptions::instance().getIpVersion() == 4 ?
706 DHCPV4_REQUESTED_IP_OFFSET : DHCPV6_IA_NA_OFFSET;
709 }
710 return (rip_offset);
711}
712
713uint64_t
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
723uint64_t
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
733int
735 int srvid_offset = CommandOptions::instance().getIpVersion() == 4 ?
736 DHCPV4_SERVERID_OFFSET : DHCPV6_SERVERID_OFFSET;
739 }
740 return (srvid_offset);
741}
742
744TestControl::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
751int
752TestControl::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
761void
763 int status = 0;
764 while (wait3(&status, WNOHANG, NULL) > 0) {
765 // continue
766 }
767}
768
769void
771 interrupted_ = true;
772}
773
774void
776 template_packets_v4_.clear();
777 template_packets_v6_.clear();
778 template_buffers_.clear();
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) {
784 }
785}
786
787void
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]);
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]);
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
838int
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()) {
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.
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) {
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 =
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) {
929 "unable to enable multicast on socket " << sock
930 << ". errno = " << errno);
931 }
932 }
933 }
934
935 return (sock);
936}
937
938void
940 const uint64_t packets_num,
941 const bool preload /* = false */) {
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
979uint64_t
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
990uint64_t
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
1002void
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
1017void
1018TestControl::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
1074void
1077 if (options.getIpVersion() == 4) {
1080 } else if (options.getIpVersion() == 6) {
1083 }
1084}
1085
1086void
1088 double rate = 0;
1090 std::string exchange_name = "4-way exchanges";
1091 if (options.getIpVersion() == 4) {
1092 StatsMgr4::ExchangeType xchg_type =
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 =
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
1123void
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
1139void
1141 printRate();
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
1164std::string
1165TestControl::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
1180void
1181TestControl::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
1228void
1230 const Pkt4Ptr& pkt4) {
1231 if (pkt4->getType() == DHCPOFFER) {
1232 Pkt4Ptr discover_pkt4(stats_mgr4_->passRcvdPacket(StatsMgr4::XCHG_DO,
1233 pkt4));
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
1271void
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));
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
1327uint64_t
1329 bool receiving = true;
1330 uint64_t received = 0;
1331 while (receiving) {
1332 if (CommandOptions::instance().getIpVersion() == 4) {
1333 Pkt4Ptr pkt4;
1334 try {
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 {
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
1379void
1381 static bool factories_registered = false;
1382 if (!factories_registered) {
1383 // DHCP_MESSAGE_TYPE option factory.
1387 // DHCP_SERVER_IDENTIFIER option factory.
1391 // DHCP_PARAMETER_REQUEST_LIST option factory.
1395 }
1396 factories_registered = true;
1397}
1398
1399void
1401 static bool factories_registered = false;
1402 if (!factories_registered) {
1403 // D60_ELAPSED_TIME
1407 // D6O_RAPID_COMMIT
1411 // D6O_ORO (option request option) factory.
1413 D6O_ORO,
1415 // D6O_CLIENTID option factory.
1419 // D6O_SERVERID option factory.
1423 // D6O_IA_NA option factory.
1425 D6O_IA_NA,
1427
1428 // D6O_IA_PD option factory.
1430 D6O_IA_PD,
1432
1433
1434 }
1435 factories_registered = true;
1436}
1437
1438void
1441 switch(options.getIpVersion()) {
1442 case 4:
1444 break;
1445 case 6:
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
1454void
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.
1470 first_packet_serverid_.clear();
1471 interrupted_ = false;
1472}
1473
1474int
1476 // Reset singleton state before test starts.
1477 reset();
1478
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) {
1486 "command options must be parsed before running a test");
1487 } else if (options.getIpVersion() == 4) {
1488 // Turn off packet queueing.
1491 } else {
1492 // Turn off packet queueing.
1495 }
1496
1497 uint32_t clients_num = options.getClientsNum() == 0 ?
1498 1 : options.getClientsNum();
1500
1501 // Diagnostics are command line options mainly.
1503 // Option factories have to be registered.
1505 TestControlSocket socket(openSocket());
1506 if (!socket.valid_) {
1507 isc_throw(Unexpected, "invalid socket descriptor");
1508 }
1509 // Initialize packet templates.
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.
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();
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 =
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 =
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) {
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.
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')) {
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
1643void
1644TestControl::runWrapped(bool do_stop /*= false */) const {
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
1660void
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
1669void
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
1678void
1680 const bool preload /*= false*/) {
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);
1699 buf_msg_type));
1700 pkt4->addOption(Option::factory(Option::V4,
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
1728void
1730 const std::vector<uint8_t>& template_buf,
1731 const bool preload /* = false */) {
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
1780bool
1782 // Update timestamp of last sent renewal.
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
1810bool
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) {
1821 } else {
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
1847void
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)) {
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 =
1882 OptionBuffer()));
1883 opt_requested_address->setUint32(yiaddr.toUint32());
1884 pkt4->addOption(opt_requested_address);
1885 OptionPtr opt_parameter_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
1913void
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,
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,
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,
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
2027void
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 =
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,
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
2084void
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,
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,
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') &&
2200 }
2201}
2202
2203void
2205 const bool preload /*= false*/) {
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()
2229 pkt6->addOption(Option::factory(Option::V6, D6O_IA_NA));
2230 }
2231 // IA_PD
2232 if (CommandOptions::instance().getLeaseType()
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
2255void
2257 const std::vector<uint8_t>& template_buf,
2258 const bool preload /*= false*/) {
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
2305void
2307 const Pkt4Ptr& pkt) {
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
2331void
2333 const Pkt6Ptr& pkt) {
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
2365void
2367 // All all extra options that the user may have specified
2369 const dhcp::OptionCollection& extra_opts = options.getExtraOpts();
2370 for (auto entry : extra_opts) {
2371 pkt->addOption(entry.second);
2372 }
2373}
2374
2375void
2377 // All all extra options that the user may have specified
2379 const dhcp::OptionCollection& extra_opts = options.getExtraOpts();
2380 for (auto entry : extra_opts) {
2381 pkt->addOption(entry.second);
2382 }
2383}
2384
2385bool
2386TestControl::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
A generic exception that is thrown if a parameter given to a method is considered invalid in that con...
This is a base class for exceptions thrown from the DNS library module.
virtual const char * what() const
Returns a C-style character string of the cause of the exception.
A generic exception that is thrown if a function is called in a prohibited way.
A generic exception that is thrown if a parameter given to a method or function is considered invalid...
A generic exception that is thrown if a parameter given to a method would refer to or modify out-of-r...
A generic exception that is thrown when an unexpected error condition occurs.
int openSocketFromAddress(const isc::asiolink::IOAddress &addr, const uint16_t port)
Opens UDP/IP socket and binds to address specified.
Definition: iface_mgr.cc:843
int openSocketFromIface(const std::string &ifname, const uint16_t port, const uint8_t family)
Opens UDP/IP socket and binds it to interface specified.
Definition: iface_mgr.cc:802
Pkt6Ptr receive6(uint32_t timeout_sec, uint32_t timeout_usec=0)
Receive IPv4 packets or data from external sockets.
Definition: iface_mgr.cc:1188
static IfaceMgr & instance()
IfaceMgr is a singleton class.
Definition: iface_mgr.cc:53
bool configureDHCPPacketQueue(const uint16_t family, data::ConstElementPtr queue_control)
Configures DHCP packet queue.
Definition: iface_mgr.cc:1712
int openSocketFromRemoteAddress(const isc::asiolink::IOAddress &remote_addr, const uint16_t port)
Opens UDP/IP socket to be used to connect to remote address.
Definition: iface_mgr.cc:866
IfacePtr getIface(int ifindex)
Returns interface specified interface index.
Definition: iface_mgr.cc:752
bool send(const Pkt6Ptr &pkt)
Sends an IPv6 packet.
Definition: iface_mgr.cc:953
Pkt4Ptr receive4(uint32_t timeout_sec, uint32_t timeout_usec=0)
Receive IPv4 packets or data from external sockets.
Definition: iface_mgr.cc:977
static void OptionFactoryRegister(Option::Universe u, uint16_t type, Option::Factory *factory)
Registers factory method that produces options of specific option types.
Definition: libdhcp++.cc:830
static OptionPtr factory(Option::Universe u, uint16_t type, const OptionBuffer &buf)
Factory function to create instance of option.
Definition: option.cc:29
Universe
defines option universe DHCPv4 or DHCPv6
Definition: option.h:67
Represents DHCPv4 packet.
Definition: pkt4.h:38
Represents a DHCPv6 packet.
Definition: pkt6.h:44
int getAggressivity() const
Returns aggressivity value.
int getServerIdOffset() const
Returns template offset for server-ID.
std::string getWrapped() const
Returns wrapped command.
int getRenewRate() const
Returns a rate at which DHCPv6 Renew messages are sent.
uint8_t getIpVersion() const
Returns IP version.
std::vector< uint8_t > getDuidTemplate() const
Returns DUID template.
int getRate() const
Returns exchange rate.
bool isRapidCommit() const
Check if rapid commit option used.
std::string getLocalName() const
Returns local address or interface name.
const isc::dhcp::OptionCollection & getExtraOpts() const
Returns extra options to be inserted.
int getLocalPort() const
Returns local port number.
int getReportDelay() const
Returns delay between two performance reports.
std::vector< int > getRandomOffset() const
Returns template offsets for rnd.
std::vector< double > getMaxDropPercentage() const
Returns maximal percentage of drops.
ExchangeMode
2-way (cmd line param -i) or 4-way exchanges
const MacAddrsVector & getMacsFromFile() const
Returns reference to a vector of MAC addresses read from a file.
bool isBroadcast() const
Checks if broadcast address is to be used.
std::vector< std::string > getTemplateFiles() const
Returns template file names.
std::vector< int > getTransactionIdOffset() const
brief Returns template offsets for xid.
std::vector< std::vector< uint8_t > > MacAddrsVector
A vector holding MAC addresses.
int getElapsedTimeOffset() const
Returns template offset for elapsed time.
std::string getServerName() const
Returns server name.
std::vector< int > getNumRequests() const
Returns maximum number of exchanges.
bool isUseRelayedV6() const
Check if generated DHCPv6 messages should appear as relayed.
int getExitWaitTime() const
Returns the time in microseconds to delay the program by.
uint32_t getClientsNum() const
Returns number of simulated clients.
int getPreload() const
Returns number of preload exchanges.
bool isSeeded() const
Checks if seed provided.
static CommandOptions & instance()
CommandOptions is a singleton class.
std::vector< double > getDropTime() const
Returns drop time.
std::vector< int > getMaxDrop() const
Returns maximum drops number.
int getPeriod() const
Returns test period.
int getReleaseRate() const
Returns a rate at which DHCPv6 Release messages are sent.
uint32_t getSeed() const
Returns random seed.
ExchangeMode getExchangeMode() const
Returns packet exchange mode.
void printCommandLine() const
Print command line arguments.
bool isInterface() const
Checks if interface name was used.
std::vector< uint8_t > getMacTemplate() const
Returns MAC address template.
int getRequestedIpOffset() const
Returns template offset for requested IP.
DHCP option at specific offset.
Exception thrown when the required option is not found in a packet.
Definition: test_control.h:54
PerfPkt4 (DHCPv4 packet)
Definition: perf_pkt4.h:40
PerfPkt6 (DHCPv6 packet)
Definition: perf_pkt6.h:40
A message sending rate control class for perfdhcp.
Definition: rate_control.h:38
void setAggressivity(const int aggressivity)
Sets the value of aggressivity.
void updateSendTime()
Sets the timestamp of the last sent message to current time.
boost::posix_time::ptime getDue() const
Returns current due time to send next message.
Definition: rate_control.h:56
bool isLateSent() const
Returns the value of the late send flag.
Definition: rate_control.h:104
void setRate(const int rate)
Sets the new rate.
uint64_t getOutboundMessageCount()
Returns number of messages to be sent "now".
Definition: rate_control.cc:36
ExchangeType
DHCP packet exchange types.
@ XCHG_SA
DHCPv6 SOLICIT-ADVERTISE.
@ XCHG_RNA
DHCPv4 REQUEST-ACK (renewal)
@ XCHG_RA
DHCPv4 REQUEST-ACK.
@ XCHG_RL
DHCPv6 RELEASE-REPLY.
@ XCHG_RR
DHCPv6 REQUEST-REPLY.
@ XCHG_RN
DHCPv6 RENEW-REPLY.
@ XCHG_DO
DHCPv4 DISCOVER-OFFER.
Sequential numbers generator class.
Definition: test_control.h:215
Test Control class.
Definition: test_control.h:121
void sendDiscover4(const TestControlSocket &socket, const bool preload=false)
Send DHCPv4 DISCOVER message.
void saveFirstPacket(const dhcp::Pkt4Ptr &pkt)
Save the first DHCPv4 sent packet of the specified type.
TestControl()
Default constructor.
static const uint8_t HW_ETHER_LEN
Length of the Ethernet HW address (MAC) in bytes.
Definition: test_control.h:247
isc::util::random::UniformRandomIntegerGenerator number_generator_
Generate uniformly distributed integers in range of [min, max].
Definition: test_control.h:298
std::map< uint8_t, dhcp::Pkt4Ptr > template_packets_v4_
First packets send.
StatsMgr< dhcp::Pkt6 > StatsMgr6
Statistics Manager for DHCPv6.
Definition: test_control.h:129
void addExtraOpts(const dhcp::Pkt4Ptr &pkt4)
Inserts extra options specified by user.
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
void printDiagnostics() const
Print main diagnostics data.
static void handleChild(int sig)
Handle child signal.
void registerOptionFactories4() const
Register option factory functions for DHCPv4.
NumberGeneratorPtr transid_gen_
Transaction id generator.
void sendRequest4(const TestControlSocket &socket, const dhcp::Pkt4Ptr &discover_pkt4, const dhcp::Pkt4Ptr &offer_pkt4)
Send DHCPv4 REQUEST message.
NumberGeneratorPtr macaddr_gen_
Numbers generator for MAC address.
uint64_t getRcvdPacketsNum(ExchangeType xchg_type) const
Get number of received packets.
int getRequestedIpOffset() const
Return requested ip offset in a packet.
void processReceivedPacket4(const TestControlSocket &socket, const dhcp::Pkt4Ptr &pkt4)
Process received DHCPv4 packet.
boost::shared_ptr< StatsMgr4 > StatsMgr4Ptr
Pointer to Statistics Manager for DHCPv4;.
Definition: test_control.h:127
boost::shared_ptr< NumberGenerator > NumberGeneratorPtr
The default generator pointer.
Definition: test_control.h:212
int openSocket() const
Open socket to communicate with DHCP server.
void sendRequest6(const TestControlSocket &socket, const dhcp::Pkt6Ptr &advertise_pkt6)
Send DHCPv6 REQUEST message.
static bool interrupted_
Is program interrupted.
std::string byte2Hex(const uint8_t b) const
Convert binary value to hex string.
void readPacketTemplate(const std::string &file_name)
Read DHCP message template from file.
TemplateBuffer getTemplateBuffer(const size_t idx) const
Return template buffer.
std::vector< uint8_t > generateMacAddress(uint8_t &randomized)
Generate MAC address.
StatsMgr6Ptr stats_mgr6_
Statistics Manager 6.
void processReceivedPacket6(const TestControlSocket &socket, const dhcp::Pkt6Ptr &pkt6)
Process received DHCPv6 packet.
static TestControl & instance()
TestControl is a singleton class.
void setMacAddrGenerator(const NumberGeneratorPtr &generator)
Set new MAC address generator.
Definition: test_control.h:281
boost::posix_time::ptime last_report_
Last intermediate report time.
bool haveAllPacketsBeenReceived() const
Checks if all expected packets were already received.
Definition: test_control.cc:76
StatsMgr4Ptr stats_mgr4_
Statistics Manager 4.
void cleanCachedPackets()
Removes cached DHCPv6 Reply packets every second.
bool sendRequestFromAck(const TestControlSocket &socket)
Send DHCPv4 renew (DHCPREQUEST) using specified socket.
void sendPackets(const TestControlSocket &socket, const uint64_t packets_num, const bool preload=false)
Send number of packets to initiate new exchanges.
dhcp::Pkt4Ptr createRequestFromAck(const dhcp::Pkt4Ptr &ack)
Creates DHCPREQUEST from a DHCPACK message.
std::string vector2Hex(const std::vector< uint8_t > &vec, const std::string &separator="") const
Convert vector in hexadecimal string.
uint32_t getCurrentTimeout() const
Returns a timeout for packet reception.
RateControl release_rate_control_
A rate control class for Release messages.
static dhcp::OptionPtr factoryOptionRequestOption6(dhcp::Option::Universe u, uint16_t type, const dhcp::OptionBuffer &buf)
Factory function to create DHCPv6 ORO option.
bool sendMessageFromReply(const uint16_t msg_type, const TestControlSocket &socket)
Send DHCPv6 Renew or Release message using specified socket.
static dhcp::OptionPtr factoryIapd6(dhcp::Option::Universe u, uint16_t type, const dhcp::OptionBuffer &buf)
Factory function to create IA_PD option.
std::map< uint8_t, dhcp::Pkt6Ptr > template_packets_v6_
static dhcp::OptionPtr factoryRapidCommit6(dhcp::Option::Universe u, uint16_t type, const dhcp::OptionBuffer &buf)
Factory function to create DHCPv6 RAPID_COMMIT option instance.
PacketStorage< dhcp::Pkt6 > reply_storage_
A storage for reply messages.
void registerOptionFactories6() const
Register option factory functions for DHCPv6.
bool checkExitConditions() const
Check if test exit conditions fulfilled.
void registerOptionFactories() const
Register option factory functions for DHCPv4 or DHCPv6.
uint64_t receivePackets(const TestControlSocket &socket)
Receive DHCPv4 or DHCPv6 packets from the server.
std::vector< uint8_t > TemplateBuffer
Packet template buffer.
Definition: test_control.h:135
void runWrapped(bool do_stop=false) const
Run wrapped command.
uint32_t getElapsedTime(const T &pkt1, const T &pkt2)
Calculate elapsed time between two packets.
void reset()
Resets internal state of the object.
void setDefaults6(const TestControlSocket &socket, const dhcp::Pkt6Ptr &pkt)
Set default DHCPv6 packet parameters.
int run()
brief\ Run performance test.
boost::shared_ptr< StatsMgr6 > StatsMgr6Ptr
Pointer to Statistics Manager for DHCPv6.
Definition: test_control.h:131
int getRandomOffset(const int arg_idx) const
Return randomization offset in a packet.
dhcp::Pkt6Ptr createMessageFromReply(const uint16_t msg_type, const dhcp::Pkt6Ptr &reply)
Creates DHCPv6 message from the Reply packet.
int getElapsedTimeOffset() const
Return elapsed time offset in a packet.
uint64_t sendMultipleRequests(const TestControlSocket &socket, const uint64_t msg_num)
Send number of DHCPREQUEST (renew) messages to a server.
bool testDiags(const char diag) const
Find if diagnostic flag has been set.
void printTemplates() const
Print templates information.
void setDefaults4(const TestControlSocket &socket, const dhcp::Pkt4Ptr &pkt)
Set default DHCPv4 packet parameters.
TemplateBufferCollection template_buffers_
Packet template buffers.
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.
void printStats() const
Print performance statistics.
PacketStorage< dhcp::Pkt4 > ack_storage_
A storage for DHCPACK messages.
void copyIaOptions(const dhcp::Pkt6Ptr &pkt_from, dhcp::Pkt6Ptr &pkt_to)
Copies IA_NA or IA_PD option from one packet to another.
StatsMgr< dhcp::Pkt4 > StatsMgr4
Statistics Manager for DHCPv4.
Definition: test_control.h:125
void setTransidGenerator(const NumberGeneratorPtr &generator)
Set new transaction id generator.
Definition: test_control.h:270
static dhcp::OptionPtr factoryGeneric(dhcp::Option::Universe u, uint16_t type, const dhcp::OptionBuffer &buf)
Factory function to create generic option.
static dhcp::OptionPtr factoryRequestList4(dhcp::Option::Universe u, uint16_t type, const dhcp::OptionBuffer &buf)
Factory function to create DHCPv4 Request List option.
void sendSolicit6(const TestControlSocket &socket, const bool preload=false)
Send DHCPv6 SOLICIT message.
uint64_t getSentPacketsNum(ExchangeType xchg_type) const
Get number of sent packets.
void initializeStatsMgr()
Initializes Statistics Manager.
static dhcp::OptionPtr factoryElapsedTime6(dhcp::Option::Universe u, uint16_t type, const dhcp::OptionBuffer &buf)
Factory function to create DHCPv6 ELAPSED_TIME option.
static void handleInterrupt(int sig)
Handle interrupt signal.
int getTransactionIdOffset(const int arg_idx) const
Return transaction id offset in a packet.
RateControl renew_rate_control_
A rate control class for Renew messages.
void initPacketTemplates()
Reads packet templates from files.
void printRate() const
Print rate statistics.
void printIntermediateStats()
Print intermediate statistics.
void printTemplate(const uint8_t packet_type) const
Print template information.
int getServerIdOffset() const
Return server id offset in a packet.
std::vector< uint8_t > generateDuid(uint8_t &randomized)
Generate DUID.
StatsMgr ::ExchangeType ExchangeType
Packet exchange type.
Definition: test_control.h:133
void checkLateMessages(RateControl &rate_control)
Increments counter of late sent messages if required.
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
dhcp::OptionPtr generateClientId(const dhcp::HWAddrPtr &hwaddr) const
Generate DHCPv4 client identifier from HW address.
RateControl basic_rate_control_
A rate control class for Discover and Solicit messages.
dhcp::OptionBuffer first_packet_serverid_
Buffer holding server id received in first packet.
uint32_t generateTransid()
generate transaction id.
Definition: test_control.h:508
static dhcp::OptionPtr factoryIana6(dhcp::Option::Universe u, uint16_t type, const dhcp::OptionBuffer &buf)
Factory function to create IA_NA option.
The OutputBuffer class is a buffer abstraction for manipulating mutable data.
Definition: buffer.h:294
size_t getLength() const
Return the length of data written in the buffer.
Definition: buffer.h:403
const void * getData() const
Return a pointer to the head of the data stored in the buffer.
Definition: buffer.h:401
@ D6O_SERVERID
Definition: dhcp6.h:22
@ D6O_CLIENTID
Definition: dhcp6.h:21
@ D6O_NAME_SERVERS
Definition: dhcp6.h:43
@ D6O_RAPID_COMMIT
Definition: dhcp6.h:34
@ D6O_IA_NA
Definition: dhcp6.h:23
@ D6O_ORO
Definition: dhcp6.h:26
@ D6O_IA_PD
Definition: dhcp6.h:45
@ D6O_DOMAIN_SEARCH
Definition: dhcp6.h:44
@ D6O_ELAPSED_TIME
Definition: dhcp6.h:28
@ DHCPV6_ADVERTISE
Definition: dhcp6.h:214
@ DHCPV6_REQUEST
Definition: dhcp6.h:215
@ DHCPV6_RENEW
Definition: dhcp6.h:217
@ DHCPV6_REPLY
Definition: dhcp6.h:219
@ DHCPV6_SOLICIT
Definition: dhcp6.h:213
@ DHCPV6_RELEASE
Definition: dhcp6.h:220
@ DHCPV6_RELAY_FORW
Definition: dhcp6.h:224
#define isc_throw(type, stream)
A shortcut macro to insert known values into exception arguments.
boost::shared_ptr< Element > ElementPtr
Definition: data.h:22
@ DHO_SUBNET_MASK
Definition: dhcp4.h:70
@ DHO_ROUTERS
Definition: dhcp4.h:72
@ DHO_DOMAIN_NAME
Definition: dhcp4.h:84
@ DHO_DOMAIN_NAME_SERVERS
Definition: dhcp4.h:75
@ DHO_DHCP_MESSAGE_TYPE
Definition: dhcp4.h:122
@ DHO_DHCP_SERVER_IDENTIFIER
Definition: dhcp4.h:123
@ DHO_HOST_NAME
Definition: dhcp4.h:81
@ DHO_DHCP_CLIENT_IDENTIFIER
Definition: dhcp4.h:130
@ DHO_DHCP_REQUESTED_ADDRESS
Definition: dhcp4.h:119
@ DHO_TIME_OFFSET
Definition: dhcp4.h:71
@ DHO_DHCP_PARAMETER_REQUEST_LIST
Definition: dhcp4.h:124
@ DHO_BROADCAST_ADDRESS
Definition: dhcp4.h:97
boost::shared_ptr< Pkt4 > Pkt4Ptr
A pointer to Pkt4 object.
Definition: pkt4.h:546
boost::shared_ptr< Iface > IfacePtr
Definition: iface_mgr.h:457
std::multimap< unsigned int, OptionPtr > OptionCollection
A collection of DHCP (v4 or v6) options.
Definition: option.h:41
boost::shared_ptr< HWAddr > HWAddrPtr
Shared pointer to a hardware address structure.
Definition: hwaddr.h:154
@ DHCPREQUEST
Definition: dhcp4.h:230
@ DHCPOFFER
Definition: dhcp4.h:229
@ DHCPDISCOVER
Definition: dhcp4.h:228
@ DHCPACK
Definition: dhcp4.h:232
boost::shared_ptr< Pkt6 > Pkt6Ptr
A pointer to Pkt6 packet.
Definition: pkt6.h:31
std::vector< uint8_t > OptionBuffer
buffer types used in DHCP code.
Definition: option.h:25
@ HTYPE_ETHER
Ethernet 10Mbps.
Definition: dhcp4.h:56
boost::shared_ptr< Option > OptionPtr
Definition: option.h:38
boost::shared_ptr< PerfPkt4 > PerfPkt4Ptr
Definition: perf_pkt4.h:126
ptime late_exit_target_time_
Definition: test_control.cc:45
boost::shared_ptr< PerfPkt6 > PerfPkt6Ptr
Definition: perf_pkt6.h:126
Defines the logger used by the top-level component of kea-dhcp-ddns.
structure that describes a single relay information
Definition: pkt6.h:85
uint8_t msg_type_
message type (RELAY-FORW oro RELAY-REPL)
Definition: pkt6.h:94
isc::asiolink::IOAddress linkaddr_
fixed field in relay-forw/relay-reply
Definition: pkt6.h:96
uint8_t hop_count_
number of traversed relays (up to 32)
Definition: pkt6.h:95
isc::asiolink::IOAddress peeraddr_
fixed field in relay-forw/relay-reply
Definition: pkt6.h:97
Holds information about socket.
Definition: socket_info.h:19
int sockfd_
IPv4 or IPv6.
Definition: socket_info.h:26
isc::asiolink::IOAddress addr_
Definition: socket_info.h:21
~TestControlSocket()
Destructor of the socket wrapper class.
TestControlSocket(const int socket)
Constructor of socket wrapper class.