Kea 3.1.1
test_control.cc
Go to the documentation of this file.
1// Copyright (C) 2012-2025 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 <perfdhcp/receiver.h>
12#include <perfdhcp/perf_pkt4.h>
13#include <perfdhcp/perf_pkt6.h>
14
16#include <dhcp/libdhcp++.h>
17#include <dhcp/iface_mgr.h>
18#include <dhcp/dhcp4.h>
19#include <dhcp/option6_ia.h>
20#include <dhcp/option6_iaaddr.h>
22#include <dhcp/option_int.h>
24
25#include <boost/date_time/posix_time/posix_time.hpp>
26#include <algorithm>
27#include <fstream>
28#include <stdio.h>
29#include <stdlib.h>
30#include <stdint.h>
31#include <unistd.h>
32#include <signal.h>
33#include <sstream>
34#include <sys/wait.h>
35
36using namespace std;
37using namespace boost::posix_time;
38using namespace isc;
39using namespace isc::dhcp;
40using namespace isc::asiolink;
41
42namespace isc {
43namespace perfdhcp {
44
45bool TestControl::interrupted_ = false;
46
47bool
49 uint32_t const wait_time = options_.getExitWaitTime();
50
51 // If we care and not all packets are in yet
52 if (wait_time && !haveAllPacketsBeenReceived()) {
53 const ptime now = microsec_clock::universal_time();
54
55 // Init the end time if it hasn't started yet
56 if (exit_time_.is_not_a_date_time()) {
57 exit_time_ = now + time_duration(microseconds(wait_time));
58 }
59
60 // If we're not at end time yet, return true
61 return (now < exit_time_);
62 }
63
64 // No need to wait, return false;
65 return (false);
66}
67
68bool
70 const uint8_t& ipversion = options_.getIpVersion();
71 const std::vector<int>& num_request = options_.getNumRequests();
72 const size_t& num_request_size = num_request.size();
73
74 if (num_request_size == 0) {
75 return (false);
76 }
77
78 uint32_t responses = 0;
79 uint32_t requests = num_request[0];
80 if (num_request_size >= 2) {
81 requests += num_request[1];
82 }
83
84 if (ipversion == 4) {
85 responses = stats_mgr_.getRcvdPacketsNum(ExchangeType::DO) +
86 stats_mgr_.getRcvdPacketsNum(ExchangeType::RA);
87 } else {
88 responses = stats_mgr_.getRcvdPacketsNum(ExchangeType::SA) +
89 stats_mgr_.getRcvdPacketsNum(ExchangeType::RR);
90 }
91
92 return (responses == requests);
93}
94
95void
97 // When Renews are not sent, Reply packets are not cached so there
98 // is nothing to do.
99 if (options_.getRenewRate() == 0) {
100 return;
101 }
102
103 static boost::posix_time::ptime last_clean =
104 microsec_clock::universal_time();
105
106 // Check how much time has passed since last cleanup.
107 time_period time_since_clean(last_clean,
108 microsec_clock::universal_time());
109 // Cleanup every 1 second.
110 if (time_since_clean.length().total_seconds() >= 1) {
111 // Calculate how many cached packets to remove. Actually we could
112 // just leave enough packets to handle Renews for 1 second but
113 // since we want to randomize leases to be renewed so leave 5
114 // times more packets to randomize from.
116 if (reply_storage_.size() > 5 * size_t(options_.getRenewRate())) {
117 reply_storage_.clear(reply_storage_.size() -
118 5 * options_.getRenewRate());
119 }
120 // Remember when we performed a cleanup for the last time.
121 // We want to do the next cleanup not earlier than in one second.
122 last_clean = microsec_clock::universal_time();
123 }
124}
125
126void
127TestControl::copyIaOptions(const Pkt6Ptr& pkt_from, Pkt6Ptr& pkt_to) {
128 if (!pkt_from || !pkt_to) {
129 isc_throw(BadValue, "NULL pointers must not be specified as arguments"
130 " for the copyIaOptions function");
131 }
132 // IA_NA
133 if (options_.getLeaseType()
135 OptionPtr option = pkt_from->getOption(D6O_IA_NA);
136 if (!option) {
137 isc_throw(NotFound, "IA_NA option not found in the"
138 " server's response");
139 }
140 pkt_to->addOption(option);
141 }
142 // IA_PD
143 if (options_.getLeaseType()
145 OptionPtr option = pkt_from->getOption(D6O_IA_PD);
146 if (!option) {
147 isc_throw(NotFound, "IA_PD option not found in the"
148 " server's response");
149 }
150 pkt_to->addOption(option);
151 }
152}
153
154std::string
155TestControl::byte2Hex(const uint8_t b) {
156 const int b1 = b / 16;
157 const int b0 = b % 16;
158 ostringstream stream;
159 stream << std::hex << b1 << b0 << std::dec;
160 return (stream.str());
161}
162
164TestControl::createMessageFromAck(const uint16_t msg_type,
165 const dhcp::Pkt4Ptr& ack) {
166 // Restrict messages to Release and Renew.
167 if (msg_type != DHCPREQUEST && msg_type != DHCPRELEASE) {
168 isc_throw(isc::BadValue, "invalid message type " << msg_type
169 << " to be created from Reply, expected DHCPREQUEST or"
170 " DHCPRELEASE");
171 }
172
173 // Get the string representation of the message - to be used for error
174 // logging purposes.
175 auto msg_type_str = [=]() -> const char* {
176 return (msg_type == DHCPREQUEST ? "Request" : "Release");
177 };
178
179 if (!ack) {
180 isc_throw(isc::BadValue, "Unable to create "
181 << msg_type_str()
182 << " from a null DHCPACK message");
183 } else if (ack->getYiaddr().isV4Zero()) {
185 "Unable to create "
186 << msg_type_str()
187 << " from a DHCPACK message containing yiaddr of 0");
188 }
189 Pkt4Ptr msg(new Pkt4(msg_type, generateTransid()));
190 msg->setCiaddr(ack->getYiaddr());
191 msg->setHWAddr(ack->getHWAddr());
192 msg->addOption(generateClientId(msg->getHWAddr()));
193 if (msg_type == DHCPRELEASE) {
194 // RFC 2132: DHCPRELEASE MUST include server ID.
195 if (options_.isUseFirst()) {
196 // Honor the '-1' flag if it exists.
197 if (first_packet_serverid_.empty()) {
199 "Unable to create "
200 << msg_type_str()
201 << "from the first packet which lacks the server "
202 "identifier option");
203 }
204 msg->addOption(Option::factory(Option::V4,
207 } else {
208 // Otherwise take it from the DHCPACK message.
209 OptionPtr server_identifier(
210 ack->getOption(DHO_DHCP_SERVER_IDENTIFIER));
211 if (!server_identifier) {
213 "Unable to create "
214 << msg_type_str()
215 << "from a DHCPACK message without the server "
216 "identifier option");
217 }
218 msg->addOption(server_identifier);
219 }
220 }
221 return (msg);
222}
223
226 const dhcp::Pkt6Ptr& reply) {
227 // Restrict messages to Release and Renew.
228 if (msg_type != DHCPV6_RENEW && msg_type != DHCPV6_RELEASE) {
229 isc_throw(isc::BadValue, "invalid message type " << msg_type
230 << " to be created from Reply, expected DHCPV6_RENEW or"
231 " DHCPV6_RELEASE");
232 }
233
234 // Get the string representation of the message - to be used for error
235 // logging purposes.
236 auto msg_type_str = [=]() -> const char* {
237 return (msg_type == DHCPV6_RENEW ? "Renew" : "Release");
238 };
239
240 // Reply message must be specified.
241 if (!reply) {
242 isc_throw(isc::BadValue, "Unable to create " << msg_type_str()
243 << " message from the Reply message because the instance of"
244 " the Reply message is NULL");
245 }
246
247 Pkt6Ptr msg(new Pkt6(msg_type, generateTransid()));
248 // Client id.
249 OptionPtr opt_clientid = reply->getOption(D6O_CLIENTID);
250 if (!opt_clientid) {
251 isc_throw(isc::Unexpected, "failed to create " << msg_type_str()
252 << " message because client id option has not been found"
253 " in the Reply message");
254 }
255 msg->addOption(opt_clientid);
256 // Server id.
257 OptionPtr opt_serverid = reply->getOption(D6O_SERVERID);
258 if (!opt_serverid) {
259 isc_throw(isc::Unexpected, "failed to create " << msg_type_str()
260 << " because server id option has not been found in the"
261 " Reply message");
262 }
263 msg->addOption(opt_serverid);
264 copyIaOptions(reply, msg);
265 return (msg);
266}
267
270 const OptionBuffer& buf) {
271 if (buf.size() == 2) {
272 return (OptionPtr(new Option(Option::V6, D6O_ELAPSED_TIME, buf)));
273 } else if (buf.size() == 0) {
275 OptionBuffer(2, 0))));
276 }
278 "elapsed time option buffer size has to be 0 or 2");
279}
280
283 const OptionBuffer& buf) {
284 OptionPtr opt(new Option(u, type, buf));
285 return (opt);
286}
287
290 const OptionBuffer& buf) {
292 const uint8_t buf_array[] = {
293 0, 0, 0, 1, // IAID = 1
294 0, 0, 3600 >> 8, 3600 & 0xff, // T1 = 3600
295 0, 0, 5400 >> 8, 5400 & 0xff, // T2 = 5400
296 };
297 OptionBuffer buf_ia_na(buf_array, buf_array + sizeof(buf_array));
298 for (size_t i = 0; i < buf.size(); ++i) {
299 buf_ia_na.push_back(buf[i]);
300 }
301 return (OptionPtr(new Option(Option::V6, D6O_IA_NA, buf_ia_na)));
302}
303
306 const OptionBuffer& buf) {
308 static const uint8_t buf_array[] = {
309 0, 0, 0, 1, // IAID = 1
310 0, 0, 3600 >> 8, 3600 & 0xff, // T1 = 3600
311 0, 0, 5400 >> 8, 5400 & 0xff, // T2 = 5400
312 };
313 OptionBuffer buf_ia_pd(buf_array, buf_array + sizeof(buf_array));
314 // Append sub-options to IA_PD.
315 buf_ia_pd.insert(buf_ia_pd.end(), buf.begin(), buf.end());
316 return (OptionPtr(new Option(Option::V6, D6O_IA_PD, buf_ia_pd)));
317}
318
319
325
328 uint16_t,
329 const OptionBuffer&) {
330 const uint8_t buf_array[] = {
333 };
334 OptionBuffer buf_with_options(buf_array, buf_array + sizeof(buf_array));
335 return (OptionPtr(new Option(Option::V6, D6O_ORO, buf_with_options)));
336}
337
338
341 uint16_t type,
342 const OptionBuffer& buf) {
343 const uint8_t buf_array[] = {
351 };
352
353 OptionBuffer buf_with_options(buf_array, buf_array + sizeof(buf_array));
354 OptionPtr opt(new Option(u, type, buf));
355 opt->setData(buf_with_options.begin(), buf_with_options.end());
356 return (opt);
357}
358
359std::vector<uint8_t>
361 const CommandOptions::MacAddrsVector& macs = options_.getMacsFromFile();
362 // if we are using the -M option return a random one from the list...
363 if (macs.size() > 0) {
364 uint16_t r = random_generator_->generate();
365 if (r >= macs.size()) {
366 r = 0;
367 }
368 return (macs[r]);
369
370 } else {
371 // ... otherwise use the standard behavior
372 uint32_t clients_num = options_.getClientsNum();
373 if (clients_num < 2) {
374 return (options_.getMacTemplate());
375 }
376 // Get the base MAC address. We are going to randomize part of it.
377 std::vector<uint8_t> mac_addr(options_.getMacTemplate());
378 if (mac_addr.size() != HW_ETHER_LEN) {
379 isc_throw(BadValue, "invalid MAC address template specified");
380 }
381 uint32_t r = macaddr_gen_->generate();
382 randomized = 0;
383 // Randomize MAC address octets.
384 for (std::vector<uint8_t>::iterator it = mac_addr.end() - 1;
385 it >= mac_addr.begin();
386 --it) {
387 // Add the random value to the current octet.
388 (*it) += r;
389 ++randomized;
390 if (r < 256) {
391 // If we are here it means that there is no sense
392 // to randomize the remaining octets of MAC address
393 // because the following bytes of random value
394 // are zero and it will have no effect.
395 break;
396 }
397 // Randomize the next octet with the following
398 // byte of random value.
399 r >>= 8;
400 }
401 return (mac_addr);
402 }
403}
404
407 vector<uint8_t> client_id;
408 client_id.push_back(static_cast<uint8_t>(hwaddr->htype_));
409 for (uint8_t const& byte : hwaddr->hwaddr_) {
410 client_id.push_back(byte);
411 }
413 client_id)));
414}
415
416std::vector<uint8_t>
417TestControl::generateDuid(uint8_t& randomized) {
418 std::vector<uint8_t> mac_addr(generateMacAddress(randomized));
419 const CommandOptions::MacAddrsVector& macs = options_.getMacsFromFile();
420 // pick a random mac address if we are using option -M..
421 if (macs.size() > 0) {
422 uint16_t r = random_generator_->generate();
423 if (r >= macs.size()) {
424 r = 0;
425 }
426 std::vector<uint8_t> mac = macs[r];
427 // DUID_LL is in this format
428 // 0 1 2 3
429 // 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
430 // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
431 // | 3 | hardware type (16 bits) |
432 // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
433 // . .
434 // . link-layer address (variable length) .
435 // . .
436 // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
437
438 // No C++11 so initializer list support, building a vector<uint8_t> is a
439 // pain...
440 uint8_t duid_ll[] = {0, 3, 0, 1, 0, 0, 0, 0, 0, 0};
441 // copy duid_ll array into the vector
442 std::vector<uint8_t> duid(duid_ll,
443 duid_ll + sizeof(duid_ll) / sizeof(duid_ll[0]));
444 // put the mac address bytes at the end
445 std::copy(mac.begin(), mac.end(), duid.begin() + 4);
446 return (duid);
447 } else {
448 uint32_t clients_num = options_.getClientsNum();
449 if ((clients_num == 0) || (clients_num == 1)) {
450 return (options_.getDuidTemplate());
451 }
452 // Get the base DUID. We are going to randomize part of it.
453 std::vector<uint8_t> duid(options_.getDuidTemplate());
455 duid.resize(duid.size());
456 std::copy(mac_addr.begin(), mac_addr.end(),
457 duid.begin() + duid.size() - mac_addr.size());
458 return (duid);
459 }
460}
461
462int
464 int elp_offset = options_.getIpVersion() == 4 ?
465 DHCPV4_ELAPSED_TIME_OFFSET : DHCPV6_ELAPSED_TIME_OFFSET;
466 if (options_.getElapsedTimeOffset() > 0) {
467 elp_offset = options_.getElapsedTimeOffset();
468 }
469 return (elp_offset);
470}
471
472template<class T>
473uint32_t
474TestControl::getElapsedTime(const T& pkt1, const T& pkt2) {
475 using namespace boost::posix_time;
476 ptime pkt1_time = pkt1->getTimestamp();
477 ptime pkt2_time = pkt2->getTimestamp();
478 if (pkt1_time.is_not_a_date_time() ||
479 pkt2_time.is_not_a_date_time()) {
480 isc_throw(InvalidOperation, "packet timestamp not set");;
481 }
482 time_period elapsed_period(pkt1_time, pkt2_time);
483 return (elapsed_period.is_null() ? 0 :
484 elapsed_period.length().total_milliseconds());
485}
486
487int
488TestControl::getRandomOffset(const int arg_idx) const {
489 int rand_offset = options_.getIpVersion() == 4 ?
490 DHCPV4_RANDOMIZATION_OFFSET : DHCPV6_RANDOMIZATION_OFFSET;
491 if (options_.getRandomOffset().size() > size_t(arg_idx)) {
492 rand_offset = options_.getRandomOffset()[arg_idx];
493 }
494 return (rand_offset);
495}
496
497int
499 int rip_offset = options_.getIpVersion() == 4 ?
500 DHCPV4_REQUESTED_IP_OFFSET : DHCPV6_IA_NA_OFFSET;
501 if (options_.getRequestedIpOffset() > 0) {
502 rip_offset = options_.getRequestedIpOffset();
503 }
504 return (rip_offset);
505}
506
507int
509 int srvid_offset = options_.getIpVersion() == 4 ?
510 DHCPV4_SERVERID_OFFSET : DHCPV6_SERVERID_OFFSET;
511 if (options_.getServerIdOffset() > 0) {
512 srvid_offset = options_.getServerIdOffset();
513 }
514 return (srvid_offset);
515}
516
518TestControl::getTemplateBuffer(const size_t idx) const {
519 if (template_buffers_.size() > idx) {
520 return (template_buffers_[idx]);
521 }
522 isc_throw(OutOfRange, "invalid buffer index");
523}
524
525int
526TestControl::getTransactionIdOffset(const int arg_idx) const {
527 int xid_offset = options_.getIpVersion() == 4 ?
528 DHCPV4_TRANSID_OFFSET : DHCPV6_TRANSID_OFFSET;
529 if (options_.getTransactionIdOffset().size() > size_t(arg_idx)) {
530 xid_offset = options_.getTransactionIdOffset()[arg_idx];
531 }
532 return (xid_offset);
533}
534
535void
537 int status = 0;
538 while (wait3(&status, WNOHANG, NULL) > 0) {
539 // continue
540 }
541}
542
543void
547
548void
550 template_packets_v4_.clear();
551 template_packets_v6_.clear();
552 template_buffers_.clear();
553 std::vector<std::string> template_files = options_.getTemplateFiles();
554 for (auto const& it : template_files) {
556 }
557}
558
559void
560TestControl::sendPackets(const uint64_t packets_num,
561 const bool preload /* = false */) {
562 for (uint64_t i = packets_num; i > 0; --i) {
563 if (options_.getIpVersion() == 4) {
564 // No template packets means that no -T option was specified.
565 // We have to build packets ourselves.
566 if (template_buffers_.empty()) {
567 sendDiscover4(preload);
568 } else {
571 sendDiscover4(template_buffers_[0], preload);
572 }
573 } else {
574 // No template packets means that no -T option was specified.
575 // We have to build packets ourselves.
576 if (template_buffers_.empty()) {
577 sendSolicit6(preload);
578 } else {
581 sendSolicit6(template_buffers_[0], preload);
582 }
583 }
584 }
585}
586
587uint64_t
589 const uint64_t msg_num) {
590 for (uint64_t i = 0; i < msg_num; ++i) {
591 if (!sendMessageFromAck(msg_type)) {
592 return (i);
593 }
594 }
595 return (msg_num);
596}
597
598uint64_t
600 const uint64_t msg_num) {
601 for (uint64_t i = 0; i < msg_num; ++i) {
602 if (!sendMessageFromReply(msg_type)) {
603 return (i);
604 }
605 }
606 return (msg_num);
607}
608
609void
611 if (options_.testDiags('a')) {
612 // Print all command line parameters.
613 options_.printCommandLine();
614 // Print MAC and DUID.
615 std::cout << "Set MAC to " << vector2Hex(options_.getMacTemplate(), "::")
616 << std::endl;
617 if (options_.getDuidTemplate().size() > 0) {
618 std::cout << "Set DUID to " << vector2Hex(options_.getDuidTemplate()) << std::endl;
619 }
620 }
621}
622
623void
624TestControl::printTemplate(const uint8_t packet_type) const {
625 std::string hex_buf;
626 int arg_idx = 0;
627 if (options_.getIpVersion() == 4) {
628 if (packet_type == DHCPREQUEST) {
629 arg_idx = 1;
630 }
631 std::map<uint8_t, dhcp::Pkt4Ptr>::const_iterator pkt_it =
632 template_packets_v4_.find(packet_type);
633 if ((pkt_it != template_packets_v4_.end()) &&
634 pkt_it->second) {
635 hex_buf = vector2Hex(pkt_it->second->getBuffer().getVector());
636 }
637 } else if (options_.getIpVersion() == 6) {
638 if (packet_type == DHCPV6_REQUEST) {
639 arg_idx = 1;
640 }
641 std::map<uint8_t, dhcp::Pkt6Ptr>::const_iterator pkt_it =
642 template_packets_v6_.find(packet_type);
643 if (pkt_it != template_packets_v6_.end() &&
644 pkt_it->second) {
645 hex_buf = vector2Hex(pkt_it->second->getBuffer().getVector());
646 }
647 }
648 std::cout << "xid-offset=" << getTransactionIdOffset(arg_idx) << std::endl;
649 std::cout << "random-offset=" << getRandomOffset(arg_idx) << std::endl;
650 if (arg_idx > 0) {
651 std::cout << "srvid-offset=" << getServerIdOffset() << std::endl;
652 std::cout << "time-offset=" << getElapsedTimeOffset() << std::endl;
653 std::cout << "ip-offset=" << getRequestedIpOffset() << std::endl;
654 }
655
656 std::cout << "contents: " << std::endl;
657 int line_len = 32;
658 int i = 0;
659 // Save cout fmtflags.
660 auto flags(std::cout.flags());
661 while (line_len == 32) {
662 if (hex_buf.length() - i < 32) {
663 line_len = hex_buf.length() - i;
664 };
665 if (line_len > 0) {
666 std::cout << setfill('0') << setw(4) << std::hex << i << std::dec
667 << " " << hex_buf.substr(i, line_len) << std::endl;
668 }
669 i += 32;
670 }
671 // Restore cout fmtflags.
672 std::cout.flags(flags);
673 std::cout << std::endl;
674}
675
676void
678 if (options_.getIpVersion() == 4) {
681 } else if (options_.getIpVersion() == 6) {
684 }
685}
686
687void
689 double rate = 0;
690 std::string exchange_name = "4-way exchanges";
691 ExchangeType xchg_type = ExchangeType::DO;
692 if (options_.getIpVersion() == 4) {
693 xchg_type =
694 options_.getExchangeMode() == CommandOptions::DO_SA ?
696 if (xchg_type == ExchangeType::DO) {
697 exchange_name = "DISCOVER-OFFER";
698 }
699 } else if (options_.getIpVersion() == 6) {
700 xchg_type =
701 options_.getExchangeMode() == CommandOptions::DO_SA ?
703 if (xchg_type == ExchangeType::SA) {
704 exchange_name = options_.isRapidCommit() ? "Solicit-Reply" :
705 "Solicit-Advertise";
706 }
707 }
708 double duration =
709 stats_mgr_.getTestPeriod().length().total_nanoseconds() / 1e9;
710 rate = stats_mgr_.getRcvdPacketsNum(xchg_type) / duration;
711 std::ostringstream s;
712 s << "***Rate statistics***" << std::endl;
713 s << "Rate: " << rate << " " << exchange_name << "/second";
714 if (options_.getRate() > 0) {
715 s << ", expected rate: " << options_.getRate() << std::endl;
716 }
717
718 std::cout << s.str() << std::endl;
719
720 std::cout <<"***Malformed Packets***" << std::endl
721 << "Malformed packets: " << ExchangeStats::malformed_pkts_
722 << std::endl;
723}
724
725void
727 int delay = options_.getReportDelay();
728 ptime now = microsec_clock::universal_time();
729 time_period time_since_report(last_report_, now);
730 if (time_since_report.length().total_seconds() >= delay) {
731 stats_mgr_.printIntermediateStats(options_.getCleanReport(),
732 options_.getCleanReportSeparator());
733 last_report_ = now;
734 }
735}
736
737void
739 printRate();
740 stats_mgr_.printStats();
741 if (options_.testDiags('i')) {
742 stats_mgr_.printCustomCounters();
743 }
744}
745
746std::string
747TestControl::vector2Hex(const std::vector<uint8_t>& vec,
748 const std::string& separator /* = "" */) {
749 std::ostringstream stream;
750 bool first = true;
751 for (auto const& it : vec) {
752 if (first) {
753 stream << byte2Hex(it);
754 first = false;
755 } else {
756 stream << separator << byte2Hex(it);
757 }
758 }
759 return (stream.str());
760}
761
762void
763TestControl::readPacketTemplate(const std::string& file_name) {
764 std::ifstream temp_file;
765 temp_file.open(file_name.c_str(), ios::in | ios::binary | ios::ate);
766 if (!temp_file.is_open()) {
767 isc_throw(BadValue, "unable to open template file " << file_name);
768 }
769 // Read template file contents.
770 std::streampos temp_size = temp_file.tellg();
771 if (temp_size == std::streampos(0)) {
772 temp_file.close();
773 isc_throw(OutOfRange, "the template file " << file_name << " is empty");
774 }
775 temp_file.seekg(0, ios::beg);
776 std::vector<char> file_contents(temp_size);
777 temp_file.read(&file_contents[0], temp_size);
778 temp_file.close();
779 // Spaces are allowed so we have to strip the contents
780 // from them. In the same time we want to make sure that
781 // apart from spaces the file contains hexadecimal digits
782 // only.
783 std::vector<char> hex_digits;
784 for (size_t i = 0; i < file_contents.size(); ++i) {
785 if (isxdigit(file_contents[i])) {
786 hex_digits.push_back(file_contents[i]);
787 } else if (!isxdigit(file_contents[i]) &&
788 !isspace(file_contents[i])) {
789 isc_throw(BadValue, "'" << file_contents[i] << "' is not a"
790 " hexadecimal digit");
791 }
792 }
793 // Expect even number of digits.
794 if (hex_digits.size() % 2 != 0) {
795 isc_throw(OutOfRange, "odd number of digits in template file");
796 } else if (hex_digits.empty()) {
797 isc_throw(OutOfRange, "template file " << file_name << " is empty");
798 }
799 std::vector<uint8_t> binary_stream;
800 for (size_t i = 0; i < hex_digits.size(); i += 2) {
801 stringstream s;
802 s << "0x" << hex_digits[i] << hex_digits[i+1];
803 int b;
804 s >> std::hex >> b;
805 binary_stream.push_back(static_cast<uint8_t>(b));
806 }
807 template_buffers_.push_back(binary_stream);
808}
809
810void
812 if (pkt4->getType() == DHCPOFFER) {
813 PktPtr pkt = stats_mgr_.passRcvdPacket(ExchangeType::DO, pkt4);
815 Pkt4Ptr discover_pkt4(boost::dynamic_pointer_cast<Pkt4>(pkt));
816 CommandOptions::ExchangeMode xchg_mode = options_.getExchangeMode();
817 if ((xchg_mode == CommandOptions::DORA_SARR) && discover_pkt4) {
818 if (template_buffers_.size() < 2) {
819 sendRequest4(discover_pkt4, pkt4);
820 } else {
823 sendRequest4(template_buffers_[1], discover_pkt4, pkt4);
824 }
825 }
826 } else if (pkt4->getType() == DHCPACK) {
827 // If received message is DHCPACK, we have to check if this is
828 // a response to 4-way exchange. We'll match this packet with
829 // a DHCPREQUEST sent as part of the 4-way exchanges.
830 if (stats_mgr_.passRcvdPacket(ExchangeType::RA, pkt4)) {
832 // The DHCPACK belongs to DHCPREQUEST-DHCPACK exchange type.
833 // So, we may need to keep this DHCPACK in the storage if renews.
834 // Note that, DHCPACK messages hold the information about
835 // leases assigned. We use this information to renew.
836 if (stats_mgr_.hasExchangeStats(ExchangeType::RNA) ||
837 stats_mgr_.hasExchangeStats(ExchangeType::RLA)) {
838 // Renew or release messages are sent, because StatsMgr has the
839 // specific exchange type specified. Let's append the DHCPACK
840 // message to a storage.
841 ack_storage_.append(pkt4);
842 }
843 // The DHCPACK message is not a server's response to the DHCPREQUEST
844 // message sent within the 4-way exchange. It may be a response to a
845 // renewal. In this case we first check if StatsMgr has exchange type
846 // for renew specified, and if it has, if there is a corresponding
847 // renew message for the received DHCPACK.
848 } else if (stats_mgr_.hasExchangeStats(ExchangeType::RNA)) {
849 stats_mgr_.passRcvdPacket(ExchangeType::RNA, pkt4);
850 }
851 }
852}
853
854void
856 // check if received address is unique
857 if (options_.getAddrUnique()) {
858 std::set<std::string> current;
859 // addresses were already checked in validateIA
860 // we can safely assume that those are correct
861 for (auto const& opt : pkt6->options_) {
862 switch (opt.second->getType()) {
863 case D6O_IA_PD: {
864 // add address and check if it has not been already assigned
865 // addresses should be unique cross options of the packet
866 auto ret = current.emplace(boost::dynamic_pointer_cast<
867 Option6IAPrefix>(opt.second->getOption(D6O_IAPREFIX))->getAddress().toText());
868 if (!ret.second) {
869 stats_mgr_.updateNonUniqueAddrNum(xchg_type);
870 }
871 break;
872 }
873 case D6O_IA_NA: {
874 // add address and check if it has not been already assigned
875 // addresses should be unique cross options of the packet
876 auto ret = current.emplace(boost::dynamic_pointer_cast<
877 Option6IAAddr>(opt.second->getOption(D6O_IAADDR))->getAddress().toText());
878 if (!ret.second) {
879 stats_mgr_.updateNonUniqueAddrNum(xchg_type);
880 }
881 break;
882 }
883 default:
884 break;
885 }
886 }
887 // addresses should be unique cross packets
888 addUniqeAddr(current, xchg_type);
889 }
890}
891
892void
894 // check if received address is unique
895 if (options_.getAddrUnique()) {
896 // addresses were already checked in validateIA
897 // we can safely assume that those are correct
898 std::set<std::string> current;
899 current.insert(pkt4->getYiaddr().toText());
900 // addresses should be unique cross packets
901 addUniqeAddr(current, xchg_type);
902 }
903}
904
905bool
907 // check if iaaddr exists - if it does, we can continue sending request
908 // if not we will update statistics about rejected leases
909 // @todo it's checking just one iaaddress option for now it's ok
910 // but when perfdhcp will be extended to create message with multiple IA
911 // this will have to be iterate on:
912 // OptionCollection ias = pkt6->getOptions(D6O_IA_NA);
913 Option6IAPrefixPtr iapref;
914 Option6IAAddrPtr iaaddr;
915 if (pkt6->getOption(D6O_IA_PD)) {
916 iapref = boost::dynamic_pointer_cast<
917 Option6IAPrefix>(pkt6->getOption(D6O_IA_PD)->getOption(D6O_IAPREFIX));
918 }
919 if (pkt6->getOption(D6O_IA_NA)) {
920 iaaddr = boost::dynamic_pointer_cast<
921 Option6IAAddr>(pkt6->getOption(D6O_IA_NA)->getOption(D6O_IAADDR));
922 }
923
924 bool address_and_prefix = options_.getLeaseType().includes(
926 bool prefix_only = options_.getLeaseType().includes(
928 bool address_only = options_.getLeaseType().includes(
930 if ((address_and_prefix && iapref && iaaddr) ||
931 (prefix_only && iapref && !address_and_prefix) ||
932 (address_only && iaaddr && !address_and_prefix)) {
933 return (true);
934 } else {
935 return (false);
936 }
937}
938
939void
941 uint8_t packet_type = pkt6->getType();
942 if (packet_type == DHCPV6_ADVERTISE) {
943 PktPtr pkt = stats_mgr_.passRcvdPacket(ExchangeType::SA, pkt6);
944 Pkt6Ptr solicit_pkt6(boost::dynamic_pointer_cast<Pkt6>(pkt));
945 CommandOptions::ExchangeMode xchg_mode = options_.getExchangeMode();
946 if ((xchg_mode == CommandOptions::DORA_SARR) && solicit_pkt6) {
947 if (validateIA(pkt6)) {
948 // if address is correct - check uniqueness
950 if (template_buffers_.size() < 2) {
951 sendRequest6(pkt6);
952 } else {
956 }
957 } else {
958 stats_mgr_.updateRejLeases(ExchangeType::SA);
959 }
960 }
961 } else if (packet_type == DHCPV6_REPLY) {
962 // If the received message is Reply, we have to find out which exchange
963 // type the Reply message belongs to. It is doable by matching the Reply
964 // transaction id with the transaction id of the sent Request, Renew
965 // or Release. First we start with the Request.
966 if (stats_mgr_.passRcvdPacket(ExchangeType::RR, pkt6)) {
967 // The Reply belongs to Request-Reply exchange type. So, we may need
968 // to keep this Reply in the storage if Renews or/and Releases are
969 // being sent. Note that, Reply messages hold the information about
970 // leases assigned. We use this information to construct Renew and
971 // Release messages.
972 if (validateIA(pkt6)) {
973 // if address is correct - check uniqueness
975 // check if there is correct IA to continue with Renew/Release
976 if (stats_mgr_.hasExchangeStats(ExchangeType::RN) ||
977 stats_mgr_.hasExchangeStats(ExchangeType::RL)) {
978 // Renew or Release messages are sent, because StatsMgr has the
979 // specific exchange type specified. Let's append the Reply
980 // message to a storage.
981 reply_storage_.append(pkt6);
982 }
983 } else {
984 stats_mgr_.updateRejLeases(ExchangeType::RR);
985 }
986 // The Reply message is not a server's response to the Request message
987 // sent within the 4-way exchange. It may be a response to the Renew
988 // or Release message. In the if clause we first check if StatsMgr
989 // has exchange type for Renew specified, and if it has, if there is
990 // a corresponding Renew message for the received Reply. If not,
991 // we check that StatsMgr has exchange type for Release specified,
992 // as possibly the Reply has been sent in response to Release.
993 } else if (!(stats_mgr_.hasExchangeStats(ExchangeType::RN) &&
994 stats_mgr_.passRcvdPacket(ExchangeType::RN, pkt6)) &&
995 stats_mgr_.hasExchangeStats(ExchangeType::RL)) {
996 // At this point, it is only possible that the Reply has been sent
997 // in response to a Release. Try to match the Reply with Release.
998 stats_mgr_.passRcvdPacket(ExchangeType::RL, pkt6);
999 }
1000 }
1001}
1002
1003unsigned int
1005 unsigned int pkt_count = 0;
1006 PktPtr pkt;
1007 while ((pkt = receiver_.getPkt())) {
1008 pkt_count += 1;
1009 if (options_.getIpVersion() == 4) {
1010 Pkt4Ptr pkt4 = boost::dynamic_pointer_cast<Pkt4>(pkt);
1012 } else {
1013 Pkt6Ptr pkt6 = boost::dynamic_pointer_cast<Pkt6>(pkt);
1015 }
1016 }
1017 return pkt_count;
1018}
1019void
1021 static bool factories_registered = false;
1022 if (!factories_registered) {
1023 // DHCP_MESSAGE_TYPE option factory.
1027 // DHCP_SERVER_IDENTIFIER option factory.
1031 // DHCP_PARAMETER_REQUEST_LIST option factory.
1035 }
1036 factories_registered = true;
1037}
1038
1039void
1041 static bool factories_registered = false;
1042 if (!factories_registered) {
1043 // D6O_ELAPSED_TIME
1047 // D6O_RAPID_COMMIT
1051 // D6O_ORO (option request option) factory.
1053 D6O_ORO,
1055 // D6O_CLIENTID option factory.
1059 // D6O_SERVERID option factory.
1063 // D6O_IA_NA option factory.
1065 D6O_IA_NA,
1067
1068 // D6O_IA_PD option factory.
1070 D6O_IA_PD,
1072
1073
1074 }
1075 factories_registered = true;
1076}
1077
1078void
1080 switch(options_.getIpVersion()) {
1081 case 4:
1083 break;
1084 case 6:
1086 break;
1087 default:
1088 isc_throw(InvalidOperation, "command line options have to be parsed "
1089 "before DHCP option factories can be registered");
1090 }
1091}
1092
1093void
1095 transid_gen_.reset();
1096 last_report_ = microsec_clock::universal_time();
1097 // Actual generators will have to be set later on because we need to
1098 // get command line parameters first.
1101 first_packet_serverid_.clear();
1102 interrupted_ = false;
1103}
1104
1106 exit_time_(not_a_date_time),
1107 socket_(socket),
1108 receiver_(socket, options.isSingleThreaded(), options.getIpVersion()),
1109 stats_mgr_(options),
1110 random_generator_(new RandomGenerator(0, options.getMacsFromFile().size())),
1111 options_(options) {
1112 // Reset singleton state before test starts.
1113 reset();
1114
1115 // Ip version is not set ONLY in case the command options
1116 // were not parsed. This surely means that parse() function
1117 // was not called prior to starting the test. This is fatal
1118 // error.
1119 if (options_.getIpVersion() == 0) {
1121 "command options must be parsed before running a test");
1122 } else if (options_.getIpVersion() == 4) {
1123 // Turn off packet queueing.
1126 } else {
1127 // Turn off packet queueing.
1130 }
1131
1132 uint32_t clients_num = options_.getClientsNum() == 0 ?
1133 1 : options_.getClientsNum();
1135
1136 // Diagnostics are command line options mainly.
1138 // Option factories have to be registered.
1140 // Initialize packet templates.
1142 // Initialize randomization seed.
1143 if (options_.isSeeded()) {
1144 srandom(options_.getSeed());
1145 } else {
1146 // Seed with current time.
1147 time_period duration(from_iso_string("20111231T235959"),
1148 microsec_clock::universal_time());
1149 srandom(duration.length().total_seconds()
1150 + duration.length().fractional_seconds());
1151 }
1152 // If user interrupts the program we will exit gracefully.
1153 signal(SIGINT, TestControl::handleInterrupt);
1154}
1155
1156void
1157TestControl::runWrapped(bool do_stop /*= false */) const {
1158 if (!options_.getWrapped().empty()) {
1159 pid_t pid = 0;
1160 signal(SIGCHLD, handleChild);
1161 pid = fork();
1162 if (pid < 0) {
1163 isc_throw(Unexpected, "unable to fork");
1164 } else if (pid == 0) {
1165 execlp(options_.getWrapped().c_str(), do_stop ? "stop" : "start", (void*)0);
1166 }
1167 }
1168}
1169
1170void
1172 if (options_.testDiags('T')) {
1173 if (template_packets_v4_.find(pkt->getType()) == template_packets_v4_.end()) {
1174 template_packets_v4_[pkt->getType()] = pkt;
1175 }
1176 }
1177}
1178
1179void
1181 if (options_.testDiags('T')) {
1182 if (template_packets_v6_.find(pkt->getType()) == template_packets_v6_.end()) {
1183 template_packets_v6_[pkt->getType()] = pkt;
1184 }
1185 }
1186}
1187
1188void
1189TestControl::sendDiscover4(const bool preload /*= false*/) {
1190 // Generate the MAC address to be passed in the packet.
1191 uint8_t randomized = 0;
1192 std::vector<uint8_t> mac_address = generateMacAddress(randomized);
1193 // Generate transaction id to be set for the new exchange.
1194 const uint32_t transid = generateTransid();
1195 Pkt4Ptr pkt4(new Pkt4(DHCPDISCOVER, transid));
1196 if (!pkt4) {
1197 isc_throw(Unexpected, "failed to create DISCOVER packet");
1198 }
1199
1200 // Delete the default Message Type option set by Pkt4
1201 pkt4->delOption(DHO_DHCP_MESSAGE_TYPE);
1202
1203 // Set options: DHCP_MESSAGE_TYPE and DHCP_PARAMETER_REQUEST_LIST
1204 OptionBuffer buf_msg_type;
1205 buf_msg_type.push_back(DHCPDISCOVER);
1207 buf_msg_type));
1208 pkt4->addOption(Option::factory(Option::V4,
1210
1211
1212 // Set client's and server's ports as well as server's address,
1213 // and local (relay) address.
1214 setDefaults4(pkt4);
1215
1216 // Set hardware address
1217 pkt4->setHWAddr(HTYPE_ETHER, mac_address.size(), mac_address);
1218
1219 // Set client identifier
1220 pkt4->addOption(generateClientId(pkt4->getHWAddr()));
1221
1222 // Check if we need to simulate HA failures by pretending no responses were received.
1223 // The DHCP protocol signals that by increasing secs field (seconds since the configuration attempt started).
1224 if (options_.getIncreaseElapsedTime() &&
1225 stats_mgr_.getTestPeriod().length().total_seconds() >= options_.getWaitForElapsedTime() &&
1226 stats_mgr_.getTestPeriod().length().total_seconds() < options_.getWaitForElapsedTime() +
1227 options_.getIncreaseElapsedTime()) {
1228
1229 // Keep increasing elapsed time. The value should start increasing steadily.
1230 uint32_t val = stats_mgr_.getTestPeriod().length().total_seconds() - options_.getWaitForElapsedTime() + 1;
1231 if (val > 65535) {
1232 val = 65535;
1233 }
1234 pkt4->setSecs(static_cast<uint16_t>(val));
1235 }
1236
1237 // Add any extra options that user may have specified.
1238 addExtraOpts(pkt4);
1239
1240 pkt4->pack();
1241 socket_.send(pkt4);
1242 if (!preload) {
1243 stats_mgr_.passSentPacket(ExchangeType::DO, pkt4);
1244 }
1245
1246 saveFirstPacket(pkt4);
1247}
1248
1249void
1250TestControl::sendDiscover4(const std::vector<uint8_t>& template_buf,
1251 const bool preload /* = false */) {
1252 // Get the first argument if multiple the same arguments specified
1253 // in the command line. First one refers to DISCOVER packets.
1254 const uint8_t arg_idx = 0;
1255 // Generate the MAC address to be passed in the packet.
1256 uint8_t randomized = 0;
1257 std::vector<uint8_t> mac_address = generateMacAddress(randomized);
1258 // Generate transaction id to be set for the new exchange.
1259 const uint32_t transid = generateTransid();
1260 // Get transaction id offset.
1261 size_t transid_offset = getTransactionIdOffset(arg_idx);
1262 // Get randomization offset.
1263 // We need to go back by HW_ETHER_LEN (MAC address length)
1264 // because this offset points to last octet of MAC address.
1265 size_t rand_offset = getRandomOffset(arg_idx) - HW_ETHER_LEN + 1;
1266 // Create temporary buffer with template contents. We will
1267 // modify this temporary buffer but we don't want to modify
1268 // the original template.
1269 std::vector<uint8_t> in_buf(template_buf.begin(),
1270 template_buf.end());
1271 // Check if we are not going out of bounds.
1272 if (rand_offset + HW_ETHER_LEN > in_buf.size()) {
1273 isc_throw(OutOfRange, "randomization offset is out of bounds");
1274 }
1275 PerfPkt4Ptr pkt4(new PerfPkt4(&in_buf[0], in_buf.size(),
1276 transid_offset,
1277 transid));
1278
1279 // Replace MAC address in the template with actual MAC address.
1280 pkt4->writeAt(rand_offset, mac_address.begin(), mac_address.end());
1281 // Create a packet from the temporary buffer.
1282 setDefaults4(boost::static_pointer_cast<Pkt4>(pkt4));
1283 // Pack the input packet buffer to output buffer so as it can
1284 // be sent to server.
1285 pkt4->rawPack();
1286 socket_.send(boost::static_pointer_cast<Pkt4>(pkt4));
1287 if (!preload) {
1288 // Update packet stats.
1289 stats_mgr_.passSentPacket(ExchangeType::DO,
1290 boost::static_pointer_cast<Pkt4>(pkt4));
1291 }
1292 saveFirstPacket(pkt4);
1293}
1294
1295bool
1296TestControl::sendMessageFromAck(const uint16_t msg_type) {
1297 // We only permit Request or Release messages to be sent using this
1298 // function.
1299 if (msg_type != DHCPREQUEST && msg_type != DHCPRELEASE) {
1301 "invalid message type "
1302 << msg_type
1303 << " to be sent, expected DHCPREQUEST or DHCPRELEASE");
1304 }
1305
1306 // Get one of the recorded DHCPACK messages.
1307 Pkt4Ptr ack = ack_storage_.getRandom();
1308 if (!ack) {
1309 return (false);
1310 }
1311
1312 // Create message of the specified type.
1313 Pkt4Ptr msg = createMessageFromAck(msg_type, ack);
1314 setDefaults4(msg);
1315
1316 // Override relay address
1317 msg->setGiaddr(ack->getGiaddr());
1318
1319 // Add any extra options that user may have specified.
1320 addExtraOpts(msg);
1321
1322 // Pack it.
1323 msg->pack();
1324
1325 // And send it.
1326 socket_.send(msg);
1328 stats_mgr_.passSentPacket((msg_type == DHCPREQUEST ? ExchangeType::RNA :
1330 msg);
1331 return (true);
1332}
1333
1334
1335bool
1336TestControl::sendMessageFromReply(const uint16_t msg_type) {
1337 // We only permit Release or Renew messages to be sent using this function.
1338 if (msg_type != DHCPV6_RENEW && msg_type != DHCPV6_RELEASE) {
1339 isc_throw(isc::BadValue, "invalid message type " << msg_type
1340 << " to be sent, expected DHCPV6_RENEW or DHCPV6_RELEASE");
1341 }
1342
1343 // Get one of the recorded DHCPV6_OFFER messages.
1344 Pkt6Ptr reply = reply_storage_.getRandom();
1345 if (!reply) {
1346 return (false);
1347 }
1348 // Prepare the message of the specified type.
1349 Pkt6Ptr msg = createMessageFromReply(msg_type, reply);
1350 setDefaults6(msg);
1351
1352 // Add any extra options that user may have specified.
1353 addExtraOpts(msg);
1354
1355 // Pack it.
1356 msg->pack();
1357
1358 // And send it.
1359 socket_.send(msg);
1361 stats_mgr_.passSentPacket((msg_type == DHCPV6_RENEW ? ExchangeType::RN
1362 : ExchangeType::RL), msg);
1363 return (true);
1364}
1365
1366void
1368 const dhcp::Pkt4Ptr& offer_pkt4) {
1369 // Use the same transaction id as the one used in the discovery packet.
1370 const uint32_t transid = discover_pkt4->getTransid();
1371 Pkt4Ptr pkt4(new Pkt4(DHCPREQUEST, transid));
1372
1373 // Use first flags indicates that we want to use the server
1374 // id captured in first packet.
1375 if (options_.isUseFirst() &&
1376 (first_packet_serverid_.size() > 0)) {
1379 } else {
1380 OptionPtr opt_serverid =
1381 offer_pkt4->getOption(DHO_DHCP_SERVER_IDENTIFIER);
1382 if (!opt_serverid) {
1383 isc_throw(BadValue, "there is no SERVER_IDENTIFIER option "
1384 << "in OFFER message");
1385 }
1386 if (stats_mgr_.getRcvdPacketsNum(ExchangeType::DO) == 1) {
1387 first_packet_serverid_ = opt_serverid->getData();
1388 }
1389 pkt4->addOption(opt_serverid);
1390 }
1391
1393 asiolink::IOAddress yiaddr = offer_pkt4->getYiaddr();
1394 if (!yiaddr.isV4()) {
1395 isc_throw(BadValue, "the YIADDR returned in OFFER packet is not "
1396 " IPv4 address");
1397 }
1398 OptionPtr opt_requested_address =
1400 OptionBuffer()));
1401 opt_requested_address->setUint32(yiaddr.toUint32());
1402 pkt4->addOption(opt_requested_address);
1403 OptionPtr opt_parameter_list =
1405 pkt4->addOption(opt_parameter_list);
1406 // Set client's and server's ports as well as server's address
1407 setDefaults4(pkt4);
1408 // Override relay address
1409 pkt4->setGiaddr(offer_pkt4->getGiaddr());
1410 // Add any extra options that user may have specified.
1411 addExtraOpts(pkt4);
1412
1413 // Set hardware address
1414 pkt4->setHWAddr(offer_pkt4->getHWAddr());
1415 // Set client id.
1416 pkt4->addOption(generateClientId(pkt4->getHWAddr()));
1417 // Set elapsed time.
1418 uint32_t elapsed_time = getElapsedTime<Pkt4Ptr>(discover_pkt4, offer_pkt4);
1419 pkt4->setSecs(static_cast<uint16_t>(elapsed_time / 1000));
1420 // Prepare on wire data to send.
1421 pkt4->pack();
1422 socket_.send(pkt4);
1423 stats_mgr_.passSentPacket(ExchangeType::RA, pkt4);
1424 saveFirstPacket(pkt4);
1425}
1426
1427void
1428TestControl::sendRequest4(const std::vector<uint8_t>& template_buf,
1429 const dhcp::Pkt4Ptr& discover_pkt4,
1430 const dhcp::Pkt4Ptr& offer_pkt4) {
1431 // Get the second argument if multiple the same arguments specified
1432 // in the command line. Second one refers to REQUEST packets.
1433 const uint8_t arg_idx = 1;
1434 // Use the same transaction id as the one used in the discovery packet.
1435 const uint32_t transid = discover_pkt4->getTransid();
1436 // Get transaction id offset.
1437 size_t transid_offset = getTransactionIdOffset(arg_idx);
1438 // Get the offset of MAC's last octet.
1439 // We need to go back by HW_ETHER_LEN (MAC address length)
1440 // because this offset points to last octet of MAC address.
1441 size_t rand_offset = getRandomOffset(arg_idx) - HW_ETHER_LEN + 1;
1442 // Create temporary buffer from the template.
1443 std::vector<uint8_t> in_buf(template_buf.begin(),
1444 template_buf.end());
1445 // Check if given randomization offset is not out of bounds.
1446 if (rand_offset + HW_ETHER_LEN > in_buf.size()) {
1447 isc_throw(OutOfRange, "randomization offset is out of bounds");
1448 }
1449
1450 // Create packet from the temporary buffer.
1451 PerfPkt4Ptr pkt4(new PerfPkt4(&in_buf[0], in_buf.size(),
1452 transid_offset,
1453 transid));
1454
1455 // Set hardware address from OFFER packet received.
1456 HWAddrPtr hwaddr = offer_pkt4->getHWAddr();
1457 std::vector<uint8_t> mac_address(HW_ETHER_LEN, 0);
1458 uint8_t hw_len = hwaddr->hwaddr_.size();
1459 if (hw_len != 0) {
1460 memcpy(&mac_address[0], &hwaddr->hwaddr_[0],
1461 hw_len > HW_ETHER_LEN ? HW_ETHER_LEN : hw_len);
1462 }
1463 pkt4->writeAt(rand_offset, mac_address.begin(), mac_address.end());
1464
1465 // Set elapsed time.
1466 size_t elp_offset = getElapsedTimeOffset();
1467 uint32_t elapsed_time = getElapsedTime<Pkt4Ptr>(discover_pkt4, offer_pkt4);
1468 pkt4->writeValueAt<uint16_t>(elp_offset,
1469 static_cast<uint16_t>(elapsed_time / 1000));
1470
1471 // Get the actual server id offset.
1472 size_t sid_offset = getServerIdOffset();
1473 // Use first flags indicates that we want to use the server
1474 // id captured in first packet.
1475 if (options_.isUseFirst() &&
1476 (first_packet_serverid_.size() > 0)) {
1477 boost::shared_ptr<LocalizedOption>
1478 opt_serverid(new LocalizedOption(Option::V4,
1481 sid_offset));
1482 pkt4->addOption(opt_serverid);
1483 } else {
1484 // Copy the contents of server identifier received in
1485 // OFFER packet to put this into REQUEST.
1486 OptionPtr opt_serverid_offer =
1487 offer_pkt4->getOption(DHO_DHCP_SERVER_IDENTIFIER);
1488 if (!opt_serverid_offer) {
1489 isc_throw(BadValue, "there is no SERVER_IDENTIFIER option "
1490 << "in OFFER message");
1491 }
1492 boost::shared_ptr<LocalizedOption>
1493 opt_serverid(new LocalizedOption(Option::V4,
1495 opt_serverid_offer->getData(),
1496 sid_offset));
1497 pkt4->addOption(opt_serverid);
1498 if (stats_mgr_.getRcvdPacketsNum(ExchangeType::DO) == 1) {
1499 first_packet_serverid_ = opt_serverid_offer->getData();
1500 }
1501 }
1502
1504 asiolink::IOAddress yiaddr = offer_pkt4->getYiaddr();
1505 if (!yiaddr.isV4()) {
1506 isc_throw(BadValue, "the YIADDR returned in OFFER packet is not "
1507 " IPv4 address");
1508 }
1509
1510 // Get the actual offset of requested ip.
1511 size_t rip_offset = getRequestedIpOffset();
1512 // Place requested IP option at specified position (rip_offset).
1513 boost::shared_ptr<LocalizedOption>
1514 opt_requested_ip(new LocalizedOption(Option::V4,
1516 OptionBuffer(),
1517 rip_offset));
1518 // The IOAddress is convertible to uint32_t and returns exactly what we need.
1519 opt_requested_ip->setUint32(yiaddr.toUint32());
1520 pkt4->addOption(opt_requested_ip);
1521
1522 setDefaults4(boost::static_pointer_cast<Pkt4>(pkt4));
1523
1524 // Add any extra options that user may have specified.
1525 addExtraOpts(pkt4);
1526
1527 // Prepare on-wire data.
1528 pkt4->rawPack();
1529 socket_.send(boost::static_pointer_cast<Pkt4>(pkt4));
1530 // Update packet stats.
1531 stats_mgr_.passSentPacket(ExchangeType::RA,
1532 boost::static_pointer_cast<Pkt4>(pkt4));
1533 saveFirstPacket(pkt4);
1534}
1535
1536void
1537TestControl::sendRequest6(const Pkt6Ptr& advertise_pkt6) {
1538 const uint32_t transid = generateTransid();
1539 Pkt6Ptr pkt6(new Pkt6(DHCPV6_REQUEST, transid));
1540 // Set elapsed time.
1541 OptionPtr opt_elapsed_time =
1543 pkt6->addOption(opt_elapsed_time);
1544 // Set client id.
1545 OptionPtr opt_clientid = advertise_pkt6->getOption(D6O_CLIENTID);
1546 if (!opt_clientid) {
1547 isc_throw(Unexpected, "client id not found in received packet");
1548 }
1549 pkt6->addOption(opt_clientid);
1550
1551 // Use first flags indicates that we want to use the server
1552 // id captured in first packet.
1553 if (options_.isUseFirst() &&
1554 (first_packet_serverid_.size() > 0)) {
1555 pkt6->addOption(Option::factory(Option::V6, D6O_SERVERID,
1557 } else {
1558 OptionPtr opt_serverid = advertise_pkt6->getOption(D6O_SERVERID);
1559 if (!opt_serverid) {
1560 isc_throw(Unexpected, "server id not found in received packet");
1561 }
1562 if (stats_mgr_.getRcvdPacketsNum(ExchangeType::SA) == 1) {
1563 first_packet_serverid_ = opt_serverid->getData();
1564 }
1565 pkt6->addOption(opt_serverid);
1566 }
1567
1568 // Copy IA_NA or IA_PD option from the Advertise message to the Request
1569 // message being sent to the server. This will throw exception if the
1570 // option to be copied is not found. Note that this function will copy
1571 // one of IA_NA or IA_PD options, depending on the lease-type value
1572 // specified in the command line.
1573 copyIaOptions(advertise_pkt6, pkt6);
1574
1575 // Set default packet data.
1576 setDefaults6(pkt6);
1577
1578 // Add any extra options that user may have specified.
1579 addExtraOpts(pkt6);
1580
1581 // Prepare on-wire data.
1582 pkt6->pack();
1583 socket_.send(pkt6);
1584 stats_mgr_.passSentPacket(ExchangeType::RR, pkt6);
1585 saveFirstPacket(pkt6);
1586}
1587
1588void
1589TestControl::sendRequest6(const std::vector<uint8_t>& template_buf,
1590 const Pkt6Ptr& advertise_pkt6) {
1591 // Get the second argument if multiple the same arguments specified
1592 // in the command line. Second one refers to REQUEST packets.
1593 const uint8_t arg_idx = 1;
1594 // Generate transaction id.
1595 const uint32_t transid = generateTransid();
1596 // Get transaction id offset.
1597 size_t transid_offset = getTransactionIdOffset(arg_idx);
1598 PerfPkt6Ptr pkt6(new PerfPkt6(&template_buf[0], template_buf.size(),
1599 transid_offset, transid));
1600 // Set elapsed time.
1601 size_t elp_offset = getElapsedTimeOffset();
1602 boost::shared_ptr<LocalizedOption>
1603 opt_elapsed_time(new LocalizedOption(Option::V6, D6O_ELAPSED_TIME,
1604 OptionBuffer(), elp_offset));
1605 pkt6->addOption(opt_elapsed_time);
1606
1607 // Get the actual server id offset.
1608 size_t sid_offset = getServerIdOffset();
1609 // Use first flags indicates that we want to use the server
1610 // id captured in first packet.
1611 if (options_.isUseFirst() &&
1612 (first_packet_serverid_.size() > 0)) {
1613 boost::shared_ptr<LocalizedOption>
1614 opt_serverid(new LocalizedOption(Option::V6,
1617 sid_offset));
1618 pkt6->addOption(opt_serverid);
1619
1620 } else {
1621 // Copy the contents of server identifier received in
1622 // ADVERTISE packet to put this into REQUEST.
1623 OptionPtr opt_serverid_advertise =
1624 advertise_pkt6->getOption(D6O_SERVERID);
1625 if (!opt_serverid_advertise) {
1626 isc_throw(BadValue, "there is no SERVERID option "
1627 << "in ADVERTISE message");
1628 }
1629 boost::shared_ptr<LocalizedOption>
1630 opt_serverid(new LocalizedOption(Option::V6,
1632 opt_serverid_advertise->getData(),
1633 sid_offset));
1634 pkt6->addOption(opt_serverid);
1635 if (stats_mgr_.getRcvdPacketsNum(ExchangeType::SA) == 1) {
1636 first_packet_serverid_ = opt_serverid_advertise->getData();
1637 }
1638 }
1639 // Set IA_NA
1640 OptionPtr opt_ia_na_advertise = advertise_pkt6->getOption(D6O_IA_NA);
1641 if (!opt_ia_na_advertise) {
1642 isc_throw(Unexpected, "DHCPv6 IA_NA option not found in received "
1643 "packet");
1644 }
1645 size_t addr_offset = getRequestedIpOffset();
1646 boost::shared_ptr<LocalizedOption>
1647 opt_ia_na(new LocalizedOption(Option::V6, D6O_IA_NA, opt_ia_na_advertise->getData(), addr_offset));
1648 if (!opt_ia_na->valid()) {
1649 isc_throw(BadValue, "Option IA_NA in advertise packet is invalid");
1650 }
1651 pkt6->addOption(opt_ia_na);
1652 // Set server id.
1653 OptionPtr opt_serverid_advertise = advertise_pkt6->getOption(D6O_SERVERID);
1654 if (!opt_serverid_advertise) {
1655 isc_throw(Unexpected, "DHCPV6 SERVERID option not found in received "
1656 "packet");
1657 }
1658 size_t srvid_offset = getServerIdOffset();
1659 boost::shared_ptr<LocalizedOption>
1660 opt_serverid(new LocalizedOption(Option::V6, D6O_SERVERID,
1661 opt_serverid_advertise->getData(),
1662 srvid_offset));
1663 pkt6->addOption(opt_serverid);
1664 // Get randomization offset.
1665 size_t rand_offset = getRandomOffset(arg_idx);
1666 OptionPtr opt_clientid_advertise = advertise_pkt6->getOption(D6O_CLIENTID);
1667 if (!opt_clientid_advertise) {
1668 isc_throw(Unexpected, "DHCPV6 CLIENTID option not found in received packet");
1669 }
1670 rand_offset -= (opt_clientid_advertise->len() - 1);
1671 // Set client id.
1672 boost::shared_ptr<LocalizedOption>
1673 opt_clientid(new LocalizedOption(Option::V6, D6O_CLIENTID,
1674 opt_clientid_advertise->getData(),
1675 rand_offset));
1676 pkt6->addOption(opt_clientid);
1677 // Set default packet data.
1678 setDefaults6(pkt6);
1679
1680 // Add any extra options that user may have specified.
1681 addExtraOpts(pkt6);
1682
1683 // Prepare on wire data.
1684 pkt6->rawPack();
1685 // Send packet.
1686 socket_.send(pkt6);
1687 // Update packet stats.
1688 stats_mgr_.passSentPacket(ExchangeType::RR, pkt6);
1689
1690 // When 'T' diagnostics flag is specified it means that user requested
1691 // printing packet contents. It will be just one (first) packet which
1692 // contents will be printed. Here we check if this packet has been already
1693 // collected. If it hasn't we save this packet so as we can print its
1694 // contents when test is finished.
1695 if (options_.testDiags('T') &&
1698 }
1699}
1700
1701void
1702TestControl::sendSolicit6(const bool preload /*= false*/) {
1703 // Generate DUID to be passed to the packet
1704 uint8_t randomized = 0;
1705 std::vector<uint8_t> duid = generateDuid(randomized);
1706 // Generate transaction id to be set for the new exchange.
1707 const uint32_t transid = generateTransid();
1708 Pkt6Ptr pkt6(new Pkt6(DHCPV6_SOLICIT, transid));
1709 if (!pkt6) {
1710 isc_throw(Unexpected, "failed to create SOLICIT packet");
1711 }
1712
1713 // Check if we need to simulate HA failures by pretending no responses were received.
1714 // The DHCPv6 protocol signals that by increasing the elapsed option field. Note it is in 1/100 of a second.
1715 if (options_.getIncreaseElapsedTime() &&
1716 stats_mgr_.getTestPeriod().length().total_seconds() >= options_.getWaitForElapsedTime() &&
1717 stats_mgr_.getTestPeriod().length().total_seconds() < options_.getWaitForElapsedTime() +
1718 options_.getIncreaseElapsedTime()) {
1719
1720
1721 // Keep increasing elapsed time. The value should start increasing steadily.
1722 uint32_t val = (stats_mgr_.getTestPeriod().length().total_seconds() - options_.getWaitForElapsedTime() + 1)*100;
1723 if (val > 65535) {
1724 val = 65535;
1725 }
1727 pkt6->addOption(elapsed);
1728 } else {
1729 pkt6->addOption(Option::factory(Option::V6, D6O_ELAPSED_TIME));
1730 }
1731
1732 if (options_.isRapidCommit()) {
1733 pkt6->addOption(Option::factory(Option::V6, D6O_RAPID_COMMIT));
1734 }
1735 pkt6->addOption(Option::factory(Option::V6, D6O_CLIENTID, duid));
1736 pkt6->addOption(Option::factory(Option::V6, D6O_ORO));
1737
1738
1739 // Depending on the lease-type option specified, we should request
1740 // IPv6 address (with IA_NA) or IPv6 prefix (IA_PD) or both.
1741
1742 // IA_NA
1743 if (options_.getLeaseType().includes(CommandOptions::LeaseType::ADDRESS)) {
1744 pkt6->addOption(Option::factory(Option::V6, D6O_IA_NA));
1745 }
1746 // IA_PD
1747 if (options_.getLeaseType().includes(CommandOptions::LeaseType::PREFIX)) {
1748 pkt6->addOption(Option::factory(Option::V6, D6O_IA_PD));
1749 }
1750
1751 setDefaults6(pkt6);
1752
1753 // Add any extra options that user may have specified.
1754 addExtraOpts(pkt6);
1755
1756 pkt6->pack();
1757 socket_.send(pkt6);
1758 if (!preload) {
1759 stats_mgr_.passSentPacket(ExchangeType::SA, pkt6);
1760 }
1761
1762 saveFirstPacket(pkt6);
1763}
1764
1765void
1766TestControl::sendSolicit6(const std::vector<uint8_t>& template_buf,
1767 const bool preload /*= false*/) {
1768 const int arg_idx = 0;
1769 // Get transaction id offset.
1770 size_t transid_offset = getTransactionIdOffset(arg_idx);
1771 // Generate transaction id to be set for the new exchange.
1772 const uint32_t transid = generateTransid();
1773 // Create packet.
1774 PerfPkt6Ptr pkt6(new PerfPkt6(&template_buf[0], template_buf.size(),
1775 transid_offset, transid));
1776 if (!pkt6) {
1777 isc_throw(Unexpected, "failed to create SOLICIT packet");
1778 }
1779 size_t rand_offset = getRandomOffset(arg_idx);
1780 // randomized will pick number of bytes randomized so we can
1781 // just use part of the generated duid and substitute a few bytes
1783 uint8_t randomized = 0;
1784 std::vector<uint8_t> duid = generateDuid(randomized);
1785 if (rand_offset > template_buf.size()) {
1786 isc_throw(OutOfRange, "randomization offset is out of bounds");
1787 }
1788 // Store random part of the DUID into the packet.
1789 pkt6->writeAt(rand_offset - randomized + 1,
1790 duid.end() - randomized, duid.end());
1791
1792 // Prepare on-wire data.
1793 pkt6->rawPack();
1794 setDefaults6(pkt6);
1795
1796 // Add any extra options that user may have specified.
1797 addExtraOpts(pkt6);
1798
1799 // Send solicit packet.
1800 socket_.send(pkt6);
1801 if (!preload) {
1802 // Update packet stats.
1803 stats_mgr_.passSentPacket(ExchangeType::SA, pkt6);
1804 }
1805 saveFirstPacket(pkt6);
1806}
1807
1808
1809void
1811 // Interface name.
1812 IfacePtr iface = socket_.getIface();
1813 if (iface == NULL) {
1814 isc_throw(BadValue, "unable to find interface with given index");
1815 }
1816 pkt->setIface(iface->getName());
1817 // Interface index.
1818 pkt->setIndex(socket_.ifindex_);
1819 // Local client's port (68)
1820 pkt->setLocalPort(DHCP4_CLIENT_PORT);
1821 // Server's port (67)
1822 if (options_.getRemotePort()) {
1823 pkt->setRemotePort(options_.getRemotePort());
1824 } else {
1825 pkt->setRemotePort(DHCP4_SERVER_PORT);
1826 }
1827 // The remote server's name or IP.
1828 pkt->setRemoteAddr(IOAddress(options_.getServerName()));
1829 // Set local address.
1830 pkt->setLocalAddr(socket_.addr_);
1831 // Set relay (GIADDR) address to local address if multiple
1832 // subnet mode is not enabled
1833 if (!options_.checkMultiSubnet()) {
1834 pkt->setGiaddr(socket_.addr_);
1835 } else {
1836 pkt->setGiaddr(IOAddress(options_.getRandRelayAddr()));
1837 }
1838 // Pretend that we have one relay (which is us).
1839 pkt->setHops(1);
1840}
1841
1842void
1844 // Interface name.
1845 IfacePtr iface = socket_.getIface();
1846 if (iface == NULL) {
1847 isc_throw(BadValue, "unable to find interface with given index");
1848 }
1849 pkt->setIface(iface->getName());
1850 // Interface index.
1851 pkt->setIndex(socket_.ifindex_);
1852 // Local client's port (547)
1853 pkt->setLocalPort(DHCP6_CLIENT_PORT);
1854 // Server's port (548)
1855 if (options_.getRemotePort()) {
1856 pkt->setRemotePort(options_.getRemotePort());
1857 } else {
1858 pkt->setRemotePort(DHCP6_SERVER_PORT);
1859 }
1860 // Set local address.
1861 pkt->setLocalAddr(socket_.addr_);
1862 // The remote server's name or IP.
1863 pkt->setRemoteAddr(IOAddress(options_.getServerName()));
1864
1865 // only act as a relay agent when told so.
1868 if (options_.isUseRelayedV6()) {
1869 Pkt6::RelayInfo relay_info;
1870 relay_info.msg_type_ = DHCPV6_RELAY_FORW;
1871 relay_info.hop_count_ = 0;
1872 if (options_.checkMultiSubnet()) {
1873 relay_info.linkaddr_ = IOAddress(options_.getRandRelayAddr());
1874 } else {
1875 relay_info.linkaddr_ = socket_.addr_;
1876 }
1877 relay_info.peeraddr_ = socket_.addr_;
1878 relay_info.options_.insert(options_.getRelayOpts().begin(), options_.getRelayOpts().end());
1879 pkt->addRelayInfo(relay_info);
1880 }
1881}
1882
1883namespace {
1884
1885static OptionBuffer const concatenateBuffers(OptionBuffer const& a,
1886 OptionBuffer const& b) {
1887 OptionBuffer result;
1888 result.insert(result.end(), a.begin(), a.end());
1889 result.insert(result.end(), b.begin(), b.end());
1890 return (result);
1891}
1892
1893static void mergeOptionIntoPacket(Pkt4Ptr const& packet,
1894 OptionPtr const& extra_option) {
1895 uint16_t const code(extra_option->getType());
1896 // If option already exists...
1897 OptionPtr const& option(packet->getOption(code));
1898 if (option) {
1899 switch (code) {
1900 // List here all the options for which we want to concatenate buffers.
1902 packet->delOption(code);
1903 packet->addOption(boost::make_shared<Option>(
1904 Option::V4, code,
1905 concatenateBuffers(option->getData(),
1906 extra_option->getData())));
1907 return;
1908 default:
1909 // For all others, add option as usual, it will result in "Option
1910 // already present in this message" error.
1911 break;
1912 }
1913 }
1914 packet->addOption(extra_option);
1915}
1916
1917} // namespace
1918
1919void
1921 // Add all extra options that the user may have specified.
1922 const dhcp::OptionCollection& extra_opts = options_.getExtraOpts();
1923 for (auto const& entry : extra_opts) {
1924 mergeOptionIntoPacket(pkt, entry.second);
1925 }
1926}
1927
1928void
1930 // Add all extra options that the user may have specified.
1931 const dhcp::OptionCollection& extra_opts = options_.getExtraOpts();
1932 for (auto const& entry : extra_opts) {
1933 pkt->addOption(entry.second);
1934 }
1935}
1936
1937} // namespace perfdhcp
1938} // namespace isc
A generic exception that is thrown if a parameter given to a method is considered invalid in that con...
A generic exception that is thrown if a function is called in a prohibited way.
A generic exception that is thrown when an object can not be found.
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.
static IfaceMgr & instance()
IfaceMgr is a singleton class.
Definition iface_mgr.cc:54
bool configureDHCPPacketQueue(const uint16_t family, data::ConstElementPtr queue_control)
Configures DHCP packet queue.
static void OptionFactoryRegister(Option::Universe u, uint16_t type, Option::Factory *factory)
Registers factory method that produces options of specific option types.
isc::asiolink::IOAddress getAddress() const
Returns address contained within this option.
Class that represents IAPREFIX option in DHCPv6.
Forward declaration to OptionInt.
Definition option_int.h:49
static OptionPtr factory(Option::Universe u, uint16_t type, const OptionBuffer &buf)
Factory function to create instance of option.
Definition option.cc:33
Universe
defines option universe DHCPv4 or DHCPv6
Definition option.h:90
Represents DHCPv4 packet.
Definition pkt4.h:37
Represents a DHCPv6 packet.
Definition pkt6.h:44
Socket wrapper structure.
Definition perf_socket.h:26
ExchangeMode
2-way (cmd line param -i) or 4-way exchanges
std::vector< std::vector< uint8_t > > MacAddrsVector
A vector holding MAC addresses.
DHCP option at specific offset.
PerfPkt4 (DHCPv4 packet)
Definition perf_pkt4.h:42
PerfPkt6 (DHCPv6 packet)
Definition perf_pkt6.h:41
Random numbers generator class.
Sequential numbers generator class.
void saveFirstPacket(const dhcp::Pkt4Ptr &pkt)
Save the first DHCPv4 sent packet of the specified type.
void address6Uniqueness(const dhcp::Pkt6Ptr &pkt6, ExchangeType xchg_type)
Process received v6 addresses uniqueness.
static const uint8_t HW_ETHER_LEN
Length of the Ethernet HW address (MAC) in bytes.
bool waitToExit()
Delay the exit by a fixed given time to catch up to all exchanges that were already started.
std::map< uint8_t, dhcp::Pkt4Ptr > template_packets_v4_
First packets send.
bool sendMessageFromReply(const uint16_t msg_type)
Send DHCPv6 Renew or Release message.
void addExtraOpts(const dhcp::Pkt4Ptr &pkt4)
Inserts extra options specified by user.
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.
NumberGeneratorPtr macaddr_gen_
Numbers generator for MAC address.
int getRequestedIpOffset() const
Return requested ip offset in a packet.
NumberGeneratorPtr random_generator_
Generate uniformly distributed integers in range of [min, max].
boost::shared_ptr< NumberGenerator > NumberGeneratorPtr
The default generator pointer.
uint64_t sendMultipleMessages4(const uint32_t msg_type, const uint64_t msg_num)
Send number of DHCPREQUEST (renew) messages to a server.
bool validateIA(const dhcp::Pkt6Ptr &pkt6)
Process IA in received DHCPv6 packet.
dhcp::Pkt4Ptr createMessageFromAck(const uint16_t msg_type, const dhcp::Pkt4Ptr &ack)
Creates DHCPREQUEST from a DHCPACK message.
static bool interrupted_
Program interrupted flag.
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.
void setDefaults4(const dhcp::Pkt4Ptr &pkt)
Set default DHCPv4 packet parameters.
boost::posix_time::ptime exit_time_
Initialized at first exit condition with the time perfdhcp should exit.
CommandOptions & options_
Command options.
Receiver receiver_
Receiver used to receive DHCP traffic.
static std::string vector2Hex(const std::vector< uint8_t > &vec, const std::string &separator="")
Convert vector in hexadecimal string.
uint64_t sendMultipleMessages6(const uint32_t msg_type, const uint64_t msg_num)
Send number of DHCPv6 Renew or Release messages to the server.
void setMacAddrGenerator(const NumberGeneratorPtr &generator)
Set new MAC address generator.
void setDefaults6(const dhcp::Pkt6Ptr &pkt)
Set default DHCPv6 packet parameters.
boost::posix_time::ptime last_report_
Last intermediate report time.
bool haveAllPacketsBeenReceived() const
Checks if all expected packets were already received.
void cleanCachedPackets()
Removes cached DHCPv6 Reply packets every second.
void processReceivedPacket4(const dhcp::Pkt4Ptr &pkt4)
Process received DHCPv4 packet.
void sendRequest6(const dhcp::Pkt6Ptr &advertise_pkt6)
Send DHCPv6 REQUEST message.
TestControl(CommandOptions &options, BasePerfSocket &socket)
Default constructor.
static dhcp::OptionPtr factoryOptionRequestOption6(dhcp::Option::Universe u, uint16_t type, const dhcp::OptionBuffer &buf)
Factory function to create DHCPv6 ORO option.
void sendSolicit6(const bool preload=false)
Send DHCPv6 SOLICIT message.
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_
Template for 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_
Storage for reply messages.
void registerOptionFactories6() const
Register option factory functions for DHCPv6.
void sendPackets(const uint64_t packets_num, const bool preload=false)
Send number of packets to initiate new exchanges.
void registerOptionFactories() const
Register option factory functions for DHCPv4 or DHCPv6.
std::vector< uint8_t > TemplateBuffer
Packet template buffer.
bool sendMessageFromAck(const uint16_t msg_type)
Send DHCPv4 renew (DHCPREQUEST).
void runWrapped(bool do_stop=false) const
Run wrapped command.
void processReceivedPacket6(const dhcp::Pkt6Ptr &pkt6)
Process received DHCPv6 packet.
uint32_t getElapsedTime(const T &pkt1, const T &pkt2)
Calculate elapsed time between two packets.
BasePerfSocket & socket_
Socket used for DHCP traffic.
void reset()
Resets internal state of the object.
void addUniqeAddr(const std::set< std::string > &current, ExchangeType xchg_type)
add unique address to already assigned list.
void address4Uniqueness(const dhcp::Pkt4Ptr &pkt4, ExchangeType xchg_type)
Process received v4 addresses uniqueness.
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.
static std::string byte2Hex(const uint8_t b)
Convert binary value to hex string.
void printTemplates() const
Print templates information.
void sendRequest4(const dhcp::Pkt4Ptr &discover_pkt4, const dhcp::Pkt4Ptr &offer_pkt4)
Send DHCPv4 REQUEST message.
unsigned int consumeReceivedPackets()
Pull packets from receiver and process them.
TemplateBufferCollection template_buffers_
Packet template buffers.
StatsMgr stats_mgr_
Statistics Manager.
void printStats() const
Print performance statistics.
PacketStorage< dhcp::Pkt4 > ack_storage_
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.
void setTransidGenerator(const NumberGeneratorPtr &generator)
Set new transaction id generator.
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.
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.
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.
void sendDiscover4(const bool preload=false)
Send DHCPv4 DISCOVER message.
dhcp::OptionPtr generateClientId(const dhcp::HWAddrPtr &hwaddr) const
Generate DHCPv4 client identifier from HW address.
dhcp::OptionBuffer first_packet_serverid_
Buffer holding server id received in first packet.
uint32_t generateTransid()
generate transaction id.
static dhcp::OptionPtr factoryIana6(dhcp::Option::Universe u, uint16_t type, const dhcp::OptionBuffer &buf)
Factory function to create IA_NA option.
@ 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_IAADDR
Definition dhcp6.h:25
@ D6O_ELAPSED_TIME
Definition dhcp6.h:28
@ D6O_IAPREFIX
Definition dhcp6.h:46
@ DHCPV6_ADVERTISE
Definition dhcp6.h:209
@ DHCPV6_REQUEST
Definition dhcp6.h:210
@ DHCPV6_RENEW
Definition dhcp6.h:212
@ DHCPV6_REPLY
Definition dhcp6.h:214
@ DHCPV6_SOLICIT
Definition dhcp6.h:208
@ DHCPV6_RELEASE
Definition dhcp6.h:215
@ DHCPV6_RELAY_FORW
Definition dhcp6.h:219
#define isc_throw(type, stream)
A shortcut macro to insert known values into exception arguments.
boost::shared_ptr< Element > ElementPtr
Definition data.h:28
boost::shared_ptr< isc::dhcp::Pkt > PktPtr
A pointer to either Pkt4 or Pkt6 packet.
Definition pkt.h:999
@ 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:556
boost::shared_ptr< Iface > IfacePtr
Type definition for the pointer to an Iface object.
Definition iface_mgr.h:487
std::multimap< unsigned int, OptionPtr > OptionCollection
A collection of DHCP (v4 or v6) options.
Definition option.h:40
boost::shared_ptr< Option6IAPrefix > Option6IAPrefixPtr
Pointer to the Option6IAPrefix object.
boost::shared_ptr< HWAddr > HWAddrPtr
Shared pointer to a hardware address structure.
Definition hwaddr.h:154
boost::shared_ptr< Option6IAAddr > Option6IAAddrPtr
A pointer to the isc::dhcp::Option6IAAddr object.
@ DHCPREQUEST
Definition dhcp4.h:237
@ DHCPOFFER
Definition dhcp4.h:236
@ DHCPRELEASE
Definition dhcp4.h:241
@ DHCPDISCOVER
Definition dhcp4.h:235
@ DHCPACK
Definition dhcp4.h:239
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:24
@ HTYPE_ETHER
Ethernet 10Mbps.
Definition dhcp4.h:56
boost::shared_ptr< Option > OptionPtr
Definition option.h:37
boost::shared_ptr< PerfPkt4 > PerfPkt4Ptr
Definition perf_pkt4.h:128
ExchangeType
DHCP packet exchange types.
@ SA
DHCPv6 SOLICIT-ADVERTISE.
@ RNA
DHCPv4 REQUEST-ACK (renewal)
boost::shared_ptr< PerfPkt6 > PerfPkt6Ptr
Definition perf_pkt6.h:127
Defines the logger used by the top-level component of kea-lfc.
structure that describes a single relay information
Definition pkt6.h:85
isc::dhcp::OptionCollection options_
options received from a specified relay, except relay-msg option
Definition pkt6.h:104
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