Kea 2.5.5
test_control.cc
Go to the documentation of this file.
1// Copyright (C) 2012-2023 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 * 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) {
274 return (OptionPtr(new Option(Option::V6, D6O_ELAPSED_TIME,
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&) {
323 return (OptionPtr(new Option(Option::V6, D6O_RAPID_COMMIT, 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 = number_generator_();
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 std::vector<uint8_t> client_id(1, static_cast<uint8_t>(hwaddr->htype_));
408 client_id.insert(client_id.end(), hwaddr->hwaddr_.begin(),
409 hwaddr->hwaddr_.end());
410 return (OptionPtr(new Option(Option::V4, DHO_DHCP_CLIENT_IDENTIFIER,
411 client_id)));
412}
413
414std::vector<uint8_t>
415TestControl::generateDuid(uint8_t& randomized) {
416 std::vector<uint8_t> mac_addr(generateMacAddress(randomized));
418 // pick a random mac address if we are using option -M..
419 if (macs.size() > 0) {
420 uint16_t r = number_generator_();
421 if (r >= macs.size()) {
422 r = 0;
423 }
424 std::vector<uint8_t> mac = macs[r];
425 // DUID_LL is in this format
426 // 0 1 2 3
427 // 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
428 // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
429 // | 3 | hardware type (16 bits) |
430 // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
431 // . .
432 // . link-layer address (variable length) .
433 // . .
434 // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
435
436 // No C++11 so initializer list support, building a vector<uint8_t> is a
437 // pain...
438 uint8_t duid_ll[] = {0, 3, 0, 1, 0, 0, 0, 0, 0, 0};
439 // copy duid_ll array into the vector
440 std::vector<uint8_t> duid(duid_ll,
441 duid_ll + sizeof(duid_ll) / sizeof(duid_ll[0]));
442 // put the mac address bytes at the end
443 std::copy(mac.begin(), mac.end(), duid.begin() + 4);
444 return (duid);
445 } else {
446 uint32_t clients_num = options_.getClientsNum();
447 if ((clients_num == 0) || (clients_num == 1)) {
448 return (options_.getDuidTemplate());
449 }
450 // Get the base DUID. We are going to randomize part of it.
451 std::vector<uint8_t> duid(options_.getDuidTemplate());
453 duid.resize(duid.size());
454 std::copy(mac_addr.begin(), mac_addr.end(),
455 duid.begin() + duid.size() - mac_addr.size());
456 return (duid);
457 }
458}
459
460int
462 int elp_offset = options_.getIpVersion() == 4 ?
463 DHCPV4_ELAPSED_TIME_OFFSET : DHCPV6_ELAPSED_TIME_OFFSET;
464 if (options_.getElapsedTimeOffset() > 0) {
465 elp_offset = options_.getElapsedTimeOffset();
466 }
467 return (elp_offset);
468}
469
470template<class T>
471uint32_t
472TestControl::getElapsedTime(const T& pkt1, const T& pkt2) {
473 using namespace boost::posix_time;
474 ptime pkt1_time = pkt1->getTimestamp();
475 ptime pkt2_time = pkt2->getTimestamp();
476 if (pkt1_time.is_not_a_date_time() ||
477 pkt2_time.is_not_a_date_time()) {
478 isc_throw(InvalidOperation, "packet timestamp not set");;
479 }
480 time_period elapsed_period(pkt1_time, pkt2_time);
481 return (elapsed_period.is_null() ? 0 :
482 elapsed_period.length().total_milliseconds());
483}
484
485int
486TestControl::getRandomOffset(const int arg_idx) const {
487 int rand_offset = options_.getIpVersion() == 4 ?
488 DHCPV4_RANDOMIZATION_OFFSET : DHCPV6_RANDOMIZATION_OFFSET;
489 if (options_.getRandomOffset().size() > arg_idx) {
490 rand_offset = options_.getRandomOffset()[arg_idx];
491 }
492 return (rand_offset);
493}
494
495int
497 int rip_offset = options_.getIpVersion() == 4 ?
498 DHCPV4_REQUESTED_IP_OFFSET : DHCPV6_IA_NA_OFFSET;
499 if (options_.getRequestedIpOffset() > 0) {
500 rip_offset = options_.getRequestedIpOffset();
501 }
502 return (rip_offset);
503}
504
505int
507 int srvid_offset = options_.getIpVersion() == 4 ?
508 DHCPV4_SERVERID_OFFSET : DHCPV6_SERVERID_OFFSET;
509 if (options_.getServerIdOffset() > 0) {
510 srvid_offset = options_.getServerIdOffset();
511 }
512 return (srvid_offset);
513}
514
516TestControl::getTemplateBuffer(const size_t idx) const {
517 if (template_buffers_.size() > idx) {
518 return (template_buffers_[idx]);
519 }
520 isc_throw(OutOfRange, "invalid buffer index");
521}
522
523int
524TestControl::getTransactionIdOffset(const int arg_idx) const {
525 int xid_offset = options_.getIpVersion() == 4 ?
526 DHCPV4_TRANSID_OFFSET : DHCPV6_TRANSID_OFFSET;
527 if (options_.getTransactionIdOffset().size() > arg_idx) {
528 xid_offset = options_.getTransactionIdOffset()[arg_idx];
529 }
530 return (xid_offset);
531}
532
533void
535 int status = 0;
536 while (wait3(&status, WNOHANG, NULL) > 0) {
537 // continue
538 }
539}
540
541void
543 interrupted_ = true;
544}
545
546void
548 template_packets_v4_.clear();
549 template_packets_v6_.clear();
550 template_buffers_.clear();
551 std::vector<std::string> template_files = options_.getTemplateFiles();
552 for (std::vector<std::string>::const_iterator it = template_files.begin();
553 it != template_files.end(); ++it) {
555 }
556}
557
558void
559TestControl::sendPackets(const uint64_t packets_num,
560 const bool preload /* = false */) {
561 for (uint64_t i = packets_num; i > 0; --i) {
562 if (options_.getIpVersion() == 4) {
563 // No template packets means that no -T option was specified.
564 // We have to build packets ourselves.
565 if (template_buffers_.empty()) {
566 sendDiscover4(preload);
567 } else {
570 sendDiscover4(template_buffers_[0], preload);
571 }
572 } else {
573 // No template packets means that no -T option was specified.
574 // We have to build packets ourselves.
575 if (template_buffers_.empty()) {
576 sendSolicit6(preload);
577 } else {
580 sendSolicit6(template_buffers_[0], preload);
581 }
582 }
583 }
584}
585
586uint64_t
588 const uint64_t msg_num) {
589 for (uint64_t i = 0; i < msg_num; ++i) {
590 if (!sendMessageFromAck(msg_type)) {
591 return (i);
592 }
593 }
594 return (msg_num);
595}
596
597uint64_t
599 const uint64_t msg_num) {
600 for (uint64_t i = 0; i < msg_num; ++i) {
601 if (!sendMessageFromReply(msg_type)) {
602 return (i);
603 }
604 }
605 return (msg_num);
606}
607
608void
610 if (options_.testDiags('a')) {
611 // Print all command line parameters.
613 // Print MAC and DUID.
614 std::cout << "Set MAC to " << vector2Hex(options_.getMacTemplate(), "::")
615 << std::endl;
616 if (options_.getDuidTemplate().size() > 0) {
617 std::cout << "Set DUID to " << vector2Hex(options_.getDuidTemplate()) << std::endl;
618 }
619 }
620}
621
622void
623TestControl::printTemplate(const uint8_t packet_type) const {
624 std::string hex_buf;
625 int arg_idx = 0;
626 if (options_.getIpVersion() == 4) {
627 if (packet_type == DHCPREQUEST) {
628 arg_idx = 1;
629 }
630 std::map<uint8_t, dhcp::Pkt4Ptr>::const_iterator pkt_it =
631 template_packets_v4_.find(packet_type);
632 if ((pkt_it != template_packets_v4_.end()) &&
633 pkt_it->second) {
634 const util::OutputBuffer& out_buf(pkt_it->second->getBuffer());
635 const char* out_buf_data =
636 static_cast<const char*>(out_buf.getData());
637 std::vector<uint8_t> buf(out_buf_data, out_buf_data + out_buf.getLength());
638 hex_buf = vector2Hex(buf);
639 }
640 } else if (options_.getIpVersion() == 6) {
641 if (packet_type == DHCPV6_REQUEST) {
642 arg_idx = 1;
643 }
644 std::map<uint8_t, dhcp::Pkt6Ptr>::const_iterator pkt_it =
645 template_packets_v6_.find(packet_type);
646 if (pkt_it != template_packets_v6_.end() &&
647 pkt_it->second) {
648 const util::OutputBuffer& out_buf(pkt_it->second->getBuffer());
649 const char* out_buf_data =
650 static_cast<const char*>(out_buf.getData());
651 std::vector<uint8_t> buf(out_buf_data, out_buf_data + out_buf.getLength());
652 hex_buf = vector2Hex(buf);
653 }
654 }
655 std::cout << "xid-offset=" << getTransactionIdOffset(arg_idx) << std::endl;
656 std::cout << "random-offset=" << getRandomOffset(arg_idx) << std::endl;
657 if (arg_idx > 0) {
658 std::cout << "srvid-offset=" << getServerIdOffset() << std::endl;
659 std::cout << "time-offset=" << getElapsedTimeOffset() << std::endl;
660 std::cout << "ip-offset=" << getRequestedIpOffset() << std::endl;
661 }
662
663 std::cout << "contents: " << std::endl;
664 int line_len = 32;
665 int i = 0;
666 while (line_len == 32) {
667 if (hex_buf.length() - i < 32) {
668 line_len = hex_buf.length() - i;
669 };
670 if (line_len > 0) {
671 std::cout << setfill('0') << setw(4) << std::hex << i << std::dec
672 << " " << hex_buf.substr(i, line_len) << std::endl;
673 }
674 i += 32;
675 }
676 std::cout << std::endl;
677}
678
679void
681 if (options_.getIpVersion() == 4) {
684 } else if (options_.getIpVersion() == 6) {
687 }
688}
689
690void
692 double rate = 0;
693 std::string exchange_name = "4-way exchanges";
694 ExchangeType xchg_type = ExchangeType::DO;
695 if (options_.getIpVersion() == 4) {
696 xchg_type =
699 if (xchg_type == ExchangeType::DO) {
700 exchange_name = "DISCOVER-OFFER";
701 }
702 } else if (options_.getIpVersion() == 6) {
703 xchg_type =
706 if (xchg_type == ExchangeType::SA) {
707 exchange_name = options_.isRapidCommit() ? "Solicit-Reply" :
708 "Solicit-Advertise";
709 }
710 }
711 double duration =
712 stats_mgr_.getTestPeriod().length().total_nanoseconds() / 1e9;
713 rate = stats_mgr_.getRcvdPacketsNum(xchg_type) / duration;
714 std::ostringstream s;
715 s << "***Rate statistics***" << std::endl;
716 s << "Rate: " << rate << " " << exchange_name << "/second";
717 if (options_.getRate() > 0) {
718 s << ", expected rate: " << options_.getRate() << std::endl;
719 }
720
721 std::cout << s.str() << std::endl;
722
723 std::cout <<"***Malformed Packets***" << std::endl
724 << "Malformed packets: " << ExchangeStats::malformed_pkts_
725 << std::endl;
726}
727
728void
730 int delay = options_.getReportDelay();
731 ptime now = microsec_clock::universal_time();
732 time_period time_since_report(last_report_, now);
733 if (time_since_report.length().total_seconds() >= delay) {
736 last_report_ = now;
737 }
738}
739
740void
742 printRate();
744 if (options_.testDiags('i')) {
746 }
747}
748
749std::string
750TestControl::vector2Hex(const std::vector<uint8_t>& vec,
751 const std::string& separator /* = "" */) {
752 std::ostringstream stream;
753 for (std::vector<uint8_t>::const_iterator it = vec.begin();
754 it != vec.end();
755 ++it) {
756 if (it == vec.begin()) {
757 stream << byte2Hex(*it);
758 } else {
759 stream << separator << byte2Hex(*it);
760 }
761 }
762 return (stream.str());
763}
764
765void
766TestControl::readPacketTemplate(const std::string& file_name) {
767 std::ifstream temp_file;
768 temp_file.open(file_name.c_str(), ios::in | ios::binary | ios::ate);
769 if (!temp_file.is_open()) {
770 isc_throw(BadValue, "unable to open template file " << file_name);
771 }
772 // Read template file contents.
773 std::streampos temp_size = temp_file.tellg();
774 if (temp_size == std::streampos(0)) {
775 temp_file.close();
776 isc_throw(OutOfRange, "the template file " << file_name << " is empty");
777 }
778 temp_file.seekg(0, ios::beg);
779 std::vector<char> file_contents(temp_size);
780 temp_file.read(&file_contents[0], temp_size);
781 temp_file.close();
782 // Spaces are allowed so we have to strip the contents
783 // from them. In the same time we want to make sure that
784 // apart from spaces the file contains hexadecimal digits
785 // only.
786 std::vector<char> hex_digits;
787 for (size_t i = 0; i < file_contents.size(); ++i) {
788 if (isxdigit(file_contents[i])) {
789 hex_digits.push_back(file_contents[i]);
790 } else if (!isxdigit(file_contents[i]) &&
791 !isspace(file_contents[i])) {
792 isc_throw(BadValue, "'" << file_contents[i] << "' is not a"
793 " hexadecimal digit");
794 }
795 }
796 // Expect even number of digits.
797 if (hex_digits.size() % 2 != 0) {
798 isc_throw(OutOfRange, "odd number of digits in template file");
799 } else if (hex_digits.empty()) {
800 isc_throw(OutOfRange, "template file " << file_name << " is empty");
801 }
802 std::vector<uint8_t> binary_stream;
803 for (size_t i = 0; i < hex_digits.size(); i += 2) {
804 stringstream s;
805 s << "0x" << hex_digits[i] << hex_digits[i+1];
806 int b;
807 s >> std::hex >> b;
808 binary_stream.push_back(static_cast<uint8_t>(b));
809 }
810 template_buffers_.push_back(binary_stream);
811}
812
813void
815 if (pkt4->getType() == DHCPOFFER) {
818 Pkt4Ptr discover_pkt4(boost::dynamic_pointer_cast<Pkt4>(pkt));
820 if ((xchg_mode == CommandOptions::DORA_SARR) && discover_pkt4) {
821 if (template_buffers_.size() < 2) {
822 sendRequest4(discover_pkt4, pkt4);
823 } else {
826 sendRequest4(template_buffers_[1], discover_pkt4, pkt4);
827 }
828 }
829 } else if (pkt4->getType() == DHCPACK) {
830 // If received message is DHCPACK, we have to check if this is
831 // a response to 4-way exchange. We'll match this packet with
832 // a DHCPREQUEST sent as part of the 4-way exchanges.
835 // The DHCPACK belongs to DHCPREQUEST-DHCPACK exchange type.
836 // So, we may need to keep this DHCPACK in the storage if renews.
837 // Note that, DHCPACK messages hold the information about
838 // leases assigned. We use this information to renew.
841 // Renew or release messages are sent, because StatsMgr has the
842 // specific exchange type specified. Let's append the DHCPACK
843 // message to a storage.
844 ack_storage_.append(pkt4);
845 }
846 // The DHCPACK message is not a server's response to the DHCPREQUEST
847 // message sent within the 4-way exchange. It may be a response to a
848 // renewal. In this case we first check if StatsMgr has exchange type
849 // for renew specified, and if it has, if there is a corresponding
850 // renew message for the received DHCPACK.
853 }
854 }
855}
856
857void
859 // check if received address is unique
860 if (options_.getAddrUnique()) {
861 std::set<std::string> current;
862 // addresses were already checked in validateIA
863 // we can safely assume that those are correct
864 for (const auto& opt : pkt6->options_) {
865 switch (opt.second->getType()) {
866 case D6O_IA_PD: {
867 // add address and check if it has not been already assigned
868 // addresses should be unique cross options of the packet
869 auto ret = current.emplace(boost::dynamic_pointer_cast<
870 Option6IAPrefix>(opt.second->getOption(D6O_IAPREFIX))->getAddress().toText());
871 if (!ret.second) {
873 }
874 break;
875 }
876 case D6O_IA_NA: {
877 // add address and check if it has not been already assigned
878 // addresses should be unique cross options of the packet
879 auto ret = current.emplace(boost::dynamic_pointer_cast<
880 Option6IAAddr>(opt.second->getOption(D6O_IAADDR))->getAddress().toText());
881 if (!ret.second) {
883 }
884 break;
885 }
886 default:
887 break;
888 }
889 }
890 // addresses should be unique cross packets
891 addUniqeAddr(current, xchg_type);
892 }
893}
894
895void
897 // check if received address is unique
898 if (options_.getAddrUnique()) {
899 // addresses were already checked in validateIA
900 // we can safely assume that those are correct
901 std::set<std::string> current;
902 current.insert(pkt4->getYiaddr().toText());
903 // addresses should be unique cross packets
904 addUniqeAddr(current, xchg_type);
905 }
906}
907
908bool
910 // check if iaaddr exists - if it does, we can continue sending request
911 // if not we will update statistics about rejected leases
912 // @todo it's checking just one iaaddress option for now it's ok
913 // but when perfdhcp will be extended to create message with multiple IA
914 // this will have to be iterate on:
915 // OptionCollection ias = pkt6->getOptions(D6O_IA_NA);
916 Option6IAPrefixPtr iapref;
917 Option6IAAddrPtr iaaddr;
918 if (pkt6->getOption(D6O_IA_PD)) {
919 iapref = boost::dynamic_pointer_cast<
920 Option6IAPrefix>(pkt6->getOption(D6O_IA_PD)->getOption(D6O_IAPREFIX));
921 }
922 if (pkt6->getOption(D6O_IA_NA)) {
923 iaaddr = boost::dynamic_pointer_cast<
924 Option6IAAddr>(pkt6->getOption(D6O_IA_NA)->getOption(D6O_IAADDR));
925 }
926
927 bool address_and_prefix = options_.getLeaseType().includes(
929 bool prefix_only = options_.getLeaseType().includes(
931 bool address_only = options_.getLeaseType().includes(
933 if ((address_and_prefix && iapref && iaaddr) ||
934 (prefix_only && iapref && !address_and_prefix) ||
935 (address_only && iaaddr && !address_and_prefix)) {
936 return true;
937 } else {
938 return false;
939 }
940}
941
942void
944 uint8_t packet_type = pkt6->getType();
945 if (packet_type == DHCPV6_ADVERTISE) {
947 Pkt6Ptr solicit_pkt6(boost::dynamic_pointer_cast<Pkt6>(pkt));
949 if ((xchg_mode == CommandOptions::DORA_SARR) && solicit_pkt6) {
950 if (validateIA(pkt6)) {
951 // if address is correct - check uniqueness
953 if (template_buffers_.size() < 2) {
954 sendRequest6(pkt6);
955 } else {
959 }
960 } else {
962 }
963 }
964 } else if (packet_type == DHCPV6_REPLY) {
965 // If the received message is Reply, we have to find out which exchange
966 // type the Reply message belongs to. It is doable by matching the Reply
967 // transaction id with the transaction id of the sent Request, Renew
968 // or Release. First we start with the Request.
970 // The Reply belongs to Request-Reply exchange type. So, we may need
971 // to keep this Reply in the storage if Renews or/and Releases are
972 // being sent. Note that, Reply messages hold the information about
973 // leases assigned. We use this information to construct Renew and
974 // Release messages.
975 if (validateIA(pkt6)) {
976 // if address is correct - check uniqueness
978 // check if there is correct IA to continue with Renew/Release
981 // Renew or Release messages are sent, because StatsMgr has the
982 // specific exchange type specified. Let's append the Reply
983 // message to a storage.
984 reply_storage_.append(pkt6);
985 }
986 } else {
988 }
989 // The Reply message is not a server's response to the Request message
990 // sent within the 4-way exchange. It may be a response to the Renew
991 // or Release message. In the if clause we first check if StatsMgr
992 // has exchange type for Renew specified, and if it has, if there is
993 // a corresponding Renew message for the received Reply. If not,
994 // we check that StatsMgr has exchange type for Release specified,
995 // as possibly the Reply has been sent in response to Release.
999 // At this point, it is only possible that the Reply has been sent
1000 // in response to a Release. Try to match the Reply with Release.
1002 }
1003 }
1004}
1005
1006unsigned int
1008 unsigned int pkt_count = 0;
1009 PktPtr pkt;
1010 while ((pkt = receiver_.getPkt())) {
1011 pkt_count += 1;
1012 if (options_.getIpVersion() == 4) {
1013 Pkt4Ptr pkt4 = boost::dynamic_pointer_cast<Pkt4>(pkt);
1015 } else {
1016 Pkt6Ptr pkt6 = boost::dynamic_pointer_cast<Pkt6>(pkt);
1018 }
1019 }
1020 return pkt_count;
1021}
1022void
1024 static bool factories_registered = false;
1025 if (!factories_registered) {
1026 // DHCP_MESSAGE_TYPE option factory.
1027 LibDHCP::OptionFactoryRegister(Option::V4,
1030 // DHCP_SERVER_IDENTIFIER option factory.
1031 LibDHCP::OptionFactoryRegister(Option::V4,
1034 // DHCP_PARAMETER_REQUEST_LIST option factory.
1035 LibDHCP::OptionFactoryRegister(Option::V4,
1038 }
1039 factories_registered = true;
1040}
1041
1042void
1044 static bool factories_registered = false;
1045 if (!factories_registered) {
1046 // D6O_ELAPSED_TIME
1047 LibDHCP::OptionFactoryRegister(Option::V6,
1050 // D6O_RAPID_COMMIT
1051 LibDHCP::OptionFactoryRegister(Option::V6,
1054 // D6O_ORO (option request option) factory.
1055 LibDHCP::OptionFactoryRegister(Option::V6,
1056 D6O_ORO,
1058 // D6O_CLIENTID option factory.
1059 LibDHCP::OptionFactoryRegister(Option::V6,
1062 // D6O_SERVERID option factory.
1063 LibDHCP::OptionFactoryRegister(Option::V6,
1066 // D6O_IA_NA option factory.
1067 LibDHCP::OptionFactoryRegister(Option::V6,
1068 D6O_IA_NA,
1070
1071 // D6O_IA_PD option factory.
1072 LibDHCP::OptionFactoryRegister(Option::V6,
1073 D6O_IA_PD,
1075
1076
1077 }
1078 factories_registered = true;
1079}
1080
1081void
1083 switch(options_.getIpVersion()) {
1084 case 4:
1086 break;
1087 case 6:
1089 break;
1090 default:
1091 isc_throw(InvalidOperation, "command line options have to be parsed "
1092 "before DHCP option factories can be registered");
1093 }
1094}
1095
1096void
1098 transid_gen_.reset();
1099 last_report_ = microsec_clock::universal_time();
1100 // Actual generators will have to be set later on because we need to
1101 // get command line parameters first.
1104 first_packet_serverid_.clear();
1105 interrupted_ = false;
1106}
1107
1109 exit_time_(not_a_date_time),
1110 number_generator_(0, options.getMacsFromFile().size()),
1111 socket_(socket),
1112 receiver_(socket, options.isSingleThreaded(), options.getIpVersion()),
1113 stats_mgr_(options),
1114 options_(options)
1115{
1116 // Reset singleton state before test starts.
1117 reset();
1118
1119 // Ip version is not set ONLY in case the command options
1120 // were not parsed. This surely means that parse() function
1121 // was not called prior to starting the test. This is fatal
1122 // error.
1123 if (options_.getIpVersion() == 0) {
1125 "command options must be parsed before running a test");
1126 } else if (options_.getIpVersion() == 4) {
1127 // Turn off packet queueing.
1128 IfaceMgr::instance().configureDHCPPacketQueue(AF_INET, data::ElementPtr());
1130 } else {
1131 // Turn off packet queueing.
1132 IfaceMgr::instance().configureDHCPPacketQueue(AF_INET6, data::ElementPtr());
1134 }
1135
1136 uint32_t clients_num = options_.getClientsNum() == 0 ?
1137 1 : options_.getClientsNum();
1139
1140 // Diagnostics are command line options mainly.
1142 // Option factories have to be registered.
1144 // Initialize packet templates.
1146 // Initialize randomization seed.
1147 if (options_.isSeeded()) {
1148 srandom(options_.getSeed());
1149 } else {
1150 // Seed with current time.
1151 time_period duration(from_iso_string("20111231T235959"),
1152 microsec_clock::universal_time());
1153 srandom(duration.length().total_seconds()
1154 + duration.length().fractional_seconds());
1155 }
1156 // If user interrupts the program we will exit gracefully.
1157 signal(SIGINT, TestControl::handleInterrupt);
1158}
1159
1160void
1161TestControl::runWrapped(bool do_stop /*= false */) const {
1162 if (!options_.getWrapped().empty()) {
1163 pid_t pid = 0;
1164 signal(SIGCHLD, handleChild);
1165 pid = fork();
1166 if (pid < 0) {
1167 isc_throw(Unexpected, "unable to fork");
1168 } else if (pid == 0) {
1169 execlp(options_.getWrapped().c_str(), do_stop ? "stop" : "start", (void*)0);
1170 }
1171 }
1172}
1173
1174void
1176 if (options_.testDiags('T')) {
1177 if (template_packets_v4_.find(pkt->getType()) == template_packets_v4_.end()) {
1178 template_packets_v4_[pkt->getType()] = pkt;
1179 }
1180 }
1181}
1182
1183void
1185 if (options_.testDiags('T')) {
1186 if (template_packets_v6_.find(pkt->getType()) == template_packets_v6_.end()) {
1187 template_packets_v6_[pkt->getType()] = pkt;
1188 }
1189 }
1190}
1191
1192void
1193TestControl::sendDiscover4(const bool preload /*= false*/) {
1194 // Generate the MAC address to be passed in the packet.
1195 uint8_t randomized = 0;
1196 std::vector<uint8_t> mac_address = generateMacAddress(randomized);
1197 // Generate transaction id to be set for the new exchange.
1198 const uint32_t transid = generateTransid();
1199 Pkt4Ptr pkt4(new Pkt4(DHCPDISCOVER, transid));
1200 if (!pkt4) {
1201 isc_throw(Unexpected, "failed to create DISCOVER packet");
1202 }
1203
1204 // Delete the default Message Type option set by Pkt4
1205 pkt4->delOption(DHO_DHCP_MESSAGE_TYPE);
1206
1207 // Set options: DHCP_MESSAGE_TYPE and DHCP_PARAMETER_REQUEST_LIST
1208 OptionBuffer buf_msg_type;
1209 buf_msg_type.push_back(DHCPDISCOVER);
1210 pkt4->addOption(Option::factory(Option::V4, DHO_DHCP_MESSAGE_TYPE,
1211 buf_msg_type));
1212 pkt4->addOption(Option::factory(Option::V4,
1214
1215
1216 // Set client's and server's ports as well as server's address,
1217 // and local (relay) address.
1218 setDefaults4(pkt4);
1219
1220 // Set hardware address
1221 pkt4->setHWAddr(HTYPE_ETHER, mac_address.size(), mac_address);
1222
1223 // Set client identifier
1224 pkt4->addOption(generateClientId(pkt4->getHWAddr()));
1225
1226 // Check if we need to simulate HA failures by pretending no responses were received.
1227 // The DHCP protocol signals that by increasing secs field (seconds since the configuration attempt started).
1229 stats_mgr_.getTestPeriod().length().total_seconds() >= options_.getWaitForElapsedTime() &&
1230 stats_mgr_.getTestPeriod().length().total_seconds() < options_.getWaitForElapsedTime() +
1232
1233 // Keep increasing elapsed time. The value should start increasing steadily.
1234 uint32_t val = stats_mgr_.getTestPeriod().length().total_seconds() - options_.getWaitForElapsedTime() + 1;
1235 if (val > 65535) {
1236 val = 65535;
1237 }
1238 pkt4->setSecs(static_cast<uint16_t>(val));
1239 }
1240
1241 // Add any extra options that user may have specified.
1242 addExtraOpts(pkt4);
1243
1244 pkt4->pack();
1245 socket_.send(pkt4);
1246 if (!preload) {
1248 }
1249
1250 saveFirstPacket(pkt4);
1251}
1252
1253void
1254TestControl::sendDiscover4(const std::vector<uint8_t>& template_buf,
1255 const bool preload /* = false */) {
1256 // Get the first argument if multiple the same arguments specified
1257 // in the command line. First one refers to DISCOVER packets.
1258 const uint8_t arg_idx = 0;
1259 // Generate the MAC address to be passed in the packet.
1260 uint8_t randomized = 0;
1261 std::vector<uint8_t> mac_address = generateMacAddress(randomized);
1262 // Generate transaction id to be set for the new exchange.
1263 const uint32_t transid = generateTransid();
1264 // Get transaction id offset.
1265 size_t transid_offset = getTransactionIdOffset(arg_idx);
1266 // Get randomization offset.
1267 // We need to go back by HW_ETHER_LEN (MAC address length)
1268 // because this offset points to last octet of MAC address.
1269 size_t rand_offset = getRandomOffset(arg_idx) - HW_ETHER_LEN + 1;
1270 // Create temporary buffer with template contents. We will
1271 // modify this temporary buffer but we don't want to modify
1272 // the original template.
1273 std::vector<uint8_t> in_buf(template_buf.begin(),
1274 template_buf.end());
1275 // Check if we are not going out of bounds.
1276 if (rand_offset + HW_ETHER_LEN > in_buf.size()) {
1277 isc_throw(OutOfRange, "randomization offset is out of bounds");
1278 }
1279 PerfPkt4Ptr pkt4(new PerfPkt4(&in_buf[0], in_buf.size(),
1280 transid_offset,
1281 transid));
1282
1283 // Replace MAC address in the template with actual MAC address.
1284 pkt4->writeAt(rand_offset, mac_address.begin(), mac_address.end());
1285 // Create a packet from the temporary buffer.
1286 setDefaults4(boost::static_pointer_cast<Pkt4>(pkt4));
1287 // Pack the input packet buffer to output buffer so as it can
1288 // be sent to server.
1289 pkt4->rawPack();
1290 socket_.send(boost::static_pointer_cast<Pkt4>(pkt4));
1291 if (!preload) {
1292 // Update packet stats.
1294 boost::static_pointer_cast<Pkt4>(pkt4));
1295 }
1296 saveFirstPacket(pkt4);
1297}
1298
1299bool
1300TestControl::sendMessageFromAck(const uint16_t msg_type) {
1301 // We only permit Request or Release messages to be sent using this
1302 // function.
1303 if (msg_type != DHCPREQUEST && msg_type != DHCPRELEASE) {
1305 "invalid message type "
1306 << msg_type
1307 << " to be sent, expected DHCPREQUEST or DHCPRELEASE");
1308 }
1309
1310 // Get one of the recorded DHCPACK messages.
1311 Pkt4Ptr ack = ack_storage_.getRandom();
1312 if (!ack) {
1313 return (false);
1314 }
1315
1316 // Create message of the specified type.
1317 Pkt4Ptr msg = createMessageFromAck(msg_type, ack);
1318 setDefaults4(msg);
1319
1320 // Override relay address
1321 msg->setGiaddr(ack->getGiaddr());
1322
1323 // Add any extra options that user may have specified.
1324 addExtraOpts(msg);
1325
1326 // Pack it.
1327 msg->pack();
1328
1329 // And send it.
1330 socket_.send(msg);
1334 msg);
1335 return (true);
1336}
1337
1338
1339bool
1340TestControl::sendMessageFromReply(const uint16_t msg_type) {
1341 // We only permit Release or Renew messages to be sent using this function.
1342 if (msg_type != DHCPV6_RENEW && msg_type != DHCPV6_RELEASE) {
1343 isc_throw(isc::BadValue, "invalid message type " << msg_type
1344 << " to be sent, expected DHCPV6_RENEW or DHCPV6_RELEASE");
1345 }
1346
1347 // Get one of the recorded DHCPV6_OFFER messages.
1348 Pkt6Ptr reply = reply_storage_.getRandom();
1349 if (!reply) {
1350 return (false);
1351 }
1352 // Prepare the message of the specified type.
1353 Pkt6Ptr msg = createMessageFromReply(msg_type, reply);
1354 setDefaults6(msg);
1355
1356 // Add any extra options that user may have specified.
1357 addExtraOpts(msg);
1358
1359 // Pack it.
1360 msg->pack();
1361
1362 // And send it.
1363 socket_.send(msg);
1366 : ExchangeType::RL), msg);
1367 return (true);
1368}
1369
1370void
1372 const dhcp::Pkt4Ptr& offer_pkt4) {
1373 // Use the same transaction id as the one used in the discovery packet.
1374 const uint32_t transid = discover_pkt4->getTransid();
1375 Pkt4Ptr pkt4(new Pkt4(DHCPREQUEST, transid));
1376
1377 // Use first flags indicates that we want to use the server
1378 // id captured in first packet.
1379 if (options_.isUseFirst() &&
1380 (first_packet_serverid_.size() > 0)) {
1381 pkt4->addOption(Option::factory(Option::V4, DHO_DHCP_SERVER_IDENTIFIER,
1383 } else {
1384 OptionPtr opt_serverid =
1385 offer_pkt4->getOption(DHO_DHCP_SERVER_IDENTIFIER);
1386 if (!opt_serverid) {
1387 isc_throw(BadValue, "there is no SERVER_IDENTIFIER option "
1388 << "in OFFER message");
1389 }
1391 first_packet_serverid_ = opt_serverid->getData();
1392 }
1393 pkt4->addOption(opt_serverid);
1394 }
1395
1397 asiolink::IOAddress yiaddr = offer_pkt4->getYiaddr();
1398 if (!yiaddr.isV4()) {
1399 isc_throw(BadValue, "the YIADDR returned in OFFER packet is not "
1400 " IPv4 address");
1401 }
1402 OptionPtr opt_requested_address =
1404 OptionBuffer()));
1405 opt_requested_address->setUint32(yiaddr.toUint32());
1406 pkt4->addOption(opt_requested_address);
1407 OptionPtr opt_parameter_list =
1408 Option::factory(Option::V4, DHO_DHCP_PARAMETER_REQUEST_LIST);
1409 pkt4->addOption(opt_parameter_list);
1410 // Set client's and server's ports as well as server's address
1411 setDefaults4(pkt4);
1412 // Override relay address
1413 pkt4->setGiaddr(offer_pkt4->getGiaddr());
1414 // Add any extra options that user may have specified.
1415 addExtraOpts(pkt4);
1416
1417 // Set hardware address
1418 pkt4->setHWAddr(offer_pkt4->getHWAddr());
1419 // Set client id.
1420 pkt4->addOption(generateClientId(pkt4->getHWAddr()));
1421 // Set elapsed time.
1422 uint32_t elapsed_time = getElapsedTime<Pkt4Ptr>(discover_pkt4, offer_pkt4);
1423 pkt4->setSecs(static_cast<uint16_t>(elapsed_time / 1000));
1424 // Prepare on wire data to send.
1425 pkt4->pack();
1426 socket_.send(pkt4);
1428 saveFirstPacket(pkt4);
1429}
1430
1431void
1432TestControl::sendRequest4(const std::vector<uint8_t>& template_buf,
1433 const dhcp::Pkt4Ptr& discover_pkt4,
1434 const dhcp::Pkt4Ptr& offer_pkt4) {
1435 // Get the second argument if multiple the same arguments specified
1436 // in the command line. Second one refers to REQUEST packets.
1437 const uint8_t arg_idx = 1;
1438 // Use the same transaction id as the one used in the discovery packet.
1439 const uint32_t transid = discover_pkt4->getTransid();
1440 // Get transaction id offset.
1441 size_t transid_offset = getTransactionIdOffset(arg_idx);
1442 // Get the offset of MAC's last octet.
1443 // We need to go back by HW_ETHER_LEN (MAC address length)
1444 // because this offset points to last octet of MAC address.
1445 size_t rand_offset = getRandomOffset(arg_idx) - HW_ETHER_LEN + 1;
1446 // Create temporary buffer from the template.
1447 std::vector<uint8_t> in_buf(template_buf.begin(),
1448 template_buf.end());
1449 // Check if given randomization offset is not out of bounds.
1450 if (rand_offset + HW_ETHER_LEN > in_buf.size()) {
1451 isc_throw(OutOfRange, "randomization offset is out of bounds");
1452 }
1453
1454 // Create packet from the temporary buffer.
1455 PerfPkt4Ptr pkt4(new PerfPkt4(&in_buf[0], in_buf.size(),
1456 transid_offset,
1457 transid));
1458
1459 // Set hardware address from OFFER packet received.
1460 HWAddrPtr hwaddr = offer_pkt4->getHWAddr();
1461 std::vector<uint8_t> mac_address(HW_ETHER_LEN, 0);
1462 uint8_t hw_len = hwaddr->hwaddr_.size();
1463 if (hw_len != 0) {
1464 memcpy(&mac_address[0], &hwaddr->hwaddr_[0],
1465 hw_len > HW_ETHER_LEN ? HW_ETHER_LEN : hw_len);
1466 }
1467 pkt4->writeAt(rand_offset, mac_address.begin(), mac_address.end());
1468
1469 // Set elapsed time.
1470 size_t elp_offset = getElapsedTimeOffset();
1471 uint32_t elapsed_time = getElapsedTime<Pkt4Ptr>(discover_pkt4, offer_pkt4);
1472 pkt4->writeValueAt<uint16_t>(elp_offset,
1473 static_cast<uint16_t>(elapsed_time / 1000));
1474
1475 // Get the actual server id offset.
1476 size_t sid_offset = getServerIdOffset();
1477 // Use first flags indicates that we want to use the server
1478 // id captured in first packet.
1479 if (options_.isUseFirst() &&
1480 (first_packet_serverid_.size() > 0)) {
1481 boost::shared_ptr<LocalizedOption>
1482 opt_serverid(new LocalizedOption(Option::V4,
1485 sid_offset));
1486 pkt4->addOption(opt_serverid);
1487 } else {
1488 // Copy the contents of server identifier received in
1489 // OFFER packet to put this into REQUEST.
1490 OptionPtr opt_serverid_offer =
1491 offer_pkt4->getOption(DHO_DHCP_SERVER_IDENTIFIER);
1492 if (!opt_serverid_offer) {
1493 isc_throw(BadValue, "there is no SERVER_IDENTIFIER option "
1494 << "in OFFER message");
1495 }
1496 boost::shared_ptr<LocalizedOption>
1497 opt_serverid(new LocalizedOption(Option::V4,
1499 opt_serverid_offer->getData(),
1500 sid_offset));
1501 pkt4->addOption(opt_serverid);
1503 first_packet_serverid_ = opt_serverid_offer->getData();
1504 }
1505 }
1506
1508 asiolink::IOAddress yiaddr = offer_pkt4->getYiaddr();
1509 if (!yiaddr.isV4()) {
1510 isc_throw(BadValue, "the YIADDR returned in OFFER packet is not "
1511 " IPv4 address");
1512 }
1513
1514 // Get the actual offset of requested ip.
1515 size_t rip_offset = getRequestedIpOffset();
1516 // Place requested IP option at specified position (rip_offset).
1517 boost::shared_ptr<LocalizedOption>
1518 opt_requested_ip(new LocalizedOption(Option::V4,
1520 OptionBuffer(),
1521 rip_offset));
1522 // The IOAddress is convertible to uint32_t and returns exactly what we need.
1523 opt_requested_ip->setUint32(yiaddr.toUint32());
1524 pkt4->addOption(opt_requested_ip);
1525
1526 setDefaults4(boost::static_pointer_cast<Pkt4>(pkt4));
1527
1528 // Add any extra options that user may have specified.
1529 addExtraOpts(pkt4);
1530
1531 // Prepare on-wire data.
1532 pkt4->rawPack();
1533 socket_.send(boost::static_pointer_cast<Pkt4>(pkt4));
1534 // Update packet stats.
1536 boost::static_pointer_cast<Pkt4>(pkt4));
1537 saveFirstPacket(pkt4);
1538}
1539
1540void
1541TestControl::sendRequest6(const Pkt6Ptr& advertise_pkt6) {
1542 const uint32_t transid = generateTransid();
1543 Pkt6Ptr pkt6(new Pkt6(DHCPV6_REQUEST, transid));
1544 // Set elapsed time.
1545 OptionPtr opt_elapsed_time =
1546 Option::factory(Option::V6, D6O_ELAPSED_TIME);
1547 pkt6->addOption(opt_elapsed_time);
1548 // Set client id.
1549 OptionPtr opt_clientid = advertise_pkt6->getOption(D6O_CLIENTID);
1550 if (!opt_clientid) {
1551 isc_throw(Unexpected, "client id not found in received packet");
1552 }
1553 pkt6->addOption(opt_clientid);
1554
1555 // Use first flags indicates that we want to use the server
1556 // id captured in first packet.
1557 if (options_.isUseFirst() &&
1558 (first_packet_serverid_.size() > 0)) {
1559 pkt6->addOption(Option::factory(Option::V6, D6O_SERVERID,
1561 } else {
1562 OptionPtr opt_serverid = advertise_pkt6->getOption(D6O_SERVERID);
1563 if (!opt_serverid) {
1564 isc_throw(Unexpected, "server id not found in received packet");
1565 }
1567 first_packet_serverid_ = opt_serverid->getData();
1568 }
1569 pkt6->addOption(opt_serverid);
1570 }
1571
1572 // Copy IA_NA or IA_PD option from the Advertise message to the Request
1573 // message being sent to the server. This will throw exception if the
1574 // option to be copied is not found. Note that this function will copy
1575 // one of IA_NA or IA_PD options, depending on the lease-type value
1576 // specified in the command line.
1577 copyIaOptions(advertise_pkt6, pkt6);
1578
1579 // Set default packet data.
1580 setDefaults6(pkt6);
1581
1582 // Add any extra options that user may have specified.
1583 addExtraOpts(pkt6);
1584
1585 // Prepare on-wire data.
1586 pkt6->pack();
1587 socket_.send(pkt6);
1589 saveFirstPacket(pkt6);
1590}
1591
1592void
1593TestControl::sendRequest6(const std::vector<uint8_t>& template_buf,
1594 const Pkt6Ptr& advertise_pkt6) {
1595 // Get the second argument if multiple the same arguments specified
1596 // in the command line. Second one refers to REQUEST packets.
1597 const uint8_t arg_idx = 1;
1598 // Generate transaction id.
1599 const uint32_t transid = generateTransid();
1600 // Get transaction id offset.
1601 size_t transid_offset = getTransactionIdOffset(arg_idx);
1602 PerfPkt6Ptr pkt6(new PerfPkt6(&template_buf[0], template_buf.size(),
1603 transid_offset, transid));
1604 // Set elapsed time.
1605 size_t elp_offset = getElapsedTimeOffset();
1606 boost::shared_ptr<LocalizedOption>
1607 opt_elapsed_time(new LocalizedOption(Option::V6, D6O_ELAPSED_TIME,
1608 OptionBuffer(), elp_offset));
1609 pkt6->addOption(opt_elapsed_time);
1610
1611 // Get the actual server id offset.
1612 size_t sid_offset = getServerIdOffset();
1613 // Use first flags indicates that we want to use the server
1614 // id captured in first packet.
1615 if (options_.isUseFirst() &&
1616 (first_packet_serverid_.size() > 0)) {
1617 boost::shared_ptr<LocalizedOption>
1618 opt_serverid(new LocalizedOption(Option::V6,
1621 sid_offset));
1622 pkt6->addOption(opt_serverid);
1623
1624 } else {
1625 // Copy the contents of server identifier received in
1626 // ADVERTISE packet to put this into REQUEST.
1627 OptionPtr opt_serverid_advertise =
1628 advertise_pkt6->getOption(D6O_SERVERID);
1629 if (!opt_serverid_advertise) {
1630 isc_throw(BadValue, "there is no SERVERID option "
1631 << "in ADVERTISE message");
1632 }
1633 boost::shared_ptr<LocalizedOption>
1634 opt_serverid(new LocalizedOption(Option::V6,
1636 opt_serverid_advertise->getData(),
1637 sid_offset));
1638 pkt6->addOption(opt_serverid);
1640 first_packet_serverid_ = opt_serverid_advertise->getData();
1641 }
1642 }
1643 // Set IA_NA
1644 OptionPtr opt_ia_na_advertise = advertise_pkt6->getOption(D6O_IA_NA);
1645 if (!opt_ia_na_advertise) {
1646 isc_throw(Unexpected, "DHCPv6 IA_NA option not found in received "
1647 "packet");
1648 }
1649 size_t addr_offset = getRequestedIpOffset();
1650 boost::shared_ptr<LocalizedOption>
1651 opt_ia_na(new LocalizedOption(Option::V6, D6O_IA_NA, opt_ia_na_advertise->getData(), addr_offset));
1652 if (!opt_ia_na->valid()) {
1653 isc_throw(BadValue, "Option IA_NA in advertise packet is invalid");
1654 }
1655 pkt6->addOption(opt_ia_na);
1656 // Set server id.
1657 OptionPtr opt_serverid_advertise = advertise_pkt6->getOption(D6O_SERVERID);
1658 if (!opt_serverid_advertise) {
1659 isc_throw(Unexpected, "DHCPV6 SERVERID option not found in received "
1660 "packet");
1661 }
1662 size_t srvid_offset = getServerIdOffset();
1663 boost::shared_ptr<LocalizedOption>
1664 opt_serverid(new LocalizedOption(Option::V6, D6O_SERVERID,
1665 opt_serverid_advertise->getData(),
1666 srvid_offset));
1667 pkt6->addOption(opt_serverid);
1668 // Get randomization offset.
1669 size_t rand_offset = getRandomOffset(arg_idx);
1670 OptionPtr opt_clientid_advertise = advertise_pkt6->getOption(D6O_CLIENTID);
1671 if (!opt_clientid_advertise) {
1672 isc_throw(Unexpected, "DHCPV6 CLIENTID option not found in received packet");
1673 }
1674 rand_offset -= (opt_clientid_advertise->len() - 1);
1675 // Set client id.
1676 boost::shared_ptr<LocalizedOption>
1677 opt_clientid(new LocalizedOption(Option::V6, D6O_CLIENTID,
1678 opt_clientid_advertise->getData(),
1679 rand_offset));
1680 pkt6->addOption(opt_clientid);
1681 // Set default packet data.
1682 setDefaults6(pkt6);
1683
1684 // Add any extra options that user may have specified.
1685 addExtraOpts(pkt6);
1686
1687 // Prepare on wire data.
1688 pkt6->rawPack();
1689 // Send packet.
1690 socket_.send(pkt6);
1691 // Update packet stats.
1693
1694 // When 'T' diagnostics flag is specified it means that user requested
1695 // printing packet contents. It will be just one (first) packet which
1696 // contents will be printed. Here we check if this packet has been already
1697 // collected. If it hasn't we save this packet so as we can print its
1698 // contents when test is finished.
1699 if (options_.testDiags('T') &&
1702 }
1703}
1704
1705void
1706TestControl::sendSolicit6(const bool preload /*= false*/) {
1707 // Generate DUID to be passed to the packet
1708 uint8_t randomized = 0;
1709 std::vector<uint8_t> duid = generateDuid(randomized);
1710 // Generate transaction id to be set for the new exchange.
1711 const uint32_t transid = generateTransid();
1712 Pkt6Ptr pkt6(new Pkt6(DHCPV6_SOLICIT, transid));
1713 if (!pkt6) {
1714 isc_throw(Unexpected, "failed to create SOLICIT packet");
1715 }
1716
1717 // Check if we need to simulate HA failures by pretending no responses were received.
1718 // The DHCPv6 protocol signals that by increasing the elapsed option field. Note it is in 1/100 of a second.
1720 stats_mgr_.getTestPeriod().length().total_seconds() >= options_.getWaitForElapsedTime() &&
1721 stats_mgr_.getTestPeriod().length().total_seconds() < options_.getWaitForElapsedTime() +
1723
1724
1725 // Keep increasing elapsed time. The value should start increasing steadily.
1726 uint32_t val = (stats_mgr_.getTestPeriod().length().total_seconds() - options_.getWaitForElapsedTime() + 1)*100;
1727 if (val > 65535) {
1728 val = 65535;
1729 }
1730 OptionPtr elapsed(new OptionInt<uint16_t>(Option::V6, D6O_ELAPSED_TIME, val));
1731 pkt6->addOption(elapsed);
1732 } else {
1733 pkt6->addOption(Option::factory(Option::V6, D6O_ELAPSED_TIME));
1734 }
1735
1736 if (options_.isRapidCommit()) {
1737 pkt6->addOption(Option::factory(Option::V6, D6O_RAPID_COMMIT));
1738 }
1739 pkt6->addOption(Option::factory(Option::V6, D6O_CLIENTID, duid));
1740 pkt6->addOption(Option::factory(Option::V6, D6O_ORO));
1741
1742
1743 // Depending on the lease-type option specified, we should request
1744 // IPv6 address (with IA_NA) or IPv6 prefix (IA_PD) or both.
1745
1746 // IA_NA
1748 pkt6->addOption(Option::factory(Option::V6, D6O_IA_NA));
1749 }
1750 // IA_PD
1752 pkt6->addOption(Option::factory(Option::V6, D6O_IA_PD));
1753 }
1754
1755 setDefaults6(pkt6);
1756
1757 // Add any extra options that user may have specified.
1758 addExtraOpts(pkt6);
1759
1760 pkt6->pack();
1761 socket_.send(pkt6);
1762 if (!preload) {
1764 }
1765
1766 saveFirstPacket(pkt6);
1767}
1768
1769void
1770TestControl::sendSolicit6(const std::vector<uint8_t>& template_buf,
1771 const bool preload /*= false*/) {
1772 const int arg_idx = 0;
1773 // Get transaction id offset.
1774 size_t transid_offset = getTransactionIdOffset(arg_idx);
1775 // Generate transaction id to be set for the new exchange.
1776 const uint32_t transid = generateTransid();
1777 // Create packet.
1778 PerfPkt6Ptr pkt6(new PerfPkt6(&template_buf[0], template_buf.size(),
1779 transid_offset, transid));
1780 if (!pkt6) {
1781 isc_throw(Unexpected, "failed to create SOLICIT packet");
1782 }
1783 size_t rand_offset = getRandomOffset(arg_idx);
1784 // randomized will pick number of bytes randomized so we can
1785 // just use part of the generated duid and substitute a few bytes
1787 uint8_t randomized = 0;
1788 std::vector<uint8_t> duid = generateDuid(randomized);
1789 if (rand_offset > template_buf.size()) {
1790 isc_throw(OutOfRange, "randomization offset is out of bounds");
1791 }
1792 // Store random part of the DUID into the packet.
1793 pkt6->writeAt(rand_offset - randomized + 1,
1794 duid.end() - randomized, duid.end());
1795
1796 // Prepare on-wire data.
1797 pkt6->rawPack();
1798 setDefaults6(pkt6);
1799
1800 // Add any extra options that user may have specified.
1801 addExtraOpts(pkt6);
1802
1803 // Send solicit packet.
1804 socket_.send(pkt6);
1805 if (!preload) {
1806 // Update packet stats.
1808 }
1809 saveFirstPacket(pkt6);
1810}
1811
1812
1813void
1815 // Interface name.
1816 IfacePtr iface = socket_.getIface();
1817 if (iface == NULL) {
1818 isc_throw(BadValue, "unable to find interface with given index");
1819 }
1820 pkt->setIface(iface->getName());
1821 // Interface index.
1822 pkt->setIndex(socket_.ifindex_);
1823 // Local client's port (68)
1824 pkt->setLocalPort(DHCP4_CLIENT_PORT);
1825 // Server's port (67)
1826 if (options_.getRemotePort()) {
1827 pkt->setRemotePort(options_.getRemotePort());
1828 } else {
1829 pkt->setRemotePort(DHCP4_SERVER_PORT);
1830 }
1831 // The remote server's name or IP.
1832 pkt->setRemoteAddr(IOAddress(options_.getServerName()));
1833 // Set local address.
1834 pkt->setLocalAddr(IOAddress(socket_.addr_));
1835 // Set relay (GIADDR) address to local address if multiple
1836 // subnet mode is not enabled
1837 if (!options_.checkMultiSubnet()) {
1838 pkt->setGiaddr(IOAddress(socket_.addr_));
1839 } else {
1840 pkt->setGiaddr(IOAddress(options_.getRandRelayAddr()));
1841 }
1842 // Pretend that we have one relay (which is us).
1843 pkt->setHops(1);
1844}
1845
1846void
1848 // Interface name.
1849 IfacePtr iface = socket_.getIface();
1850 if (iface == NULL) {
1851 isc_throw(BadValue, "unable to find interface with given index");
1852 }
1853 pkt->setIface(iface->getName());
1854 // Interface index.
1855 pkt->setIndex(socket_.ifindex_);
1856 // Local client's port (547)
1857 pkt->setLocalPort(DHCP6_CLIENT_PORT);
1858 // Server's port (548)
1859 if (options_.getRemotePort()) {
1860 pkt->setRemotePort(options_.getRemotePort());
1861 } else {
1862 pkt->setRemotePort(DHCP6_SERVER_PORT);
1863 }
1864 // Set local address.
1865 pkt->setLocalAddr(socket_.addr_);
1866 // The remote server's name or IP.
1867 pkt->setRemoteAddr(IOAddress(options_.getServerName()));
1868
1869 // only act as a relay agent when told so.
1872 if (options_.isUseRelayedV6()) {
1873 Pkt6::RelayInfo relay_info;
1874 relay_info.msg_type_ = DHCPV6_RELAY_FORW;
1875 relay_info.hop_count_ = 0;
1876 if (options_.checkMultiSubnet()) {
1878 } else {
1879 relay_info.linkaddr_ = IOAddress(socket_.addr_);
1880 }
1881 relay_info.peeraddr_ = IOAddress(socket_.addr_);
1882 relay_info.options_.insert(options_.getRelayOpts().begin(), options_.getRelayOpts().end());
1883 pkt->addRelayInfo(relay_info);
1884 }
1885}
1886
1887namespace {
1888
1889static OptionBuffer const concatenateBuffers(OptionBuffer const& a,
1890 OptionBuffer const& b) {
1891 OptionBuffer result;
1892 result.insert(result.end(), a.begin(), a.end());
1893 result.insert(result.end(), b.begin(), b.end());
1894 return result;
1895}
1896
1897static void mergeOptionIntoPacket(Pkt4Ptr const& packet,
1898 OptionPtr const& extra_option) {
1899 uint16_t const code(extra_option->getType());
1900 // If option already exists...
1901 OptionPtr const& option(packet->getOption(code));
1902 if (option) {
1903 switch (code) {
1904 // List here all the options for which we want to concatenate buffers.
1906 packet->delOption(code);
1907 packet->addOption(boost::make_shared<Option>(
1908 Option::V4, code,
1909 concatenateBuffers(option->getData(),
1910 extra_option->getData())));
1911 return;
1912 default:
1913 // For all others, add option as usual, it will result in "Option
1914 // already present in this message" error.
1915 break;
1916 }
1917 }
1918 packet->addOption(extra_option);
1919}
1920
1921} // namespace
1922
1923void
1925 // Add all extra options that the user may have specified.
1926 const dhcp::OptionCollection& extra_opts = options_.getExtraOpts();
1927 for (auto entry : extra_opts) {
1928 mergeOptionIntoPacket(pkt, entry.second);
1929 }
1930}
1931
1932void
1934 // Add all extra options that the user may have specified.
1935 const dhcp::OptionCollection& extra_opts = options_.getExtraOpts();
1936 for (auto entry : extra_opts) {
1937 pkt->addOption(entry.second);
1938 }
1939}
1940
1941} // namespace perfdhcp
1942} // 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.
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
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.
Sequential numbers generator class.
Definition: test_control.h:158
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:190
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.
boost::shared_ptr< NumberGenerator > NumberGeneratorPtr
The default generator pointer.
Definition: test_control.h:155
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:343
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:206
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.
UniformRandomIntegerGenerator number_generator_
Generate uniformly distributed integers in range of [min, max].
Definition: test_control.h:351
std::vector< uint8_t > TemplateBuffer
Packet template buffer.
Definition: test_control.h:124
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:615
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:195
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:538
static dhcp::OptionPtr factoryIana6(dhcp::Option::Universe u, uint16_t type, const dhcp::OptionBuffer &buf)
Factory function to create IA_NA option.
The OutputBuffer class is a buffer abstraction for manipulating mutable data.
Definition: buffer.h:294
size_t getLength() const
Return the length of data written in the buffer.
Definition: buffer.h:403
const void * getData() const
Return a pointer to the head of the data stored in the buffer.
Definition: buffer.h:401
@ D6O_SERVERID
Definition: dhcp6.h:22
@ D6O_CLIENTID
Definition: dhcp6.h:21
@ D6O_NAME_SERVERS
Definition: dhcp6.h:43
@ D6O_RAPID_COMMIT
Definition: dhcp6.h:34
@ D6O_IA_NA
Definition: dhcp6.h:23
@ D6O_ORO
Definition: dhcp6.h:26
@ D6O_IA_PD
Definition: dhcp6.h:45
@ D6O_DOMAIN_SEARCH
Definition: dhcp6.h:44
@ D6O_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.
ElementPtr copy(ConstElementPtr from, int level)
Copy the data up to a nesting level.
Definition: data.cc:1414
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:864
@ 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:238
@ DHCPOFFER
Definition: dhcp4.h:237
@ DHCPRELEASE
Definition: dhcp4.h:242
@ DHCPDISCOVER
Definition: dhcp4.h:236
@ DHCPACK
Definition: dhcp4.h:240
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