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