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