Kea 2.2.0
command_options.cc
Go to the documentation of this file.
1// Copyright (C) 2012-2021 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
12#include <dhcp/iface_mgr.h>
13#include <dhcp/duid.h>
14#include <dhcp/option.h>
16#include <util/encode/hex.h>
17#include <asiolink/io_error.h>
18
19#include <boost/lexical_cast.hpp>
20#include <boost/date_time/posix_time/posix_time.hpp>
21#include <sstream>
22#include <stdio.h>
23#include <stdlib.h>
24#include <stdint.h>
25#include <unistd.h>
26#include <fstream>
27#include <thread>
28#include <getopt.h>
29
30#ifdef HAVE_OPTRESET
31extern int optreset;
32#endif
33
34using namespace std;
35using namespace isc;
36using namespace isc::dhcp;
37
38namespace isc {
39namespace perfdhcp {
40
41// Refer to config_report so it will be embedded in the binary
43
45 : type_(ADDRESS) {
46}
47
49 : type_(lease_type) {
50}
51
52bool
53CommandOptions::LeaseType::is(const Type lease_type) const {
54 return (lease_type == type_);
55}
56
57bool
59 return (is(ADDRESS_AND_PREFIX) || (lease_type == type_));
60}
61
62void
64 type_ = lease_type;
65}
66
67void
68CommandOptions::LeaseType::fromCommandLine(const std::string& cmd_line_arg) {
69 if (cmd_line_arg == "address-only") {
70 type_ = ADDRESS;
71
72 } else if (cmd_line_arg == "prefix-only") {
73 type_ = PREFIX;
74
75 } else if (cmd_line_arg == "address-and-prefix") {
76 type_ = ADDRESS_AND_PREFIX;
77
78 } else {
79 isc_throw(isc::InvalidParameter, "value of lease-type: -e<lease-type>,"
80 " must be one of the following: 'address-only' or"
81 " 'prefix-only'");
82 }
83}
84
85std::string
87 switch (type_) {
88 case ADDRESS:
89 return ("address-only (IA_NA option added to the client's request)");
90 case PREFIX:
91 return ("prefix-only (IA_PD option added to the client's request)");
92 case ADDRESS_AND_PREFIX:
93 return ("address-and-prefix (Both IA_NA and IA_PD options added to the"
94 " client's request)");
95 default:
96 isc_throw(Unexpected, "internal error: undefined lease type code when"
97 " returning textual representation of the lease type");
98 }
99}
100
101void
103 // Default mac address used in DHCP messages
104 // if -b mac=<mac-address> was not specified
105 uint8_t mac[6] = { 0x0, 0xC, 0x1, 0x2, 0x3, 0x4 };
106
107 // Default packet drop time if -D<drop-time> parameter
108 // was not specified
109 double dt[2] = { 1., 1. };
110
111 // We don't use constructor initialization list because we
112 // will need to reset all members many times to perform unit tests
113 ipversion_ = 0;
114 exchange_mode_ = DORA_SARR;
115 lease_type_.set(LeaseType::ADDRESS);
116 rate_ = 0;
117 renew_rate_ = 0;
118 release_rate_ = 0;
119 report_delay_ = 0;
120 clean_report_ = false;
121 clean_report_separator_ = "";
122 clients_num_ = 0;
123 mac_template_.assign(mac, mac + 6);
124 duid_template_.clear();
125 base_.clear();
126 addr_unique_ = false;
127 mac_list_file_.clear();
128 mac_list_.clear();
129 relay_addr_list_file_.clear();
130 relay_addr_list_.clear();
131 multi_subnet_ = false;
132 num_request_.clear();
133 exit_wait_time_ = 0;
134 period_ = 0;
135 wait_for_elapsed_time_ = -1;
136 increased_elapsed_time_ = -1;
137 drop_time_set_ = 0;
138 drop_time_.assign(dt, dt + 2);
139 max_drop_.clear();
140 max_pdrop_.clear();
141 localname_.clear();
142 is_interface_ = false;
143 preload_ = 0;
144 local_port_ = 0;
145 remote_port_ = 0;
146 seeded_ = false;
147 seed_ = 0;
148 broadcast_ = false;
149 rapid_commit_ = false;
150 use_first_ = false;
151 template_file_.clear();
152 rnd_offset_.clear();
153 xid_offset_.clear();
154 elp_offset_ = -1;
155 sid_offset_ = -1;
156 rip_offset_ = -1;
157 diags_.clear();
158 wrapped_.clear();
159 server_name_.clear();
160 v6_relay_encapsulation_level_ = 0;
161 generateDuidTemplate();
162 extra_opts_.clear();
163 if (std::thread::hardware_concurrency() == 1) {
164 single_thread_mode_ = true;
165 } else {
166 single_thread_mode_ = false;
167 }
168 scenario_ = Scenario::BASIC;
169}
170
171bool
172CommandOptions::parse(int argc, char** const argv, bool print_cmd_line) {
173 // Reset internal variables used by getopt
174 // to eliminate undefined behavior when
175 // parsing different command lines multiple times
176
177#ifdef __GLIBC__
178 // Warning: non-portable code. This is due to a bug in glibc's
179 // getopt() which keeps internal state about an old argument vector
180 // (argc, argv) from last call and tries to scan them when a new
181 // argument vector (argc, argv) is passed. As the old vector may not
182 // be main()'s arguments, but heap allocated and may have been freed
183 // since, this becomes a use after free and results in random
184 // behavior. According to the NOTES section in glibc getopt()'s
185 // manpage, setting optind=0 resets getopt()'s state. Though this is
186 // not required in our usage of getopt(), the bug still happens
187 // unless we set optind=0.
188 //
189 // Setting optind=0 is non-portable code.
190 optind = 0;
191#else
192 optind = 1;
193#endif
194
195 // optreset is declared on BSD systems and is used to reset internal
196 // state of getopt(). When parsing command line arguments multiple
197 // times with getopt() the optreset must be set to 1 every time before
198 // parsing starts. Failing to do so will result in random behavior of
199 // getopt().
200#ifdef HAVE_OPTRESET
201 optreset = 1;
202#endif
203
204 opterr = 0;
205
206 // Reset values of class members
207 reset();
208
209 // Informs if program has been run with 'h' or 'v' option.
210 bool help_or_version_mode = initialize(argc, argv, print_cmd_line);
211 if (!help_or_version_mode) {
212 validate();
213 }
214 return (help_or_version_mode);
215}
216
217const int LONG_OPT_SCENARIO = 300;
218
219bool
220CommandOptions::initialize(int argc, char** argv, bool print_cmd_line) {
221 int opt = 0; // Subsequent options returned by getopt()
222 std::string drop_arg; // Value of -D<value>argument
223 size_t percent_loc = 0; // Location of % sign in -D<value>
224 double drop_percent = 0; // % value (1..100) in -D<value%>
225 int num_drops = 0; // Max number of drops specified in -D<value>
226 int num_req = 0; // Max number of dropped
227 // requests in -n<max-drops>
228 int offset_arg = 0; // Temporary variable holding offset arguments
229 std::string sarg; // Temporary variable for string args
230
231 std::ostringstream stream;
232 stream << "perfdhcp";
233 int num_mac_list_files = 0;
234 int num_subnet_list_files = 0;
235
236 struct option long_options[] = {
237 {"scenario", required_argument, 0, LONG_OPT_SCENARIO},
238 {0, 0, 0, 0}
239 };
240
241 // In this section we collect argument values from command line
242 // they will be tuned and validated elsewhere
243 while((opt = getopt_long(argc, argv,
244 "huv46A:r:t:R:b:n:p:d:D:l:P:a:L:N:M:s:iBc1"
245 "J:T:X:O:o:E:S:I:x:W:w:e:f:F:g:C:y:Y:",
246 long_options, NULL)) != -1) {
247 stream << " -" << static_cast<char>(opt);
248 if (optarg) {
249 stream << " " << optarg;
250 }
251 switch (opt) {
252 case '1':
253 use_first_ = true;
254 break;
255
256 // Simulate DHCPv6 relayed traffic.
257 case 'A':
258 // @todo: At the moment we only support simulating a single relay
259 // agent. In the future we should extend it to up to 32.
260 // See comment in https://github.com/isc-projects/kea/pull/22#issuecomment-243405600
261 v6_relay_encapsulation_level_ =
262 static_cast<uint8_t>(positiveInteger("-A<encapsulation-level> must"
263 " be a positive integer"));
264 if (v6_relay_encapsulation_level_ != 1) {
265 isc_throw(isc::InvalidParameter, "-A only supports 1 at the moment.");
266 }
267 break;
268
269 case 'u':
270 addr_unique_ = true;
271 break;
272
273 case '4':
274 check(ipversion_ == 6, "IP version already set to 6");
275 ipversion_ = 4;
276 break;
277
278 case '6':
279 check(ipversion_ == 4, "IP version already set to 4");
280 ipversion_ = 6;
281 break;
282
283 case 'b':
284 check(base_.size() > 3, "-b<value> already specified,"
285 " unexpected occurrence of 5th -b<value>");
286 base_.push_back(optarg);
287 decodeBase(base_.back());
288 break;
289
290 case 'B':
291 broadcast_ = true;
292 break;
293
294 case 'c':
295 rapid_commit_ = true;
296 break;
297
298 case 'C':
299 clean_report_ = true;
300 clean_report_separator_ = optarg;
301 break;
302
303 case 'd':
304 check(drop_time_set_ > 1,
305 "maximum number of drops already specified, "
306 "unexpected 3rd occurrence of -d<value>");
307 try {
308 drop_time_[drop_time_set_] =
309 boost::lexical_cast<double>(optarg);
310 } catch (const boost::bad_lexical_cast&) {
312 "value of drop time: -d<value>"
313 " must be positive number");
314 }
315 check(drop_time_[drop_time_set_] <= 0.,
316 "drop-time must be a positive number");
317 drop_time_set_ = true;
318 break;
319
320 case 'D':
321 drop_arg = std::string(optarg);
322 percent_loc = drop_arg.find('%');
323 check(max_pdrop_.size() > 1 || max_drop_.size() > 1,
324 "values of maximum drops: -D<value> already "
325 "specified, unexpected 3rd occurrence of -D<value>");
326 if ((percent_loc) != std::string::npos) {
327 try {
328 drop_percent =
329 boost::lexical_cast<double>(drop_arg.substr(0, percent_loc));
330 } catch (const boost::bad_lexical_cast&) {
332 "value of drop percentage: -D<value%>"
333 " must be 0..100");
334 }
335 check((drop_percent <= 0) || (drop_percent >= 100),
336 "value of drop percentage: -D<value%> must be 0..100");
337 max_pdrop_.push_back(drop_percent);
338 } else {
339 num_drops = positiveInteger("value of max drops number:"
340 " -D<value> must be a positive integer");
341 max_drop_.push_back(num_drops);
342 }
343 break;
344
345 case 'e':
346 initLeaseType();
347 break;
348
349 case 'E':
350 elp_offset_ = nonNegativeInteger("value of time-offset: -E<value>"
351 " must not be a negative integer");
352 break;
353
354 case 'f':
355 renew_rate_ = positiveInteger("value of the renew rate: -f<renew-rate>"
356 " must be a positive integer");
357 break;
358
359 case 'F':
360 release_rate_ = positiveInteger("value of the release rate:"
361 " -F<release-rate> must be a"
362 " positive integer");
363 break;
364
365 case 'g': {
366 auto optarg_text = std::string(optarg);
367 if (optarg_text == "single") {
368 single_thread_mode_ = true;
369 } else if (optarg_text == "multi") {
370 single_thread_mode_ = false;
371 } else {
372 isc_throw(InvalidParameter, "value of thread mode (-g) '" << optarg << "' is wrong - should be '-g single' or '-g multi'");
373 }
374 break;
375 }
376 case 'h':
377 usage();
378 return (true);
379
380 case 'i':
381 exchange_mode_ = DO_SA;
382 break;
383
384 case 'I':
385 rip_offset_ = positiveInteger("value of ip address offset:"
386 " -I<value> must be a"
387 " positive integer");
388 break;
389
390 case 'J':
391 check(num_subnet_list_files >= 1, "only one -J option can be specified");
392 num_subnet_list_files++;
393 relay_addr_list_file_ = std::string(optarg);
394 loadRelayAddr();
395 break;
396
397 case 'l':
398 localname_ = std::string(optarg);
399 initIsInterface();
400 break;
401
402 case 'L':
403 local_port_ = nonNegativeInteger("value of local port:"
404 " -L<value> must not be a"
405 " negative integer");
406 check(local_port_ >
407 static_cast<int>(std::numeric_limits<uint16_t>::max()),
408 "local-port must be lower than " +
409 boost::lexical_cast<std::string>(std::numeric_limits<uint16_t>::max()));
410 break;
411
412 case 'N':
413 remote_port_ = nonNegativeInteger("value of remote port:"
414 " -L<value> must not be a"
415 " negative integer");
416 check(remote_port_ >
417 static_cast<int>(std::numeric_limits<uint16_t>::max()),
418 "remote-port must be lower than " +
419 boost::lexical_cast<std::string>(std::numeric_limits<uint16_t>::max()));
420 break;
421
422 case 'M':
423 check(num_mac_list_files >= 1, "only one -M option can be specified");
424 num_mac_list_files++;
425 mac_list_file_ = std::string(optarg);
426 loadMacs();
427 break;
428
429 case 'W':
430 exit_wait_time_ = nonNegativeInteger("value of exist wait time: "
431 "-W<value> must not be a "
432 "negative integer");
433 break;
434
435 case 'n':
436 num_req = positiveInteger("value of num-request:"
437 " -n<value> must be a positive integer");
438 if (num_request_.size() >= 2) {
440 "value of maximum number of requests: -n<value> "
441 "already specified, unexpected 3rd occurrence"
442 " of -n<value>");
443 }
444 num_request_.push_back(num_req);
445 break;
446
447 case 'O':
448 if (rnd_offset_.size() < 2) {
449 offset_arg = positiveInteger("value of random offset: "
450 "-O<value> must be greater than 3");
451 } else {
453 "random offsets already specified,"
454 " unexpected 3rd occurrence of -O<value>");
455 }
456 check(offset_arg < 3, "value of random random-offset:"
457 " -O<value> must be greater than 3 ");
458 rnd_offset_.push_back(offset_arg);
459 break;
460 case 'o': {
461
462 // we must know how to contruct the option: whether it's v4 or v6.
463 check( (ipversion_ != 4) && (ipversion_ != 6),
464 "-4 or -6 must be explicitly specified before -o is used.");
465
466 // custom option (expected format: code,hexstring)
467 std::string opt_text = std::string(optarg);
468 size_t coma_loc = opt_text.find(',');
469 check(coma_loc == std::string::npos,
470 "-o option must provide option code, a coma and hexstring for"
471 " the option content, e.g. -o60,646f63736973 for sending option"
472 " 60 (class-id) with the value 'docsis'");
473 int code = 0;
474
475 // Try to parse the option code
476 try {
477 code = boost::lexical_cast<int>(opt_text.substr(0,coma_loc));
478 check(code <= 0, "Option code can't be negative");
479 } catch (const boost::bad_lexical_cast&) {
480 isc_throw(InvalidParameter, "Invalid option code specified for "
481 "-o option, expected format: -o<integer>,<hexstring>");
482 }
483
484 // Now try to interpret the hexstring
485 opt_text = opt_text.substr(coma_loc + 1);
486 std::vector<uint8_t> bin;
487 try {
488 isc::util::encode::decodeHex(opt_text, bin);
489 } catch (const BadValue& e) {
490 isc_throw(InvalidParameter, "Error during encoding option -o:"
491 << e.what());
492 }
493
494 // Create and remember the option.
495 OptionPtr opt(new Option(ipversion_ == 4 ? Option::V4 : Option::V6,
496 code, bin));
497 extra_opts_.insert(make_pair(code, opt));
498 break;
499 }
500 case 'p':
501 period_ = positiveInteger("value of test period:"
502 " -p<value> must be a positive integer");
503 break;
504
505 case 'P':
506 preload_ = nonNegativeInteger("number of preload packets:"
507 " -P<value> must not be "
508 "a negative integer");
509 break;
510
511 case 'r':
512 rate_ = positiveInteger("value of rate:"
513 " -r<value> must be a positive integer");
514 break;
515
516 case 'R':
517 initClientsNum();
518 break;
519
520 case 's':
521 seed_ = static_cast<unsigned int>
522 (nonNegativeInteger("value of seed:"
523 " -s <seed> must be non-negative integer"));
524 seeded_ = seed_ > 0 ? true : false;
525 break;
526
527 case 'S':
528 sid_offset_ = positiveInteger("value of server id offset:"
529 " -S<value> must be a"
530 " positive integer");
531 break;
532
533 case 't':
534 report_delay_ = positiveInteger("value of report delay:"
535 " -t<value> must be a"
536 " positive integer");
537 break;
538
539 case 'T':
540 if (template_file_.size() < 2) {
541 sarg = nonEmptyString("template file name not specified,"
542 " expected -T<filename>");
543 template_file_.push_back(sarg);
544 } else {
546 "template files are already specified,"
547 " unexpected 3rd -T<filename> occurrence");
548 }
549 break;
550
551 case 'v':
552 version();
553 return (true);
554
555 case 'w':
556 wrapped_ = nonEmptyString("command for wrapped mode:"
557 " -w<command> must be specified");
558 break;
559
560 case 'x':
561 diags_ = nonEmptyString("value of diagnostics selectors:"
562 " -x<value> must be specified");
563 break;
564
565 case 'X':
566 if (xid_offset_.size() < 2) {
567 offset_arg = positiveInteger("value of transaction id:"
568 " -X<value> must be a"
569 " positive integer");
570 } else {
572 "transaction ids already specified,"
573 " unexpected 3rd -X<value> occurrence");
574 }
575 xid_offset_.push_back(offset_arg);
576 break;
577
578 case 'Y':
579 wait_for_elapsed_time_ = nonNegativeInteger("value of time:"
580 " -Y<value> must be a non negative integer");
581 break;
582
583 case 'y':
584 increased_elapsed_time_ = positiveInteger("value of time:"
585 " -y<value> must be a positive integer");
586 break;
587
588 case LONG_OPT_SCENARIO: {
589 auto optarg_text = std::string(optarg);
590 if (optarg_text == "basic") {
591 scenario_ = Scenario::BASIC;
592 } else if (optarg_text == "avalanche") {
593 scenario_ = Scenario::AVALANCHE;
594 } else {
595 isc_throw(InvalidParameter, "scenario value '" << optarg << "' is wrong - should be 'basic' or 'avalanche'");
596 }
597 break;
598 }
599 default:
600 isc_throw(isc::InvalidParameter, "wrong command line option");
601 }
602 }
603
604 // If the IP version was not specified in the
605 // command line, assume IPv4.
606 if (ipversion_ == 0) {
607 ipversion_ = 4;
608 }
609
610 // If template packet files specified for both DISCOVER/SOLICIT
611 // and REQUEST/REPLY exchanges make sure we have transaction id
612 // and random duid offsets for both exchanges. We will duplicate
613 // value specified as -X<value> and -R<value> for second
614 // exchange if user did not specified otherwise.
615 if (template_file_.size() > 1) {
616 if (xid_offset_.size() == 1) {
617 xid_offset_.push_back(xid_offset_[0]);
618 }
619 if (rnd_offset_.size() == 1) {
620 rnd_offset_.push_back(rnd_offset_[0]);
621 }
622 }
623
624 // Get server argument
625 // NoteFF02::1:2 and FF02::1:3 are defined in RFC 8415 as
626 // All_DHCP_Relay_Agents_and_Servers and All_DHCP_Servers
627 // addresses
628 check(optind < argc -1, "extra arguments?");
629 if (optind == argc - 1) {
630 server_name_ = argv[optind];
631 stream << " " << server_name_;
632 // Decode special cases
633 if ((ipversion_ == 4) && (server_name_.compare("all") == 0)) {
634 broadcast_ = true;
635 // Use broadcast address as server name.
636 server_name_ = DHCP_IPV4_BROADCAST_ADDRESS;
637 } else if ((ipversion_ == 6) && (server_name_.compare("all") == 0)) {
639 } else if ((ipversion_ == 6) &&
640 (server_name_.compare("servers") == 0)) {
641 server_name_ = ALL_DHCP_SERVERS;
642 }
643 }
644 if (!getCleanReport()) {
645 if (print_cmd_line) {
646 std::cout << "Running: " << stream.str() << std::endl;
647 }
648
649 if (scenario_ == Scenario::BASIC) {
650 std::cout << "Scenario: basic." << std::endl;
651 } else if (scenario_ == Scenario::AVALANCHE) {
652 std::cout << "Scenario: avalanche." << std::endl;
653 }
654
655 if (!isSingleThreaded()) {
656 std::cout << "Multi-thread mode enabled." << std::endl;
657 }
658 }
659
660 // Handle the local '-l' address/interface
661 if (!localname_.empty()) {
662 if (server_name_.empty()) {
663 if (is_interface_ && (ipversion_ == 4)) {
664 broadcast_ = true;
665 server_name_ = DHCP_IPV4_BROADCAST_ADDRESS;
666 } else if (is_interface_ && (ipversion_ == 6)) {
668 }
669 }
670 }
671 if (server_name_.empty()) {
673 "without an interface, server is required");
674 }
675
676 // If DUID is not specified from command line we need to
677 // generate one.
678 if (duid_template_.empty()) {
679 generateDuidTemplate();
680 }
681 return (false);
682}
683
684void
685CommandOptions::initClientsNum() {
686 const std::string errmsg =
687 "value of -R <value> must be non-negative integer";
688
689 try {
690 // Declare clients_num as a 64-bit signed value to
691 // be able to detect negative values provided
692 // by user. We would not detect negative values
693 // if we casted directly to unsigned value.
694 long long clients_num = boost::lexical_cast<long long>(optarg);
695 check(clients_num < 0, errmsg);
696 clients_num_ = boost::lexical_cast<uint32_t>(optarg);
697 } catch (const boost::bad_lexical_cast&) {
699 }
700}
701
702void
703CommandOptions::initIsInterface() {
704 is_interface_ = false;
705 if (!localname_.empty()) {
707 if (iface_mgr.getIface(localname_) != NULL) {
708 is_interface_ = true;
709 }
710 }
711}
712
713void
714CommandOptions::decodeBase(const std::string& base) {
715 std::string b(base);
716 boost::algorithm::to_lower(b);
717
718 // Currently we only support mac and duid
719 if ((b.substr(0, 4) == "mac=") || (b.substr(0, 6) == "ether=")) {
720 decodeMacBase(b);
721 } else if (b.substr(0, 5) == "duid=") {
722 decodeDuid(b);
723 } else {
725 "base value not provided as -b<value>,"
726 " expected -b mac=<mac> or -b duid=<duid>");
727 }
728}
729
730void
731CommandOptions::decodeMacBase(const std::string& base) {
732 // Strip string from mac=
733 size_t found = base.find('=');
734 static const char* errmsg = "expected -b<base> format for"
735 " mac address is -b mac=00::0C::01::02::03::04 or"
736 " -b mac=00:0C:01:02:03:04";
737 check(found == std::string::npos, errmsg);
738
739 // Decode mac address to vector of uint8_t
740 std::istringstream s1(base.substr(found + 1));
741 std::string token;
742 mac_template_.clear();
743 // Get pieces of MAC address separated with : (or even ::)
744 while (std::getline(s1, token, ':')) {
745 // Convert token to byte value using std::istringstream
746 if (token.length() > 0) {
747 unsigned int ui = 0;
748 try {
749 // Do actual conversion
750 ui = convertHexString(token);
751 } catch (const isc::InvalidParameter&) {
753 "invalid characters in MAC provided");
754
755 }
756 // If conversion succeeded store byte value
757 mac_template_.push_back(ui);
758 }
759 }
760 // MAC address must consist of 6 octets, otherwise it is invalid
761 check(mac_template_.size() != 6, errmsg);
762}
763
764void
765CommandOptions::decodeDuid(const std::string& base) {
766 // Strip argument from duid=
767 std::vector<uint8_t> duid_template;
768 size_t found = base.find('=');
769 check(found == std::string::npos, "expected -b<base>"
770 " format for duid is -b duid=<duid>");
771 std::string b = base.substr(found + 1);
772
773 // DUID must have even number of digits and must not be longer than 64 bytes
774 check(b.length() & 1, "odd number of hexadecimal digits in duid");
775 check(b.length() > 128, "duid too large");
776 check(b.length() == 0, "no duid specified");
777
778 // Turn pairs of hexadecimal digits into vector of octets
779 for (size_t i = 0; i < b.length(); i += 2) {
780 unsigned int ui = 0;
781 try {
782 // Do actual conversion
783 ui = convertHexString(b.substr(i, 2));
784 } catch (const isc::InvalidParameter&) {
786 "invalid characters in DUID provided,"
787 " expected hex digits");
788 }
789 duid_template.push_back(static_cast<uint8_t>(ui));
790 }
791 // @todo Get rid of this limitation when we manage add support
792 // for DUIDs other than LLT. Shorter DUIDs may be useful for
793 // server testing purposes.
794 check(duid_template.size() < 6, "DUID must be at least 6 octets long");
795 // Assign the new duid only if successfully generated.
796 std::swap(duid_template, duid_template_);
797}
798
799void
800CommandOptions::generateDuidTemplate() {
801 using namespace boost::posix_time;
802 // Duid template will be most likely generated only once but
803 // it is ok if it is called more then once so we simply
804 // regenerate it and discard previous value.
805 duid_template_.clear();
806 const uint8_t duid_template_len = 14;
807 duid_template_.resize(duid_template_len);
808 // The first four octets consist of DUID LLT and hardware type.
809 duid_template_[0] = static_cast<uint8_t>(static_cast<uint16_t>(isc::dhcp::DUID::DUID_LLT) >> 8);
810 duid_template_[1] = static_cast<uint8_t>(static_cast<uint16_t>(isc::dhcp::DUID::DUID_LLT) & 0xff);
811 duid_template_[2] = HWTYPE_ETHERNET >> 8;
812 duid_template_[3] = HWTYPE_ETHERNET & 0xff;
813
814 // As described in RFC 8415: 'the time value is the time
815 // that the DUID is generated represented in seconds
816 // since midnight (UTC), January 1, 2000, modulo 2^32.'
817 ptime now = microsec_clock::universal_time();
818 ptime duid_epoch(from_iso_string("20000101T000000"));
819 time_period period(duid_epoch, now);
820 uint32_t duration_sec = htonl(period.length().total_seconds());
821 memcpy(&duid_template_[4], &duration_sec, 4);
822
823 // Set link layer address (6 octets). This value may be
824 // randomized before sending a packet to simulate different
825 // clients.
826 memcpy(&duid_template_[8], &mac_template_[0], 6);
827}
828
829uint8_t
830CommandOptions::convertHexString(const std::string& text) const {
831 unsigned int ui = 0;
832 // First, check if we are dealing with hexadecimal digits only
833 for (size_t i = 0; i < text.length(); ++i) {
834 if (!std::isxdigit(text[i])) {
836 "The following digit: " << text[i] << " in "
837 << text << "is not hexadecimal");
838 }
839 }
840 // If we are here, we have valid string to convert to octet
841 std::istringstream text_stream(text);
842 text_stream >> std::hex >> ui >> std::dec;
843 // Check if for some reason we have overflow - this should never happen!
844 if (ui > 0xFF) {
845 isc_throw(isc::InvalidParameter, "Can't convert more than"
846 " two hex digits to byte");
847 }
848 return ui;
849}
850
851bool CommandOptions::validateIP(const std::string& line) {
852 try {
853 isc::asiolink::IOAddress ip_address(line);
854 if ((getIpVersion() == 4 && !ip_address.isV4()) ||
855 (getIpVersion() == 6 && !ip_address.isV6())) {
856 return (true);
857 }
858 } catch (const isc::asiolink::IOError& e) {
859 return (true);
860 }
861 relay_addr_list_.push_back(line);
862 multi_subnet_ = true;
863 return (false);
864}
865
866void CommandOptions::loadRelayAddr() {
867 std::string line;
868 std::ifstream infile(relay_addr_list_file_.c_str());
869 size_t cnt = 0;
870 while (std::getline(infile, line)) {
871 cnt++;
872 stringstream tmp;
873 tmp << "invalid address or wrong address version in line: " << cnt;
874 check(validateIP(line), tmp.str());
875 }
876 check(cnt == 0, "file with addresses is empty!");
877}
878
879void CommandOptions::loadMacs() {
880 std::string line;
881 std::ifstream infile(mac_list_file_.c_str());
882 size_t cnt = 0;
883 while (std::getline(infile, line)) {
884 cnt++;
885 stringstream tmp;
886 tmp << "invalid mac in input line " << cnt;
887 // Let's print more meaningful error that contains line with error.
888 check(decodeMacString(line), tmp.str());
889 }
890}
891
892bool CommandOptions::decodeMacString(const std::string& line) {
893 // decode mac string into a vector of uint8_t returns true in case of error.
894 std::istringstream s(line);
895 std::string token;
896 std::vector<uint8_t> mac;
897 while(std::getline(s, token, ':')) {
898 // Convert token to byte value using std::istringstream
899 if (token.length() > 0) {
900 unsigned int ui = 0;
901 try {
902 // Do actual conversion
903 ui = convertHexString(token);
904 } catch (const isc::InvalidParameter&) {
905 return (true);
906 }
907 // If conversion succeeded store byte value
908 mac.push_back(ui);
909 }
910 }
911 mac_list_.push_back(mac);
912 return (false);
913}
914
915void
916CommandOptions::validate() {
917 check((getIpVersion() != 4) && (isBroadcast() != 0),
918 "-B is not compatible with IPv6 (-6)");
919 check((getIpVersion() != 6) && (isRapidCommit() != 0),
920 "-6 (IPv6) must be set to use -c");
921 check(getIpVersion() == 4 && isUseRelayedV6(),
922 "Can't use -4 with -A, it's a V6 only option.");
923 check((getExchangeMode() == DO_SA) && (getNumRequests().size() > 1),
924 "second -n<num-request> is not compatible with -i");
925 check((getIpVersion() == 4) && !getLeaseType().is(LeaseType::ADDRESS),
926 "-6 option must be used if lease type other than '-e address-only'"
927 " is specified");
928 check(!getTemplateFiles().empty() &&
930 "template files may be only used with '-e address-only'");
931 check((getExchangeMode() == DO_SA) && (getDropTime()[1] != 1.),
932 "second -d<drop-time> is not compatible with -i");
933 check((getExchangeMode() == DO_SA) &&
934 ((getMaxDrop().size() > 1) || (getMaxDropPercentage().size() > 1)),
935 "second -D<max-drop> is not compatible with -i");
936 check((getExchangeMode() == DO_SA) && (isUseFirst()),
937 "-1 is not compatible with -i");
938 check((getExchangeMode() == DO_SA) && (getTemplateFiles().size() > 1),
939 "second -T<template-file> is not compatible with -i");
940 check((getExchangeMode() == DO_SA) && (getTransactionIdOffset().size() > 1),
941 "second -X<xid-offset> is not compatible with -i");
942 check((getExchangeMode() == DO_SA) && (getRandomOffset().size() > 1),
943 "second -O<random-offset is not compatible with -i");
944 check((getExchangeMode() == DO_SA) && (getElapsedTimeOffset() >= 0),
945 "-E<time-offset> is not compatible with -i");
946 check((getExchangeMode() == DO_SA) && (getServerIdOffset() >= 0),
947 "-S<srvid-offset> is not compatible with -i");
948 check((getExchangeMode() == DO_SA) && (getRequestedIpOffset() >= 0),
949 "-I<ip-offset> is not compatible with -i");
950 check((getExchangeMode() == DO_SA) && (getRenewRate() != 0),
951 "-f<renew-rate> is not compatible with -i");
952 check((getExchangeMode() == DO_SA) && (getReleaseRate() != 0),
953 "-F<release-rate> is not compatible with -i");
954 check((getExchangeMode() != DO_SA) && (isRapidCommit() != 0),
955 "-i must be set to use -c");
956 check((getRate() != 0) && (getRenewRate() + getReleaseRate() > getRate()),
957 "The sum of Renew rate (-f<renew-rate>) and Release rate"
958 " (-F<release-rate>) must not be greater than the exchange"
959 " rate specified as -r<rate>");
960 check((getRate() == 0) && (getRenewRate() != 0),
961 "Renew rate specified as -f<renew-rate> must not be specified"
962 " when -r<rate> parameter is not specified");
963 check((getRate() == 0) && (getReleaseRate() != 0),
964 "Release rate specified as -F<release-rate> must not be specified"
965 " when -r<rate> parameter is not specified");
966 check((getTemplateFiles().size() < getTransactionIdOffset().size()),
967 "-T<template-file> must be set to use -X<xid-offset>");
968 check((getTemplateFiles().size() < getRandomOffset().size()),
969 "-T<template-file> must be set to use -O<random-offset>");
970 check((getTemplateFiles().size() < 2) && (getElapsedTimeOffset() >= 0),
971 "second/request -T<template-file> must be set to use -E<time-offset>");
972 check((getTemplateFiles().size() < 2) && (getServerIdOffset() >= 0),
973 "second/request -T<template-file> must be set to "
974 "use -S<srvid-offset>");
975 check((getTemplateFiles().size() < 2) && (getRequestedIpOffset() >= 0),
976 "second/request -T<template-file> must be set to "
977 "use -I<ip-offset>");
978 check((!getMacListFile().empty() && base_.size() > 0),
979 "Can't use -b with -M option");
980 check((getWaitForElapsedTime() == -1 && getIncreaseElapsedTime() != -1),
981 "Option -y can't be used without -Y");
982 check((getWaitForElapsedTime() != -1 && getIncreaseElapsedTime() == -1),
983 "Option -Y can't be used without -y");
984 auto nthreads = std::thread::hardware_concurrency();
985 if (nthreads == 1 && isSingleThreaded() == false) {
986 std::cout << "WARNING: Currently system can run only 1 thread in parallel." << std::endl
987 << "WARNING: Better results are achieved when run in single-threaded mode." << std::endl
988 << "WARNING: To switch use -g single option." << std::endl;
989 } else if (nthreads > 1 && isSingleThreaded()) {
990 std::cout << "WARNING: Currently system can run more than 1 thread in parallel." << std::endl
991 << "WARNING: Better results are achieved when run in multi-threaded mode." << std::endl
992 << "WARNING: To switch use -g multi option." << std::endl;
993 }
994
995 if (scenario_ == Scenario::AVALANCHE) {
996 check(getClientsNum() <= 0,
997 "in case of avalanche scenario number\nof clients must be specified"
998 " using -R option explicitly");
999
1000 // in case of AVALANCHE drops ie. long responses should not be observed by perfdhcp
1001 double dt[2] = { 1000.0, 1000.0 };
1002 drop_time_.assign(dt, dt + 2);
1003 if (drop_time_set_) {
1004 std::cout << "INFO: in avalanche scenario drop time is ignored" << std::endl;
1005 }
1006 }
1007}
1008
1009void
1010CommandOptions::check(bool condition, const std::string& errmsg) const {
1011 // The same could have been done with macro or just if statement but
1012 // we prefer functions to macros here
1013 std::ostringstream stream;
1014 stream << errmsg << "\n";
1015 if (condition) {
1017 }
1018}
1019
1020int
1021CommandOptions::positiveInteger(const std::string& errmsg) const {
1022 try {
1023 int value = boost::lexical_cast<int>(optarg);
1024 check(value <= 0, errmsg);
1025 return (value);
1026 } catch (const boost::bad_lexical_cast&) {
1027 isc_throw(InvalidParameter, errmsg);
1028 }
1029}
1030
1031int
1032CommandOptions::nonNegativeInteger(const std::string& errmsg) const {
1033 try {
1034 int value = boost::lexical_cast<int>(optarg);
1035 check(value < 0, errmsg);
1036 return (value);
1037 } catch (const boost::bad_lexical_cast&) {
1038 isc_throw(InvalidParameter, errmsg);
1039 }
1040}
1041
1042std::string
1043CommandOptions::nonEmptyString(const std::string& errmsg) const {
1044 std::string sarg = optarg;
1045 if (sarg.length() == 0) {
1047 }
1048 return sarg;
1049}
1050
1051void
1052CommandOptions::initLeaseType() {
1053 std::string lease_type_arg = optarg;
1054 lease_type_.fromCommandLine(lease_type_arg);
1055}
1056
1057void
1059 std::cout << "IPv" << static_cast<int>(ipversion_) << std::endl;
1060 if (exchange_mode_ == DO_SA) {
1061 if (ipversion_ == 4) {
1062 std::cout << "DISCOVER-OFFER only" << std::endl;
1063 } else {
1064 std::cout << "SOLICIT-ADVERTISE only" << std::endl;
1065 }
1066 }
1067 std::cout << "lease-type=" << getLeaseType().toText() << std::endl;
1068 if (rate_ != 0) {
1069 std::cout << "rate[1/s]=" << rate_ << std::endl;
1070 }
1071 if (getRenewRate() != 0) {
1072 std::cout << "renew-rate[1/s]=" << getRenewRate() << std::endl;
1073 }
1074 if (getReleaseRate() != 0) {
1075 std::cout << "release-rate[1/s]=" << getReleaseRate() << std::endl;
1076 }
1077 if (report_delay_ != 0) {
1078 std::cout << "report[s]=" << report_delay_ << std::endl;
1079 }
1080 if (clients_num_ != 0) {
1081 std::cout << "clients=" << clients_num_ << std::endl;
1082 }
1083 for (size_t i = 0; i < base_.size(); ++i) {
1084 std::cout << "base[" << i << "]=" << base_[i] << std::endl;
1085 }
1086 for (size_t i = 0; i < num_request_.size(); ++i) {
1087 std::cout << "num-request[" << i << "]=" << num_request_[i] << std::endl;
1088 }
1089 if (period_ != 0) {
1090 std::cout << "test-period=" << period_ << std::endl;
1091 }
1092 for (size_t i = 0; i < drop_time_.size(); ++i) {
1093 std::cout << "drop-time[" << i << "]=" << drop_time_[i] << std::endl;
1094 }
1095 for (size_t i = 0; i < max_drop_.size(); ++i) {
1096 std::cout << "max-drop{" << i << "]=" << max_drop_[i] << std::endl;
1097 }
1098 for (size_t i = 0; i < max_pdrop_.size(); ++i) {
1099 std::cout << "max-pdrop{" << i << "]=" << max_pdrop_[i] << std::endl;
1100 }
1101 if (preload_ != 0) {
1102 std::cout << "preload=" << preload_ << std::endl;
1103 }
1104 if (getLocalPort() != 0) {
1105 std::cout << "local-port=" << local_port_ << std::endl;
1106 }
1107 if (getRemotePort() != 0) {
1108 std::cout << "remote-port=" << remote_port_ << std::endl;
1109 }
1110 if (seeded_) {
1111 std::cout << "seed=" << seed_ << std::endl;
1112 }
1113 if (broadcast_) {
1114 std::cout << "broadcast" << std::endl;
1115 }
1116 if (rapid_commit_) {
1117 std::cout << "rapid-commit" << std::endl;
1118 }
1119 if (use_first_) {
1120 std::cout << "use-first" << std::endl;
1121 }
1122 if (!mac_list_file_.empty()) {
1123 std::cout << "mac-list-file=" << mac_list_file_ << std::endl;
1124 }
1125 for (size_t i = 0; i < template_file_.size(); ++i) {
1126 std::cout << "template-file[" << i << "]=" << template_file_[i] << std::endl;
1127 }
1128 for (size_t i = 0; i < xid_offset_.size(); ++i) {
1129 std::cout << "xid-offset[" << i << "]=" << xid_offset_[i] << std::endl;
1130 }
1131 if (elp_offset_ != 0) {
1132 std::cout << "elp-offset=" << elp_offset_ << std::endl;
1133 }
1134 for (size_t i = 0; i < rnd_offset_.size(); ++i) {
1135 std::cout << "rnd-offset[" << i << "]=" << rnd_offset_[i] << std::endl;
1136 }
1137 if (sid_offset_ != 0) {
1138 std::cout << "sid-offset=" << sid_offset_ << std::endl;
1139 }
1140 if (rip_offset_ != 0) {
1141 std::cout << "rip-offset=" << rip_offset_ << std::endl;
1142 }
1143 if (!diags_.empty()) {
1144 std::cout << "diagnostic-selectors=" << diags_ << std::endl;
1145 }
1146 if (!wrapped_.empty()) {
1147 std::cout << "wrapped=" << wrapped_ << std::endl;
1148 }
1149 if (!localname_.empty()) {
1150 if (is_interface_) {
1151 std::cout << "interface=" << localname_ << std::endl;
1152 } else {
1153 std::cout << "local-addr=" << localname_ << std::endl;
1154 }
1155 }
1156 if (!server_name_.empty()) {
1157 std::cout << "server=" << server_name_ << std::endl;
1158 }
1159 if (single_thread_mode_) {
1160 std::cout << "single-thread-mode" << std::endl;
1161 } else {
1162 std::cout << "multi-thread-mode" << std::endl;
1163 }
1164}
1165
1166void
1168 std::cout <<
1169R"(perfdhcp [-1] [-4 | -6] [-A encapsulation-level] [-b base] [-B] [-c]
1170 [-C separator] [-d drop-time] [-D max-drop] [-e lease-type]
1171 [-E time-offset] [-f renew-rate] [-F release-rate] [-g thread-mode]
1172 [-h] [-i] [-I ip-offset] [-J remote-address-list-file]
1173 [-l local-address|interface] [-L local-port] [-M mac-list-file]
1174 [-n num-request] [-N remote-port] [-O random-offset]
1175 [-o code,hexstring] [-p test-period] [-P preload] [-r rate]
1176 [-R num-clients] [-s seed] [-S srvid-offset] [--scenario name]
1177 [-t report] [-T template-file] [-u] [-v] [-W exit-wait-time]
1178 [-w script_name] [-x diagnostic-selector] [-X xid-offset] [server]
1179
1180The [server] argument is the name/address of the DHCP server to
1181contact. For DHCPv4 operation, exchanges are initiated by
1182transmitting a DHCP DISCOVER to this address.
1183
1184For DHCPv6 operation, exchanges are initiated by transmitting a DHCP
1185SOLICIT to this address. In the DHCPv6 case, the special name 'all'
1186can be used to refer to All_DHCP_Relay_Agents_and_Servers (the
1187multicast address FF02::1:2), or the special name 'servers' to refer
1188to All_DHCP_Servers (the multicast address FF05::1:3). The [server]
1189argument is optional only in the case that -l is used to specify an
1190interface, in which case [server] defaults to 'all'.
1191
1192The default is to perform a single 4-way exchange, effectively pinging
1193the server.
1194The -r option is used to set up a performance test, without
1195it exchanges are initiated as fast as possible.
1196The other scenario is an avalanche which is selected by
1197--scenario avalanche. It first sends as many Discovery or Solicit
1198messages as request in -R option then back off mechanism is used for
1199each simulated client until all requests are answered. At the end
1200time of whole scenario is reported.
1201
1202Options:
1203-1: Take the server-ID option from the first received message.
1204-4: DHCPv4 operation (default). This is incompatible with the -6 option.
1205-6: DHCPv6 operation. This is incompatible with the -4 option.
1206-b<base>: The base mac, duid, IP, etc, used to simulate different
1207 clients. This can be specified multiple times, each instance is
1208 in the <type>=<value> form, for instance:
1209 (and default) mac=00:0c:01:02:03:04.
1210-d<drop-time>: Specify the time after which a request is treated as
1211 having been lost. The value is given in seconds and may contain a
1212 fractional component. The default is 1 second.
1213-e<lease-type>: A type of lease being requested from the server. It
1214 may be one of the following: address-only, prefix-only or
1215 address-and-prefix. The address-only indicates that the regular
1216 address (v4 or v6) will be requested. The prefix-only indicates
1217 that the IPv6 prefix will be requested. The address-and-prefix
1218 indicates that both IPv6 address and prefix will be requested.
1219 The '-e prefix-only' and -'e address-and-prefix' must not be
1220 used with -4.
1221-E<time-offset>: Offset of the (DHCPv4) secs field / (DHCPv6)
1222 elapsed-time option in the (second/request) template.
1223 The value 0 disables it.
1224-F<release-rate>: Rate at which Release requests are sent to
1225 a server. This value is only valid when used in conjunction with
1226 the exchange rate (given by -r<rate>). Furthermore the sum of
1227 this value and the renew-rate (given by -f<rate>) must be equal
1228 to or less than the exchange rate.
1229-f<renew-rate>: Rate at which DHCPv4 or DHCPv6 renew requests are sent
1230 to a server. This value is only valid when used in conjunction
1231 with the exchange rate (given by -r<rate>). Furthermore the sum of
1232 this value and the release-rate (given by -F<rate>) must be equal
1233 to or less than the exchange rate.
1234-g<thread-mode>: 'single' or 'multi'. In multi-thread mode packets
1235 are received in separate thread. This allows better utilisation of CPUs.
1236 If more than 1 CPU is present then multi-thread mode is the default,
1237 otherwise single-thread is the default.
1238-h: Print this help.
1239-i: Do only the initial part of an exchange: DO or SA, depending on
1240 whether -6 is given.
1241-I<ip-offset>: Offset of the (DHCPv4) IP address in the requested-IP
1242 option / (DHCPv6) IA_NA option in the (second/request) template.
1243-J<remote-address-list-file>: Text file that include multiple addresses.
1244 If provided perfdhcp will choose randomly one of addresses for each
1245 exchange.
1246-l<local-addr|interface>: For DHCPv4 operation, specify the local
1247 hostname/address to use when communicating with the server. By
1248 default, the interface address through which traffic would
1249 normally be routed to the server is used.
1250 For DHCPv6 operation, specify the name of the network interface
1251 via which exchanges are initiated.
1252-L<local-port>: Specify the local port to use
1253 (the value 0 means to use the default).
1254-M<mac-list-file>: A text file containing a list of MAC addresses,
1255 one per line. If provided, a MAC address will be chosen randomly
1256 from this list for every new exchange. In the DHCPv6 case, MAC
1257 addresses are used to generate DUID-LLs. This parameter must not be
1258 used in conjunction with the -b parameter.
1259-N<remote-port>: Specify the remote port to use
1260 (the value 0 means to use the default).
1261-o<code,hexstring>: Send custom option with the specified code and the
1262 specified buffer in hexstring format.
1263-O<random-offset>: Offset of the last octet to randomize in the template.
1264-P<preload>: Initiate first <preload> exchanges back to back at startup.
1265-r<rate>: Initiate <rate> DORA/SARR (or if -i is given, DO/SA)
1266 exchanges per second. A periodic report is generated showing the
1267 number of exchanges which were not completed, as well as the
1268 average response latency. The program continues until
1269 interrupted, at which point a final report is generated.
1270-R<range>: Specify how many different clients are used. With 1
1271 (the default), all requests seem to come from the same client.
1272-s<seed>: Specify the seed for randomization, making it repeatable.
1273--scenario <name>: where name is 'basic' (default) or 'avalanche'.
1274-S<srvid-offset>: Offset of the server-ID option in the
1275 (second/request) template.
1276-T<template-file>: The name of a file containing the template to use
1277 as a stream of hexadecimal digits.
1278-u: Enable checking address uniqueness. Lease valid lifetime should not be
1279 shorter than test duration and clients should not request address more than
1280 once without releasing it first.
1281-v: Report the version number of this program.
1282-W<time>: Specifies exit-wait-time parameter, that makes perfdhcp wait
1283 for <time> us after an exit condition has been met to receive all
1284 packets without sending any new packets. Expressed in microseconds.
1285-w<wrapped>: Command to call with start/stop at the beginning/end of
1286 the program.
1287-x<diagnostic-selector>: Include extended diagnostics in the output.
1288 <diagnostic-selector> is a string of single-keywords specifying
1289 the operations for which verbose output is desired. The selector
1290 key letters are:
1291 * 'a': print the decoded command line arguments
1292 * 'e': print the exit reason
1293 * 'i': print rate processing details
1294 * 'l': print received leases
1295 * 's': print first server-id
1296 * 't': when finished, print timers of all successful exchanges
1297 * 'T': when finished, print templates
1298-X<xid-offset>: Transaction ID (aka. xid) offset in the template.
1299-Y<time>: time in seconds after which perfdhcp will start sending
1300 messages with increased elapsed time option.
1301-y<time>: period of time in seconds in which perfdhcp will be sending
1302 messages with increased elapsed time option.
1303DHCPv4 only options:
1304-B: Force broadcast handling.
1305
1306DHCPv6 only options:
1307-c: Add a rapid commit option (exchanges will be SA).
1308-A<encapsulation-level>: Specifies that relayed traffic must be
1309 generated. The argument specifies the level of encapsulation, i.e.
1310 how many relay agents are simulated. Currently the only supported
1311 <encapsulation-level> value is 1, which means that the generated
1312 traffic is an equivalent of the traffic passing through a single
1313 relay agent.
1314
1315The remaining options are typically used in conjunction with -r:
1316
1317-D<max-drop>: Abort the test immediately if max-drop requests have
1318 been dropped. max-drop must be a positive integer. If max-drop
1319 includes the suffix '%', it specifies a maximum percentage of
1320 requests that may be dropped before abort. In this case, testing
1321 of the threshold begins after 10 requests have been expected to
1322 be received.
1323-n<num-request>: Initiate <num-request> transactions. No report is
1324 generated until all transactions have been initiated/waited-for,
1325 after which a report is generated and the program terminates.
1326-p<test-period>: Send requests for the given test period, which is
1327 specified in the same manner as -d. This can be used as an
1328 alternative to -n, or both options can be given, in which case the
1329 testing is completed when either limit is reached.
1330-t<report>: Delay in seconds between two periodic reports.
1331-C<separator>: Output reduced, an argument is a separator for periodic
1332 (-t) reports generated in easy parsable mode. Data output won't be
1333 changed, remain identical as in -t option.
1334
1335Errors:
1336- tooshort: received a too short message
1337- orphans: received a message which doesn't match an exchange
1338 (duplicate, late or not related)
1339- locallimit: reached to local system limits when sending a message.
1340
1341Exit status:
1342The exit status is:
13430 on complete success.
13441 for a general error.
13452 if an error is found in the command line arguments.
13463 if there are no general failures in operation, but one or more
1347 exchanges are not successfully completed.
1348)";
1349}
1350
1351void
1353 std::cout << "VERSION: " << VERSION << std::endl;
1354}
1355
1356
1357} // namespace perfdhcp
1358} // namespace isc
A generic exception that is thrown if a parameter given to a method is considered invalid in that con...
virtual const char * what() const
Returns a C-style character string of the cause of the exception.
A generic exception that is thrown if a parameter given to a method or function is considered invalid...
A generic exception that is thrown when an unexpected error condition occurs.
@ DUID_LLT
link-layer + time, see RFC3315, section 11.2
Definition: duid.h:40
Handles network interfaces, transmission and reception.
Definition: iface_mgr.h:656
static IfaceMgr & instance()
IfaceMgr is a singleton class.
Definition: iface_mgr.cc:53
IfacePtr getIface(int ifindex)
Returns interface specified interface index.
Definition: iface_mgr.cc:902
void set(const Type lease_type)
Sets the lease type code.
void fromCommandLine(const std::string &cmd_line_arg)
Sets the lease type from the command line argument.
bool is(const Type lease_type) const
Checks if lease type has the specified code.
std::string toText() const
Return textual representation of the lease type.
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...
int getIncreaseElapsedTime() const
Returns increased elapsed time.
int getServerIdOffset() const
Returns template offset for server-ID.
bool isSingleThreaded() const
Check if single-threaded mode is enabled.
int getRenewRate() const
Returns a rate at which DHCPv6 Renew messages are sent.
uint8_t getIpVersion() const
Returns IP version.
int getRate() const
Returns exchange rate.
void version() const
Print program version.
bool isRapidCommit() const
Check if rapid commit option used.
bool isUseFirst() const
Check if server-ID to be taken from first package.
int getLocalPort() const
Returns local port number.
std::string getMacListFile() const
Returns location of the file containing list of MAC addresses.
std::vector< int > getRandomOffset() const
Returns template offsets for rnd.
std::vector< double > getMaxDropPercentage() const
Returns maximal percentage of drops.
int getRemotePort() const
Returns remote port number.
int getWaitForElapsedTime() const
Returns time to wait for elapsed time increase.
bool isBroadcast() const
Checks if broadcast address is to be used.
std::vector< std::string > getTemplateFiles() const
Returns template file names.
std::vector< int > getTransactionIdOffset() const
brief Returns template offsets for xid.
int getElapsedTimeOffset() const
Returns template offset for elapsed time.
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.
uint32_t getClientsNum() const
Returns number of simulated clients.
void reset()
Reset to defaults.
std::vector< double > getDropTime() const
Returns drop time.
std::vector< int > getMaxDrop() const
Returns maximum drops number.
static void usage()
Print usage.
bool parse(int argc, char **const argv, bool print_cmd_line=false)
Parse command line.
int getReleaseRate() const
Returns a rate at which DHCPv6 Release messages are sent.
ExchangeMode getExchangeMode() const
Returns packet exchange mode.
void printCommandLine() const
Print command line arguments.
int getCleanReport() const
Returns clean report mode.
int getRequestedIpOffset() const
Returns template offset for requested IP.
#define DHCP_IPV4_BROADCAST_ADDRESS
Definition: dhcp4.h:42
#define ALL_DHCP_SERVERS
Definition: dhcp6.h:296
#define ALL_DHCP_RELAY_AGENTS_AND_SERVERS
Definition: dhcp6.h:295
#define isc_throw(type, stream)
A shortcut macro to insert known values into exception arguments.
const char *const config_report[]
Definition: config_report.h:15
boost::shared_ptr< Option > OptionPtr
Definition: option.h:36
const char *const * perfdhcp_config_report
const int LONG_OPT_SCENARIO
void decodeHex(const string &input, vector< uint8_t > &result)
Decode a text encoded in the base16 ('hex') format into the original data.
Definition: base_n.cc:474
Defines the logger used by the top-level component of kea-lfc.