Kea 2.6.0
test_control.cc
Go to the documentation of this file.
1// Copyright (C) 2012-2024 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) {
87 } else {
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
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
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
322 const OptionBuffer&) {
324}
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>
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));
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
545 interrupted_ = true;
546}
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.
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 while (line_len == 32) {
660 if (hex_buf.length() - i < 32) {
661 line_len = hex_buf.length() - i;
662 };
663 if (line_len > 0) {
664 std::cout << setfill('0') << setw(4) << std::hex << i << std::dec
665 << " " << hex_buf.substr(i, line_len) << std::endl;
666 }
667 i += 32;
668 }
669 std::cout << std::endl;
670}
671
672void
674 if (options_.getIpVersion() == 4) {
677 } else if (options_.getIpVersion() == 6) {
680 }
681}
682
683void
685 double rate = 0;
686 std::string exchange_name = "4-way exchanges";
687 ExchangeType xchg_type = ExchangeType::DO;
688 if (options_.getIpVersion() == 4) {
689 xchg_type =
692 if (xchg_type == ExchangeType::DO) {
693 exchange_name = "DISCOVER-OFFER";
694 }
695 } else if (options_.getIpVersion() == 6) {
696 xchg_type =
699 if (xchg_type == ExchangeType::SA) {
700 exchange_name = options_.isRapidCommit() ? "Solicit-Reply" :
701 "Solicit-Advertise";
702 }
703 }
704 double duration =
705 stats_mgr_.getTestPeriod().length().total_nanoseconds() / 1e9;
706 rate = stats_mgr_.getRcvdPacketsNum(xchg_type) / duration;
707 std::ostringstream s;
708 s << "***Rate statistics***" << std::endl;
709 s << "Rate: " << rate << " " << exchange_name << "/second";
710 if (options_.getRate() > 0) {
711 s << ", expected rate: " << options_.getRate() << std::endl;
712 }
713
714 std::cout << s.str() << std::endl;
715
716 std::cout <<"***Malformed Packets***" << std::endl
717 << "Malformed packets: " << ExchangeStats::malformed_pkts_
718 << std::endl;
719}
720
721void
723 int delay = options_.getReportDelay();
724 ptime now = microsec_clock::universal_time();
725 time_period time_since_report(last_report_, now);
726 if (time_since_report.length().total_seconds() >= delay) {
729 last_report_ = now;
730 }
731}
732
733void
735 printRate();
737 if (options_.testDiags('i')) {
739 }
740}
741
742std::string
743TestControl::vector2Hex(const std::vector<uint8_t>& vec,
744 const std::string& separator /* = "" */) {
745 std::ostringstream stream;
746 bool first = true;
747 for (auto const& it : vec) {
748 if (first) {
749 stream << byte2Hex(it);
750 first = false;
751 } else {
752 stream << separator << byte2Hex(it);
753 }
754 }
755 return (stream.str());
756}
757
758void
759TestControl::readPacketTemplate(const std::string& file_name) {
760 std::ifstream temp_file;
761 temp_file.open(file_name.c_str(), ios::in | ios::binary | ios::ate);
762 if (!temp_file.is_open()) {
763 isc_throw(BadValue, "unable to open template file " << file_name);
764 }
765 // Read template file contents.
766 std::streampos temp_size = temp_file.tellg();
767 if (temp_size == std::streampos(0)) {
768 temp_file.close();
769 isc_throw(OutOfRange, "the template file " << file_name << " is empty");
770 }
771 temp_file.seekg(0, ios::beg);
772 std::vector<char> file_contents(temp_size);
773 temp_file.read(&file_contents[0], temp_size);
774 temp_file.close();
775 // Spaces are allowed so we have to strip the contents
776 // from them. In the same time we want to make sure that
777 // apart from spaces the file contains hexadecimal digits
778 // only.
779 std::vector<char> hex_digits;
780 for (size_t i = 0; i < file_contents.size(); ++i) {
781 if (isxdigit(file_contents[i])) {
782 hex_digits.push_back(file_contents[i]);
783 } else if (!isxdigit(file_contents[i]) &&
784 !isspace(file_contents[i])) {
785 isc_throw(BadValue, "'" << file_contents[i] << "' is not a"
786 " hexadecimal digit");
787 }
788 }
789 // Expect even number of digits.
790 if (hex_digits.size() % 2 != 0) {
791 isc_throw(OutOfRange, "odd number of digits in template file");
792 } else if (hex_digits.empty()) {
793 isc_throw(OutOfRange, "template file " << file_name << " is empty");
794 }
795 std::vector<uint8_t> binary_stream;
796 for (size_t i = 0; i < hex_digits.size(); i += 2) {
797 stringstream s;
798 s << "0x" << hex_digits[i] << hex_digits[i+1];
799 int b;
800 s >> std::hex >> b;
801 binary_stream.push_back(static_cast<uint8_t>(b));
802 }
803 template_buffers_.push_back(binary_stream);
804}
805
806void
808 if (pkt4->getType() == DHCPOFFER) {
811 Pkt4Ptr discover_pkt4(boost::dynamic_pointer_cast<Pkt4>(pkt));
813 if ((xchg_mode == CommandOptions::DORA_SARR) && discover_pkt4) {
814 if (template_buffers_.size() < 2) {
815 sendRequest4(discover_pkt4, pkt4);
816 } else {
819 sendRequest4(template_buffers_[1], discover_pkt4, pkt4);
820 }
821 }
822 } else if (pkt4->getType() == DHCPACK) {
823 // If received message is DHCPACK, we have to check if this is
824 // a response to 4-way exchange. We'll match this packet with
825 // a DHCPREQUEST sent as part of the 4-way exchanges.
828 // The DHCPACK belongs to DHCPREQUEST-DHCPACK exchange type.
829 // So, we may need to keep this DHCPACK in the storage if renews.
830 // Note that, DHCPACK messages hold the information about
831 // leases assigned. We use this information to renew.
834 // Renew or release messages are sent, because StatsMgr has the
835 // specific exchange type specified. Let's append the DHCPACK
836 // message to a storage.
837 ack_storage_.append(pkt4);
838 }
839 // The DHCPACK message is not a server's response to the DHCPREQUEST
840 // message sent within the 4-way exchange. It may be a response to a
841 // renewal. In this case we first check if StatsMgr has exchange type
842 // for renew specified, and if it has, if there is a corresponding
843 // renew message for the received DHCPACK.
846 }
847 }
848}
849
850void
852 // check if received address is unique
853 if (options_.getAddrUnique()) {
854 std::set<std::string> current;
855 // addresses were already checked in validateIA
856 // we can safely assume that those are correct
857 for (auto const& opt : pkt6->options_) {
858 switch (opt.second->getType()) {
859 case D6O_IA_PD: {
860 // add address and check if it has not been already assigned
861 // addresses should be unique cross options of the packet
862 auto ret = current.emplace(boost::dynamic_pointer_cast<
863 Option6IAPrefix>(opt.second->getOption(D6O_IAPREFIX))->getAddress().toText());
864 if (!ret.second) {
866 }
867 break;
868 }
869 case D6O_IA_NA: {
870 // add address and check if it has not been already assigned
871 // addresses should be unique cross options of the packet
872 auto ret = current.emplace(boost::dynamic_pointer_cast<
873 Option6IAAddr>(opt.second->getOption(D6O_IAADDR))->getAddress().toText());
874 if (!ret.second) {
876 }
877 break;
878 }
879 default:
880 break;
881 }
882 }
883 // addresses should be unique cross packets
884 addUniqeAddr(current, xchg_type);
885 }
886}
887
888void
890 // check if received address is unique
891 if (options_.getAddrUnique()) {
892 // addresses were already checked in validateIA
893 // we can safely assume that those are correct
894 std::set<std::string> current;
895 current.insert(pkt4->getYiaddr().toText());
896 // addresses should be unique cross packets
897 addUniqeAddr(current, xchg_type);
898 }
899}
900
901bool
903 // check if iaaddr exists - if it does, we can continue sending request
904 // if not we will update statistics about rejected leases
905 // @todo it's checking just one iaaddress option for now it's ok
906 // but when perfdhcp will be extended to create message with multiple IA
907 // this will have to be iterate on:
908 // OptionCollection ias = pkt6->getOptions(D6O_IA_NA);
909 Option6IAPrefixPtr iapref;
910 Option6IAAddrPtr iaaddr;
911 if (pkt6->getOption(D6O_IA_PD)) {
912 iapref = boost::dynamic_pointer_cast<
913 Option6IAPrefix>(pkt6->getOption(D6O_IA_PD)->getOption(D6O_IAPREFIX));
914 }
915 if (pkt6->getOption(D6O_IA_NA)) {
916 iaaddr = boost::dynamic_pointer_cast<
917 Option6IAAddr>(pkt6->getOption(D6O_IA_NA)->getOption(D6O_IAADDR));
918 }
919
920 bool address_and_prefix = options_.getLeaseType().includes(
922 bool prefix_only = options_.getLeaseType().includes(
924 bool address_only = options_.getLeaseType().includes(
926 if ((address_and_prefix && iapref && iaaddr) ||
927 (prefix_only && iapref && !address_and_prefix) ||
928 (address_only && iaaddr && !address_and_prefix)) {
929 return (true);
930 } else {
931 return (false);
932 }
933}
934
935void
937 uint8_t packet_type = pkt6->getType();
938 if (packet_type == DHCPV6_ADVERTISE) {
940 Pkt6Ptr solicit_pkt6(boost::dynamic_pointer_cast<Pkt6>(pkt));
942 if ((xchg_mode == CommandOptions::DORA_SARR) && solicit_pkt6) {
943 if (validateIA(pkt6)) {
944 // if address is correct - check uniqueness
946 if (template_buffers_.size() < 2) {
947 sendRequest6(pkt6);
948 } else {
952 }
953 } else {
955 }
956 }
957 } else if (packet_type == DHCPV6_REPLY) {
958 // If the received message is Reply, we have to find out which exchange
959 // type the Reply message belongs to. It is doable by matching the Reply
960 // transaction id with the transaction id of the sent Request, Renew
961 // or Release. First we start with the Request.
963 // The Reply belongs to Request-Reply exchange type. So, we may need
964 // to keep this Reply in the storage if Renews or/and Releases are
965 // being sent. Note that, Reply messages hold the information about
966 // leases assigned. We use this information to construct Renew and
967 // Release messages.
968 if (validateIA(pkt6)) {
969 // if address is correct - check uniqueness
971 // check if there is correct IA to continue with Renew/Release
974 // Renew or Release messages are sent, because StatsMgr has the
975 // specific exchange type specified. Let's append the Reply
976 // message to a storage.
977 reply_storage_.append(pkt6);
978 }
979 } else {
981 }
982 // The Reply message is not a server's response to the Request message
983 // sent within the 4-way exchange. It may be a response to the Renew
984 // or Release message. In the if clause we first check if StatsMgr
985 // has exchange type for Renew specified, and if it has, if there is
986 // a corresponding Renew message for the received Reply. If not,
987 // we check that StatsMgr has exchange type for Release specified,
988 // as possibly the Reply has been sent in response to Release.
992 // At this point, it is only possible that the Reply has been sent
993 // in response to a Release. Try to match the Reply with Release.
995 }
996 }
997}
998
999unsigned int
1001 unsigned int pkt_count = 0;
1002 PktPtr pkt;
1003 while ((pkt = receiver_.getPkt())) {
1004 pkt_count += 1;
1005 if (options_.getIpVersion() == 4) {
1006 Pkt4Ptr pkt4 = boost::dynamic_pointer_cast<Pkt4>(pkt);
1008 } else {
1009 Pkt6Ptr pkt6 = boost::dynamic_pointer_cast<Pkt6>(pkt);
1011 }
1012 }
1013 return pkt_count;
1014}
1015void
1017 static bool factories_registered = false;
1018 if (!factories_registered) {
1019 // DHCP_MESSAGE_TYPE option factory.
1023 // DHCP_SERVER_IDENTIFIER option factory.
1027 // DHCP_PARAMETER_REQUEST_LIST option factory.
1031 }
1032 factories_registered = true;
1033}
1034
1035void
1037 static bool factories_registered = false;
1038 if (!factories_registered) {
1039 // D6O_ELAPSED_TIME
1043 // D6O_RAPID_COMMIT
1047 // D6O_ORO (option request option) factory.
1049 D6O_ORO,
1051 // D6O_CLIENTID option factory.
1055 // D6O_SERVERID option factory.
1059 // D6O_IA_NA option factory.
1061 D6O_IA_NA,
1063
1064 // D6O_IA_PD option factory.
1066 D6O_IA_PD,
1068
1069
1070 }
1071 factories_registered = true;
1072}
1073
1074void
1076 switch(options_.getIpVersion()) {
1077 case 4:
1079 break;
1080 case 6:
1082 break;
1083 default:
1084 isc_throw(InvalidOperation, "command line options have to be parsed "
1085 "before DHCP option factories can be registered");
1086 }
1087}
1088
1089void
1091 transid_gen_.reset();
1092 last_report_ = microsec_clock::universal_time();
1093 // Actual generators will have to be set later on because we need to
1094 // get command line parameters first.
1097 first_packet_serverid_.clear();
1098 interrupted_ = false;
1099}
1100
1102 exit_time_(not_a_date_time),
1103 socket_(socket),
1104 receiver_(socket, options.isSingleThreaded(), options.getIpVersion()),
1105 stats_mgr_(options),
1106 random_generator_(new RandomGenerator(0, options.getMacsFromFile().size())),
1107 options_(options) {
1108 // Reset singleton state before test starts.
1109 reset();
1110
1111 // Ip version is not set ONLY in case the command options
1112 // were not parsed. This surely means that parse() function
1113 // was not called prior to starting the test. This is fatal
1114 // error.
1115 if (options_.getIpVersion() == 0) {
1117 "command options must be parsed before running a test");
1118 } else if (options_.getIpVersion() == 4) {
1119 // Turn off packet queueing.
1122 } else {
1123 // Turn off packet queueing.
1126 }
1127
1128 uint32_t clients_num = options_.getClientsNum() == 0 ?
1129 1 : options_.getClientsNum();
1131
1132 // Diagnostics are command line options mainly.
1134 // Option factories have to be registered.
1136 // Initialize packet templates.
1138 // Initialize randomization seed.
1139 if (options_.isSeeded()) {
1140 srandom(options_.getSeed());
1141 } else {
1142 // Seed with current time.
1143 time_period duration(from_iso_string("20111231T235959"),
1144 microsec_clock::universal_time());
1145 srandom(duration.length().total_seconds()
1146 + duration.length().fractional_seconds());
1147 }
1148 // If user interrupts the program we will exit gracefully.
1149 signal(SIGINT, TestControl::handleInterrupt);
1150}
1151
1152void
1153TestControl::runWrapped(bool do_stop /*= false */) const {
1154 if (!options_.getWrapped().empty()) {
1155 pid_t pid = 0;
1156 signal(SIGCHLD, handleChild);
1157 pid = fork();
1158 if (pid < 0) {
1159 isc_throw(Unexpected, "unable to fork");
1160 } else if (pid == 0) {
1161 execlp(options_.getWrapped().c_str(), do_stop ? "stop" : "start", (void*)0);
1162 }
1163 }
1164}
1165
1166void
1168 if (options_.testDiags('T')) {
1169 if (template_packets_v4_.find(pkt->getType()) == template_packets_v4_.end()) {
1170 template_packets_v4_[pkt->getType()] = pkt;
1171 }
1172 }
1173}
1174
1175void
1177 if (options_.testDiags('T')) {
1178 if (template_packets_v6_.find(pkt->getType()) == template_packets_v6_.end()) {
1179 template_packets_v6_[pkt->getType()] = pkt;
1180 }
1181 }
1182}
1183
1184void
1185TestControl::sendDiscover4(const bool preload /*= false*/) {
1186 // Generate the MAC address to be passed in the packet.
1187 uint8_t randomized = 0;
1188 std::vector<uint8_t> mac_address = generateMacAddress(randomized);
1189 // Generate transaction id to be set for the new exchange.
1190 const uint32_t transid = generateTransid();
1191 Pkt4Ptr pkt4(new Pkt4(DHCPDISCOVER, transid));
1192 if (!pkt4) {
1193 isc_throw(Unexpected, "failed to create DISCOVER packet");
1194 }
1195
1196 // Delete the default Message Type option set by Pkt4
1197 pkt4->delOption(DHO_DHCP_MESSAGE_TYPE);
1198
1199 // Set options: DHCP_MESSAGE_TYPE and DHCP_PARAMETER_REQUEST_LIST
1200 OptionBuffer buf_msg_type;
1201 buf_msg_type.push_back(DHCPDISCOVER);
1203 buf_msg_type));
1204 pkt4->addOption(Option::factory(Option::V4,
1206
1207
1208 // Set client's and server's ports as well as server's address,
1209 // and local (relay) address.
1210 setDefaults4(pkt4);
1211
1212 // Set hardware address
1213 pkt4->setHWAddr(HTYPE_ETHER, mac_address.size(), mac_address);
1214
1215 // Set client identifier
1216 pkt4->addOption(generateClientId(pkt4->getHWAddr()));
1217
1218 // Check if we need to simulate HA failures by pretending no responses were received.
1219 // The DHCP protocol signals that by increasing secs field (seconds since the configuration attempt started).
1221 stats_mgr_.getTestPeriod().length().total_seconds() >= options_.getWaitForElapsedTime() &&
1222 stats_mgr_.getTestPeriod().length().total_seconds() < options_.getWaitForElapsedTime() +
1224
1225 // Keep increasing elapsed time. The value should start increasing steadily.
1226 uint32_t val = stats_mgr_.getTestPeriod().length().total_seconds() - options_.getWaitForElapsedTime() + 1;
1227 if (val > 65535) {
1228 val = 65535;
1229 }
1230 pkt4->setSecs(static_cast<uint16_t>(val));
1231 }
1232
1233 // Add any extra options that user may have specified.
1234 addExtraOpts(pkt4);
1235
1236 pkt4->pack();
1237 socket_.send(pkt4);
1238 if (!preload) {
1240 }
1241
1242 saveFirstPacket(pkt4);
1243}
1244
1245void
1246TestControl::sendDiscover4(const std::vector<uint8_t>& template_buf,
1247 const bool preload /* = false */) {
1248 // Get the first argument if multiple the same arguments specified
1249 // in the command line. First one refers to DISCOVER packets.
1250 const uint8_t arg_idx = 0;
1251 // Generate the MAC address to be passed in the packet.
1252 uint8_t randomized = 0;
1253 std::vector<uint8_t> mac_address = generateMacAddress(randomized);
1254 // Generate transaction id to be set for the new exchange.
1255 const uint32_t transid = generateTransid();
1256 // Get transaction id offset.
1257 size_t transid_offset = getTransactionIdOffset(arg_idx);
1258 // Get randomization offset.
1259 // We need to go back by HW_ETHER_LEN (MAC address length)
1260 // because this offset points to last octet of MAC address.
1261 size_t rand_offset = getRandomOffset(arg_idx) - HW_ETHER_LEN + 1;
1262 // Create temporary buffer with template contents. We will
1263 // modify this temporary buffer but we don't want to modify
1264 // the original template.
1265 std::vector<uint8_t> in_buf(template_buf.begin(),
1266 template_buf.end());
1267 // Check if we are not going out of bounds.
1268 if (rand_offset + HW_ETHER_LEN > in_buf.size()) {
1269 isc_throw(OutOfRange, "randomization offset is out of bounds");
1270 }
1271 PerfPkt4Ptr pkt4(new PerfPkt4(&in_buf[0], in_buf.size(),
1272 transid_offset,
1273 transid));
1274
1275 // Replace MAC address in the template with actual MAC address.
1276 pkt4->writeAt(rand_offset, mac_address.begin(), mac_address.end());
1277 // Create a packet from the temporary buffer.
1278 setDefaults4(boost::static_pointer_cast<Pkt4>(pkt4));
1279 // Pack the input packet buffer to output buffer so as it can
1280 // be sent to server.
1281 pkt4->rawPack();
1282 socket_.send(boost::static_pointer_cast<Pkt4>(pkt4));
1283 if (!preload) {
1284 // Update packet stats.
1286 boost::static_pointer_cast<Pkt4>(pkt4));
1287 }
1288 saveFirstPacket(pkt4);
1289}
1290
1291bool
1292TestControl::sendMessageFromAck(const uint16_t msg_type) {
1293 // We only permit Request or Release messages to be sent using this
1294 // function.
1295 if (msg_type != DHCPREQUEST && msg_type != DHCPRELEASE) {
1297 "invalid message type "
1298 << msg_type
1299 << " to be sent, expected DHCPREQUEST or DHCPRELEASE");
1300 }
1301
1302 // Get one of the recorded DHCPACK messages.
1303 Pkt4Ptr ack = ack_storage_.getRandom();
1304 if (!ack) {
1305 return (false);
1306 }
1307
1308 // Create message of the specified type.
1309 Pkt4Ptr msg = createMessageFromAck(msg_type, ack);
1310 setDefaults4(msg);
1311
1312 // Override relay address
1313 msg->setGiaddr(ack->getGiaddr());
1314
1315 // Add any extra options that user may have specified.
1316 addExtraOpts(msg);
1317
1318 // Pack it.
1319 msg->pack();
1320
1321 // And send it.
1322 socket_.send(msg);
1326 msg);
1327 return (true);
1328}
1329
1330
1331bool
1332TestControl::sendMessageFromReply(const uint16_t msg_type) {
1333 // We only permit Release or Renew messages to be sent using this function.
1334 if (msg_type != DHCPV6_RENEW && msg_type != DHCPV6_RELEASE) {
1335 isc_throw(isc::BadValue, "invalid message type " << msg_type
1336 << " to be sent, expected DHCPV6_RENEW or DHCPV6_RELEASE");
1337 }
1338
1339 // Get one of the recorded DHCPV6_OFFER messages.
1340 Pkt6Ptr reply = reply_storage_.getRandom();
1341 if (!reply) {
1342 return (false);
1343 }
1344 // Prepare the message of the specified type.
1345 Pkt6Ptr msg = createMessageFromReply(msg_type, reply);
1346 setDefaults6(msg);
1347
1348 // Add any extra options that user may have specified.
1349 addExtraOpts(msg);
1350
1351 // Pack it.
1352 msg->pack();
1353
1354 // And send it.
1355 socket_.send(msg);
1358 : ExchangeType::RL), msg);
1359 return (true);
1360}
1361
1362void
1364 const dhcp::Pkt4Ptr& offer_pkt4) {
1365 // Use the same transaction id as the one used in the discovery packet.
1366 const uint32_t transid = discover_pkt4->getTransid();
1367 Pkt4Ptr pkt4(new Pkt4(DHCPREQUEST, transid));
1368
1369 // Use first flags indicates that we want to use the server
1370 // id captured in first packet.
1371 if (options_.isUseFirst() &&
1372 (first_packet_serverid_.size() > 0)) {
1375 } else {
1376 OptionPtr opt_serverid =
1377 offer_pkt4->getOption(DHO_DHCP_SERVER_IDENTIFIER);
1378 if (!opt_serverid) {
1379 isc_throw(BadValue, "there is no SERVER_IDENTIFIER option "
1380 << "in OFFER message");
1381 }
1383 first_packet_serverid_ = opt_serverid->getData();
1384 }
1385 pkt4->addOption(opt_serverid);
1386 }
1387
1389 asiolink::IOAddress yiaddr = offer_pkt4->getYiaddr();
1390 if (!yiaddr.isV4()) {
1391 isc_throw(BadValue, "the YIADDR returned in OFFER packet is not "
1392 " IPv4 address");
1393 }
1394 OptionPtr opt_requested_address =
1396 OptionBuffer()));
1397 opt_requested_address->setUint32(yiaddr.toUint32());
1398 pkt4->addOption(opt_requested_address);
1399 OptionPtr opt_parameter_list =
1401 pkt4->addOption(opt_parameter_list);
1402 // Set client's and server's ports as well as server's address
1403 setDefaults4(pkt4);
1404 // Override relay address
1405 pkt4->setGiaddr(offer_pkt4->getGiaddr());
1406 // Add any extra options that user may have specified.
1407 addExtraOpts(pkt4);
1408
1409 // Set hardware address
1410 pkt4->setHWAddr(offer_pkt4->getHWAddr());
1411 // Set client id.
1412 pkt4->addOption(generateClientId(pkt4->getHWAddr()));
1413 // Set elapsed time.
1414 uint32_t elapsed_time = getElapsedTime<Pkt4Ptr>(discover_pkt4, offer_pkt4);
1415 pkt4->setSecs(static_cast<uint16_t>(elapsed_time / 1000));
1416 // Prepare on wire data to send.
1417 pkt4->pack();
1418 socket_.send(pkt4);
1420 saveFirstPacket(pkt4);
1421}
1422
1423void
1424TestControl::sendRequest4(const std::vector<uint8_t>& template_buf,
1425 const dhcp::Pkt4Ptr& discover_pkt4,
1426 const dhcp::Pkt4Ptr& offer_pkt4) {
1427 // Get the second argument if multiple the same arguments specified
1428 // in the command line. Second one refers to REQUEST packets.
1429 const uint8_t arg_idx = 1;
1430 // Use the same transaction id as the one used in the discovery packet.
1431 const uint32_t transid = discover_pkt4->getTransid();
1432 // Get transaction id offset.
1433 size_t transid_offset = getTransactionIdOffset(arg_idx);
1434 // Get the offset of MAC's last octet.
1435 // We need to go back by HW_ETHER_LEN (MAC address length)
1436 // because this offset points to last octet of MAC address.
1437 size_t rand_offset = getRandomOffset(arg_idx) - HW_ETHER_LEN + 1;
1438 // Create temporary buffer from the template.
1439 std::vector<uint8_t> in_buf(template_buf.begin(),
1440 template_buf.end());
1441 // Check if given randomization offset is not out of bounds.
1442 if (rand_offset + HW_ETHER_LEN > in_buf.size()) {
1443 isc_throw(OutOfRange, "randomization offset is out of bounds");
1444 }
1445
1446 // Create packet from the temporary buffer.
1447 PerfPkt4Ptr pkt4(new PerfPkt4(&in_buf[0], in_buf.size(),
1448 transid_offset,
1449 transid));
1450
1451 // Set hardware address from OFFER packet received.
1452 HWAddrPtr hwaddr = offer_pkt4->getHWAddr();
1453 std::vector<uint8_t> mac_address(HW_ETHER_LEN, 0);
1454 uint8_t hw_len = hwaddr->hwaddr_.size();
1455 if (hw_len != 0) {
1456 memcpy(&mac_address[0], &hwaddr->hwaddr_[0],
1457 hw_len > HW_ETHER_LEN ? HW_ETHER_LEN : hw_len);
1458 }
1459 pkt4->writeAt(rand_offset, mac_address.begin(), mac_address.end());
1460
1461 // Set elapsed time.
1462 size_t elp_offset = getElapsedTimeOffset();
1463 uint32_t elapsed_time = getElapsedTime<Pkt4Ptr>(discover_pkt4, offer_pkt4);
1464 pkt4->writeValueAt<uint16_t>(elp_offset,
1465 static_cast<uint16_t>(elapsed_time / 1000));
1466
1467 // Get the actual server id offset.
1468 size_t sid_offset = getServerIdOffset();
1469 // Use first flags indicates that we want to use the server
1470 // id captured in first packet.
1471 if (options_.isUseFirst() &&
1472 (first_packet_serverid_.size() > 0)) {
1473 boost::shared_ptr<LocalizedOption>
1474 opt_serverid(new LocalizedOption(Option::V4,
1477 sid_offset));
1478 pkt4->addOption(opt_serverid);
1479 } else {
1480 // Copy the contents of server identifier received in
1481 // OFFER packet to put this into REQUEST.
1482 OptionPtr opt_serverid_offer =
1483 offer_pkt4->getOption(DHO_DHCP_SERVER_IDENTIFIER);
1484 if (!opt_serverid_offer) {
1485 isc_throw(BadValue, "there is no SERVER_IDENTIFIER option "
1486 << "in OFFER message");
1487 }
1488 boost::shared_ptr<LocalizedOption>
1489 opt_serverid(new LocalizedOption(Option::V4,
1491 opt_serverid_offer->getData(),
1492 sid_offset));
1493 pkt4->addOption(opt_serverid);
1495 first_packet_serverid_ = opt_serverid_offer->getData();
1496 }
1497 }
1498
1500 asiolink::IOAddress yiaddr = offer_pkt4->getYiaddr();
1501 if (!yiaddr.isV4()) {
1502 isc_throw(BadValue, "the YIADDR returned in OFFER packet is not "
1503 " IPv4 address");
1504 }
1505
1506 // Get the actual offset of requested ip.
1507 size_t rip_offset = getRequestedIpOffset();
1508 // Place requested IP option at specified position (rip_offset).
1509 boost::shared_ptr<LocalizedOption>
1510 opt_requested_ip(new LocalizedOption(Option::V4,
1512 OptionBuffer(),
1513 rip_offset));
1514 // The IOAddress is convertible to uint32_t and returns exactly what we need.
1515 opt_requested_ip->setUint32(yiaddr.toUint32());
1516 pkt4->addOption(opt_requested_ip);
1517
1518 setDefaults4(boost::static_pointer_cast<Pkt4>(pkt4));
1519
1520 // Add any extra options that user may have specified.
1521 addExtraOpts(pkt4);
1522
1523 // Prepare on-wire data.
1524 pkt4->rawPack();
1525 socket_.send(boost::static_pointer_cast<Pkt4>(pkt4));
1526 // Update packet stats.
1528 boost::static_pointer_cast<Pkt4>(pkt4));
1529 saveFirstPacket(pkt4);
1530}
1531
1532void
1533TestControl::sendRequest6(const Pkt6Ptr& advertise_pkt6) {
1534 const uint32_t transid = generateTransid();
1535 Pkt6Ptr pkt6(new Pkt6(DHCPV6_REQUEST, transid));
1536 // Set elapsed time.
1537 OptionPtr opt_elapsed_time =
1539 pkt6->addOption(opt_elapsed_time);
1540 // Set client id.
1541 OptionPtr opt_clientid = advertise_pkt6->getOption(D6O_CLIENTID);
1542 if (!opt_clientid) {
1543 isc_throw(Unexpected, "client id not found in received packet");
1544 }
1545 pkt6->addOption(opt_clientid);
1546
1547 // Use first flags indicates that we want to use the server
1548 // id captured in first packet.
1549 if (options_.isUseFirst() &&
1550 (first_packet_serverid_.size() > 0)) {
1551 pkt6->addOption(Option::factory(Option::V6, D6O_SERVERID,
1553 } else {
1554 OptionPtr opt_serverid = advertise_pkt6->getOption(D6O_SERVERID);
1555 if (!opt_serverid) {
1556 isc_throw(Unexpected, "server id not found in received packet");
1557 }
1559 first_packet_serverid_ = opt_serverid->getData();
1560 }
1561 pkt6->addOption(opt_serverid);
1562 }
1563
1564 // Copy IA_NA or IA_PD option from the Advertise message to the Request
1565 // message being sent to the server. This will throw exception if the
1566 // option to be copied is not found. Note that this function will copy
1567 // one of IA_NA or IA_PD options, depending on the lease-type value
1568 // specified in the command line.
1569 copyIaOptions(advertise_pkt6, pkt6);
1570
1571 // Set default packet data.
1572 setDefaults6(pkt6);
1573
1574 // Add any extra options that user may have specified.
1575 addExtraOpts(pkt6);
1576
1577 // Prepare on-wire data.
1578 pkt6->pack();
1579 socket_.send(pkt6);
1581 saveFirstPacket(pkt6);
1582}
1583
1584void
1585TestControl::sendRequest6(const std::vector<uint8_t>& template_buf,
1586 const Pkt6Ptr& advertise_pkt6) {
1587 // Get the second argument if multiple the same arguments specified
1588 // in the command line. Second one refers to REQUEST packets.
1589 const uint8_t arg_idx = 1;
1590 // Generate transaction id.
1591 const uint32_t transid = generateTransid();
1592 // Get transaction id offset.
1593 size_t transid_offset = getTransactionIdOffset(arg_idx);
1594 PerfPkt6Ptr pkt6(new PerfPkt6(&template_buf[0], template_buf.size(),
1595 transid_offset, transid));
1596 // Set elapsed time.
1597 size_t elp_offset = getElapsedTimeOffset();
1598 boost::shared_ptr<LocalizedOption>
1599 opt_elapsed_time(new LocalizedOption(Option::V6, D6O_ELAPSED_TIME,
1600 OptionBuffer(), elp_offset));
1601 pkt6->addOption(opt_elapsed_time);
1602
1603 // Get the actual server id offset.
1604 size_t sid_offset = getServerIdOffset();
1605 // Use first flags indicates that we want to use the server
1606 // id captured in first packet.
1607 if (options_.isUseFirst() &&
1608 (first_packet_serverid_.size() > 0)) {
1609 boost::shared_ptr<LocalizedOption>
1610 opt_serverid(new LocalizedOption(Option::V6,
1613 sid_offset));
1614 pkt6->addOption(opt_serverid);
1615
1616 } else {
1617 // Copy the contents of server identifier received in
1618 // ADVERTISE packet to put this into REQUEST.
1619 OptionPtr opt_serverid_advertise =
1620 advertise_pkt6->getOption(D6O_SERVERID);
1621 if (!opt_serverid_advertise) {
1622 isc_throw(BadValue, "there is no SERVERID option "
1623 << "in ADVERTISE message");
1624 }
1625 boost::shared_ptr<LocalizedOption>
1626 opt_serverid(new LocalizedOption(Option::V6,
1628 opt_serverid_advertise->getData(),
1629 sid_offset));
1630 pkt6->addOption(opt_serverid);
1632 first_packet_serverid_ = opt_serverid_advertise->getData();
1633 }
1634 }
1635 // Set IA_NA
1636 OptionPtr opt_ia_na_advertise = advertise_pkt6->getOption(D6O_IA_NA);
1637 if (!opt_ia_na_advertise) {
1638 isc_throw(Unexpected, "DHCPv6 IA_NA option not found in received "
1639 "packet");
1640 }
1641 size_t addr_offset = getRequestedIpOffset();
1642 boost::shared_ptr<LocalizedOption>
1643 opt_ia_na(new LocalizedOption(Option::V6, D6O_IA_NA, opt_ia_na_advertise->getData(), addr_offset));
1644 if (!opt_ia_na->valid()) {
1645 isc_throw(BadValue, "Option IA_NA in advertise packet is invalid");
1646 }
1647 pkt6->addOption(opt_ia_na);
1648 // Set server id.
1649 OptionPtr opt_serverid_advertise = advertise_pkt6->getOption(D6O_SERVERID);
1650 if (!opt_serverid_advertise) {
1651 isc_throw(Unexpected, "DHCPV6 SERVERID option not found in received "
1652 "packet");
1653 }
1654 size_t srvid_offset = getServerIdOffset();
1655 boost::shared_ptr<LocalizedOption>
1656 opt_serverid(new LocalizedOption(Option::V6, D6O_SERVERID,
1657 opt_serverid_advertise->getData(),
1658 srvid_offset));
1659 pkt6->addOption(opt_serverid);
1660 // Get randomization offset.
1661 size_t rand_offset = getRandomOffset(arg_idx);
1662 OptionPtr opt_clientid_advertise = advertise_pkt6->getOption(D6O_CLIENTID);
1663 if (!opt_clientid_advertise) {
1664 isc_throw(Unexpected, "DHCPV6 CLIENTID option not found in received packet");
1665 }
1666 rand_offset -= (opt_clientid_advertise->len() - 1);
1667 // Set client id.
1668 boost::shared_ptr<LocalizedOption>
1669 opt_clientid(new LocalizedOption(Option::V6, D6O_CLIENTID,
1670 opt_clientid_advertise->getData(),
1671 rand_offset));
1672 pkt6->addOption(opt_clientid);
1673 // Set default packet data.
1674 setDefaults6(pkt6);
1675
1676 // Add any extra options that user may have specified.
1677 addExtraOpts(pkt6);
1678
1679 // Prepare on wire data.
1680 pkt6->rawPack();
1681 // Send packet.
1682 socket_.send(pkt6);
1683 // Update packet stats.
1685
1686 // When 'T' diagnostics flag is specified it means that user requested
1687 // printing packet contents. It will be just one (first) packet which
1688 // contents will be printed. Here we check if this packet has been already
1689 // collected. If it hasn't we save this packet so as we can print its
1690 // contents when test is finished.
1691 if (options_.testDiags('T') &&
1694 }
1695}
1696
1697void
1698TestControl::sendSolicit6(const bool preload /*= false*/) {
1699 // Generate DUID to be passed to the packet
1700 uint8_t randomized = 0;
1701 std::vector<uint8_t> duid = generateDuid(randomized);
1702 // Generate transaction id to be set for the new exchange.
1703 const uint32_t transid = generateTransid();
1704 Pkt6Ptr pkt6(new Pkt6(DHCPV6_SOLICIT, transid));
1705 if (!pkt6) {
1706 isc_throw(Unexpected, "failed to create SOLICIT packet");
1707 }
1708
1709 // Check if we need to simulate HA failures by pretending no responses were received.
1710 // The DHCPv6 protocol signals that by increasing the elapsed option field. Note it is in 1/100 of a second.
1712 stats_mgr_.getTestPeriod().length().total_seconds() >= options_.getWaitForElapsedTime() &&
1713 stats_mgr_.getTestPeriod().length().total_seconds() < options_.getWaitForElapsedTime() +
1715
1716
1717 // Keep increasing elapsed time. The value should start increasing steadily.
1718 uint32_t val = (stats_mgr_.getTestPeriod().length().total_seconds() - options_.getWaitForElapsedTime() + 1)*100;
1719 if (val > 65535) {
1720 val = 65535;
1721 }
1723 pkt6->addOption(elapsed);
1724 } else {
1725 pkt6->addOption(Option::factory(Option::V6, D6O_ELAPSED_TIME));
1726 }
1727
1728 if (options_.isRapidCommit()) {
1729 pkt6->addOption(Option::factory(Option::V6, D6O_RAPID_COMMIT));
1730 }
1731 pkt6->addOption(Option::factory(Option::V6, D6O_CLIENTID, duid));
1732 pkt6->addOption(Option::factory(Option::V6, D6O_ORO));
1733
1734
1735 // Depending on the lease-type option specified, we should request
1736 // IPv6 address (with IA_NA) or IPv6 prefix (IA_PD) or both.
1737
1738 // IA_NA
1740 pkt6->addOption(Option::factory(Option::V6, D6O_IA_NA));
1741 }
1742 // IA_PD
1744 pkt6->addOption(Option::factory(Option::V6, D6O_IA_PD));
1745 }
1746
1747 setDefaults6(pkt6);
1748
1749 // Add any extra options that user may have specified.
1750 addExtraOpts(pkt6);
1751
1752 pkt6->pack();
1753 socket_.send(pkt6);
1754 if (!preload) {
1756 }
1757
1758 saveFirstPacket(pkt6);
1759}
1760
1761void
1762TestControl::sendSolicit6(const std::vector<uint8_t>& template_buf,
1763 const bool preload /*= false*/) {
1764 const int arg_idx = 0;
1765 // Get transaction id offset.
1766 size_t transid_offset = getTransactionIdOffset(arg_idx);
1767 // Generate transaction id to be set for the new exchange.
1768 const uint32_t transid = generateTransid();
1769 // Create packet.
1770 PerfPkt6Ptr pkt6(new PerfPkt6(&template_buf[0], template_buf.size(),
1771 transid_offset, transid));
1772 if (!pkt6) {
1773 isc_throw(Unexpected, "failed to create SOLICIT packet");
1774 }
1775 size_t rand_offset = getRandomOffset(arg_idx);
1776 // randomized will pick number of bytes randomized so we can
1777 // just use part of the generated duid and substitute a few bytes
1779 uint8_t randomized = 0;
1780 std::vector<uint8_t> duid = generateDuid(randomized);
1781 if (rand_offset > template_buf.size()) {
1782 isc_throw(OutOfRange, "randomization offset is out of bounds");
1783 }
1784 // Store random part of the DUID into the packet.
1785 pkt6->writeAt(rand_offset - randomized + 1,
1786 duid.end() - randomized, duid.end());
1787
1788 // Prepare on-wire data.
1789 pkt6->rawPack();
1790 setDefaults6(pkt6);
1791
1792 // Add any extra options that user may have specified.
1793 addExtraOpts(pkt6);
1794
1795 // Send solicit packet.
1796 socket_.send(pkt6);
1797 if (!preload) {
1798 // Update packet stats.
1800 }
1801 saveFirstPacket(pkt6);
1802}
1803
1804
1805void
1807 // Interface name.
1808 IfacePtr iface = socket_.getIface();
1809 if (iface == NULL) {
1810 isc_throw(BadValue, "unable to find interface with given index");
1811 }
1812 pkt->setIface(iface->getName());
1813 // Interface index.
1814 pkt->setIndex(socket_.ifindex_);
1815 // Local client's port (68)
1816 pkt->setLocalPort(DHCP4_CLIENT_PORT);
1817 // Server's port (67)
1818 if (options_.getRemotePort()) {
1819 pkt->setRemotePort(options_.getRemotePort());
1820 } else {
1821 pkt->setRemotePort(DHCP4_SERVER_PORT);
1822 }
1823 // The remote server's name or IP.
1824 pkt->setRemoteAddr(IOAddress(options_.getServerName()));
1825 // Set local address.
1826 pkt->setLocalAddr(socket_.addr_);
1827 // Set relay (GIADDR) address to local address if multiple
1828 // subnet mode is not enabled
1829 if (!options_.checkMultiSubnet()) {
1830 pkt->setGiaddr(socket_.addr_);
1831 } else {
1832 pkt->setGiaddr(IOAddress(options_.getRandRelayAddr()));
1833 }
1834 // Pretend that we have one relay (which is us).
1835 pkt->setHops(1);
1836}
1837
1838void
1840 // Interface name.
1841 IfacePtr iface = socket_.getIface();
1842 if (iface == NULL) {
1843 isc_throw(BadValue, "unable to find interface with given index");
1844 }
1845 pkt->setIface(iface->getName());
1846 // Interface index.
1847 pkt->setIndex(socket_.ifindex_);
1848 // Local client's port (547)
1849 pkt->setLocalPort(DHCP6_CLIENT_PORT);
1850 // Server's port (548)
1851 if (options_.getRemotePort()) {
1852 pkt->setRemotePort(options_.getRemotePort());
1853 } else {
1854 pkt->setRemotePort(DHCP6_SERVER_PORT);
1855 }
1856 // Set local address.
1857 pkt->setLocalAddr(socket_.addr_);
1858 // The remote server's name or IP.
1859 pkt->setRemoteAddr(IOAddress(options_.getServerName()));
1860
1861 // only act as a relay agent when told so.
1864 if (options_.isUseRelayedV6()) {
1865 Pkt6::RelayInfo relay_info;
1866 relay_info.msg_type_ = DHCPV6_RELAY_FORW;
1867 relay_info.hop_count_ = 0;
1868 if (options_.checkMultiSubnet()) {
1870 } else {
1871 relay_info.linkaddr_ = socket_.addr_;
1872 }
1873 relay_info.peeraddr_ = socket_.addr_;
1874 relay_info.options_.insert(options_.getRelayOpts().begin(), options_.getRelayOpts().end());
1875 pkt->addRelayInfo(relay_info);
1876 }
1877}
1878
1879namespace {
1880
1881static OptionBuffer const concatenateBuffers(OptionBuffer const& a,
1882 OptionBuffer const& b) {
1883 OptionBuffer result;
1884 result.insert(result.end(), a.begin(), a.end());
1885 result.insert(result.end(), b.begin(), b.end());
1886 return (result);
1887}
1888
1889static void mergeOptionIntoPacket(Pkt4Ptr const& packet,
1890 OptionPtr const& extra_option) {
1891 uint16_t const code(extra_option->getType());
1892 // If option already exists...
1893 OptionPtr const& option(packet->getOption(code));
1894 if (option) {
1895 switch (code) {
1896 // List here all the options for which we want to concatenate buffers.
1898 packet->delOption(code);
1899 packet->addOption(boost::make_shared<Option>(
1900 Option::V4, code,
1901 concatenateBuffers(option->getData(),
1902 extra_option->getData())));
1903 return;
1904 default:
1905 // For all others, add option as usual, it will result in "Option
1906 // already present in this message" error.
1907 break;
1908 }
1909 }
1910 packet->addOption(extra_option);
1911}
1912
1913} // namespace
1914
1915void
1917 // Add all extra options that the user may have specified.
1918 const dhcp::OptionCollection& extra_opts = options_.getExtraOpts();
1919 for (auto const& entry : extra_opts) {
1920 mergeOptionIntoPacket(pkt, entry.second);
1921 }
1922}
1923
1924void
1926 // Add all extra options that the user may have specified.
1927 const dhcp::OptionCollection& extra_opts = options_.getExtraOpts();
1928 for (auto const& entry : extra_opts) {
1929 pkt->addOption(entry.second);
1930 }
1931}
1932
1933} // namespace perfdhcp
1934} // 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.
Definition: iface_mgr.cc:1943
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:1245
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:83
Represents DHCPv4 packet.
Definition: pkt4.h:37
Represents a DHCPv6 packet.
Definition: pkt6.h:44
Socket wrapper structure.
Definition: perf_socket.h:26
virtual dhcp::IfacePtr getIface()=0
See description of this method in PerfSocket class below.
unsigned int ifindex_
Interface index.
Definition: perf_socket.h:29
virtual bool send(const dhcp::Pkt4Ptr &pkt)=0
See description of this method in PerfSocket class below.
bool includes(const Type lease_type) const
Checks if lease type implies request for the address, prefix (or both) as specified by the function a...
bool testDiags(const char diag)
Find if diagnostic flag has been set.
std::string getRandRelayAddr()
Returns random relay address.
int getIncreaseElapsedTime() const
Returns increased elapsed time.
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.
bool getAddrUnique() const
Returns address uniqueness value.
int getRate() const
Returns exchange rate.
std::string getCleanReportSeparator() const
Returns clean report separator.
bool isRapidCommit() const
Check if rapid commit option used.
bool checkMultiSubnet()
Check if multi subnet mode is enabled.
const isc::dhcp::OptionCollection & getExtraOpts() const
Returns extra options to be inserted.
bool isUseFirst() const
Check if server-ID to be taken from first package.
int getReportDelay() const
Returns delay between two performance reports.
std::vector< int > getRandomOffset() const
Returns template offsets for rnd.
int getRemotePort() const
Returns remote port number.
int getWaitForElapsedTime() const
Returns time to wait for elapsed time increase.
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.
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.
const isc::dhcp::OptionCollection & getRelayOpts(uint8_t encapsulation_level=1) const
Returns relay options to be inserted at given level of encapsulation.
std::vector< int > getNumRequests() const
Returns maximum number of exchanges.
LeaseType getLeaseType() const
\ brief Returns the type of lease being requested.
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.
bool isSeeded() const
Checks if seed provided.
uint32_t getSeed() const
Returns random seed.
ExchangeMode getExchangeMode() const
Returns packet exchange mode.
void printCommandLine() const
Print command line arguments.
int getCleanReport() const
Returns clean report mode.
std::vector< uint8_t > getMacTemplate() const
Returns MAC address template.
int getRequestedIpOffset() const
Returns template offset for requested IP.
DHCP option at specific offset.
PerfPkt4 (DHCPv4 packet)
Definition: perf_pkt4.h:42
PerfPkt6 (DHCPv6 packet)
Definition: perf_pkt6.h:41
dhcp::PktPtr getPkt()
Get DHCP packet.
Definition: receiver.cc:58
void updateRejLeases(const ExchangeType xchg_type)
Increase total number of rejected leases.
void printStats() const
Print statistics counters for all exchange types.
void printCustomCounters() const
Print names and values of custom counters.
void updateNonUniqueAddrNum(const ExchangeType xchg_type)
Increase total number of non unique addresses.
uint64_t getRcvdPacketsNum(const ExchangeType xchg_type) const
Return total number of received packets.
bool hasExchangeStats(const ExchangeType xchg_type) const
Check if the exchange type has been specified.
void printIntermediateStats(bool clean_report, std::string clean_sep) const
Print intermediate statistics.
boost::posix_time::time_period getTestPeriod() const
Get time period since the start of test.
void passSentPacket(const ExchangeType xchg_type, const dhcp::PktPtr &packet)
Adds new packet to the sent packets list.
dhcp::PktPtr passRcvdPacket(const ExchangeType xchg_type, const dhcp::PktPtr &packet)
Add new received packet and match with sent packet.
Random numbers generator class.
Definition: test_control.h:187
Sequential numbers generator class.
Definition: test_control.h:157
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.
Definition: test_control.h:222
bool waitToExit()
Delay the exit by a fixed given time to catch up to all exchanges that were already started.
Definition: test_control.cc:48
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.
Definition: test_control.h:154
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.
Definition: test_control.h:375
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.
Definition: test_control.h:238
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.
Definition: test_control.cc:69
void cleanCachedPackets()
Removes cached DHCPv6 Reply packets every second.
Definition: test_control.cc:96
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.
Definition: test_control.h:123
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.
Definition: test_control.h:644
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.
Definition: test_control.h:227
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.
Definition: test_control.h:567
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:199
@ DHCPV6_REQUEST
Definition: dhcp6.h:200
@ DHCPV6_RENEW
Definition: dhcp6.h:202
@ DHCPV6_REPLY
Definition: dhcp6.h:204
@ DHCPV6_SOLICIT
Definition: dhcp6.h:198
@ DHCPV6_RELEASE
Definition: dhcp6.h:205
@ DHCPV6_RELAY_FORW
Definition: dhcp6.h:209
#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:982
@ 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:555
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.
@ RA
DHCPv4 REQUEST-ACK.
@ SA
DHCPv6 SOLICIT-ADVERTISE.
@ RNA
DHCPv4 REQUEST-ACK (renewal)
@ RL
DHCPv6 RELEASE-REPLY.
@ RN
DHCPv6 RENEW-REPLY.
@ DO
DHCPv4 DISCOVER-OFFER.
@ RR
DHCPv6 REQUEST-REPLY.
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
isc::asiolink::IOAddress addr_
Definition: socket_info.h:21